diff options
80 files changed, 1304 insertions, 1185 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 460906817e..98b78592e2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -84,9 +84,9 @@ task: CI_VCPKG_TAG: '2021.05.12' VCPKG_DOWNLOADS: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\downloads' VCPKG_DEFAULT_BINARY_CACHE: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\archives' - QT_DOWNLOAD_URL: 'https://download.qt.io/official_releases/qt/5.12/5.12.11/single/qt-everywhere-src-5.12.11.zip' - QT_LOCAL_PATH: 'C:\qt-everywhere-src-5.12.11.zip' - QT_SOURCE_DIR: 'C:\qt-everywhere-src-5.12.11' + QT_DOWNLOAD_URL: 'https://download.qt.io/official_releases/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.zip' + QT_LOCAL_PATH: 'C:\qt-everywhere-src-5.15.2.zip' + QT_SOURCE_DIR: 'C:\qt-everywhere-src-5.15.2' QTBASEDIR: 'C:\Qt_static' x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"' IgnoreWarnIntDirInTempDetected: 'true' @@ -115,7 +115,7 @@ task: - cd %QT_SOURCE_DIR% - mkdir build - cd build - - ..\configure -release -silent -opensource -confirm-license -opengl desktop -no-shared -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -no-libjpeg -nomake examples -nomake tests -nomake tools -no-dbus -no-libudev -no-icu -no-gtk -no-opengles3 -no-angle -no-sql-sqlite -no-sql-odbc -no-sqlite -no-libudev -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquickcontrols -skip qtquickcontrols2 -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-sql -no-feature-sqlmodel -prefix %QTBASEDIR% + - ..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml -prefix %QTBASEDIR% - jom - jom install vcpkg_tools_cache: diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 8b90ee3dc4..8346e549b8 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -67,10 +67,11 @@ AC_DEFUN([BITCOIN_QT_INIT],[ AS_IF([test "x$with_gui" = xqt5_debug], [AS_CASE([$host], [*darwin*], [qt_lib_suffix=_debug], - [*mingw*], [qt_lib_suffix=d], [qt_lib_suffix= ]); bitcoin_qt_want_version=qt5], [qt_lib_suffix= ]) + AS_CASE([$host], [*android*], [qt_lib_suffix=_$ANDROID_ARCH]) + AC_ARG_WITH([qt-incdir],[AS_HELP_STRING([--with-qt-incdir=INC_DIR],[specify qt include path (overridden by pkgconfig)])], [qt_include_path=$withval], []) AC_ARG_WITH([qt-libdir],[AS_HELP_STRING([--with-qt-libdir=LIB_DIR],[specify qt lib path (overridden by pkgconfig)])], [qt_lib_path=$withval], []) AC_ARG_WITH([qt-plugindir],[AS_HELP_STRING([--with-qt-plugindir=PLUGIN_DIR],[specify qt plugin path (overridden by pkgconfig)])], [qt_plugin_path=$withval], []) @@ -149,8 +150,6 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsVistaStylePlugin], [-lqwindowsvistastyle]) AC_DEFINE([QT_QPA_PLATFORM_WINDOWS], [1], [Define this symbol if the qt platform is windows]) elif test "x$TARGET_OS" = xlinux; then - dnl workaround for https://bugreports.qt.io/browse/QTBUG-74874 - AX_CHECK_LINK_FLAG([-lxcb-shm], [QT_LIBS="$QT_LIBS -lxcb-shm"], [AC_MSG_ERROR([could not link against -lxcb-shm])]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb]) AC_DEFINE([QT_QPA_PLATFORM_XCB], [1], [Define this symbol if the qt platform is xcb]) elif test "x$TARGET_OS" = xdarwin; then @@ -162,7 +161,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMacStylePlugin], [-lqmacstyle]) AC_DEFINE([QT_QPA_PLATFORM_COCOA], [1], [Define this symbol if the qt platform is cocoa]) elif test "x$TARGET_OS" = xandroid; then - QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype $QT_LIBS" + QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lplugins_platforms_qtforandroid_$ANDROID_ARCH -ljnigraphics -landroid -lqtfreetype_$ANDROID_ARCH $QT_LIBS" AC_DEFINE([QT_QPA_PLATFORM_ANDROID], [1], [Define this symbol if the qt platform is android]) fi fi @@ -353,6 +352,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ PKG_CHECK_MODULES([QT_INPUT], [${qt_lib_prefix}InputSupport], [QT_LIBS="$QT_INPUT_LIBS $QT_LIBS"]) PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"]) PKG_CHECK_MODULES([QT_XCBQPA], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_XCBQPA_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_XKBCOMMON], [${qt_lib_prefix}XkbCommonSupport], [QT_LIBS="$QT_XKBCOMMON_LIBS $QT_LIBS"]) elif test "x$TARGET_OS" = xdarwin; then PKG_CHECK_MODULES([QT_CLIPBOARD], [${qt_lib_prefix}ClipboardSupport${qt_lib_suffix}], [QT_LIBS="$QT_CLIPBOARD_LIBS $QT_LIBS"]) PKG_CHECK_MODULES([QT_GRAPHICS], [${qt_lib_prefix}GraphicsSupport${qt_lib_suffix}], [QT_LIBS="$QT_GRAPHICS_LIBS $QT_LIBS"]) @@ -360,7 +360,8 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ elif test "x$TARGET_OS" = xwindows; then PKG_CHECK_MODULES([QT_WINDOWSUIAUTOMATION], [${qt_lib_prefix}WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="$QT_WINDOWSUIAUTOMATION_LIBS $QT_LIBS"]) elif test "x$TARGET_OS" = xandroid; then - PKG_CHECK_MODULES([QT_EGL], [${qt_lib_prefix}EglSupport], [QT_LIBS="$QT_EGL_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_EGL], [${qt_lib_prefix}EglSupport${qt_lib_suffix}], [QT_LIBS="$QT_EGL_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport${qt_lib_suffix}], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"]) fi ]) diff --git a/build_msvc/README.md b/build_msvc/README.md index 36fb942c8e..cabe4d55ec 100644 --- a/build_msvc/README.md +++ b/build_msvc/README.md @@ -28,14 +28,14 @@ Qt --------------------- To build Bitcoin Core with the GUI, a static build of Qt is required. -1. Download a single ZIP archive of Qt source code from https://download.qt.io/official_releases/qt/ (e.g., [`qt-everywhere-src-5.12.11.zip`](https://download.qt.io/official_releases/qt/5.12/5.12.11/single/qt-everywhere-src-5.12.11.zip)), and expand it into a dedicated folder. The following instructions assume that this folder is `C:\dev\qt-source`. +1. Download a single ZIP archive of Qt source code from https://download.qt.io/official_releases/qt/ (e.g., [`qt-everywhere-src-5.15.2.zip`](https://download.qt.io/official_releases/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.zip)), and expand it into a dedicated folder. The following instructions assume that this folder is `C:\dev\qt-source`. 2. Open "x64 Native Tools Command Prompt for VS 2019", and input the following commands: ```cmd cd C:\dev\qt-source mkdir build cd build -..\configure -release -silent -opensource -confirm-license -opengl desktop -no-shared -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -no-libjpeg -nomake examples -nomake tests -nomake tools -no-dbus -no-libudev -no-icu -no-gtk -no-opengles3 -no-angle -no-sql-sqlite -no-sql-odbc -no-sqlite -no-libudev -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquickcontrols -skip qtquickcontrols2 -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-sql -no-feature-sqlmodel -prefix C:\Qt_static +..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml -prefix C:\Qt_static nmake nmake install ``` diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 15d4e729ac..ec3b7691e9 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -105,7 +105,19 @@ ELF_ALLOWED_LIBRARIES = { 'libxkbcommon-x11.so.0', # keyboard keymapping 'libfontconfig.so.1', # font support 'libfreetype.so.6', # font parsing -'libdl.so.2' # programming interface to dynamic linker +'libdl.so.2', # programming interface to dynamic linker +'libxcb-icccm.so.4', +'libxcb-image.so.0', +'libxcb-shm.so.0', +'libxcb-keysyms.so.1', +'libxcb-randr.so.0', +'libxcb-render-util.so.0', +'libxcb-render.so.0', +'libxcb-shape.so.0', +'libxcb-sync.so.1', +'libxcb-xfixes.so.0', +'libxcb-xinerama.so.0', +'libxcb-xkb.so.1', } MACHO_ALLOWED_LIBRARIES = { @@ -116,6 +128,7 @@ MACHO_ALLOWED_LIBRARIES = { 'AppKit', # user interface 'ApplicationServices', # common application tasks. 'Carbon', # deprecated c back-compat API +'ColorSync', 'CoreFoundation', # low level func, data types 'CoreGraphics', # 2D rendering 'CoreServices', # operating system services diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk index 41c1114be0..c2089d1ccb 100644 --- a/depends/packages/expat.mk +++ b/depends/packages/expat.mk @@ -1,12 +1,13 @@ package=expat -$(package)_version=2.2.7 -$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_2_7/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=cbc9102f4a31a8dafd42d642e9a3aa31e79a0aedaa1f6efd2795ebc83174ec18 +$(package)_version=2.4.1 +$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_4_1/ +$(package)_file_name=$(package)-$($(package)_version).tar.xz +$(package)_sha256_hash=cf032d0dba9b928636548e32b327a2d66b1aab63c4f4a13dd132c2d1d2f2fb6a define $(package)_set_vars $(package)_config_opts=--disable-shared --without-docbook --without-tests --without-examples $(package)_config_opts += --disable-dependency-tracking --enable-option-checking + $(package)_config_opts += --without-xmlwf $(package)_config_opts_linux=--with-pic endef @@ -23,5 +24,5 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds - rm -rf share lib/*.la + rm -rf share lib/cmake lib/*.la endef diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk index aebc8a5f3b..6f5dbe0f01 100644 --- a/depends/packages/freetype.mk +++ b/depends/packages/freetype.mk @@ -1,12 +1,12 @@ package=freetype -$(package)_version=2.7.1 +$(package)_version=2.11.0 $(package)_download_path=https://download.savannah.gnu.org/releases/$(package) -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 +$(package)_file_name=$(package)-$($(package)_version).tar.xz +$(package)_sha256_hash=8bee39bd3968c4804b70614a0a3ad597299ad0e824bc8aad5ce8aaf48067bde7 define $(package)_set_vars $(package)_config_opts=--without-zlib --without-png --without-harfbuzz --without-bzip2 --disable-static - $(package)_config_opts += --enable-option-checking + $(package)_config_opts += --enable-option-checking --without-brotli $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk index 24e0e9d325..b7e032c0b2 100644 --- a/depends/packages/libXau.mk +++ b/depends/packages/libXau.mk @@ -1,8 +1,8 @@ package=libXau -$(package)_version=1.0.8 +$(package)_version=1.0.9 $(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2 +$(package)_sha256_hash=ccf8cbf0dbf676faa2ea0a6d64bcc3b6746064722b606c8c52917ed00dcb73ec $(package)_dependencies=xproto # When updating this package, check the default value of diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk index 710f43959c..99a7ee2fbd 100644 --- a/depends/packages/libxcb.mk +++ b/depends/packages/libxcb.mk @@ -1,25 +1,21 @@ package=libxcb -$(package)_version=1.10 +$(package)_version=1.14 $(package)_download_path=https://xcb.freedesktop.org/dist -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5 +$(package)_file_name=$(package)-$($(package)_version).tar.xz +$(package)_sha256_hash=a55ed6db98d43469801262d81dc2572ed124edc3db31059d4e9916eb9f844c34 $(package)_dependencies=xcb_proto libXau define $(package)_set_vars -$(package)_config_opts=--disable-static --disable-build-docs --without-doxygen --without-launchd +$(package)_config_opts=--disable-static --disable-devel-docs --without-doxygen --without-launchd $(package)_config_opts += --disable-dependency-tracking --enable-option-checking -# Because we pass -qt-xcb to Qt, it will compile in a set of xcb helper libraries and extensions, -# so we skip building all of the extensions here. -# More info is available from: https://doc.qt.io/qt-5.9/linux-requirements.html +# Disable uneeded extensions. +# More info is available from: https://doc.qt.io/qt-5.15/linux-requirements.html $(package)_config_opts += --disable-composite --disable-damage --disable-dpms $(package)_config_opts += --disable-dri2 --disable-dri3 --disable-glx -$(package)_config_opts += --disable-present --disable-randr --disable-record -$(package)_config_opts += --disable-render --disable-resource --disable-screensaver -$(package)_config_opts += --disable-shape --disable-sync -$(package)_config_opts += --disable-xevie --disable-xfixes --disable-xfree86-dri -$(package)_config_opts += --disable-xinerama --disable-xinput -$(package)_config_opts += --disable-xprint --disable-selinux --disable-xtest -$(package)_config_opts += --disable-xv --disable-xvmc +$(package)_config_opts += --disable-present --disable-record --disable-resource +$(package)_config_opts += --disable-screensaver --disable-xevie --disable-xfree86-dri +$(package)_config_opts += --disable-xinput --disable-xprint --disable-selinux +$(package)_config_opts += --disable-xtest --disable-xv --disable-xvmc endef define $(package)_preprocess_cmds diff --git a/depends/packages/libxcb_util.mk b/depends/packages/libxcb_util.mk new file mode 100644 index 0000000000..2b65b58597 --- /dev/null +++ b/depends/packages/libxcb_util.mk @@ -0,0 +1,32 @@ +package=libxcb_util +$(package)_version=0.4.0 +$(package)_download_path=https://xcb.freedesktop.org/dist +$(package)_file_name=xcb-util-$($(package)_version).tar.bz2 +$(package)_sha256_hash=46e49469cb3b594af1d33176cd7565def2be3fa8be4371d62271fabb5eae50e9 +$(package)_dependencies=libxcb + +define $(package)_set_vars +$(package)_config_opts = --disable-shared --disable-devel-docs --without-doxygen +$(package)_config_opts += --disable-dependency-tracking --enable-option-checking +$(package)_cflags += -fPIC +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc lib/*.la +endef diff --git a/depends/packages/libxcb_util_image.mk b/depends/packages/libxcb_util_image.mk new file mode 100644 index 0000000000..d12d67e8e8 --- /dev/null +++ b/depends/packages/libxcb_util_image.mk @@ -0,0 +1,31 @@ +package=libxcb_util_image +$(package)_version=0.4.0 +$(package)_download_path=https://xcb.freedesktop.org/dist +$(package)_file_name=xcb-util-image-$($(package)_version).tar.bz2 +$(package)_sha256_hash=2db96a37d78831d643538dd1b595d7d712e04bdccf8896a5e18ce0f398ea2ffc +$(package)_dependencies=libxcb libxcb_util + +define $(package)_set_vars +$(package)_config_opts=--disable-static --disable-devel-docs --without-doxygen +$(package)_config_opts+= --disable-dependency-tracking --enable-option-checking +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc lib/*.la +endef diff --git a/depends/packages/libxcb_util_keysyms.mk b/depends/packages/libxcb_util_keysyms.mk new file mode 100644 index 0000000000..d4f72dedbe --- /dev/null +++ b/depends/packages/libxcb_util_keysyms.mk @@ -0,0 +1,31 @@ +package=libxcb_util_keysyms +$(package)_version=0.4.0 +$(package)_download_path=https://xcb.freedesktop.org/dist +$(package)_file_name=xcb-util-keysyms-$($(package)_version).tar.bz2 +$(package)_sha256_hash=0ef8490ff1dede52b7de533158547f8b454b241aa3e4dcca369507f66f216dd9 +$(package)_dependencies=libxcb xproto + +define $(package)_set_vars +$(package)_config_opts=--disable-static --disable-devel-docs --without-doxygen +$(package)_config_opts += --disable-dependency-tracking --enable-option-checking +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc lib/*.la +endef diff --git a/depends/packages/libxcb_util_render.mk b/depends/packages/libxcb_util_render.mk new file mode 100644 index 0000000000..28f1fb073c --- /dev/null +++ b/depends/packages/libxcb_util_render.mk @@ -0,0 +1,31 @@ +package=libxcb_util_render +$(package)_version=0.3.9 +$(package)_download_path=https://xcb.freedesktop.org/dist +$(package)_file_name=xcb-util-renderutil-$($(package)_version).tar.bz2 +$(package)_sha256_hash=c6e97e48fb1286d6394dddb1c1732f00227c70bd1bedb7d1acabefdd340bea5b +$(package)_dependencies=libxcb + +define $(package)_set_vars +$(package)_config_opts=--disable-static --disable-devel-docs --without-doxygen +$(package)_config_opts += --disable-dependency-tracking --enable-option-checking +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc lib/*.la +endef diff --git a/depends/packages/libxcb_util_wm.mk b/depends/packages/libxcb_util_wm.mk new file mode 100644 index 0000000000..3b905ba4ec --- /dev/null +++ b/depends/packages/libxcb_util_wm.mk @@ -0,0 +1,31 @@ +package=libxcb_util_wm +$(package)_version=0.4.1 +$(package)_download_path=https://xcb.freedesktop.org/dist +$(package)_file_name=xcb-util-wm-$($(package)_version).tar.bz2 +$(package)_sha256_hash=28bf8179640eaa89276d2b0f1ce4285103d136be6c98262b6151aaee1d3c2a3f +$(package)_dependencies=libxcb + +define $(package)_set_vars +$(package)_config_opts=--disable-static --disable-devel-docs --without-doxygen +$(package)_config_opts += --disable-dependency-tracking --enable-option-checking +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc lib/*.la +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 9094e327ef..426fa2e748 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -2,7 +2,7 @@ packages:=boost libevent qrencode_packages = qrencode -qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon +qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon libxcb_util libxcb_util_render libxcb_util_keysyms libxcb_util_image libxcb_util_wm qt_android_packages=qt qt_darwin_packages=qt diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index ea110b1653..c379e74f9f 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,25 +1,24 @@ -PACKAGE=qt -$(package)_version=5.12.11 -$(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules +package=qt +$(package)_version=5.15.2 +$(package)_download_path=https://download.qt.io/official_releases/qt/5.15/$($(package)_version)/submodules $(package)_suffix=everywhere-src-$($(package)_version).tar.xz $(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=1c1b4e33137ca77881074c140d54c3c9747e845a31338cfe8680f171f0bc3a39 -$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon +$(package)_sha256_hash=909fad2591ee367993a75d7e2ea50ad4db332f05e1c38dd7a5a274e156a4e0f8 +$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon libxcb_util libxcb_util_render libxcb_util_keysyms libxcb_util_image libxcb_util_wm $(package)_qt_libs=corelib network widgets gui plugins testlib $(package)_linguist_tools = lrelease lupdate lconvert $(package)_patches = qt.pro qttools_src.pro $(package)_patches += fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch -$(package)_patches += support_new_android_ndks.patch fix_android_jni_static.patch dont_hardcode_pwd.patch -$(package)_patches += dont_hardcode_x86_64.patch -$(package)_patches+= fix_lib_paths.patch fix_android_pch.patch -$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch fix_limits_header.patch -$(package)_patches+= fix_montery_include.patch +$(package)_patches += dont_hardcode_x86_64.patch fix_montery_include.patch +$(package)_patches += fix_android_jni_static.patch dont_hardcode_pwd.patch +$(package)_patches += qtbase-moc-ignore-gcc-macro.patch fix_limits_header.patch +$(package)_patches += fix_bigsur_style.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce9df06b6dee6c814164b7c3 +$(package)_qttranslations_sha256_hash=d5788e86257b21d5323f1efd94376a213e091d1e5e03b45a95dd052b5f570db8 $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=98b2aaca230458f65996f3534fd471d2ffd038dd58ac997c0589c06dc2385b4f +$(package)_qttools_sha256_hash=c189d0ce1ff7c739db9a3ace52ac3e24cb8fd6dbf234e49f075249b38f43c1cc $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) @@ -30,14 +29,14 @@ $(package)_config_opts_release += -silent $(package)_config_opts_debug = -debug $(package)_config_opts_debug += -optimized-tools $(package)_config_opts += -bindir $(build_prefix)/bin -$(package)_config_opts += -c++std c++1z +$(package)_config_opts += -c++std c++17 $(package)_config_opts += -confirm-license $(package)_config_opts += -hostprefix $(build_prefix) $(package)_config_opts += -no-compile-examples $(package)_config_opts += -no-cups $(package)_config_opts += -no-egl $(package)_config_opts += -no-eglfs -$(package)_config_opts += -no-freetype +$(package)_config_opts += -no-evdev $(package)_config_opts += -no-gif $(package)_config_opts += -no-glib $(package)_config_opts += -no-icu @@ -52,6 +51,7 @@ $(package)_config_opts += -no-mtdev $(package)_config_opts += -no-openssl $(package)_config_opts += -no-openvg $(package)_config_opts += -no-reduce-relocations +$(package)_config_opts += -no-schannel $(package)_config_opts += -no-sctp $(package)_config_opts += -no-securetransport $(package)_config_opts += -no-sql-db2 @@ -65,6 +65,7 @@ $(package)_config_opts += -no-sql-sqlite $(package)_config_opts += -no-sql-sqlite2 $(package)_config_opts += -no-system-proxies $(package)_config_opts += -no-use-gold-linker +$(package)_config_opts += -no-zstd $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests $(package)_config_opts += -nomake tools @@ -102,6 +103,7 @@ $(package)_config_opts += -no-feature-sqlmodel $(package)_config_opts += -no-feature-statemachine $(package)_config_opts += -no-feature-syntaxhighlighter $(package)_config_opts += -no-feature-textbrowser +$(package)_config_opts += -no-feature-textmarkdownwriter $(package)_config_opts += -no-feature-textodfwriter $(package)_config_opts += -no-feature-topleveldomain $(package)_config_opts += -no-feature-udpsocket @@ -117,6 +119,7 @@ $(package)_config_opts_darwin = -no-dbus $(package)_config_opts_darwin += -no-opengl $(package)_config_opts_darwin += -pch $(package)_config_opts_darwin += -no-feature-corewlan +$(package)_config_opts_darwin += -no-freetype $(package)_config_opts_darwin += QMAKE_MACOSX_DEPLOYMENT_TARGET=$(OSX_MIN_VERSION) ifneq ($(build_os),darwin) @@ -133,7 +136,7 @@ $(package)_config_opts_aarch64_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS $(package)_config_opts_x86_64_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS=x86_64 endif -$(package)_config_opts_linux = -qt-xcb +$(package)_config_opts_linux = -xcb $(package)_config_opts_linux += -no-xcb-xlib $(package)_config_opts_linux += -no-feature-xlib $(package)_config_opts_linux += -system-freetype @@ -152,6 +155,7 @@ $(package)_config_opts_s390x_linux = -platform linux-g++ -xplatform bitcoin-linu $(package)_config_opts_mingw32 = -no-opengl $(package)_config_opts_mingw32 += -no-dbus +$(package)_config_opts_mingw32 += -no-freetype $(package)_config_opts_mingw32 += -xplatform win32-g++ $(package)_config_opts_mingw32 += "QMAKE_CFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" $(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" @@ -166,7 +170,6 @@ $(package)_config_opts_android += -android-ndk-platform android-$(ANDROID_API_LE $(package)_config_opts_android += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_android += -egl $(package)_config_opts_android += -qpa xcb -$(package)_config_opts_android += -no-eglfs $(package)_config_opts_android += -no-dbus $(package)_config_opts_android += -opengl es2 $(package)_config_opts_android += -qt-freetype @@ -227,15 +230,13 @@ define $(package)_preprocess_cmds patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \ - patch -p1 -i $($(package)_patch_dir)/support_new_android_ndks.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_android_pch.patch && \ patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \ patch -p1 -i $($(package)_patch_dir)/dont_hardcode_x86_64.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \ patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_limits_header.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_montery_include.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_bigsur_style.patch && \ mkdir -p qtbase/mkspecs/macx-clang-linux &&\ cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk index 01203a0718..9be822506d 100644 --- a/depends/packages/xcb_proto.mk +++ b/depends/packages/xcb_proto.mk @@ -1,8 +1,8 @@ package=xcb_proto -$(package)_version=1.10 -$(package)_download_path=https://xcb.freedesktop.org/dist -$(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 -$(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 +$(package)_version=1.14.1 +$(package)_download_path=https://xorg.freedesktop.org/archive/individual/proto +$(package)_file_name=xcb-proto-$($(package)_version).tar.xz +$(package)_sha256_hash=f04add9a972ac334ea11d9d7eb4fc7f8883835da3e4859c9afa971efdf57fcc3 define $(package)_config_cmds $($(package)_autoconf) @@ -17,6 +17,5 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds - find -name "*.pyc" -delete && \ - find -name "*.pyo" -delete + rm -rf lib/python*/site-packages/xcbgen/__pycache__ endef diff --git a/depends/packages/xproto.mk b/depends/packages/xproto.mk index 6bd867d02b..7a43c52faf 100644 --- a/depends/packages/xproto.mk +++ b/depends/packages/xproto.mk @@ -1,8 +1,8 @@ package=xproto -$(package)_version=7.0.26 +$(package)_version=7.0.31 $(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f +$(package)_sha256_hash=c6f9747da0bd3a95f86b17fb8dd5e717c8f3ab7f0ece3ba1b247899ec1ef7747 define $(package)_set_vars $(package)_config_opts=--without-fop --without-xmlto --without-xsltproc --disable-specs diff --git a/depends/patches/qt/dont_hardcode_x86_64.patch b/depends/patches/qt/dont_hardcode_x86_64.patch index 0e1ca6acda..5c1e030fa4 100644 --- a/depends/patches/qt/dont_hardcode_x86_64.patch +++ b/depends/patches/qt/dont_hardcode_x86_64.patch @@ -9,14 +9,14 @@ https://codereview.qt-project.org/q/I70db7e4c27f0d3da5d0af33cb491d72c312d3fa8 --- old/qtbase/configure.json +++ new/qtbase/configure.json -@@ -208,11 +208,18 @@ +@@ -244,11 +244,18 @@ "testTypeDependencies": { - "linkerSupportsFlag": [ "use_gold_linker" ], -- "verifySpec": [ "shared", "use_gold_linker", "compiler-flags", "qmakeargs", "commit" ], + "linkerSupportsFlag": [ "use_bfd_linker", "use_gold_linker", "use_lld_linker" ], +- "verifySpec": [ "shared", "use_bfd_linker", "use_gold_linker", "use_lld_linker", "compiler-flags", "qmakeargs", "commit" ], + "verifySpec": [ + "shared", -+ "use_gold_linker", ++ "use_bfd_linker", "use_gold_linker", "use_lld_linker", + "compiler-flags", "qmakeargs", + "simulator_and_device", + "thread", @@ -30,7 +30,7 @@ https://codereview.qt-project.org/q/I70db7e4c27f0d3da5d0af33cb491d72c312d3fa8 }, "testTypeAliases": { -@@ -653,7 +660,7 @@ +@@ -762,7 +769,7 @@ }, "architecture": { "label": "Architecture", @@ -40,11 +40,10 @@ https://codereview.qt-project.org/q/I70db7e4c27f0d3da5d0af33cb491d72c312d3fa8 "pkg-config": { "label": "Using pkg-config", diff --git a/configure.pri b/configure.pri -index 33c90a8c2f..71767e29d6 100644 - +index 49755f7abfd..8be9b10d7d4 100644 --- old/qtbase/configure.pri +++ new/qtbase/configure.pri -@@ -642,6 +642,13 @@ defineTest(qtConfOutput_commitOptions) { +@@ -662,6 +662,13 @@ defineTest(qtConfOutput_commitOptions) { write_file($$QT_BUILD_TREE/mkspecs/qdevice.pri, $${currentConfig}.output.devicePro)|error() } @@ -59,24 +58,22 @@ index 33c90a8c2f..71767e29d6 100644 defineTest(processQtPath) { out_var = config.rel_input.$${2} diff --git a/mkspecs/common/macx.conf b/mkspecs/common/macx.conf -index 7d4a406134..de96c12fc9 100644 - +index d16b77acb8e..4ba0a8eaa36 100644 --- old/qtbase/mkspecs/common/macx.conf +++ new/qtbase/mkspecs/common/macx.conf @@ -6,7 +6,6 @@ QMAKE_PLATFORM += macos osx macx QMAKE_MAC_SDK = macosx - QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.12 + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13 -QMAKE_APPLE_DEVICE_ARCHS = x86_64 - QT_MAC_SDK_VERSION_MIN = 10.13 - QT_MAC_SDK_VERSION_MAX = 11.0 + # Should be 10.15, but as long as the CI builds with + # older SDKs we have to keep this. diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf -index d052808c14..0a89effe87 100644 - +index 92a9112bca6..d888731ec8d 100644 --- old/qtbase/mkspecs/features/mac/default_post.prf +++ new/qtbase/mkspecs/features/mac/default_post.prf -@@ -89,6 +89,11 @@ app_extension_api_only { +@@ -90,6 +90,11 @@ app_extension_api_only { QMAKE_LFLAGS += $$QMAKE_CFLAGS_APPLICATION_EXTENSION } @@ -88,7 +85,7 @@ index d052808c14..0a89effe87 100644 macx-xcode { qmake_pkginfo_typeinfo.name = QMAKE_PKGINFO_TYPEINFO !isEmpty(QMAKE_PKGINFO_TYPEINFO): \ -@@ -144,9 +149,6 @@ macx-xcode { +@@ -145,9 +150,6 @@ macx-xcode { simulator: VALID_SIMULATOR_ARCHS = $$QMAKE_APPLE_SIMULATOR_ARCHS VALID_ARCHS = $$VALID_DEVICE_ARCHS $$VALID_SIMULATOR_ARCHS @@ -99,8 +96,7 @@ index d052808c14..0a89effe87 100644 ACTIVE_ARCHS = $(filter $(EXPORT_VALID_ARCHS), $(ARCHS)) diff --git a/mkspecs/features/toolchain.prf b/mkspecs/features/toolchain.prf -index 5003679bd0..c7c080cb07 100644 - +index efbe7c1e55b..8add6dc8043 100644 --- old/qtbase/mkspecs/features/toolchain.prf +++ new/qtbase/mkspecs/features/toolchain.prf @@ -182,9 +182,14 @@ isEmpty($${target_prefix}.INCDIRS) { diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch index a186aeb8f6..bb64661761 100644 --- a/depends/patches/qt/fix_android_jni_static.patch +++ b/depends/patches/qt/fix_android_jni_static.patch @@ -1,6 +1,6 @@ --- old/qtbase/src/plugins/platforms/android/androidjnimain.cpp +++ new/qtbase/src/plugins/platforms/android/androidjnimain.cpp -@@ -898,6 +898,14 @@ +@@ -914,6 +914,14 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } diff --git a/depends/patches/qt/fix_android_pch.patch b/depends/patches/qt/fix_android_pch.patch deleted file mode 100644 index 195e1c5e59..0000000000 --- a/depends/patches/qt/fix_android_pch.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- old/qtbase/mkspecs/common/android-base-head.conf -+++ new/qtbase/mkspecs/common/android-base-head.conf -@@ -72,6 +72,6 @@ CROSS_COMPILE = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLS_PREFIX- - QMAKE_PCH_OUTPUT_EXT = .gch - - QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} --QMAKE_CFLAGS_USE_PRECOMPILE = -include ${QMAKE_PCH_OUTPUT_BASE} -+QMAKE_CFLAGS_USE_PRECOMPILE = -include-pch ${QMAKE_PCH_OUTPUT} - QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} - QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE diff --git a/depends/patches/qt/fix_bigsur_style.patch b/depends/patches/qt/fix_bigsur_style.patch new file mode 100644 index 0000000000..0f6c848bb0 --- /dev/null +++ b/depends/patches/qt/fix_bigsur_style.patch @@ -0,0 +1,90 @@ +Fix rendering in macOS BigSur + +See: https://bugreports.qt.io/browse/QTBUG-86513. + +Upstream commits (combined in this patch): + - Qt 6.0: 40fb97e97f550b8afd13ecc3a038d9d0c2d82bbb + - Qt 6.0: 3857f104cac127f62e64e55a20613f0ac2e6b843 + - Qt 6.1: abee4cdd5925a8513f51784754fca8fa35031732 + +--- old/qtbase/src/plugins/styles/mac/qmacstyle_mac.mm ++++ new/qtbase/src/plugins/styles/mac/qmacstyle_mac.mm +@@ -3870,6 +3870,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + const auto cs = d->effectiveAquaSizeConstrain(opt, w); + // Extra hacks to get the proper pressed appreance when not selected or selected and inactive + const bool needsInactiveHack = (!isActive && isSelected); ++ const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; + const auto ct = !needsInactiveHack && (isSelected || tp == QStyleOptionTab::OnlyOneTab) ? + QMacStylePrivate::Button_PushButton : + QMacStylePrivate::Button_PopupButton; +@@ -3878,6 +3879,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + + auto vOffset = isPopupButton ? 1 : 2; ++ if (isBigSurOrAbove) { ++ // Make it 1, otherwise, offset is very visible compared ++ // to selected tab (which is not a popup button). ++ vOffset = 1; ++ } ++ + if (tabDirection == QMacStylePrivate::East) + vOffset -= 1; + const auto outerAdjust = isPopupButton ? 1 : 4; +@@ -3894,9 +3901,22 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); + else + frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); ++ ++ if (isSelected && isBigSurOrAbove) { ++ // 1 pixed of 'roundness' is still visible on the right ++ // (the left is OK, it's rounded). ++ frameRect = frameRect.adjusted(0, 0, 1, 0); ++ } ++ + break; + case QStyleOptionTab::Middle: + frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0); ++ ++ if (isSelected && isBigSurOrAbove) { ++ // 1 pixel of 'roundness' is still visible on both ++ // sides - left and right. ++ frameRect = frameRect.adjusted(-1, 0, 1, 0); ++ } + break; + case QStyleOptionTab::End: + // Pressed state hack: tweak adjustments in preparation for flip below +@@ -3904,6 +3924,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); + else + frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); ++ ++ if (isSelected && isBigSurOrAbove) { ++ // 1 pixel of 'roundness' is still visible on the left. ++ frameRect = frameRect.adjusted(-1, 0, 0, 0); ++ } + break; + case QStyleOptionTab::OnlyOneTab: + frameRect = frameRect.adjusted(-outerAdjust, 0, outerAdjust, 0); +@@ -3951,7 +3976,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; + NSPopUpButtonCell *pbCell = nil; + auto rAdjusted = r; +- if (isPopupButton && tp == QStyleOptionTab::OnlyOneTab) { ++ if (isPopupButton && (tp == QStyleOptionTab::OnlyOneTab || isBigSurOrAbove)) { ++ // Note: starting from macOS BigSur NSPopupButton has this ++ // arrow 'button' in a different place and it became ++ // quite visible 'in between' inactive tabs. + pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); + oldPosition = pbCell.arrowPosition; + pbCell.arrowPosition = NSPopUpNoArrow; +@@ -3959,6 +3987,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter + // NSPopUpButton in this state is smaller. + rAdjusted.origin.x -= 3; + rAdjusted.size.width += 6; ++ if (isBigSurOrAbove) { ++ if (tp == QStyleOptionTab::End) ++ rAdjusted.origin.x -= 2; ++ } + } + } + diff --git a/depends/patches/qt/fix_lib_paths.patch b/depends/patches/qt/fix_lib_paths.patch deleted file mode 100644 index d1a15373f4..0000000000 --- a/depends/patches/qt/fix_lib_paths.patch +++ /dev/null @@ -1,193 +0,0 @@ ---- old/qtbase/mkspecs/common/mac.conf -+++ new/qtbase/mkspecs/common/mac.conf -@@ -14,7 +14,6 @@ - - QMAKE_RESOURCE = /Developer/Tools/Rez - QMAKE_EXTENSION_SHLIB = dylib --QMAKE_EXTENSIONS_AUX_SHLIB = tbd - QMAKE_LIBDIR = - - # sdk.prf will prefix the proper SDK sysroot - ---- old/qtbase/mkspecs/features/qmake_use.prf -+++ new/qtbase/mkspecs/features/qmake_use.prf -@@ -22,6 +22,8 @@ - !defined(QMAKE_LIBS_$$nu, var): \ - error("Library '$$lower($$replace(nu, _, -))' is not defined.") - -+ QMAKE_LIBDIR += $$eval(QMAKE_LIBDIR_$$nu) -+ - debug: \ - LIBS$${suffix} += $$eval(QMAKE_LIBS_$${nu}_DEBUG) $$eval(QMAKE_LIBS_$$nu) - else: \ - ---- old/qtbase/mkspecs/features/qt_configure.prf -+++ new/qtbase/mkspecs/features/qt_configure.prf -@@ -526,98 +526,23 @@ - return($$sysrootified) - } - --# libs-var, libs, in-paths, out-paths-var -+# libs-var, libs, in-paths - defineTest(qtConfResolveLibs) { -- ret = true -- paths = $$3 -- out = -- copy = false -- for (l, 2) { -- $$copy { -- copy = false -- out += $$l -- } else: equals(l, "-s") { -- # em++ flag to link libraries from emscripten-ports; passed on literally. -- copy = true -- out += $$l -- } else: contains(l, "^-L.*") { -- lp = $$replace(l, "^-L", ) -- gcc: lp = $$qtGccSysrootifiedPath($$lp) -- !exists($$lp/.) { -- qtLog("Library path $$val_escape(lp) is invalid.") -- ret = false -- } else { -- paths += $$lp -- } -- } else: contains(l, "^-l.*") { -- lib = $$replace(l, "^-l", ) -- lcan = -- integrity:contains(lib, "^.*\\.a") { -- # INTEGRITY compiler searches for exact filename -- # if -l argument has .a suffix -- lcan += $${lib} -- } else: contains(lib, "^:.*") { -- # Use exact filename when -l:filename syntax is used. -- lib ~= s/^:// -- lcan += $${lib} -- } else: unix { -- # Under UNIX, we look for actual shared libraries, in addition -- # to static ones. -- shexts = $$QMAKE_EXTENSION_SHLIB $$QMAKE_EXTENSIONS_AUX_SHLIB -- for (ext, shexts) { -- lcan += $${QMAKE_PREFIX_SHLIB}$${lib}.$${ext} -- } -- lcan += \ -- $${QMAKE_PREFIX_STATICLIB}$${lib}.$${QMAKE_EXTENSION_STATICLIB} -- } else { -- # Under Windows, we look only for static libraries, as even for DLLs -- # one actually links against a static import library. -- mingw { -- lcan += \ -- # MinGW supports UNIX-style library naming in addition to -- # the MSVC style. -- lib$${lib}.dll.a lib$${lib}.a \ -- # Fun fact: prefix-less libraries are also supported. -- $${lib}.dll.a $${lib}.a -- } -- lcan += $${lib}.lib -- } -- l = $$qtConfFindInPathList($$lcan, $$paths $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS) -- isEmpty(l) { -- qtLog("None of [$$val_escape(lcan)] found in [$$val_escape(paths)] and global paths.") -- ret = false -- } else { -- out += $$l -- } -- } else { -- out += $$l -- } -- } -- $$1 = $$out -+ for (path, 3): \ -+ pre_lflags += -L$$path -+ $$1 = $$pre_lflags $$2 - export($$1) -- !isEmpty(4) { -- $$4 = $$paths -- export($$4) -- } -- return($$ret) --} -- --# source-var --defineTest(qtConfResolveAllLibs) { -- ret = true -- !qtConfResolveLibs($${1}.libs, $$eval($${1}.libs), , $${1}.libdirs): \ -- ret = false -- for (b, $${1}.builds._KEYS_): \ -- !qtConfResolveLibs($${1}.builds.$${b}, $$eval($${1}.builds.$${b}), $$eval($${1}.libdirs), ): \ -- ret = false -- return($$ret) -+ return(true) - } - - # libs-var, in-paths, libs - defineTest(qtConfResolvePathLibs) { - ret = true -- gcc: 2 = $$qtGccSysrootifiedPaths($$2) -- for (libdir, 2) { -+ gcc: \ -+ local_paths = $$qtGccSysrootifiedPaths($$2) -+ else: \ -+ local_paths = $$2 -+ for (libdir, local_paths) { - !exists($$libdir/.) { - qtLog("Library path $$val_escape(libdir) is invalid.") - ret = false -@@ -667,8 +592,11 @@ - # includes-var, in-paths, test-object-var - defineTest(qtConfResolvePathIncs) { - ret = true -- gcc: 2 = $$qtGccSysrootifiedPaths($$2) -- for (incdir, 2) { -+ gcc: \ -+ local_paths = $$qtGccSysrootifiedPaths($$2) -+ else: \ -+ local_paths = $$2 -+ for (incdir, local_paths) { - !exists($$incdir/.) { - qtLog("Include path $$val_escape(incdir) is invalid.") - ret = false -@@ -727,6 +655,7 @@ - vars += $$eval(config.commandline.rev_assignments.$${iv}) - defined(config.input.$${iv}, var) { - eval($${1}.builds.$${b} = $$eval(config.input.$${iv})) -+ export($${1}.builds.$${b}) - $${1}.builds._KEYS_ *= $${b} - any = true - } else { -@@ -741,11 +670,14 @@ - export($${1}.builds._KEYS_) - # we also reset the generic libs, to avoid surprises. - $${1}.libs = -+ export($${1}.libs) - } - - # direct libs. overwrites inline libs. -- defined(config.input.$${input}.libs, var): \ -+ defined(config.input.$${input}.libs, var) { - eval($${1}.libs = $$eval(config.input.$${input}.libs)) -+ export($${1}.libs) -+ } - - includes = $$eval(config.input.$${input}.incdir) - -@@ -754,6 +686,7 @@ - !isEmpty(prefix) { - includes += $$prefix/include - $${1}.libs = -L$$prefix/lib $$eval($${1}.libs) -+ export($${1}.libs) - } - - libdir = $$eval(config.input.$${input}.libdir) -@@ -762,11 +695,9 @@ - for (ld, libdir): \ - libs += -L$$ld - $${1}.libs = $$libs $$eval($${1}.libs) -+ export($${1}.libs) - } - -- !qtConfResolveAllLibs($$1): \ -- return(false) -- - !qtConfResolvePathIncs($${1}.includedir, $$includes, $$2): \ - return(false) - diff --git a/depends/patches/qt/fix_limits_header.patch b/depends/patches/qt/fix_limits_header.patch index e4313770e5..cb5a8cd1b5 100644 --- a/depends/patches/qt/fix_limits_header.patch +++ b/depends/patches/qt/fix_limits_header.patch @@ -19,17 +19,16 @@ Upstream commits: #include <stdlib.h> #include <string.h> ---- old/qtbase/src/corelib/tools/qbytearraymatcher.h -+++ new/qtbase/src/corelib/tools/qbytearraymatcher.h -@@ -42,6 +42,8 @@ - - #include <QtCore/qbytearray.h> +--- old/qtbase/src/corelib/global/qfloat16.h ++++ new/qtbase/src/corelib/global/qfloat16.h +@@ -43,6 +43,7 @@ + #include <QtCore/qglobal.h> + #include <QtCore/qmetatype.h> +#include <limits> -+ - QT_BEGIN_NAMESPACE - + #include <string.h> + #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__AVX2__) && !defined(__F16C__) --- old/qtbase/src/tools/moc/generator.cpp +++ new/qtbase/src/tools/moc/generator.cpp @@ -42,3 +41,24 @@ Upstream commits: #include <math.h> #include <stdio.h> +--- old/qtbase/src/corelib/text/qbytearraymatcher.h ++++ new/qtbase/src/corelib/text/qbytearraymatcher.h +@@ -42,6 +42,8 @@ + + #include <QtCore/qbytearray.h> + ++#include <limits> ++ + QT_BEGIN_NAMESPACE + + +--- old/qtbase/src/corelib/tools/qoffsetstringarray_p.h ++++ new/qtbase/src/corelib/tools/qoffsetstringarray_p.h +@@ -55,6 +55,7 @@ + + #include <tuple> + #include <array> ++#include <limits> + + QT_BEGIN_NAMESPACE + diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch index a5de2b4b9e..73f4d89f73 100644 --- a/depends/patches/qt/fix_qt_pkgconfig.patch +++ b/depends/patches/qt/fix_qt_pkgconfig.patch @@ -4,20 +4,8 @@ load(qt_targets) # this builds on top of qt_common --!internal_module:if(unix|mingw) { +-!internal_module:if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) { +if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) { CONFIG += create_pc QMAKE_PKGCONFIG_DESTDIR = pkgconfig host_build: \ -@@ -284,9 +284,9 @@ load(qt_targets) - QMAKE_PKGCONFIG_CFLAGS = -D$$MODULE_DEFINE -I${includedir}/$$MODULE_INCNAME - } - QMAKE_PKGCONFIG_NAME = $$replace(TARGET, ^Qt, "Qt$$QT_MAJOR_VERSION ") -- QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION) -+ QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)$$qtPlatformTargetSuffix() - for(i, MODULE_DEPENDS): \ -- QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0)) -+ QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))$$qtPlatformTargetSuffix() - isEmpty(QMAKE_PKGCONFIG_DESCRIPTION): \ - QMAKE_PKGCONFIG_DESCRIPTION = $$replace(TARGET, ^Qt, "Qt ") module - !isEmpty(lib_replace0.match) { diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch index f4a6f09ee4..d6846aaca2 100644 --- a/depends/patches/qt/no-xlib.patch +++ b/depends/patches/qt/no-xlib.patch @@ -22,7 +22,7 @@ index 7c62c2e2b3..c05c6c0a07 100644 #include <xcb/xfixes.h> #include <xcb/xcb_image.h> -@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window) +@@ -391,6 +391,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window) xcb_flush(xcb_connection()); } @@ -30,7 +30,7 @@ index 7c62c2e2b3..c05c6c0a07 100644 static int cursorIdForShape(int cshape) { int cursorId = 0; -@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape) +@@ -444,6 +445,7 @@ static int cursorIdForShape(int cshape) } return cursorId; } @@ -38,7 +38,7 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) { -@@ -556,7 +560,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) +@@ -556,7 +558,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { xcb_connection_t *conn = xcb_connection(); @@ -48,16 +48,15 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t cursor = XCB_NONE; // Try Xcursor first -@@ -585,7 +591,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) - +@@ -586,6 +590,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) // Non-standard X11 cursors are created from bitmaps cursor = createNonStandardCursor(cshape); -- + +#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) // Create a glpyh cursor if everything else failed if (!cursor && cursorId) { cursor = xcb_generate_id(conn); -@@ -593,6 +599,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +@@ -593,6 +598,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) cursorId, cursorId + 1, 0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0); } diff --git a/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch index 0358bea6e9..f0c14a9400 100644 --- a/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch +++ b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch @@ -7,7 +7,7 @@ Upstream report: https://bugreports.qt.io/browse/QTBUG-83160 diff --git a/qtbase/src/tools/moc/main.cpp b/qtbase/src/tools/moc/main.cpp --- a/qtbase/src/tools/moc/main.cpp +++ b/qtbase/src/tools/moc/main.cpp -@@ -188,6 +188,7 @@ int runMoc(int argc, char **argv) +@@ -238,6 +238,7 @@ int runMoc(int argc, char **argv) dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__"); pp.macros["__attribute__"] = dummyVariadicFunctionMacro; pp.macros["__declspec"] = dummyVariadicFunctionMacro; diff --git a/depends/patches/qt/support_new_android_ndks.patch b/depends/patches/qt/support_new_android_ndks.patch deleted file mode 100644 index 85c8ae2132..0000000000 --- a/depends/patches/qt/support_new_android_ndks.patch +++ /dev/null @@ -1,122 +0,0 @@ -Follow Google's BuildSystemMaintainers doc to support future NDK releases. - -Upstream commit: - - Qt 5.14: 9b14950ff600a4ce5a8698b67ab38907c50417f1 - ---- old/qtbase/mkspecs/android-clang/qmake.conf -+++ new/qtbase/mkspecs/android-clang/qmake.conf -@@ -14,43 +14,29 @@ - QMAKE_CC = $$NDK_LLVM_PATH/bin/clang - QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++ - -+# Follow https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md -+ - equals(ANDROID_TARGET_ARCH, armeabi-v7a): \ -- QMAKE_CFLAGS += -target armv7-none-linux-androideabi --else: equals(ANDROID_TARGET_ARCH, armeabi): \ -- QMAKE_CFLAGS += -target armv5te-none-linux-androideabi -+ QMAKE_CFLAGS = -target armv7a-linux-androideabi$$replace(ANDROID_PLATFORM, "android-", "") - else: equals(ANDROID_TARGET_ARCH, arm64-v8a): \ -- QMAKE_CFLAGS += -target aarch64-none-linux-android -+ QMAKE_CFLAGS = -target aarch64-linux-android$$replace(ANDROID_PLATFORM, "android-", "") - else: equals(ANDROID_TARGET_ARCH, x86): \ -- QMAKE_CFLAGS += -target i686-none-linux-android -mstackrealign -+ QMAKE_CFLAGS = -target i686-linux-android$$replace(ANDROID_PLATFORM, "android-", "") -mstackrealign - else: equals(ANDROID_TARGET_ARCH, x86_64): \ -- QMAKE_CFLAGS += -target x86_64-none-linux-android --else: equals(ANDROID_TARGET_ARCH, mips): \ -- QMAKE_CFLAGS += -target mipsel-none-linux-android --else: equals(ANDROID_TARGET_ARCH, mips64): \ -- QMAKE_CFLAGS += -target mips64el-none-linux-android -- --QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH -fno-limit-debug-info -- --QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++ --equals(ANDROID_TARGET_ARCH, armeabi-v7a): QMAKE_LINK += -Wl,--exclude-libs,libunwind.a -- --QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \ -- -isystem $$NDK_ROOT/sysroot/usr/include/$$NDK_TOOLS_PREFIX \ -- -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++/include \ -- -isystem $$NDK_ROOT/sources/android/support/include \ -- -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++abi/include -+ QMAKE_CFLAGS = -target x86_64-linux-android$$replace(ANDROID_PLATFORM, "android-", "") - --ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH -+QMAKE_CFLAGS += -fno-limit-debug-info - --ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so -+QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS - --ANDROID_USE_LLVM = true -+ANDROID_STDCPP_PATH = $$NDK_LLVM_PATH/sysroot/usr/lib/$$NDK_TOOLS_PREFIX/libc++_shared.so - --exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ -- ANDROID_CXX_STL_LIBS = -lc++ --else: \ -- ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "") -+ANDROID_USE_LLVM = true - --QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz -+QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz -+QMAKE_LIBDIR_POST = -+QMAKE_LFLAGS = -+QMAKE_LIBS_PRIVATE = -+ANDROID_CXX_STL_LIBS = - - include(../common/android-base-tail.conf) - ---- old/qtbase/mkspecs/common/android-base-head.conf -+++ new/qtbase/mkspecs/common/android-base-head.conf -@@ -64,7 +58,6 @@ - } - - CONFIG += $$ANDROID_PLATFORM --QMAKE_CFLAGS = -D__ANDROID_API__=$$replace(ANDROID_PLATFORM, "android-", "") - - ANDROID_PLATFORM_ROOT_PATH = $$NDK_ROOT/platforms/$$ANDROID_PLATFORM/arch-$$ANDROID_ARCHITECTURE/ - ---- old/qtbase/mkspecs/common/android-base-tail.conf -+++ new/qtbase/mkspecs/common/android-base-tail.conf -@@ -6,22 +6,17 @@ - QMAKE_CFLAGS += -fstack-protector-strong -DANDROID - - equals(ANDROID_TARGET_ARCH, armeabi-v7a): \ -- QMAKE_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -fno-builtin-memmove -+ QMAKE_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp - else: equals(ANDROID_TARGET_ARCH, armeabi): \ -- QMAKE_CFLAGS += -march=armv5te -mtune=xscale -msoft-float -fno-builtin-memmove --# -fno-builtin-memmove is used to workaround https://code.google.com/p/android/issues/detail?id=81692 -+ QMAKE_CFLAGS += -march=armv5te -mtune=xscale -msoft-float - - QMAKE_CFLAGS_WARN_ON = -Wall -W - QMAKE_CFLAGS_WARN_OFF = - equals(ANDROID_TARGET_ARCH, armeabi-v7a) | equals(ANDROID_TARGET_ARCH, armeabi) { - CONFIG += optimize_size - QMAKE_CFLAGS_DEBUG = -g -marm -O0 -- equals(ANDROID_TARGET_ARCH, armeabi):if(equals(NDK_TOOLCHAIN_VERSION, 4.8)|equals(NDK_TOOLCHAIN_VERSION, 4.9)) { -- DEFINES += QT_OS_ANDROID_GCC_48_WORKAROUND -- } else { -- QMAKE_CFLAGS_RELEASE += -mthumb -- QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -mthumb -- } -+ QMAKE_CFLAGS_RELEASE += -mthumb -+ QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -mthumb - } - - QMAKE_CFLAGS_SHLIB = -fPIC -@@ -61,15 +56,12 @@ - QMAKE_RANLIB = $${CROSS_COMPILE}ranlib - - QMAKE_INCDIR_POST = --QMAKE_LIBDIR_POST = $$ANDROID_SOURCES_CXX_STL_LIBDIR - QMAKE_INCDIR_X11 = - QMAKE_LIBDIR_X11 = - QMAKE_INCDIR_OPENGL = - QMAKE_LIBDIR_OPENGL = - - QMAKE_LINK_SHLIB = $$QMAKE_LINK --QMAKE_LFLAGS = --sysroot=$$ANDROID_PLATFORM_ROOT_PATH --equals(ANDROID_TARGET_ARCH, x86_64) QMAKE_LFLAGS += -L$$ANDROID_PLATFORM_ROOT_PATH/usr/lib64 - QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared - QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared - QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB diff --git a/doc/dependencies.md b/doc/dependencies.md index 6ccb53f6b3..f2b6760ac2 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -8,9 +8,8 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | | Boost | [1.71.0](https://www.boost.org/users/download/) | [1.64.0](https://github.com/bitcoin/bitcoin/pull/22320) | No | | | | Clang<sup>[ \* ](#note1)</sup> | | [7.0](https://releases.llvm.org/download.html) (C++17 & std::filesystem support) | | | | -| Expat | [2.2.7](https://libexpat.github.io/) | | No | Yes | | | fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | | -| FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Android only) | +| FreeType | [2.11.0](https://download.savannah.gnu.org/releases/freetype) | | No | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Android only) | | GCC | | [8.1](https://gcc.gnu.org/) (C++17 & std::filesystem support) | | | | | glibc | | [2.18](https://www.gnu.org/software/libc/) | | | | | | HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | diff --git a/src/Makefile.am b/src/Makefile.am index 4a60ce8b90..72f548c192 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -410,9 +410,10 @@ libbitcoin_wallet_a_SOURCES = \ wallet/interfaces.cpp \ wallet/load.cpp \ wallet/receive.cpp \ + wallet/rpc/backup.cpp \ + wallet/rpc/encrypt.cpp \ wallet/rpc/signmessage.cpp \ wallet/rpc/util.cpp \ - wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/scriptpubkeyman.cpp \ wallet/spend.cpp \ diff --git a/src/base58.cpp b/src/base58.cpp index fb04673c5c..b122e0055e 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -149,7 +149,7 @@ std::string EncodeBase58Check(Span<const unsigned char> input) return false; } // re-calculate the checksum, ensure it matches the included 4-byte checksum - uint256 hash = Hash(MakeSpan(vchRet).first(vchRet.size() - 4)); + uint256 hash = Hash(Span{vchRet}.first(vchRet.size() - 4)); if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { vchRet.clear(); return false; diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 41fa0b6fa0..31a1e62d6b 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -81,7 +81,7 @@ GCSFilter::GCSFilter(const Params& params) GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter) : m_params(params), m_encoded(std::move(encoded_filter)) { - VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0}; uint64_t N = ReadCompactSize(stream); m_N = static_cast<uint32_t>(N); @@ -92,7 +92,7 @@ GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_fi // Verify that the encoded filter contains exactly N elements. If it has too much or too little // data, a std::ios_base::failure exception will be raised. - BitStreamReader<VectorReader> bitreader(stream); + BitStreamReader<SpanReader> bitreader{stream}; for (uint64_t i = 0; i < m_N; ++i) { GolombRiceDecode(bitreader, m_params.m_P); } @@ -133,13 +133,13 @@ GCSFilter::GCSFilter(const Params& params, const ElementSet& elements) bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const { - VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0}; // Seek forward by size of N uint64_t N = ReadCompactSize(stream); assert(N == m_N); - BitStreamReader<VectorReader> bitreader(stream); + BitStreamReader<SpanReader> bitreader{stream}; uint64_t value = 0; size_t hashes_index = 0; diff --git a/src/compressor.h b/src/compressor.h index 40b2496f06..00b777a111 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -65,12 +65,12 @@ struct ScriptCompression void Ser(Stream &s, const CScript& script) { CompressedScript compr; if (CompressScript(script, compr)) { - s << MakeSpan(compr); + s << Span{compr}; return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); - s << MakeSpan(script); + s << Span{script}; } template<typename Stream> @@ -79,7 +79,7 @@ struct ScriptCompression s >> VARINT(nSize); if (nSize < nSpecialScripts) { CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); - s >> MakeSpan(vch); + s >> Span{vch}; DecompressScript(script, nSize, vch); return; } @@ -90,7 +90,7 @@ struct ScriptCompression s.ignore(nSize); } else { script.resize(nSize); - s >> MakeSpan(script); + s >> Span{script}; } } }; diff --git a/src/net.cpp b/src/net.cpp index f6d40a7854..2d9e69b6fb 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -761,7 +761,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n", SanitizeString(msg.m_command), msg.m_message_size, - HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)), + HexStr(Span{hash}.first(CMessageHeader::CHECKSUM_SIZE)), HexStr(hdr.pchChecksum), m_node_id); reject_message = true; @@ -1583,8 +1583,9 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes, if (nBytes > 0) { bool notify = false; - if (!pnode->ReceiveMsgBytes(Span<const uint8_t>(pchBuf, nBytes), notify)) + if (!pnode->ReceiveMsgBytes({pchBuf, (size_t)nBytes}, notify)) { pnode->CloseSocketDisconnect(); + } RecordBytesRecv(nBytes); if (notify) { size_t nSizeAdded = 0; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3f755eb178..d832ff016b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1815,7 +1815,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) { assert(!"cannot load block from disk"); } - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data))); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data})); // Don't set pblock as we've sent the block } else { // Send block from disk diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 7f1dd698b0..f6bc7fbb94 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -196,7 +196,7 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS SHA3_256 hasher; - hasher.Write(MakeSpan(prefix).first(prefix_len)); + hasher.Write(Span{prefix}.first(prefix_len)); hasher.Write(addr_pubkey); hasher.Write(VERSION); @@ -303,7 +303,7 @@ CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope) { - SetLegacyIPv6(Span<const uint8_t>(reinterpret_cast<const uint8_t*>(&ipv6Addr), sizeof(ipv6Addr))); + SetLegacyIPv6({reinterpret_cast<const uint8_t*>(&ipv6Addr), sizeof(ipv6Addr)}); m_scope_id = scope; } @@ -693,13 +693,13 @@ uint32_t CNetAddr::GetLinkedIPv4() const return ReadBE32(m_addr.data()); } else if (IsRFC6052() || IsRFC6145()) { // mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address - return ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data()); + return ReadBE32(Span{m_addr}.last(ADDR_IPV4_SIZE).data()); } else if (IsRFC3964()) { // 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6 - return ReadBE32(MakeSpan(m_addr).subspan(2, ADDR_IPV4_SIZE).data()); + return ReadBE32(Span{m_addr}.subspan(2, ADDR_IPV4_SIZE).data()); } else if (IsRFC4380()) { // Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped - return ~ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data()); + return ~ReadBE32(Span{m_addr}.last(ADDR_IPV4_SIZE).data()); } assert(false); } diff --git a/src/netaddress.h b/src/netaddress.h index a5b74eb35b..c8af4a9605 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -433,7 +433,7 @@ private: if (SetNetFromBIP155Network(bip155_net, address_size)) { m_addr.resize(address_size); - s >> MakeSpan(m_addr); + s >> Span{m_addr}; if (m_net != NET_IPV6) { return; diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index d8c21bd833..36cf786bd5 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -493,6 +493,12 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock) { LOCK(m_cs_fee_estimator); + return _removeTx(hash, inBlock); +} + +bool CBlockPolicyEstimator::_removeTx(const uint256& hash, bool inBlock) +{ + AssertLockHeld(m_cs_fee_estimator); std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash); if (pos != mapMemPoolTxs.end()) { feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock); @@ -576,7 +582,8 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) { - if (!removeTx(entry->GetTx().GetHash(), true)) { + AssertLockHeld(m_cs_fee_estimator); + if (!_removeTx(entry->GetTx().GetHash(), true)) { // This transaction wasn't being tracked for fee estimation return false; } @@ -985,7 +992,7 @@ void CBlockPolicyEstimator::FlushUnconfirmed() { // Remove every entry in mapMemPoolTxs while (!mapMemPoolTxs.empty()) { auto mi = mapMemPoolTxs.begin(); - removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs + _removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs } int64_t endclear = GetTimeMicros(); LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001); diff --git a/src/policy/fees.h b/src/policy/fees.h index 27f9120c64..37a7051045 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -186,47 +186,59 @@ public: /** Process all the transactions that have been included in a block */ void processBlock(unsigned int nBlockHeight, - std::vector<const CTxMemPoolEntry*>& entries); + std::vector<const CTxMemPoolEntry*>& entries) + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Process a transaction accepted to the mempool*/ - void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate); + void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate) + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Remove a transaction from the mempool tracking stats*/ - bool removeTx(uint256 hash, bool inBlock); + bool removeTx(uint256 hash, bool inBlock) + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** DEPRECATED. Return a feerate estimate */ - CFeeRate estimateFee(int confTarget) const; + CFeeRate estimateFee(int confTarget) const + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Estimate feerate needed to get be included in a block within confTarget * blocks. If no answer can be given at confTarget, return an estimate at * the closest target where one can be given. 'conservative' estimates are * valid over longer time horizons also. */ - CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const; + CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Return a specific fee estimate calculation with a given success * threshold and time horizon, and optionally return detailed data about * calculation */ - CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult *result = nullptr) const; + CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, + EstimationResult* result = nullptr) const + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Write estimation data to a file */ - bool Write(CAutoFile& fileout) const; + bool Write(CAutoFile& fileout) const + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Read estimation data from a file */ - bool Read(CAutoFile& filein); + bool Read(CAutoFile& filein) + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */ - void FlushUnconfirmed(); + void FlushUnconfirmed() + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Calculation of highest target that estimates are tracked for */ - unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; + unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); /** Drop still unconfirmed transactions and record current estimations, if the fee estimation file is present. */ - void Flush(); + void Flush() + EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator); private: - mutable RecursiveMutex m_cs_fee_estimator; + mutable Mutex m_cs_fee_estimator; unsigned int nBestSeenHeight GUARDED_BY(m_cs_fee_estimator); unsigned int firstRecordedHeight GUARDED_BY(m_cs_fee_estimator); @@ -267,6 +279,10 @@ private: unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Calculation of highest target that reasonable estimate can be provided for */ unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); + + /** A non-thread-safe helper for the removeTx function */ + bool _removeTx(const uint256& hash, bool inBlock) + EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); }; class FeeFilterRounder diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 2c30b20d5b..5d0bb68ec6 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -251,7 +251,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // - No annexes if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) { // Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341) - auto stack = MakeSpan(tx.vin[i].scriptWitness.stack); + Span stack{tx.vin[i].scriptWitness.stack}; if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) { // Annexes are nonstandard as long as no semantics are defined for them. return false; diff --git a/src/psbt.h b/src/psbt.h index 7808a247c0..1171ecf1dd 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -81,7 +81,7 @@ struct PSBTInput if (final_script_sig.empty() && final_script_witness.IsNull()) { // Write any partial signatures for (auto sig_pair : partial_sigs) { - SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first)); + SerializeToVector(s, PSBT_IN_PARTIAL_SIG, Span{sig_pair.second.first}); s << sig_pair.second.second; } diff --git a/src/pubkey.h b/src/pubkey.h index ae6356c246..2453c92d92 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -157,13 +157,13 @@ public: //! Get the KeyID of this public key (hash of its serialization) CKeyID GetID() const { - return CKeyID(Hash160(MakeSpan(vch).first(size()))); + return CKeyID(Hash160(Span{vch}.first(size()))); } //! Get the 256-bit hash of this public key. uint256 GetHash() const { - return Hash(MakeSpan(vch).first(size())); + return Hash(Span{vch}.first(size())); } /* @@ -240,7 +240,7 @@ public: explicit XOnlyPubKey(Span<const unsigned char> bytes); /** Construct an x-only pubkey from a normal pubkey. */ - explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span<const unsigned char>(pubkey.begin() + 1, pubkey.begin() + 33)) {} + explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span{pubkey}.subspan(1, 32)) {} /** Verify a Schnorr signature against this public key. * diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 211f3f506d..364962ee9a 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -428,6 +428,15 @@ namespace GUIUtil */ void ShowModalDialogAndDeleteOnClose(QDialog* dialog); + inline bool IsEscapeOrBack(int key) + { + if (key == Qt::Key_Escape) return true; +#ifdef Q_OS_ANDROID + if (key == Qt::Key_Back) return true; +#endif // Q_OS_ANDROID + return false; + } + } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 6bcdcc4c29..1c19fb4b27 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -14,6 +14,7 @@ #include <netbase.h> #include <qt/bantablemodel.h> #include <qt/clientmodel.h> +#include <qt/guiutil.h> #include <qt/peertablesortproxy.h> #include <qt/platformstyle.h> #include <qt/walletmodel.h> @@ -910,8 +911,7 @@ void RPCConsole::clear(bool keep_prompt) void RPCConsole::keyPressEvent(QKeyEvent *event) { - if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape) - { + if (windowType() != Qt::Widget && GUIUtil::IsEscapeOrBack(event->key())) { close(); } } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 6a3e160d8c..2c05fc39fd 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -36,32 +36,33 @@ static RPCHelpMan validateaddress() { - return RPCHelpMan{"validateaddress", - "\nReturn information about the given bitcoin address.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", + return RPCHelpMan{ + "validateaddress", + "\nReturn information about the given bitcoin address.\n", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, + {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address validated"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", /* optional */ true, "The hex-encoded scriptPubKey generated by the address"}, + {RPCResult::Type::BOOL, "isscript", /* optional */ true, "If the key is a script"}, + {RPCResult::Type::BOOL, "iswitness", /* optional */ true, "If the address is a witness address"}, + {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"}, + {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"}, + {RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"}, + {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)", { - {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address validated"}, - {RPCResult::Type::STR_HEX, "scriptPubKey", /* optional */ true, "The hex-encoded scriptPubKey generated by the address"}, - {RPCResult::Type::BOOL, "isscript", /* optional */ true, "If the key is a script"}, - {RPCResult::Type::BOOL, "iswitness", /* optional */ true, "If the address is a witness address"}, - {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"}, - {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"}, - {RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"}, - {RPCResult::Type::ARR, "error_locations", "Indices of likely error locations in address, if known (e.g. Bech32 errors)", - { - {RPCResult::Type::NUM, "index", "index of a potential error"}, - }}, - } - }, - RPCExamples{ - HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + - HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") - }, + {RPCResult::Type::NUM, "index", "index of a potential error"}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + + HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { std::string error_msg; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f4456bebc5..021e6ae320 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -106,82 +106,83 @@ static RPCHelpMan ping() static RPCHelpMan getpeerinfo() { - return RPCHelpMan{"getpeerinfo", - "\nReturns data about each connected network node as a json array of objects.\n", - {}, - RPCResult{ - RPCResult::Type::ARR, "", "", + return RPCHelpMan{ + "getpeerinfo", + "\nReturns data about each connected network node as a json array of objects.\n", + {}, + RPCResult{ + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { { - {RPCResult::Type::OBJ, "", "", - { - { - {RPCResult::Type::NUM, "id", "Peer index"}, - {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, - {RPCResult::Type::STR, "addrbind", /* optional */ true, "(ip:port) Bind address of the connection to the peer"}, - {RPCResult::Type::STR, "addrlocal", /* optional */ true, "(ip:port) Local address as reported by the peer"}, - {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"}, - {RPCResult::Type::NUM, "mapped_as", /* optional */ true, "The AS in the BGP route to the peer used for diversifying\n" - "peer selection (only available if the asmap config flag is set)"}, - {RPCResult::Type::STR_HEX, "services", "The services offered"}, - {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", - { - {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} - }}, - {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, - {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, - {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, - {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"}, - {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"}, - {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, - {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, - {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, - {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, - {RPCResult::Type::NUM, "pingtime", /* optional */ true, "ping time (if available)"}, - {RPCResult::Type::NUM, "minping", /* optional */ true, "minimum observed ping time (if any at all)"}, - {RPCResult::Type::NUM, "pingwait", /* optional */ true, "ping wait (if non-zero)"}, - {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, - {RPCResult::Type::STR, "subver", "The string version"}, - {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, - {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, - {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, - {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"}, - {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"}, - {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, - {RPCResult::Type::ARR, "inflight", "", - { - {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, - }}, - {RPCResult::Type::BOOL, "addr_relay_enabled", "Whether we participate in address relay with this peer"}, - {RPCResult::Type::NUM, "addr_processed", "The total number of addresses processed, excluding those dropped due to rate limiting"}, - {RPCResult::Type::NUM, "addr_rate_limited", "The total number of addresses dropped due to rate limiting"}, - {RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer", - { - {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"}, - }}, - {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, - {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", - { - {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" - "When a message type is not listed in this json object, the bytes sent are 0.\n" - "Only known message types can appear as keys in the object."} - }}, - {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "", - { - {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" - "When a message type is not listed in this json object, the bytes received are 0.\n" - "Only known message types can appear as keys in the object and all bytes received\n" - "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} - }}, - {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" - "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" - "best capture connection behaviors."}, - }}, + {RPCResult::Type::NUM, "id", "Peer index"}, + {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, + {RPCResult::Type::STR, "addrbind", /* optional */ true, "(ip:port) Bind address of the connection to the peer"}, + {RPCResult::Type::STR, "addrlocal", /* optional */ true, "(ip:port) Local address as reported by the peer"}, + {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"}, + {RPCResult::Type::NUM, "mapped_as", /* optional */ true, "The AS in the BGP route to the peer used for diversifying\n" + "peer selection (only available if the asmap config flag is set)"}, + {RPCResult::Type::STR_HEX, "services", "The services offered"}, + {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} }}, - }, - RPCExamples{ - HelpExampleCli("getpeerinfo", "") + {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, + {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, + {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, + {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"}, + {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"}, + {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, + {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, + {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, + {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, + {RPCResult::Type::NUM, "pingtime", /* optional */ true, "ping time (if available)"}, + {RPCResult::Type::NUM, "minping", /* optional */ true, "minimum observed ping time (if any at all)"}, + {RPCResult::Type::NUM, "pingwait", /* optional */ true, "ping wait (if non-zero)"}, + {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, + {RPCResult::Type::STR, "subver", "The string version"}, + {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, + {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, + {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, + {RPCResult::Type::NUM, "startingheight", /*optional=*/true, "The starting height (block) of the peer"}, + {RPCResult::Type::NUM, "synced_headers", /*optional=*/true, "The last header we have in common with this peer"}, + {RPCResult::Type::NUM, "synced_blocks", /*optional=*/true, "The last block we have in common with this peer"}, + {RPCResult::Type::ARR, "inflight", /*optional=*/true, "", + { + {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, + }}, + {RPCResult::Type::BOOL, "addr_relay_enabled", /*optional=*/true, "Whether we participate in address relay with this peer"}, + {RPCResult::Type::NUM, "addr_processed", /*optional=*/true, "The total number of addresses processed, excluding those dropped due to rate limiting"}, + {RPCResult::Type::NUM, "addr_rate_limited", /*optional=*/true, "The total number of addresses dropped due to rate limiting"}, + {RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer", + { + {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"}, + }}, + {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, + {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" + "When a message type is not listed in this json object, the bytes sent are 0.\n" + "Only known message types can appear as keys in the object."} + }}, + {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" + "When a message type is not listed in this json object, the bytes received are 0.\n" + "Only known message types can appear as keys in the object and all bytes received\n" + "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} + }}, + {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" + "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" + "best capture connection behaviors."}, + }}, + }}, + }, + RPCExamples{ + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "") - }, + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { NodeContext& node = EnsureAnyNodeContext(request.context); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 2d7f5f2894..683cf88af8 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -317,7 +317,7 @@ public: UniValue obj(UniValue::VOBJ); obj.pushKV("iswitness", true); obj.pushKV("witness_version", (int)id.version); - obj.pushKV("witness_program", HexStr(Span<const unsigned char>(id.program, id.length))); + obj.pushKV("witness_program", HexStr({id.program, id.length})); return obj; } }; diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index c3b4d1ddaa..30f929664f 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -631,7 +631,7 @@ public: out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second))); } - output_scripts = MakeScripts(pubkeys, MakeSpan(subscripts), out); + output_scripts = MakeScripts(pubkeys, Span{subscripts}, out); return true; } @@ -974,10 +974,10 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S } KeyPath path; DeriveType type = DeriveType::NO; - if (split.back() == MakeSpan("*").first(1)) { + if (split.back() == Span{"*"}.first(1)) { split.pop_back(); type = DeriveType::UNHARDENED; - } else if (split.back() == MakeSpan("*'").first(2) || split.back() == MakeSpan("*h").first(2)) { + } else if (split.back() == Span{"*'"}.first(2) || split.back() == Span{"*h"}.first(2)) { split.pop_back(); type = DeriveType::HARDENED; } @@ -1252,7 +1252,7 @@ std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseS std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider) { if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) { - XOnlyPubKey key{Span<const unsigned char>{script.data() + 1, script.data() + 33}}; + XOnlyPubKey key{Span{script}.subspan(1, 32)}; return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider)); } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d83ec7192b..6433ba1b58 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1858,7 +1858,7 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25 uint256 k = tapleaf_hash; for (int i = 0; i < path_len; ++i) { CHashWriter ss_branch{HASHER_TAPBRANCH}; - Span<const unsigned char> node(control.data() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE); + Span node{Span{control}.subspan(TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE)}; if (std::lexicographical_compare(k.begin(), k.end(), node.begin(), node.end())) { ss_branch << k << node; } else { @@ -1874,7 +1874,7 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE); assert(program.size() >= uint256::size()); //! The internal pubkey (x-only, so no Y coordinate parity). - const XOnlyPubKey p{Span<const unsigned char>{control.data() + 1, control.data() + TAPROOT_CONTROL_BASE_SIZE}}; + const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)}; //! The output pubkey (taken from the scriptPubKey). const XOnlyPubKey q{program}; // Compute the Merkle root from the leaf and the provided path. @@ -1886,7 +1886,7 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh) { CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR) - Span<const valtype> stack{witness.stack}; + Span stack{witness.stack}; ScriptExecutionData execdata; if (witversion == 0) { diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8e044b1e00..3f7060879c 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -167,7 +167,7 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu // <xonly pubkey> OP_CHECKSIG if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) { - XOnlyPubKey pubkey(MakeSpan(script).subspan(1, 32)); + XOnlyPubKey pubkey{Span{script}.subspan(1, 32)}; std::vector<unsigned char> sig; if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) { result = Vector(std::move(sig)); diff --git a/src/script/sign.h b/src/script/sign.h index 62335b644a..50525af332 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -149,7 +149,7 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k if (!keypath_pair.first.IsValid()) { throw std::ios_base::failure("Invalid CPubKey being serialized"); } - SerializeToVector(s, type, MakeSpan(keypath_pair.first)); + SerializeToVector(s, type, Span{keypath_pair.first}); WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t)); s << keypath_pair.second.fingerprint; for (const auto& path : keypath_pair.second.path) { diff --git a/src/signet.cpp b/src/signet.cpp index aafd1999ee..5cecb8decc 100644 --- a/src/signet.cpp +++ b/src/signet.cpp @@ -38,7 +38,7 @@ static bool FetchAndClearCommitmentSection(const Span<const uint8_t> header, CSc std::vector<uint8_t> pushdata; while (witness_commitment.GetOp(pc, opcode, pushdata)) { if (pushdata.size() > 0) { - if (!found_header && pushdata.size() > (size_t) header.size() && Span<const uint8_t>(pushdata.data(), header.size()) == header) { + if (!found_header && pushdata.size() > (size_t)header.size() && Span{pushdata}.first(header.size()) == header) { // pushdata only counts if it has the header _and_ some data result.insert(result.end(), pushdata.begin() + header.size(), pushdata.end()); pushdata.erase(pushdata.begin() + header.size(), pushdata.end()); @@ -98,7 +98,7 @@ std::optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& c // no signet solution -- allow this to support OP_TRUE as trivial block challenge } else { try { - VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0); + SpanReader v{SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0}; v >> tx_spending.vin[0].scriptSig; v >> tx_spending.vin[0].scriptWitness.stack; if (!v.empty()) return std::nullopt; // extraneous data encountered diff --git a/src/span.h b/src/span.h index 746e41f2f4..6703e889c5 100644 --- a/src/span.h +++ b/src/span.h @@ -222,13 +222,15 @@ public: template <typename O> friend class Span; }; -// MakeSpan helps constructing a Span of the right type automatically. -/** MakeSpan for arrays: */ -template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); } -/** MakeSpan for temporaries / rvalue references, only supporting const output. */ -template <typename V> constexpr auto MakeSpan(V&& v SPAN_ATTR_LIFETIMEBOUND) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); } -/** MakeSpan for (lvalue) references, supporting mutable output. */ -template <typename V> constexpr auto MakeSpan(V& v SPAN_ATTR_LIFETIMEBOUND) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; } +// Deduction guides for Span +// For the pointer/size based and iterator based constructor: +template <typename T, typename EndOrSize> Span(T*, EndOrSize) -> Span<T>; +// For the array constructor: +template <typename T, std::size_t N> Span(T (&)[N]) -> Span<T>; +// For the temporaries/rvalue references constructor, only supporting const output. +template <typename T> Span(T&&) -> Span<std::enable_if_t<!std::is_lvalue_reference_v<T>, const std::remove_pointer_t<decltype(std::declval<T&&>().data())>>>; +// For (lvalue) references, supporting mutable output. +template <typename T> Span(T&) -> Span<std::remove_pointer_t<decltype(std::declval<T&>().data())>>; /** Pop the last element off a span, and return a reference to that element. */ template <typename T> @@ -256,12 +258,12 @@ Span<std::byte> AsWritableBytes(Span<T> s) noexcept template <typename V> Span<const std::byte> MakeByteSpan(V&& v) noexcept { - return AsBytes(MakeSpan(std::forward<V>(v))); + return AsBytes(Span{std::forward<V>(v)}); } template <typename V> Span<std::byte> MakeWritableByteSpan(V&& v) noexcept { - return AsWritableBytes(MakeSpan(std::forward<V>(v))); + return AsWritableBytes(Span{std::forward<V>(v)}); } // Helper functions to safely cast to unsigned char pointers. @@ -274,7 +276,7 @@ inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_c // Helper function to safely convert a Span to a Span<[const] unsigned char>. template <typename T> constexpr auto UCharSpanCast(Span<T> s) -> Span<typename std::remove_pointer<decltype(UCharCast(s.data()))>::type> { return {UCharCast(s.data()), s.size()}; } -/** Like MakeSpan, but for (const) unsigned char member types only. Only works for (un)signed char containers. */ -template <typename V> constexpr auto MakeUCharSpan(V&& v) -> decltype(UCharSpanCast(MakeSpan(std::forward<V>(v)))) { return UCharSpanCast(MakeSpan(std::forward<V>(v))); } +/** Like the Span constructor, but for (const) unsigned char member types only. Only works for (un)signed char containers. */ +template <typename V> constexpr auto MakeUCharSpan(V&& v) -> decltype(UCharSpanCast(Span{std::forward<V>(v)})) { return UCharSpanCast(Span{std::forward<V>(v)}); } #endif // BITCOIN_SPAN_H diff --git a/src/streams.h b/src/streams.h index 9e8f379cd2..dbb942f306 100644 --- a/src/streams.h +++ b/src/streams.h @@ -128,15 +128,14 @@ private: size_t nPos; }; -/** Minimal stream for reading from an existing vector by reference +/** Minimal stream for reading from an existing byte array by Span. */ -class VectorReader +class SpanReader { private: const int m_type; const int m_version; - const std::vector<unsigned char>& m_data; - size_t m_pos = 0; + Span<const unsigned char> m_data; public: @@ -146,12 +145,13 @@ public: * @param[in] data Referenced byte vector to overwrite/append * @param[in] pos Starting position. Vector index where reads should start. */ - VectorReader(int type, int version, const std::vector<unsigned char>& data, size_t pos) - : m_type(type), m_version(version), m_data(data), m_pos(pos) + SpanReader(int type, int version, Span<const unsigned char> data, size_t pos) + : m_type(type), m_version(version), m_data(data) { - if (m_pos > m_data.size()) { - throw std::ios_base::failure("VectorReader(...): end of data (m_pos > m_data.size())"); + if (pos > m_data.size()) { + throw std::ios_base::failure("SpanReader(...): end of data (pos > m_data.size())"); } + data = data.subspan(pos); } /** @@ -159,15 +159,15 @@ public: * @param[in] args A list of items to deserialize starting at pos. */ template <typename... Args> - VectorReader(int type, int version, const std::vector<unsigned char>& data, size_t pos, + SpanReader(int type, int version, Span<const unsigned char> data, size_t pos, Args&&... args) - : VectorReader(type, version, data, pos) + : SpanReader(type, version, data, pos) { ::UnserializeMany(*this, std::forward<Args>(args)...); } template<typename T> - VectorReader& operator>>(T&& obj) + SpanReader& operator>>(T&& obj) { // Unserialize from this stream ::Unserialize(*this, obj); @@ -177,8 +177,8 @@ public: int GetVersion() const { return m_version; } int GetType() const { return m_type; } - size_t size() const { return m_data.size() - m_pos; } - bool empty() const { return m_data.size() == m_pos; } + size_t size() const { return m_data.size(); } + bool empty() const { return m_data.empty(); } void read(char* dst, size_t n) { @@ -187,12 +187,11 @@ public: } // Read from the beginning of the buffer - size_t pos_next = m_pos + n; - if (pos_next > m_data.size()) { - throw std::ios_base::failure("VectorReader::read(): end of data"); + if (n > m_data.size()) { + throw std::ios_base::failure("SpanReader::read(): end of data"); } - memcpy(dst, m_data.data() + m_pos, n); - m_pos = pos_next; + memcpy(dst, m_data.data(), n); + m_data = m_data.subspan(n); } }; diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index cbdedd1b85..bedef5de37 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -770,8 +770,8 @@ static void TestSHA3_256(const std::string& input, const std::string& output) int s1 = InsecureRandRange(in_bytes.size() + 1); int s2 = InsecureRandRange(in_bytes.size() + 1 - s1); int s3 = in_bytes.size() - s1 - s2; - sha.Write(MakeSpan(in_bytes).first(s1)).Write(MakeSpan(in_bytes).subspan(s1, s2)); - sha.Write(MakeSpan(in_bytes).last(s3)).Finalize(out); + sha.Write(Span{in_bytes}.first(s1)).Write(Span{in_bytes}.subspan(s1, s2)); + sha.Write(Span{in_bytes}.last(s3)).Finalize(out); BOOST_CHECK(std::equal(std::begin(out_bytes), std::end(out_bytes), out)); } diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp index d402f8632c..c5e9c56049 100644 --- a/src/test/fuzz/asmap.cpp +++ b/src/test/fuzz/asmap.cpp @@ -49,7 +49,7 @@ FUZZ_TARGET(asmap) CNetAddr net_addr; if (ipv6) { assert(addr_size == ADDR_IPV6_SIZE); - net_addr.SetLegacyIPv6(Span<const uint8_t>(addr_data, addr_size)); + net_addr.SetLegacyIPv6({addr_data, addr_size}); } else { assert(addr_size == ADDR_IPV4_SIZE); in_addr ipv4; diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp index c99bf940c7..7b4634c67b 100644 --- a/src/test/fuzz/golomb_rice.cpp +++ b/src/test/fuzz/golomb_rice.cpp @@ -82,8 +82,8 @@ FUZZ_TARGET(golomb_rice) std::vector<uint64_t> decoded_deltas; { - VectorReader stream{SER_NETWORK, 0, golomb_rice_data, 0}; - BitStreamReader<VectorReader> bitreader(stream); + SpanReader stream{SER_NETWORK, 0, golomb_rice_data, 0}; + BitStreamReader<SpanReader> bitreader{stream}; const uint32_t n = static_cast<uint32_t>(ReadCompactSize(stream)); for (uint32_t i = 0; i < n; ++i) { decoded_deltas.push_back(GolombRiceDecode(bitreader, BASIC_FILTER_P)); @@ -94,14 +94,14 @@ FUZZ_TARGET(golomb_rice) { const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider, 1024); - VectorReader stream{SER_NETWORK, 0, random_bytes, 0}; + SpanReader stream{SER_NETWORK, 0, random_bytes, 0}; uint32_t n; try { n = static_cast<uint32_t>(ReadCompactSize(stream)); } catch (const std::ios_base::failure&) { return; } - BitStreamReader<VectorReader> bitreader(stream); + BitStreamReader<SpanReader> bitreader{stream}; for (uint32_t i = 0; i < std::min<uint32_t>(n, 1024); ++i) { try { (void)GolombRiceDecode(bitreader, BASIC_FILTER_P); diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 251687104e..790421d60c 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -41,13 +41,17 @@ struct RPCFuzzTestingSetup : public TestingSetup { { } - UniValue CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments) + void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments) { JSONRPCRequest request; request.context = &m_node; request.strMethod = rpc_method; - request.params = RPCConvertValues(rpc_method, arguments); - return tableRPC.execute(request); + try { + request.params = RPCConvertValues(rpc_method, arguments); + } catch (const std::runtime_error&) { + return; + } + tableRPC.execute(request); } std::vector<std::string> GetRPCCommands() const @@ -353,7 +357,11 @@ FUZZ_TARGET_INIT(rpc, initialize_rpc) } try { rpc_testing_setup->CallRPC(rpc_command, arguments); - } catch (const UniValue&) { - } catch (const std::runtime_error&) { + } catch (const UniValue& json_rpc_error) { + const std::string error_msg{find_value(json_rpc_error, "message").get_str()}; + if (error_msg.find("Internal bug detected") != std::string::npos) { + // Only allow the intentional internal bug + assert(error_msg.find("trigger_internal_bug") != std::string::npos); + } } } diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp index 4669f783aa..d661d79e84 100644 --- a/src/test/fuzz/script_assets_test_minimizer.cpp +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -54,7 +54,7 @@ CMutableTransaction TxFromHex(const std::string& str) { CMutableTransaction tx; try { - VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx; + SpanReader{SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0} >> tx; } catch (const std::ios_base::failure&) { throw std::runtime_error("Tx deserialization failure"); } @@ -68,7 +68,7 @@ std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue) for (size_t i = 0; i < univalue.size(); ++i) { CTxOut txout; try { - VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout; + SpanReader{SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0} >> txout; } catch (const std::ios_base::failure&) { throw std::runtime_error("Prevout invalid format"); } diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 02039cba81..1b9f0c8a02 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -38,7 +38,7 @@ FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain) { CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION}; const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; - outfile << Span<const uint8_t>{file_data}; + outfile << Span{file_data}; } const auto ActivateFuzzedSnapshot{[&] { diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index 0361618c82..02268dbcf5 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse) privkey = DecodeSecret(exp_base58string); BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); - BOOST_CHECK_MESSAGE(Span<const uint8_t>{privkey} == Span<const uint8_t>{exp_payload}, "key mismatch:" + strTest); + BOOST_CHECK_MESSAGE(Span{privkey} == Span{exp_payload}, "key mismatch:" + strTest); // Private key must be invalid public key destination = DecodeDestination(exp_base58string); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index c5bd9c73fd..d0f0e7d50f 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -386,9 +386,9 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) s.SetVersion(s.GetVersion() | ADDRV2_FORMAT); // Valid IPv4. - s << MakeSpan(ParseHex("01" // network type (IPv4) - "04" // address length - "01020304")); // address + s << Span{ParseHex("01" // network type (IPv4) + "04" // address length + "01020304")}; // address s >> addr; BOOST_CHECK(addr.IsValid()); BOOST_CHECK(addr.IsIPv4()); @@ -397,35 +397,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Invalid IPv4, valid length but address itself is shorter. - s << MakeSpan(ParseHex("01" // network type (IPv4) - "04" // address length - "0102")); // address + s << Span{ParseHex("01" // network type (IPv4) + "04" // address length + "0102")}; // address BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("end of data")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Invalid IPv4, with bogus length. - s << MakeSpan(ParseHex("01" // network type (IPv4) - "05" // address length - "01020304")); // address + s << Span{ParseHex("01" // network type (IPv4) + "05" // address length + "01020304")}; // address BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("BIP155 IPv4 address with length 5 (should be 4)")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Invalid IPv4, with extreme length. - s << MakeSpan(ParseHex("01" // network type (IPv4) - "fd0102" // address length (513 as CompactSize) - "01020304")); // address + s << Span{ParseHex("01" // network type (IPv4) + "fd0102" // address length (513 as CompactSize) + "01020304")}; // address BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("Address too long: 513 > 512")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Valid IPv6. - s << MakeSpan(ParseHex("02" // network type (IPv6) - "10" // address length - "0102030405060708090a0b0c0d0e0f10")); // address + s << Span{ParseHex("02" // network type (IPv6) + "10" // address length + "0102030405060708090a0b0c0d0e0f10")}; // address s >> addr; BOOST_CHECK(addr.IsValid()); BOOST_CHECK(addr.IsIPv6()); @@ -434,10 +434,10 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Valid IPv6, contains embedded "internal". - s << MakeSpan(ParseHex( + s << Span{ParseHex( "02" // network type (IPv6) "10" // address length - "fd6b88c08724ca978112ca1bbdcafac2")); // address: 0xfd + sha256("bitcoin")[0:5] + + "fd6b88c08724ca978112ca1bbdcafac2")}; // address: 0xfd + sha256("bitcoin")[0:5] + // sha256(name)[0:10] s >> addr; BOOST_CHECK(addr.IsInternal()); @@ -446,44 +446,44 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Invalid IPv6, with bogus length. - s << MakeSpan(ParseHex("02" // network type (IPv6) - "04" // address length - "00")); // address + s << Span{ParseHex("02" // network type (IPv6) + "04" // address length + "00")}; // address BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("BIP155 IPv6 address with length 4 (should be 16)")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Invalid IPv6, contains embedded IPv4. - s << MakeSpan(ParseHex("02" // network type (IPv6) - "10" // address length - "00000000000000000000ffff01020304")); // address + s << Span{ParseHex("02" // network type (IPv6) + "10" // address length + "00000000000000000000ffff01020304")}; // address s >> addr; BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); // Invalid IPv6, contains embedded TORv2. - s << MakeSpan(ParseHex("02" // network type (IPv6) - "10" // address length - "fd87d87eeb430102030405060708090a")); // address + s << Span{ParseHex("02" // network type (IPv6) + "10" // address length + "fd87d87eeb430102030405060708090a")}; // address s >> addr; BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); // TORv2, no longer supported. - s << MakeSpan(ParseHex("03" // network type (TORv2) - "0a" // address length - "f1f2f3f4f5f6f7f8f9fa")); // address + s << Span{ParseHex("03" // network type (TORv2) + "0a" // address length + "f1f2f3f4f5f6f7f8f9fa")}; // address s >> addr; BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); // Valid TORv3. - s << MakeSpan(ParseHex("04" // network type (TORv3) - "20" // address length - "79bcc625184b05194975c28b66b66b04" // address - "69f7f6556fb1ac3189a79b40dda32f1f" - )); + s << Span{ParseHex("04" // network type (TORv3) + "20" // address length + "79bcc625184b05194975c28b66b66b04" // address + "69f7f6556fb1ac3189a79b40dda32f1f" + )}; s >> addr; BOOST_CHECK(addr.IsValid()); BOOST_CHECK(addr.IsTor()); @@ -493,20 +493,20 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Invalid TORv3, with bogus length. - s << MakeSpan(ParseHex("04" // network type (TORv3) - "00" // address length - "00" // address - )); + s << Span{ParseHex("04" // network type (TORv3) + "00" // address length + "00" // address + )}; BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("BIP155 TORv3 address with length 0 (should be 32)")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Valid I2P. - s << MakeSpan(ParseHex("05" // network type (I2P) - "20" // address length - "a2894dabaec08c0051a481a6dac88b64" // address - "f98232ae42d4b6fd2fa81952dfe36a87")); + s << Span{ParseHex("05" // network type (I2P) + "20" // address length + "a2894dabaec08c0051a481a6dac88b64" // address + "f98232ae42d4b6fd2fa81952dfe36a87")}; s >> addr; BOOST_CHECK(addr.IsValid()); BOOST_CHECK(addr.IsI2P()); @@ -516,20 +516,20 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Invalid I2P, with bogus length. - s << MakeSpan(ParseHex("05" // network type (I2P) - "03" // address length - "00" // address - )); + s << Span{ParseHex("05" // network type (I2P) + "03" // address length + "00" // address + )}; BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("BIP155 I2P address with length 3 (should be 32)")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Valid CJDNS. - s << MakeSpan(ParseHex("06" // network type (CJDNS) - "10" // address length - "fc000001000200030004000500060007" // address - )); + s << Span{ParseHex("06" // network type (CJDNS) + "10" // address length + "fc000001000200030004000500060007" // address + )}; s >> addr; BOOST_CHECK(addr.IsValid()); BOOST_CHECK(addr.IsCJDNS()); @@ -538,49 +538,49 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_REQUIRE(s.empty()); // Invalid CJDNS, wrong prefix. - s << MakeSpan(ParseHex("06" // network type (CJDNS) - "10" // address length - "aa000001000200030004000500060007" // address - )); + s << Span{ParseHex("06" // network type (CJDNS) + "10" // address length + "aa000001000200030004000500060007" // address + )}; s >> addr; BOOST_CHECK(addr.IsCJDNS()); BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); // Invalid CJDNS, with bogus length. - s << MakeSpan(ParseHex("06" // network type (CJDNS) - "01" // address length - "00" // address - )); + s << Span{ParseHex("06" // network type (CJDNS) + "01" // address length + "00" // address + )}; BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("BIP155 CJDNS address with length 1 (should be 16)")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Unknown, with extreme length. - s << MakeSpan(ParseHex("aa" // network type (unknown) - "fe00000002" // address length (CompactSize's MAX_SIZE) - "01020304050607" // address - )); + s << Span{ParseHex("aa" // network type (unknown) + "fe00000002" // address length (CompactSize's MAX_SIZE) + "01020304050607" // address + )}; BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("Address too long: 33554432 > 512")); BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. s.clear(); // Unknown, with reasonable length. - s << MakeSpan(ParseHex("aa" // network type (unknown) - "04" // address length - "01020304" // address - )); + s << Span{ParseHex("aa" // network type (unknown) + "04" // address length + "01020304" // address + )}; s >> addr; BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); // Unknown, with zero length. - s << MakeSpan(ParseHex("aa" // network type (unknown) - "00" // address length - "" // address - )); + s << Span{ParseHex("aa" // network type (unknown) + "00" // address length + "" // address + )}; s >> addr; BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index a89eab68e9..12432828ac 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -284,7 +284,7 @@ public: CScript scriptPubKey = script; if (wm == WitnessMode::PKH) { uint160 hash; - CHash160().Write(MakeSpan(script).subspan(1)).Finalize(hash); + CHash160().Write(Span{script}.subspan(1)).Finalize(hash); script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(hash) << OP_EQUALVERIFY << OP_CHECKSIG; scriptPubKey = CScript() << witnessversion << ToByteVector(hash); } else if (wm == WitnessMode::SH) { @@ -1473,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) static CMutableTransaction TxFromHex(const std::string& str) { CMutableTransaction tx; - VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx; + SpanReader{SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0} >> tx; return tx; } @@ -1483,7 +1483,7 @@ static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue) std::vector<CTxOut> prevouts; for (size_t i = 0; i < univalue.size(); ++i) { CTxOut txout; - VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout; + SpanReader{SER_DISK, 0, ParseHex(univalue[i].get_str()), 0} >> txout; prevouts.push_back(std::move(txout)); } return prevouts; @@ -1754,7 +1754,7 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors) for (const auto& vec : vectors.getValues()) { auto txhex = ParseHex(vec["given"]["rawUnsignedTx"].get_str()); CMutableTransaction tx; - VectorReader(SER_NETWORK, PROTOCOL_VERSION, txhex, 0) >> tx; + SpanReader{SER_NETWORK, PROTOCOL_VERSION, txhex, 0} >> tx; std::vector<CTxOut> utxos; for (const auto& utxo_spent : vec["given"]["utxosSpent"].getValues()) { auto script_bytes = ParseHex(utxo_spent["scriptPubKey"].get_str()); @@ -1812,7 +1812,7 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors) BOOST_CHECK_EQUAL(HexStr(sighash), input["intermediary"]["sigHash"].get_str()); // To verify the sigmsg, hash the expected sigmsg, and compare it with the (expected) sighash. - BOOST_CHECK_EQUAL(HexStr((CHashWriter(HASHER_TAPSIGHASH) << MakeSpan(ParseHex(input["intermediary"]["sigMsg"].get_str()))).GetSHA256()), input["intermediary"]["sigHash"].get_str()); + BOOST_CHECK_EQUAL(HexStr((CHashWriter(HASHER_TAPSIGHASH) << Span{ParseHex(input["intermediary"]["sigMsg"].get_str())}).GetSHA256()), input["intermediary"]["sigHash"].get_str()); } } diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index b8d76c9608..f551663789 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) { std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6}; - VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); + SpanReader reader{SER_NETWORK, INIT_PROTO_VERSION, vch, 0}; BOOST_CHECK_EQUAL(reader.size(), 6U); BOOST_CHECK(!reader.empty()); @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); // Read a 4 bytes as a signed int from the beginning of the buffer. - VectorReader new_reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); + SpanReader new_reader{SER_NETWORK, INIT_PROTO_VERSION, vch, 0}; new_reader >> d; BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256 BOOST_CHECK_EQUAL(new_reader.size(), 2U); @@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue) { std::vector<uint8_t> data{0x82, 0xa7, 0x31}; - VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, data, /* pos= */ 0); + SpanReader reader{SER_NETWORK, INIT_PROTO_VERSION, data, /* pos= */ 0}; uint32_t varint = 0; // Deserialize into r-value reader >> VARINT(varint); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 76a690fd28..9540cead24 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -142,13 +142,11 @@ BOOST_AUTO_TEST_CASE(util_HexStr) "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); BOOST_CHECK_EQUAL( - HexStr(Span<const unsigned char>( - ParseHex_expected + sizeof(ParseHex_expected), - ParseHex_expected + sizeof(ParseHex_expected))), + HexStr(Span{ParseHex_expected}.last(0)), ""); BOOST_CHECK_EQUAL( - HexStr(Span<const unsigned char>(ParseHex_expected, ParseHex_expected)), + HexStr(Span{ParseHex_expected}.first(0)), ""); { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 27fbb8acac..fcfc27d38e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -5,6 +5,7 @@ #include <txmempool.h> +#include <chain.h> #include <coins.h> #include <consensus/consensus.h> #include <consensus/tx_verify.h> @@ -73,16 +74,15 @@ private: const LockPoints& lp; }; -bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) +bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) { AssertLockHeld(cs_main); - assert(lp); // If there are relative lock times then the maxInputBlock will be set // If there are no relative lock times, the LockPoints don't depend on the chain - if (lp->maxInputBlock) { + if (lp.maxInputBlock) { // Check whether active_chain is an extension of the block at which the LockPoints // calculation was valid. If not LockPoints are no longer valid - if (!active_chain.Contains(lp->maxInputBlock)) { + if (!active_chain.Contains(lp.maxInputBlock)) { return false; } } @@ -649,8 +649,8 @@ void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check } RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { - LockPoints lp = it->GetLockPoints(); - if (!TestLockPointValidity(chain, &lp)) { + const LockPoints lp{it->GetLockPoints()}; + if (!TestLockPointValidity(chain, lp)) { mapTx.modify(it, update_lock_points(lp)); } } diff --git a/src/txmempool.h b/src/txmempool.h index c6e08a3ca5..f87ecc9cd0 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -14,7 +14,6 @@ #include <utility> #include <vector> -#include <chain.h> #include <coins.h> #include <consensus/amount.h> #include <indirectmap.h> @@ -26,12 +25,13 @@ #include <util/epochguard.h> #include <util/hasher.h> -#include <boost/multi_index_container.hpp> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/sequenced_index.hpp> +#include <boost/multi_index_container.hpp> class CBlockIndex; +class CChain; class CChainState; extern RecursiveMutex cs_main; @@ -53,7 +53,7 @@ struct LockPoints { /** * Test whether the LockPoints height and time are still valid on the current chain */ -bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); struct CompareIteratorByHash { // SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T> diff --git a/src/validation.cpp b/src/validation.cpp index 6ed5e7a548..203bd57676 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -357,7 +357,7 @@ void CChainState::MaybeUpdateMempoolForReorg( AssertLockHeld(::cs_main); const CTransaction& tx = it->GetTx(); LockPoints lp = it->GetLockPoints(); - bool validLP = TestLockPointValidity(m_chain, &lp); + const bool validLP{TestLockPointValidity(m_chain, lp)}; CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool); if (!CheckFinalTx(m_chain.Tip(), tx, flags) || !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) { @@ -371,8 +371,8 @@ void CChainState::MaybeUpdateMempoolForReorg( continue; const Coin &coin = CoinsTip().AccessCoin(txin.prevout); assert(!coin.IsSpent()); - unsigned int nMemPoolHeight = m_chain.Tip()->nHeight + 1; - if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) { + const auto mempool_spend_height{m_chain.Tip()->nHeight + 1}; + if (coin.IsSpent() || (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY)) { should_remove = true; break; } @@ -723,6 +723,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // to coins_to_uncache) m_view.SetBackend(m_dummy); + assert(m_active_chainstate.m_blockman.LookupBlockIndex(m_view.GetBestBlock()) == m_active_chainstate.m_chain.Tip()); + // Only accept BIP68 sequence locked transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. @@ -731,7 +733,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final"); - if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) { + // The mempool holds txs for the next block, so pass height+1 to CheckTxInputs + if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) { return false; // state filled in by CheckTxInputs } @@ -1311,14 +1314,6 @@ bool CScriptCheck::operator()() { return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error); } -int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs) -{ - AssertLockHeld(cs_main); - CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); - return pindexPrev->nHeight + 1; -} - - static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache; static CSHA256 g_scriptExecutionCacheHasher; diff --git a/src/validation.h b/src/validation.h index 881438f37a..9385dfdab4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -478,13 +478,6 @@ public: //! Returns last CBlockIndex* that is a checkpoint CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - /** - * Return the spend height, which is one more than the inputs.GetBestBlock(). - * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) - * This is also true for mempool checks. - */ - int GetSpendHeight(const CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - ~BlockManager() { Unload(); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpc/backup.cpp index db22a19a63..a61ebb26b3 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpc/backup.cpp @@ -35,7 +35,7 @@ std::string static EncodeDumpString(const std::string &str) { std::stringstream ret; for (const unsigned char c : str) { if (c <= 32 || c >= 128 || c == '%') { - ret << '%' << HexStr(Span<const unsigned char>(&c, 1)); + ret << '%' << HexStr({&c, 1}); } else { ret << c; } @@ -1808,11 +1808,10 @@ RPCHelpMan listdescriptors() } spk.pushKV("desc", descriptor); spk.pushKV("timestamp", wallet_descriptor.creation_time); - const bool active = active_spk_mans.count(desc_spk_man) != 0; - spk.pushKV("active", active); - const auto& type = wallet_descriptor.descriptor->GetOutputType(); - if (active && type) { - spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man); + spk.pushKV("active", active_spk_mans.count(desc_spk_man) != 0); + const auto internal = wallet->IsInternalScriptPubKeyMan(desc_spk_man); + if (internal.has_value()) { + spk.pushKV("internal", *internal); } if (wallet_descriptor.descriptor->IsRange()) { UniValue range(UniValue::VARR); @@ -1832,3 +1831,99 @@ RPCHelpMan listdescriptors() }, }; } + +RPCHelpMan backupwallet() +{ + return RPCHelpMan{"backupwallet", + "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n", + { + {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"}, + }, + RPCResult{RPCResult::Type::NONE, "", ""}, + RPCExamples{ + HelpExampleCli("backupwallet", "\"backup.dat\"") + + HelpExampleRpc("backupwallet", "\"backup.dat\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + + LOCK(pwallet->cs_wallet); + + std::string strDest = request.params[0].get_str(); + if (!pwallet->BackupWallet(strDest)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + } + + return NullUniValue; +}, + }; +} + + +RPCHelpMan restorewallet() +{ + return RPCHelpMan{ + "restorewallet", + "\nRestore and loads a wallet from backup.\n", + { + {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"}, + {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."}, + {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "The wallet name if restored successfully."}, + {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."}, + } + }, + RPCExamples{ + HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") + + HelpExampleRpc("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") + + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) + + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + WalletContext& context = EnsureWalletContext(request.context); + + auto backup_file = fs::u8path(request.params[1].get_str()); + + if (!fs::exists(backup_file)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Backup file does not exist"); + } + + std::string wallet_name = request.params[0].get_str(); + + const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); + + if (fs::exists(wallet_path)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet name already exists."); + } + + if (!TryCreateDirectories(wallet_path)) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.u8string())); + } + + auto wallet_file = wallet_path / "wallet.dat"; + + fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists); + + auto [wallet, warnings] = LoadWalletHelper(context, request.params[2], wallet_name); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("name", wallet->GetName()); + obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); + + return obj; + +}, + }; +} diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp new file mode 100644 index 0000000000..e659f434a3 --- /dev/null +++ b/src/wallet/rpc/encrypt.cpp @@ -0,0 +1,248 @@ +// Copyright (c) 2011-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <rpc/util.h> +#include <wallet/rpc/util.h> +#include <wallet/wallet.h> + + +RPCHelpMan walletpassphrase() +{ + return RPCHelpMan{"walletpassphrase", + "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" + "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" + "\nNote:\n" + "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" + "time that overrides the old one.\n", + { + {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"}, + {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."}, + }, + RPCResult{RPCResult::Type::NONE, "", ""}, + RPCExamples{ + "\nUnlock the wallet for 60 seconds\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + + "\nLock the wallet again (before 60 seconds)\n" + + HelpExampleCli("walletlock", "") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + CWallet* const pwallet = wallet.get(); + + int64_t nSleepTime; + int64_t relock_time; + // Prevent concurrent calls to walletpassphrase with the same wallet. + LOCK(pwallet->m_unlock_mutex); + { + LOCK(pwallet->cs_wallet); + + if (!pwallet->IsCrypted()) { + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + } + + // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + strWalletPass = request.params[0].get_str().c_str(); + + // Get the timeout + nSleepTime = request.params[1].get_int64(); + // Timeout cannot be negative, otherwise it will relock immediately + if (nSleepTime < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative."); + } + // Clamp timeout + constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug? + if (nSleepTime > MAX_SLEEP_TIME) { + nSleepTime = MAX_SLEEP_TIME; + } + + if (strWalletPass.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); + } + + if (!pwallet->Unlock(strWalletPass)) { + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + + pwallet->TopUpKeyPool(); + + pwallet->nRelockTime = GetTime() + nSleepTime; + relock_time = pwallet->nRelockTime; + } + + // rpcRunLater must be called without cs_wallet held otherwise a deadlock + // can occur. The deadlock would happen when RPCRunLater removes the + // previous timer (and waits for the callback to finish if already running) + // and the callback locks cs_wallet. + AssertLockNotHeld(wallet->cs_wallet); + // Keep a weak pointer to the wallet so that it is possible to unload the + // wallet before the following callback is called. If a valid shared pointer + // is acquired in the callback then the wallet is still loaded. + std::weak_ptr<CWallet> weak_wallet = wallet; + pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] { + if (auto shared_wallet = weak_wallet.lock()) { + LOCK(shared_wallet->cs_wallet); + // Skip if this is not the most recent rpcRunLater callback. + if (shared_wallet->nRelockTime != relock_time) return; + shared_wallet->Lock(); + shared_wallet->nRelockTime = 0; + } + }, nSleepTime); + + return NullUniValue; +}, + }; +} + + +RPCHelpMan walletpassphrasechange() +{ + return RPCHelpMan{"walletpassphrasechange", + "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n", + { + {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"}, + {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"}, + }, + RPCResult{RPCResult::Type::NONE, "", ""}, + RPCExamples{ + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + LOCK(pwallet->cs_wallet); + + if (!pwallet->IsCrypted()) { + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + } + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = request.params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = request.params[1].get_str().c_str(); + + if (strOldWalletPass.empty() || strNewWalletPass.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); + } + + if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) { + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + + return NullUniValue; +}, + }; +} + + +RPCHelpMan walletlock() +{ + return RPCHelpMan{"walletlock", + "\nRemoves the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked.\n", + {}, + RPCResult{RPCResult::Type::NONE, "", ""}, + RPCExamples{ + "\nSet the passphrase for 2 minutes to perform a transaction\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + + "\nPerform a send (requires passphrase set)\n" + + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") + + "\nClear the passphrase since we are done before 2 minutes is up\n" + + HelpExampleCli("walletlock", "") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("walletlock", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + LOCK(pwallet->cs_wallet); + + if (!pwallet->IsCrypted()) { + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + } + + pwallet->Lock(); + pwallet->nRelockTime = 0; + + return NullUniValue; +}, + }; +} + + +RPCHelpMan encryptwallet() +{ + return RPCHelpMan{"encryptwallet", + "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" + "After this, any calls that interact with private keys such as sending or signing \n" + "will require the passphrase to be set prior the making these calls.\n" + "Use the walletpassphrase call for this, and then walletlock call.\n" + "If the wallet is already encrypted, use the walletpassphrasechange call.\n", + { + {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."}, + }, + RPCResult{RPCResult::Type::STR, "", "A string with further instructions"}, + RPCExamples{ + "\nEncrypt your wallet\n" + + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + + "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + + "\nNow we can do something like sign\n" + + HelpExampleCli("signmessage", "\"address\" \"test message\"") + + "\nNow lock the wallet again by removing the passphrase\n" + + HelpExampleCli("walletlock", "") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + LOCK(pwallet->cs_wallet); + + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt."); + } + + if (pwallet->IsCrypted()) { + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + } + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = request.params[0].get_str().c_str(); + + if (strWalletPass.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); + } + + if (!pwallet->EncryptWallet(strWalletPass)) { + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + } + + return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; +}, + }; +} diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp index b926bfc75f..e2126b7236 100644 --- a/src/wallet/rpc/util.cpp +++ b/src/wallet/rpc/util.cpp @@ -5,6 +5,7 @@ #include <wallet/rpc/util.h> #include <rpc/util.h> +#include <util/translation.h> #include <util/url.h> #include <wallet/context.h> #include <wallet/wallet.h> @@ -120,3 +121,34 @@ std::string LabelFromValue(const UniValue& value) throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); return label; } + +std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name) +{ + DatabaseOptions options; + DatabaseStatus status; + options.require_existing = true; + bilingual_str error; + std::vector<bilingual_str> warnings; + std::optional<bool> load_on_start = load_on_start_param.isNull() ? std::nullopt : std::optional<bool>(load_on_start_param.get_bool()); + std::shared_ptr<CWallet> const wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); + + if (!wallet) { + // Map bad format to not found, since bad format is returned when the + // wallet directory exists, but doesn't contain a data file. + RPCErrorCode code = RPC_WALLET_ERROR; + switch (status) { + case DatabaseStatus::FAILED_NOT_FOUND: + case DatabaseStatus::FAILED_BAD_FORMAT: + code = RPC_WALLET_NOT_FOUND; + break; + case DatabaseStatus::FAILED_ALREADY_LOADED: + code = RPC_WALLET_ALREADY_LOADED; + break; + default: // RPC_WALLET_ERROR is returned for all other cases. + break; + } + throw JSONRPCError(code, error.original); + } + + return { wallet, warnings }; +} diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h index a493a80a74..a1fa4d49b1 100644 --- a/src/wallet/rpc/util.h +++ b/src/wallet/rpc/util.h @@ -8,7 +8,9 @@ #include <any> #include <memory> #include <string> +#include <vector> +struct bilingual_str; class CWallet; class JSONRPCRequest; class LegacyScriptPubKeyMan; @@ -35,4 +37,6 @@ bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param); bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet); std::string LabelFromValue(const UniValue& value); +std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name); + #endif // BITCOIN_WALLET_RPC_UTIL_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8cb2c46b63..181dd1bd54 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1656,41 +1656,6 @@ static RPCHelpMan abandontransaction() }; } - -static RPCHelpMan backupwallet() -{ - return RPCHelpMan{"backupwallet", - "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n", - { - {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"}, - }, - RPCResult{RPCResult::Type::NONE, "", ""}, - RPCExamples{ - HelpExampleCli("backupwallet", "\"backup.dat\"") - + HelpExampleRpc("backupwallet", "\"backup.dat\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) return NullUniValue; - - // Make sure the results are valid at least up to the most recent block - // the user could have gotten from another RPC command prior to now - pwallet->BlockUntilSyncedToCurrentChain(); - - LOCK(pwallet->cs_wallet); - - std::string strDest = request.params[0].get_str(); - if (!pwallet->BackupWallet(strDest)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); - } - - return NullUniValue; -}, - }; -} - - static RPCHelpMan keypoolrefill() { return RPCHelpMan{"keypoolrefill", @@ -1762,247 +1727,6 @@ static RPCHelpMan newkeypool() }; } - -static RPCHelpMan walletpassphrase() -{ - return RPCHelpMan{"walletpassphrase", - "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" - "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" - "\nNote:\n" - "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" - "time that overrides the old one.\n", - { - {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"}, - {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."}, - }, - RPCResult{RPCResult::Type::NONE, "", ""}, - RPCExamples{ - "\nUnlock the wallet for 60 seconds\n" - + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + - "\nLock the wallet again (before 60 seconds)\n" - + HelpExampleCli("walletlock", "") + - "\nAs a JSON-RPC call\n" - + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - CWallet* const pwallet = wallet.get(); - - int64_t nSleepTime; - int64_t relock_time; - // Prevent concurrent calls to walletpassphrase with the same wallet. - LOCK(pwallet->m_unlock_mutex); - { - LOCK(pwallet->cs_wallet); - - if (!pwallet->IsCrypted()) { - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); - } - - // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed - SecureString strWalletPass; - strWalletPass.reserve(100); - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make request.params[0] mlock()'d to begin with. - strWalletPass = request.params[0].get_str().c_str(); - - // Get the timeout - nSleepTime = request.params[1].get_int64(); - // Timeout cannot be negative, otherwise it will relock immediately - if (nSleepTime < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative."); - } - // Clamp timeout - constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug? - if (nSleepTime > MAX_SLEEP_TIME) { - nSleepTime = MAX_SLEEP_TIME; - } - - if (strWalletPass.empty()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); - } - - if (!pwallet->Unlock(strWalletPass)) { - throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - } - - pwallet->TopUpKeyPool(); - - pwallet->nRelockTime = GetTime() + nSleepTime; - relock_time = pwallet->nRelockTime; - } - - // rpcRunLater must be called without cs_wallet held otherwise a deadlock - // can occur. The deadlock would happen when RPCRunLater removes the - // previous timer (and waits for the callback to finish if already running) - // and the callback locks cs_wallet. - AssertLockNotHeld(wallet->cs_wallet); - // Keep a weak pointer to the wallet so that it is possible to unload the - // wallet before the following callback is called. If a valid shared pointer - // is acquired in the callback then the wallet is still loaded. - std::weak_ptr<CWallet> weak_wallet = wallet; - pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] { - if (auto shared_wallet = weak_wallet.lock()) { - LOCK(shared_wallet->cs_wallet); - // Skip if this is not the most recent rpcRunLater callback. - if (shared_wallet->nRelockTime != relock_time) return; - shared_wallet->Lock(); - shared_wallet->nRelockTime = 0; - } - }, nSleepTime); - - return NullUniValue; -}, - }; -} - - -static RPCHelpMan walletpassphrasechange() -{ - return RPCHelpMan{"walletpassphrasechange", - "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n", - { - {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"}, - {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"}, - }, - RPCResult{RPCResult::Type::NONE, "", ""}, - RPCExamples{ - HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") - + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) return NullUniValue; - - LOCK(pwallet->cs_wallet); - - if (!pwallet->IsCrypted()) { - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); - } - - // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) - // Alternately, find a way to make request.params[0] mlock()'d to begin with. - SecureString strOldWalletPass; - strOldWalletPass.reserve(100); - strOldWalletPass = request.params[0].get_str().c_str(); - - SecureString strNewWalletPass; - strNewWalletPass.reserve(100); - strNewWalletPass = request.params[1].get_str().c_str(); - - if (strOldWalletPass.empty() || strNewWalletPass.empty()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); - } - - if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) { - throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - } - - return NullUniValue; -}, - }; -} - - -static RPCHelpMan walletlock() -{ - return RPCHelpMan{"walletlock", - "\nRemoves the wallet encryption key from memory, locking the wallet.\n" - "After calling this method, you will need to call walletpassphrase again\n" - "before being able to call any methods which require the wallet to be unlocked.\n", - {}, - RPCResult{RPCResult::Type::NONE, "", ""}, - RPCExamples{ - "\nSet the passphrase for 2 minutes to perform a transaction\n" - + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + - "\nPerform a send (requires passphrase set)\n" - + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") + - "\nClear the passphrase since we are done before 2 minutes is up\n" - + HelpExampleCli("walletlock", "") + - "\nAs a JSON-RPC call\n" - + HelpExampleRpc("walletlock", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) return NullUniValue; - - LOCK(pwallet->cs_wallet); - - if (!pwallet->IsCrypted()) { - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); - } - - pwallet->Lock(); - pwallet->nRelockTime = 0; - - return NullUniValue; -}, - }; -} - - -static RPCHelpMan encryptwallet() -{ - return RPCHelpMan{"encryptwallet", - "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" - "After this, any calls that interact with private keys such as sending or signing \n" - "will require the passphrase to be set prior the making these calls.\n" - "Use the walletpassphrase call for this, and then walletlock call.\n" - "If the wallet is already encrypted, use the walletpassphrasechange call.\n", - { - {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."}, - }, - RPCResult{RPCResult::Type::STR, "", "A string with further instructions"}, - RPCExamples{ - "\nEncrypt your wallet\n" - + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + - "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" - + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + - "\nNow we can do something like sign\n" - + HelpExampleCli("signmessage", "\"address\" \"test message\"") + - "\nNow lock the wallet again by removing the passphrase\n" - + HelpExampleCli("walletlock", "") + - "\nAs a JSON-RPC call\n" - + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) return NullUniValue; - - LOCK(pwallet->cs_wallet); - - if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt."); - } - - if (pwallet->IsCrypted()) { - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); - } - - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make request.params[0] mlock()'d to begin with. - SecureString strWalletPass; - strWalletPass.reserve(100); - strWalletPass = request.params[0].get_str().c_str(); - - if (strWalletPass.empty()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty"); - } - - if (!pwallet->EncryptWallet(strWalletPass)) { - throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); - } - - return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; -}, - }; -} - static RPCHelpMan lockunspent() { return RPCHelpMan{"lockunspent", @@ -2467,37 +2191,6 @@ static RPCHelpMan listwallets() }; } -static std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name) -{ - DatabaseOptions options; - DatabaseStatus status; - options.require_existing = true; - bilingual_str error; - std::vector<bilingual_str> warnings; - std::optional<bool> load_on_start = load_on_start_param.isNull() ? std::nullopt : std::optional<bool>(load_on_start_param.get_bool()); - std::shared_ptr<CWallet> const wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); - - if (!wallet) { - // Map bad format to not found, since bad format is returned when the - // wallet directory exists, but doesn't contain a data file. - RPCErrorCode code = RPC_WALLET_ERROR; - switch (status) { - case DatabaseStatus::FAILED_NOT_FOUND: - case DatabaseStatus::FAILED_BAD_FORMAT: - code = RPC_WALLET_NOT_FOUND; - break; - case DatabaseStatus::FAILED_ALREADY_LOADED: - code = RPC_WALLET_ALREADY_LOADED; - break; - default: // RPC_WALLET_ERROR is returned for all other cases. - break; - } - throw JSONRPCError(code, error.original); - } - - return { wallet, warnings }; -} - static RPCHelpMan loadwallet() { return RPCHelpMan{"loadwallet", @@ -2697,68 +2390,6 @@ static RPCHelpMan createwallet() }; } -static RPCHelpMan restorewallet() -{ - return RPCHelpMan{ - "restorewallet", - "\nRestore and loads a wallet from backup.\n", - { - {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"}, - {"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."}, - {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR, "name", "The wallet name if restored successfully."}, - {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."}, - } - }, - RPCExamples{ - HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") - + HelpExampleRpc("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") - + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) - + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue -{ - - WalletContext& context = EnsureWalletContext(request.context); - - auto backup_file = fs::u8path(request.params[1].get_str()); - - if (!fs::exists(backup_file)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Backup file does not exist"); - } - - std::string wallet_name = request.params[0].get_str(); - - const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); - - if (fs::exists(wallet_path)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet name already exists."); - } - - if (!TryCreateDirectories(wallet_path)) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.u8string())); - } - - auto wallet_file = wallet_path / "wallet.dat"; - - fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists); - - auto [wallet, warnings] = LoadWalletHelper(context, request.params[2], wallet_name); - - UniValue obj(UniValue::VOBJ); - obj.pushKV("name", wallet->GetName()); - obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); - - return obj; - -}, - }; -} - static RPCHelpMan unloadwallet() { return RPCHelpMan{"unloadwallet", @@ -3940,8 +3571,12 @@ RPCHelpMan getaddressinfo() ret.pushKV("solvable", false); } + const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey); + // In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way + ScriptPubKeyMan* spk_man{nullptr}; + if (spk_mans.size()) spk_man = *spk_mans.begin(); - DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey)); + DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man); if (desc_spk_man) { std::string desc_str; if (desc_spk_man->GetDescriptorString(desc_str, /* priv */ false)) { @@ -3956,7 +3591,6 @@ RPCHelpMan getaddressinfo() ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey)); - ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey); if (spk_man) { if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) { ret.pushKV("timestamp", meta->nCreateTime); @@ -4696,6 +4330,14 @@ RPCHelpMan importmulti(); RPCHelpMan importdescriptors(); RPCHelpMan listdescriptors(); RPCHelpMan signmessage(); +RPCHelpMan backupwallet(); +RPCHelpMan restorewallet(); + +// encryption +RPCHelpMan walletpassphrase(); +RPCHelpMan walletpassphrasechange(); +RPCHelpMan walletlock(); +RPCHelpMan encryptwallet(); Span<const CRPCCommand> GetWalletRPCCommands() { @@ -4771,5 +4413,5 @@ static const CRPCCommand commands[] = { "wallet", &walletprocesspsbt, }, }; // clang-format on - return MakeSpan(commands); + return commands; } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index a82eaa4879..1769429efe 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -354,15 +354,22 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i return true; } -void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) +std::vector<WalletDestination> LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { LOCK(cs_KeyStore); + std::vector<WalletDestination> result; // extract addresses and check if they match with an unused keypool key for (const auto& keyid : GetAffectedKeys(script, *this)) { std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); if (mi != m_pool_key_to_index.end()) { WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__); - MarkReserveKeysAsUsed(mi->second); + for (const auto& keypool : MarkReserveKeysAsUsed(mi->second)) { + // derive all possible destinations as any of them could have been used + for (const auto& type : LEGACY_OUTPUT_TYPES) { + const auto& dest = GetDestinationForKey(keypool.vchPubKey, type); + result.push_back({dest, keypool.fInternal}); + } + } if (!TopUp()) { WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); @@ -384,6 +391,8 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) } } } + + return result; } void LegacyScriptPubKeyMan::UpgradeKeyMetadata() @@ -1427,7 +1436,7 @@ void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key) LearnRelatedScripts(key, OutputType::P2SH_SEGWIT); } -void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) +std::vector<CKeyPool> LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_KeyStore); bool internal = setInternalKeyPool.count(keypool_id); @@ -1435,6 +1444,7 @@ void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); auto it = setKeyPool->begin(); + std::vector<CKeyPool> result; WalletBatch batch(m_storage.GetDatabase()); while (it != std::end(*setKeyPool)) { const int64_t& index = *(it); @@ -1448,7 +1458,10 @@ void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) batch.ErasePool(index); WalletLogPrintf("keypool index %d removed\n", index); it = setKeyPool->erase(it); + result.push_back(std::move(keypool)); } + + return result; } std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider) @@ -1820,19 +1833,32 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size) return true; } -void DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) +std::vector<WalletDestination> DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { LOCK(cs_desc_man); + std::vector<WalletDestination> result; if (IsMine(script)) { int32_t index = m_map_script_pub_keys[script]; if (index >= m_wallet_descriptor.next_index) { WalletLogPrintf("%s: Detected a used keypool item at index %d, mark all keypool items up to this item as used\n", __func__, index); - m_wallet_descriptor.next_index = index + 1; + auto out_keys = std::make_unique<FlatSigningProvider>(); + std::vector<CScript> scripts_temp; + while (index >= m_wallet_descriptor.next_index) { + if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) { + throw std::runtime_error(std::string(__func__) + ": Unable to expand descriptor from cache"); + } + CTxDestination dest; + ExtractDestination(scripts_temp[0], dest); + result.push_back({dest, std::nullopt}); + m_wallet_descriptor.next_index++; + } } if (!TopUp()) { WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); } } + + return result; } void DescriptorScriptPubKeyMan::AddDescriptorKey(const CKey& key, const CPubKey &pubkey) diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 9d2304a542..ebe064fa0a 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -149,6 +149,12 @@ public: } }; +struct WalletDestination +{ + CTxDestination dest; + std::optional<bool> internal; +}; + /* * A class implementing ScriptPubKeyMan manages some (or all) scriptPubKeys used in a wallet. * It contains the scripts and keys related to the scriptPubKeys it manages. @@ -181,8 +187,14 @@ public: */ virtual bool TopUp(unsigned int size = 0) { return false; } - //! Mark unused addresses as being used - virtual void MarkUnusedAddresses(const CScript& script) {} + /** Mark unused addresses as being used + * Affects all keys up to and including the one determined by provided script. + * + * @param script determines the last key to mark as used + * + * @return All of the addresses affected + */ + virtual std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) { return {}; } /** Sets up the key generation stuff, i.e. generates new HD seeds and sets them as active. * Returns false if already setup or setup fails, true if setup is successful @@ -357,7 +369,7 @@ public: bool TopUp(unsigned int size = 0) override; - void MarkUnusedAddresses(const CScript& script) override; + std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) override; //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo void UpgradeKeyMetadata(); @@ -482,9 +494,13 @@ public: void LearnAllRelatedScripts(const CPubKey& key); /** - * Marks all keys in the keypool up to and including reserve_key as used. + * Marks all keys in the keypool up to and including the provided key as used. + * + * @param keypool_id determines the last key to mark as used + * + * @return All affected keys */ - void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); + std::vector<CKeyPool> MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } std::set<CKeyID> GetKeys() const override; @@ -564,7 +580,7 @@ public: // (with or without private keys), the "keypool" is a single xpub. bool TopUp(unsigned int size = 0) override; - void MarkUnusedAddresses(const CScript& script) override; + std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) override; bool IsHDEnabled() const override; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 9842089cf8..b4141d1f9e 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -682,7 +682,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup) vw << (int32_t)0; vw << (int32_t)1; - VectorReader vr(0, 0, malformed_record, 0); + SpanReader vr{0, 0, malformed_record, 0}; WalletDescriptor w_desc; BOOST_CHECK_EXCEPTION(vr >> w_desc, std::ios_base::failure, malformed_descriptor); } @@ -816,30 +816,35 @@ BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup) context.args = &gArgs; context.chain = m_node.chain.get(); auto wallet = TestLoadWallet(context); - CKey key; - key.MakeNewKey(true); - AddKey(*wallet, key); + AddKey(*wallet, coinbaseKey); - std::string error; + // rescan to ensure coinbase transactions from test fixture are picked up by the wallet + { + WalletRescanReserver reserver(*wallet); + reserver.reserve(); + wallet->ScanForWalletTransactions(m_node.chain->getBlockHash(0), 0, /* max height= */ {}, reserver, /* update= */ true); + } + // create one more block to get the first block coinbase to maturity m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); - CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); + // spend first coinbase tx + auto spend_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); + CreateAndProcessBlock({spend_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); SyncWithValidationInterfaceQueue(); { - auto block_hash = block_tx.GetHash(); + auto spend_tx_hash = spend_tx.GetHash(); auto prev_hash = m_coinbase_txns[0]->GetHash(); LOCK(wallet->cs_wallet); BOOST_CHECK(wallet->HasWalletSpend(prev_hash)); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 1u); - std::vector<uint256> vHashIn{ block_hash }, vHashOut; + std::vector<uint256> vHashIn{spend_tx_hash}, vHashOut; BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK); BOOST_CHECK(!wallet->HasWalletSpend(prev_hash)); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 0u); } TestUnloadWallet(std::move(wallet)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e4c3822305..2e4b6a6eb0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -919,7 +919,9 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const wtx.nOrderPos = IncOrderPosNext(&batch); wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block); - AddToSpends(hash, &batch); + if (IsFromMe(*tx.get())) { + AddToSpends(hash); + } } if (!fInsertedNew) @@ -1061,8 +1063,23 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxS // loop though all outputs for (const CTxOut& txout: tx.vout) { - for (const auto& spk_man_pair : m_spk_managers) { - spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey); + for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) { + for (auto &dest : spk_man->MarkUnusedAddresses(txout.scriptPubKey)) { + // If internal flag is not defined try to infer it from the ScriptPubKeyMan + if (!dest.internal.has_value()) { + dest.internal = IsInternalScriptPubKeyMan(spk_man); + } + + // skip if can't determine whether it's a receiving address or not + if (!dest.internal.has_value()) continue; + + // If this is a receiving address and it's not in the address book yet + // (e.g. it wasn't generated on this node or we're restoring from backup) + // add it to the address book for proper transaction accounting + if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) { + SetAddressBook(dest.dest, "", "receive"); + } + } } } @@ -2253,16 +2270,15 @@ void ReserveDestination::ReturnDestination() bool CWallet::DisplayAddress(const CTxDestination& dest) { CScript scriptPubKey = GetScriptForDestination(dest); - const auto spk_man = GetScriptPubKeyMan(scriptPubKey); - if (spk_man == nullptr) { - return false; - } - auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man); - if (signer_spk_man == nullptr) { - return false; + for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) { + auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man); + if (signer_spk_man == nullptr) { + continue; + } + ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); + return signer_spk_man->DisplayAddress(scriptPubKey, signer); } - ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); - return signer_spk_man->DisplayAddress(scriptPubKey, signer); + return false; } bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch) @@ -3050,9 +3066,10 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern return it->second; } -std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const +std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const { std::set<ScriptPubKeyMan*> spk_mans; + SignatureData sigdata; for (const auto& spk_man_pair : m_spk_managers) { if (spk_man_pair.second->CanProvide(script, sigdata)) { spk_mans.insert(spk_man_pair.second.get()); @@ -3061,17 +3078,6 @@ std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, S return spk_mans; } -ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const -{ - SignatureData sigdata; - for (const auto& spk_man_pair : m_spk_managers) { - if (spk_man_pair.second->CanProvide(script, sigdata)) { - return spk_man_pair.second.get(); - } - } - return nullptr; -} - ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const { if (m_spk_managers.count(id) > 0) { @@ -3287,6 +3293,30 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes return nullptr; } +std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const +{ + // Legacy script pubkey man can't be either external or internal + if (IsLegacy()) { + return std::nullopt; + } + + // only active ScriptPubKeyMan can be internal + if (!GetActiveScriptPubKeyMans().count(spk_man)) { + return std::nullopt; + } + + const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man); + if (!desc_spk_man) { + throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type."); + } + + LOCK(desc_spk_man->cs_desc_man); + const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType(); + assert(type.has_value()); + + return GetScriptPubKeyMan(*type, /* internal= */ true) == desc_spk_man; +} + ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) { AssertLockHeld(cs_wallet); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e294358609..dbf0f6375d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -809,14 +809,11 @@ public: //! Get the ScriptPubKeyMan for the given OutputType and internal/external chain. ScriptPubKeyMan* GetScriptPubKeyMan(const OutputType& type, bool internal) const; - //! Get the ScriptPubKeyMan for a script - ScriptPubKeyMan* GetScriptPubKeyMan(const CScript& script) const; + //! Get all the ScriptPubKeyMans for a script + std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script) const; //! Get the ScriptPubKeyMan by id ScriptPubKeyMan* GetScriptPubKeyMan(const uint256& id) const; - //! Get all of the ScriptPubKeyMans for a script given additional information in sigdata (populated by e.g. a psbt) - std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const; - //! Get the SigningProvider for a script std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const; std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script, SignatureData& sigdata) const; @@ -882,6 +879,11 @@ public: //! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const; + //! Returns whether the provided ScriptPubKeyMan is internal + //! @param[in] spk_man The ScriptPubKeyMan to test + //! @return contains value only for active DescriptorScriptPubKeyMan, otherwise undefined + std::optional<bool> IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const; + //! Add a descriptor to the wallet, return a ScriptPubKeyMan & associated output type ScriptPubKeyMan* AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); }; diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py index 19bb908b64..8afd0ff74a 100755 --- a/test/functional/feature_coinstatsindex.py +++ b/test/functional/feature_coinstatsindex.py @@ -276,14 +276,6 @@ class CoinStatsIndexTest(BitcoinTestFramework): res3 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112) assert_equal(res2, res3) - self.log.info("Test that a node aware of stale blocks syncs them as well") - node = self.nodes[0] - # Ensure the node is aware of a stale block prior to restart - node.getblock(reorg_block) - - self.restart_node(0, ["-coinstatsindex"]) - assert_raises_rpc_error(-32603, "Unable to get data because coinstatsindex is still syncing.", node.gettxoutsetinfo, 'muhash', reorg_block) - def _test_index_rejects_hash_serialized(self): self.log.info("Test that the rpc raises if the legacy hash is passed with the index") diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 8fd15a164c..fc263f17ce 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -3,6 +3,10 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the listtransactions API.""" + +import shutil +import os + from decimal import Decimal from test_framework.messages import ( @@ -17,7 +21,7 @@ from test_framework.util import ( class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): - self.num_nodes = 2 + self.num_nodes = 3 # This test isn't testing txn relay/timing, so set whitelist on the # peers for instant txn relay. This speeds up the test run time 2-3x. self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes @@ -102,7 +106,7 @@ class ListTransactionsTest(BitcoinTestFramework): {"txid": txid, "label": "watchonly"}) self.run_rbf_opt_in_test() - + self.run_externally_generated_address_test() def run_rbf_opt_in_test(self): """Test the opt-in-rbf flag for sent and received transactions.""" @@ -217,5 +221,63 @@ class ListTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no") assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown") + def run_externally_generated_address_test(self): + """Test behavior when receiving address is not in the address book.""" + + self.log.info("Setup the same wallet on two nodes") + # refill keypool otherwise the second node wouldn't recognize addresses generated on the first nodes + self.nodes[0].keypoolrefill(1000) + self.stop_nodes() + wallet0 = os.path.join(self.nodes[0].datadir, self.chain, self.default_wallet_name, "wallet.dat") + wallet2 = os.path.join(self.nodes[2].datadir, self.chain, self.default_wallet_name, "wallet.dat") + shutil.copyfile(wallet0, wallet2) + self.start_nodes() + # reconnect nodes + self.connect_nodes(0, 1) + self.connect_nodes(1, 2) + self.connect_nodes(2, 0) + + addr1 = self.nodes[0].getnewaddress("pizza1", 'legacy') + addr2 = self.nodes[0].getnewaddress("pizza2", 'p2sh-segwit') + addr3 = self.nodes[0].getnewaddress("pizza3", 'bech32') + + self.log.info("Send to externally generated addresses") + # send to an address beyond the next to be generated to test the keypool gap + self.nodes[1].sendtoaddress(addr3, "0.001") + self.generate(self.nodes[1], 1) + self.sync_all() + + # send to an address that is already marked as used due to the keypool gap mechanics + self.nodes[1].sendtoaddress(addr2, "0.001") + self.generate(self.nodes[1], 1) + self.sync_all() + + # send to self transaction + self.nodes[0].sendtoaddress(addr1, "0.001") + self.generate(self.nodes[0], 1) + self.sync_all() + + self.log.info("Verify listtransactions is the same regardless of where the address was generated") + transactions0 = self.nodes[0].listtransactions() + transactions2 = self.nodes[2].listtransactions() + + # normalize results: remove fields that normally could differ and sort + def normalize_list(txs): + for tx in txs: + tx.pop('label', None) + tx.pop('time', None) + tx.pop('timereceived', None) + txs.sort(key=lambda x: x['txid']) + + normalize_list(transactions0) + normalize_list(transactions2) + assert_equal(transactions0, transactions2) + + self.log.info("Verify labels are persistent on the node generated the addresses") + assert_equal(['pizza1'], self.nodes[0].getaddressinfo(addr1)['labels']) + assert_equal(['pizza2'], self.nodes[0].getaddressinfo(addr2)['labels']) + assert_equal(['pizza3'], self.nodes[0].getaddressinfo(addr3)['labels']) + + if __name__ == '__main__': ListTransactionsTest().main() |