diff options
133 files changed, 1749 insertions, 1232 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 7250d4ad94..097874b17a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,7 +10,7 @@ environment: QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt598x64_vs2019_v1681/qt598_x64_vs2019_1681.zip' QT_DOWNLOAD_HASH: '00cf7327818c07d74e0b1a4464ffe987c2728b00d49d4bf333065892af0515c3' QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019' - VCPKG_TAG: '2020.11-1' + VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f' install: # Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes. # - cmd: pip install zmq diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 67ae03f3a9..1fd88db08f 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -123,7 +123,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ _BITCOIN_QT_CHECK_STATIC_LIBS if test "x$qt_plugin_path" != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms -L$qt_plugin_path/styles" if test -d "$qt_plugin_path/accessible"; then QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" fi @@ -138,13 +138,23 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) fi if test "x$TARGET_OS" = xwindows; then + dnl Linking against wtsapi32 is required. See #17749 and + dnl https://bugreports.qt.io/browse/QTBUG-27097. + AX_CHECK_LINK_FLAG([-lwtsapi32], [QT_LIBS="$QT_LIBS -lwtsapi32"], [AC_MSG_ERROR([could not link against -lwtsapi32])]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsIntegrationPlugin], [-lqwindows]) 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="-lxcb-shm $QT_LIBS"], [AC_MSG_ERROR([could not link against -lxcb-shm])]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb -lxcb-static]) AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) elif test "x$TARGET_OS" = xdarwin; then + AX_CHECK_LINK_FLAG([[-framework Carbon]],[QT_LIBS="$QT_LIBS -framework Carbon"],[AC_MSG_ERROR(could not link against Carbon framework)]) + AX_CHECK_LINK_FLAG([[-framework IOSurface]],[QT_LIBS="$QT_LIBS -framework IOSurface"],[AC_MSG_ERROR(could not link against IOSurface framework)]) + AX_CHECK_LINK_FLAG([[-framework Metal]],[QT_LIBS="$QT_LIBS -framework Metal"],[AC_MSG_ERROR(could not link against Metal framework)]) + AX_CHECK_LINK_FLAG([[-framework QuartzCore]],[QT_LIBS="$QT_LIBS -framework QuartzCore"],[AC_MSG_ERROR(could not link against QuartzCore framework)]) _BITCOIN_QT_CHECK_STATIC_PLUGIN([QCocoaIntegrationPlugin], [-lqcocoa]) + _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 -lQt5EglSupport $QT_LIBS" @@ -334,7 +344,8 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ elif test "x$TARGET_OS" = xdarwin; then PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"]) PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport${qt_lib_suffix}], [QT_LIBS="-lQt5CglSupport${qt_lib_suffix} $QT_LIBS"]) + elif test "x$TARGET_OS" = xwindows; then + PKG_CHECK_MODULES([QTWINDOWSUIAUTOMATION], [Qt5WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="-lQt5WindowsUIAutomationSupport${qt_lib_suffix} $QT_LIBS"]) fi ]) diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h index 40a30b9749..dd01cb29eb 100644 --- a/build_msvc/bitcoin_config.h +++ b/build_msvc/bitcoin_config.h @@ -92,9 +92,9 @@ don't. */ #define HAVE_DECL_BSWAP_64 0 -/* Define to 1 if you have the declaration of `daemon', and to 0 if you don't. +/* Define to 1 if you have the declaration of `fork', and to 0 if you don't. */ -#define HAVE_DECL_DAEMON 0 +#define HAVE_DECL_FORK 0 /* Define to 1 if you have the declaration of `htobe16', and to 0 if you don't. */ @@ -132,6 +132,10 @@ don't. */ #define HAVE_DECL_LE64TOH 0 +/* Define to 1 if you have the declaration of `setsid', and to 0 if you don't. + */ +#define HAVE_DECL_SETSID 0 + /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #define HAVE_DECL_STRERROR_R 0 diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh index 72e29141a6..87cf8538f6 100755 --- a/ci/test/00_setup_env.sh +++ b/ci/test/00_setup_env.sh @@ -65,7 +65,7 @@ export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_SCRATCH_DIR/out/$HOST} export BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build} export PREVIOUS_RELEASES_DIR=${PREVIOUS_RELEASES_DIR:-$BASE_ROOT_DIR/releases/$HOST} export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} -export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps} +export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps bison} export GOAL=${GOAL:-install} export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_SCRATCH_DIR}/qa-assets} export PATH=${BASE_ROOT_DIR}/ci/retry:$PATH diff --git a/ci/test/00_setup_env_i686_centos.sh b/ci/test/00_setup_env_i686_centos.sh index 1ec44a7a1a..07e7c2dc27 100644 --- a/ci/test/00_setup_env_i686_centos.sh +++ b/ci/test/00_setup_env_i686_centos.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export HOST=i686-pc-linux-gnu export CONTAINER_NAME=ci_i686_centos_8 export DOCKER_NAME_TAG=centos:8 -export DOCKER_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-zmq which patch lbzip2 dash rsync coreutils" +export DOCKER_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-zmq which patch lbzip2 dash rsync coreutils bison" export GOAL="install" export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports --with-boost-process" export CONFIG_SHELL="/bin/dash" diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh index 645dcffd02..6ce3812447 100644 --- a/ci/test/00_setup_env_mac.sh +++ b/ci/test/00_setup_env_mac.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_macos_cross export DOCKER_NAME_TAG=ubuntu:20.04 # Check that Focal can cross-compile to macos (Focal is used in the gitian build as well) export HOST=x86_64-apple-darwin18 -export PACKAGES="cmake imagemagick librsvg2-bin libz-dev libtiff-tools libtinfo5 python3-dev python3-setuptools xorriso" +export PACKAGES="cmake imagemagick librsvg2-bin libz-dev libtiff-tools libtinfo5 python3-setuptools xorriso" export XCODE_VERSION=11.3.1 export XCODE_BUILD_ID=11C505 export RUN_UNIT_TESTS=false diff --git a/configure.ac b/configure.ac index 391a75eeed..0b176e6039 100644 --- a/configure.ac +++ b/configure.ac @@ -922,8 +922,9 @@ AC_CHECK_DECLS([getifaddrs, freeifaddrs],,, ) AC_CHECK_DECLS([strnlen]) -dnl Check for daemon(3), unrelated to --with-daemon (although used by it) -AC_CHECK_DECLS([daemon]) +dnl These are used for daemonization in bitcoind +AC_CHECK_DECLS([fork]) +AC_CHECK_DECLS([setsid]) AC_CHECK_DECLS([pipe2]) diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index b30ed62521..436f179d61 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -73,6 +73,8 @@ ELF_ALLOWED_LIBRARIES = { 'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker # bitcoin-qt only 'libxcb.so.1', # part of X11 +'libxkbcommon.so.0', # keyboard keymapping +'libxkbcommon-x11.so.0', # keyboard keymapping 'libfontconfig.so.1', # font support 'libfreetype.so.6', # font parsing 'libdl.so.2' # programming interface to dynamic linker @@ -98,10 +100,15 @@ MACHO_ALLOWED_LIBRARIES = { 'CoreGraphics', # 2D rendering 'CoreServices', # operating system services 'CoreText', # interface for laying out text and handling fonts. +'CoreVideo', # video processing 'Foundation', # base layer functionality for apps/frameworks 'ImageIO', # read and write image file formats. 'IOKit', # user-space access to hardware devices and drivers. +'IOSurface', # cross process image/drawing buffers 'libobjc.A.dylib', # Objective-C runtime library +'Metal', # 3D graphics +'Security', # access control and authentication +'QuartzCore', # animation } PE_ALLOWED_LIBRARIES = { @@ -116,12 +123,15 @@ PE_ALLOWED_LIBRARIES = { 'dwmapi.dll', # desktop window manager 'GDI32.dll', # graphics device interface 'IMM32.dll', # input method editor +'NETAPI32.dll', 'ole32.dll', # component object model 'OLEAUT32.dll', # OLE Automation API 'SHLWAPI.dll', # light weight shell API +'USERENV.dll', 'UxTheme.dll', 'VERSION.dll', # version checking 'WINMM.dll', # WinMM audio API +'WTSAPI32.dll', } class CPPFilt(object): diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index b06fc782a3..52e2a0514a 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -11,6 +11,7 @@ packages: - "autoconf" - "automake" - "binutils" +- "bison" - "bsdmainutils" - "ca-certificates" - "curl" diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 98c3888b45..c37ad5b842 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -23,7 +23,6 @@ packages: - "imagemagick" - "libz-dev" - "python3" -- "python3-dev" - "python3-setuptools" - "fonts-tuffy" - "xorriso" diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm index bd9f895dce..4228532cb1 100644 --- a/contrib/guix/manifest.scm +++ b/contrib/guix/manifest.scm @@ -3,6 +3,7 @@ (gnu packages autotools) (gnu packages base) (gnu packages bash) + (gnu packages bison) (gnu packages cdrom) (gnu packages check) (gnu packages cmake) @@ -219,6 +220,7 @@ chain for " target " development.")) autoconf automake pkg-config + bison ;; Scripting perl python-3 diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc index 86222295db..013a1a6070 100644 --- a/contrib/init/bitcoind.openrc +++ b/contrib/init/bitcoind.openrc @@ -60,16 +60,17 @@ start_pre() { "${BITCOIND_PIDDIR}" checkpath -f \ - -o ${BITCOIND_USER}:${BITCOIND_GROUP} \ + -o "${BITCOIND_USER}:${BITCOIND_GROUP}" \ -m 0660 \ - ${BITCOIND_CONFIGFILE} + "${BITCOIND_CONFIGFILE}" checkconfig || return 1 } checkconfig() { - if ! grep -qs '^rpcpassword=' "${BITCOIND_CONFIGFILE}" ; then + if grep -qs '^rpcuser=' "${BITCOIND_CONFIGFILE}" && \ + ! grep -qs '^rpcpassword=' "${BITCOIND_CONFIGFILE}" ; then eerror "" eerror "ERROR: You must set a secure rpcpassword to run bitcoind." eerror "The setting must appear in ${BITCOIND_CONFIGFILE}" diff --git a/depends/README.md b/depends/README.md index e72c666474..a1d4a99084 100644 --- a/depends/README.md +++ b/depends/README.md @@ -54,7 +54,7 @@ The paths are automatically configured and no other options are needed unless ta Common linux dependencies: - sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3 patch + sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3 patch bison For linux ARM cross compilation: diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk index 14abde9b0a..710f43959c 100644 --- a/depends/packages/libxcb.mk +++ b/depends/packages/libxcb.mk @@ -15,9 +15,9 @@ $(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-shm --disable-sync +$(package)_config_opts += --disable-shape --disable-sync $(package)_config_opts += --disable-xevie --disable-xfixes --disable-xfree86-dri -$(package)_config_opts += --disable-xinerama --disable-xinput --disable-xkb +$(package)_config_opts += --disable-xinerama --disable-xinput $(package)_config_opts += --disable-xprint --disable-selinux --disable-xtest $(package)_config_opts += --disable-xv --disable-xvmc endef diff --git a/depends/packages/libxkbcommon.mk b/depends/packages/libxkbcommon.mk new file mode 100644 index 0000000000..cdd5af194b --- /dev/null +++ b/depends/packages/libxkbcommon.mk @@ -0,0 +1,32 @@ +package=libxkbcommon +$(package)_version=0.8.4 +$(package)_download_path=https://xkbcommon.org/download/ +$(package)_file_name=$(package)-$($(package)_version).tar.xz +$(package)_sha256_hash=60ddcff932b7fd352752d51a5c4f04f3d0403230a584df9a2e0d5ed87c486c8b +$(package)_dependencies=libxcb + +define $(package)_set_vars +$(package)_config_opts = --enable-option-checking --disable-dependency-tracking +$(package)_config_opts += --disable-static --disable-docs +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux +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 0f35ca0d2d..810068b686 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,10 +1,8 @@ packages:=boost libevent -qt_packages = zlib - qrencode_packages = qrencode -qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig +qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon qt_android_packages=qt qt_darwin_packages=qt diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 7d122ea5f9..0ac6f496c4 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,23 +1,21 @@ PACKAGE=qt -$(package)_version=5.9.8 -$(package)_download_path=https://download.qt.io/official_releases/qt/5.9/$($(package)_version)/submodules -$(package)_suffix=opensource-src-$($(package)_version).tar.xz +$(package)_version=5.12.10 +$(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules +$(package)_suffix=everywhere-src-$($(package)_version).tar.xz $(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=9b9dec1f67df1f94bce2955c5604de992d529dde72050239154c56352da0907d -$(package)_dependencies=zlib -$(package)_linux_dependencies=freetype fontconfig libxcb +$(package)_sha256_hash=8088f174e6d28e779516c083b6087b6a9e3c8322b4bc161fd1b54195e3c86940 +$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon $(package)_qt_libs=corelib network widgets gui plugins testlib -$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch -$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch +$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch $(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch -$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch -$(package)_patches+= fix_mingw_cross_compile.patch fix_qpainter_non_determinism.patch +$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch +$(package)_patches+= fix_qpainter_non_determinism.patch fix_lib_paths.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=fb5a47799754af73d3bf501fe513342cfe2fc37f64e80df5533f6110e804220c +$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=a97556eb7b2f30252cdd8a598c396cfce2b2f79d2bae883af6d3b26a2cdcc63c +$(package)_qttools_sha256_hash=b0cfa6e7aac41b7c61fc59acc04843d7a98f9e1840370611751bcfc1834a636c $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) @@ -26,6 +24,7 @@ define $(package)_set_vars $(package)_config_opts_release = -release $(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 += -confirm-license @@ -49,7 +48,6 @@ $(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-qml-debug $(package)_config_opts += -no-sctp $(package)_config_opts += -no-securetransport $(package)_config_opts += -no-sql-db2 @@ -63,17 +61,15 @@ $(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-xinput2 $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests $(package)_config_opts += -opensource -$(package)_config_opts += -optimized-tools $(package)_config_opts += -pkg-config $(package)_config_opts += -prefix $(host_prefix) $(package)_config_opts += -qt-libpng $(package)_config_opts += -qt-pcre $(package)_config_opts += -qt-harfbuzz -$(package)_config_opts += -system-zlib +$(package)_config_opts += -qt-zlib $(package)_config_opts += -static $(package)_config_opts += -v $(package)_config_opts += -no-feature-bearermanagement @@ -94,10 +90,10 @@ $(package)_config_opts += -no-feature-printdialog $(package)_config_opts += -no-feature-printer $(package)_config_opts += -no-feature-printpreviewdialog $(package)_config_opts += -no-feature-printpreviewwidget -$(package)_config_opts += -no-feature-regularexpression $(package)_config_opts += -no-feature-sessionmanager $(package)_config_opts += -no-feature-socks5 $(package)_config_opts += -no-feature-sql +$(package)_config_opts += -no-feature-sqlmodel $(package)_config_opts += -no-feature-statemachine $(package)_config_opts += -no-feature-syntaxhighlighter $(package)_config_opts += -no-feature-textbrowser @@ -115,6 +111,7 @@ $(package)_config_opts += -no-feature-xml $(package)_config_opts_darwin = -no-dbus $(package)_config_opts_darwin += -no-opengl $(package)_config_opts_darwin += -pch +$(package)_config_opts_darwin += -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=$(OSX_MIN_VERSION) ifneq ($(build_os),darwin) $(package)_config_opts_darwin += -xplatform macx-clang-linux @@ -129,13 +126,13 @@ endif # for macOS on Apple Silicon (ARM) see https://bugreports.qt.io/browse/QTBUG-85279 $(package)_config_opts_arm_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64 -$(package)_config_opts_linux = -qt-xkbcommon-x11 -$(package)_config_opts_linux += -qt-xcb +$(package)_config_opts_linux = -qt-xcb $(package)_config_opts_linux += -no-xcb-xlib $(package)_config_opts_linux += -no-feature-xlib $(package)_config_opts_linux += -system-freetype $(package)_config_opts_linux += -fontconfig $(package)_config_opts_linux += -no-opengl +$(package)_config_opts_linux += -no-feature-vulkan $(package)_config_opts_linux += -dbus-runtime $(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++ $(package)_config_opts_i686_linux = -xplatform linux-g++-32 @@ -220,21 +217,16 @@ endef # 8. Adjust a regex in toolchain.prf, to accommodate Guix's usage of # CROSS_LIBRARY_PATH. See #15277. define $(package)_preprocess_cmds - patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \ patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \ 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_configure_mac.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_rcc_determinism.patch && \ - patch -p1 -i $($(package)_patch_dir)/xkb-default.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_qmake_conf.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_riscv64_arch.patch && \ patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_mingw_cross_compile.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_qpainter_non_determinism.patch &&\ + patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \ sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ mkdir -p qtbase/mkspecs/macx-clang-linux &&\ cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ @@ -247,8 +239,8 @@ define $(package)_preprocess_cmds sed -i.old "s|QMAKE_CFLAGS += |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "s|QMAKE_CXXFLAGS += |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "0,/^QMAKE_LFLAGS_/s|^QMAKE_LFLAGS_|!host_build: QMAKE_LFLAGS = $($(package)_ldflags)\n&|" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "s|QMAKE_CC = clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \ - sed -i.old "s|QMAKE_CXX = clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \ + sed -i.old "s|QMAKE_CC = \$$$$\$$$${CROSS_COMPILE}clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \ + sed -i.old "s|QMAKE_CXX = \$$$$\$$$${CROSS_COMPILE}clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \ sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf endef @@ -258,8 +250,6 @@ define $(package)_config_cmds export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \ cd qtbase && \ ./configure $($(package)_config_opts) && \ - echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \ - echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ cd .. && \ $(MAKE) -C qtbase sub-src-clean && \ qtbase/bin/qmake -o qttranslations/Makefile qttranslations/qttranslations.pro && \ diff --git a/depends/packages/zlib.mk b/depends/packages/zlib.mk deleted file mode 100644 index acb02020a8..0000000000 --- a/depends/packages/zlib.mk +++ /dev/null @@ -1,31 +0,0 @@ -package=zlib -$(package)_version=1.2.11 -$(package)_download_path=https://www.zlib.net -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 - -define $(package)_set_vars -$(package)_config_opts= CC="$($(package)_cc)" -$(package)_config_opts+=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" -$(package)_config_opts+=RANLIB="$($(package)_ranlib)" -$(package)_config_opts+=AR="$($(package)_ar)" -$(package)_config_opts_darwin+=AR="$($(package)_libtool)" -$(package)_config_opts_darwin+=ARFLAGS="-o" -$(package)_config_opts_android+=CHOST=$(host) -endef - -# zlib has its own custom configure script that takes in options like CC, -# CFLAGS, RANLIB, AR, and ARFLAGS from the environment rather than from -# command-line arguments. -define $(package)_config_cmds - env $($(package)_config_opts) ./configure --static --prefix=$(host_prefix) -endef - -define $(package)_build_cmds - $(MAKE) libz.a -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - diff --git a/depends/patches/qt/drop_lrelease_dependency.patch b/depends/patches/qt/drop_lrelease_dependency.patch index f6b2c9fc80..9b918af77c 100644 --- a/depends/patches/qt/drop_lrelease_dependency.patch +++ b/depends/patches/qt/drop_lrelease_dependency.patch @@ -14,7 +14,7 @@ diff --git a/qttranslations/translations/translations.pro b/qttranslations/trans index 694544c..eff339d 100644 --- a/qttranslations/translations/translations.pro +++ b/qttranslations/translations/translations.pro -@@ -109,3 +109,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} +@@ -107,3 +107,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} silent:updateqm.commands = @echo lrelease ${QMAKE_FILE_IN} && $$updateqm.commands -updateqm.depends = $$LRELEASE_EXE updateqm.name = LRELEASE ${QMAKE_FILE_IN} diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch index 2f6ff00f40..f891da6ddf 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 -@@ -890,6 +890,14 @@ +@@ -897,6 +897,14 @@ __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } diff --git a/depends/patches/qt/fix_android_qmake_conf.patch b/depends/patches/qt/fix_android_qmake_conf.patch index 13bfff9776..3a8753fd1d 100644 --- a/depends/patches/qt/fix_android_qmake_conf.patch +++ b/depends/patches/qt/fix_android_qmake_conf.patch @@ -1,20 +1,10 @@ --- old/qtbase/mkspecs/android-clang/qmake.conf +++ new/qtbase/mkspecs/android-clang/qmake.conf -@@ -30,7 +30,7 @@ - QMAKE_CFLAGS += -target mips64el-none-linux-android +@@ -47,7 +47,7 @@ ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so + ANDROID_USE_LLVM = true - QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH --QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -+QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -nostdlib++ - 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 \ -@@ -40,7 +40,7 @@ - ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH - - ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so --ANDROID_CXX_STL_LIBS = -lc++ -+ANDROID_CXX_STL_LIBS = -lc++_shared - - QMAKE_ARM_CFLAGS_RELEASE = -Oz - QMAKE_ARM_CFLAGS_RELEASE_WITH_DEBUGINFO = -g -Oz + exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ +- ANDROID_CXX_STL_LIBS = -lc++ ++ ANDROID_CXX_STL_LIBS = -lc++_shared + else: \ + ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "") diff --git a/depends/patches/qt/fix_configure_mac.patch b/depends/patches/qt/fix_configure_mac.patch deleted file mode 100644 index 0d7dd647de..0000000000 --- a/depends/patches/qt/fix_configure_mac.patch +++ /dev/null @@ -1,50 +0,0 @@ ---- old/qtbase/mkspecs/features/mac/sdk.prf 2018-02-08 10:24:48.000000000 -0800 -+++ new/qtbase/mkspecs/features/mac/sdk.prf 2018-03-23 10:38:56.000000000 -0700 -@@ -8,21 +8,21 @@ - defineReplace(xcodeSDKInfo) { - info = $$1 - equals(info, "Path"): \ -- info = --show-sdk-path -+ infoarg = --show-sdk-path - equals(info, "PlatformPath"): \ -- info = --show-sdk-platform-path -+ infoarg = --show-sdk-platform-path - equals(info, "SDKVersion"): \ -- info = --show-sdk-version -+ infoarg = --show-sdk-version - sdk = $$2 - isEmpty(sdk): \ - sdk = $$QMAKE_MAC_SDK - - isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}) { -- QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$info 2>/dev/null") -+ QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$infoarg 2>/dev/null") - # --show-sdk-platform-path won't work for Command Line Tools; this is fine - # only used by the XCTest backend to testlib -- isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(info, "--show-sdk-platform-path")): \ -- error("Could not resolve SDK $$info for \'$$sdk\'") -+ isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(infoarg, "--show-sdk-platform-path")): \ -+ error("Could not resolve SDK $$info for \'$$sdk\' using $$infoarg") - cache(QMAKE_MAC_SDK.$${sdk}.$${info}, set stash, QMAKE_MAC_SDK.$${sdk}.$${info}) - } - ---- old/qtbase/configure 2018-02-08 10:24:48.000000000 -0800 -+++ new/qtbase/configure 2018-03-23 05:42:29.000000000 -0700 -@@ -232,8 +232,13 @@ - - sdk=$(getSingleQMakeVariable "QMAKE_MAC_SDK" "$1") - if [ -z "$sdk" ]; then echo "QMAKE_MAC_SDK must be set when building on Mac" >&2; exit 1; fi -- sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) -- if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi -+ sysroot=$(getSingleQMakeVariable "QMAKE_MAC_SDK_PATH" "$1") -+ -+ echo "sysroot pre-configured as $sysroot"; -+ if [ -z "$sysroot" ]; then -+ sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) -+ if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi -+ fi - - case "$sdk" in - macosx*) - - diff --git a/depends/patches/qt/fix_lib_paths.patch b/depends/patches/qt/fix_lib_paths.patch new file mode 100644 index 0000000000..d1a15373f4 --- /dev/null +++ b/depends/patches/qt/fix_lib_paths.patch @@ -0,0 +1,193 @@ +--- 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_mingw_cross_compile.patch b/depends/patches/qt/fix_mingw_cross_compile.patch deleted file mode 100644 index 67f76f1d85..0000000000 --- a/depends/patches/qt/fix_mingw_cross_compile.patch +++ /dev/null @@ -1,25 +0,0 @@ -commit 5a992a549adfe5a587bbcd6cd2b2cee47d236e27 -Author: fanquake <fanquake@gmail.com> -Date: Fri Sep 4 08:13:44 2020 +0800 - - Work around broken mingw cross-compilation - - See upstream issues: - https://bugreports.qt.io/browse/QTBUG-63637 - https://bugreports.qt.io/browse/QTBUG-63659 - https://codereview.qt-project.org/q/8bebded9 - - We should be able to drop this once we are building qt 5.10.1 or later. - - Added in #12971. - -diff --git a/qtbase/mkspecs/win32-g++/qmake.conf b/qtbase/mkspecs/win32-g++/qmake.conf -index e071a0d1..ad229b10 100644 ---- a/qtbase/mkspecs/win32-g++/qmake.conf -+++ b/qtbase/mkspecs/win32-g++/qmake.conf -@@ -87,3 +87,5 @@ QMAKE_NM = $${CROSS_COMPILE}nm -P - include(../common/angle.conf) - - load(qt_config) -+QMAKE_LINK_OBJECT_MAX = 10 -+QMAKE_LINK_OBJECT_SCRIPT = object_script diff --git a/depends/patches/qt/fix_no_printer.patch b/depends/patches/qt/fix_no_printer.patch index f868ca2577..1372356138 100644 --- a/depends/patches/qt/fix_no_printer.patch +++ b/depends/patches/qt/fix_no_printer.patch @@ -10,10 +10,10 @@ --- x/qtbase/src/plugins/plugins.pro +++ y/qtbase/src/plugins/plugins.pro -@@ -8,6 +8,3 @@ qtHaveModule(gui) { - qtConfig(imageformatplugin): SUBDIRS *= imageformats +@@ -9,6 +9,3 @@ qtHaveModule(gui) { !android:qtConfig(library): SUBDIRS *= generic } + qtHaveModule(widgets): SUBDIRS += styles - -!winrt:qtHaveModule(printsupport): \ - SUBDIRS += printsupport diff --git a/depends/patches/qt/fix_powerpc_libpng.patch b/depends/patches/qt/fix_powerpc_libpng.patch deleted file mode 100644 index d37b6c7776..0000000000 --- a/depends/patches/qt/fix_powerpc_libpng.patch +++ /dev/null @@ -1,23 +0,0 @@ -commit 6f9feb773a43c5abfa3455da2e324180e789285b -Author: fanquake <fanquake@gmail.com> -Date: Tue Sep 15 21:44:31 2020 +0800 - - Fix PowerPC build of libpng - - See https://bugreports.qt.io/browse/QTBUG-66388. - - Can be dropped when we are building qt 5.12.0 or later. - -diff --git a/qtbase/src/3rdparty/libpng/libpng.pro b/qtbase/src/3rdparty/libpng/libpng.pro -index 577b61d8..a2f56669 100644 ---- a/qtbase/src/3rdparty/libpng/libpng.pro -+++ b/qtbase/src/3rdparty/libpng/libpng.pro -@@ -10,7 +10,7 @@ MODULE_INCLUDEPATH = $$PWD - - load(qt_helper_lib) - --DEFINES += PNG_ARM_NEON_OPT=0 -+DEFINES += PNG_ARM_NEON_OPT=0 PNG_POWERPC_VSX_OPT=0 - SOURCES += \ - png.c \ - pngerror.c \ diff --git a/depends/patches/qt/fix_qpainter_non_determinism.patch b/depends/patches/qt/fix_qpainter_non_determinism.patch index 3cfcc22f03..44c45187c5 100644 --- a/depends/patches/qt/fix_qpainter_non_determinism.patch +++ b/depends/patches/qt/fix_qpainter_non_determinism.patch @@ -22,7 +22,7 @@ diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/pa index 92ab6e8375..f018009e0b 100644 --- a/qtbase/src/gui/painting/qpaintengine_raster.cpp +++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp -@@ -3971,22 +3971,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, +@@ -4128,22 +4128,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, const QSpan *clipEnd = clip->m_spans + clip->count; while (available && spans < end ) { @@ -51,7 +51,7 @@ index 92ab6e8375..f018009e0b 100644 int sx1 = spans->x; int sx2 = sx1 + spans->len; -@@ -4005,7 +4006,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, +@@ -4162,7 +4163,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, if (len) { out->x = qMax(sx1, cx1); out->len = qMin(sx2, cx2) - out->x; diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch index 8c722ffb46..a5de2b4b9e 100644 --- a/depends/patches/qt/fix_qt_pkgconfig.patch +++ b/depends/patches/qt/fix_qt_pkgconfig.patch @@ -1,17 +1,17 @@ --- old/qtbase/mkspecs/features/qt_module.prf +++ new/qtbase/mkspecs/features/qt_module.prf -@@ -264,7 +264,7 @@ +@@ -269,7 +269,7 @@ load(qt_installs) load(qt_targets) # this builds on top of qt_common --!internal_module:!lib_bundle:if(unix|mingw) { +-!internal_module:if(unix|mingw) { +if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) { CONFIG += create_pc QMAKE_PKGCONFIG_DESTDIR = pkgconfig host_build: \ -@@ -274,9 +274,9 @@ - QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_HEADERS/raw] - QMAKE_PKGCONFIG_CFLAGS = -I${includedir}/$$MODULE_INCNAME +@@ -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() @@ -20,4 +20,4 @@ + 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 - pclib_replace.match = $$lib_replace.match + !isEmpty(lib_replace0.match) { diff --git a/depends/patches/qt/fix_rcc_determinism.patch b/depends/patches/qt/fix_rcc_determinism.patch deleted file mode 100644 index c1b07fe23a..0000000000 --- a/depends/patches/qt/fix_rcc_determinism.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- old/qtbase/src/tools/rcc/rcc.cpp -+++ new/qtbase/src/tools/rcc/rcc.cpp -@@ -207,7 +207,11 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) - if (lib.formatVersion() >= 2) { - // last modified time stamp - const QDateTime lastModified = m_fileInfo.lastModified(); -- lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0)); -+ quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0); -+ static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong(); -+ if (sourceDate != 0) -+ lastmod = sourceDate; -+ lib.writeNumber8(lastmod); - if (text || pass1) - lib.writeChar('\n'); - } diff --git a/depends/patches/qt/fix_riscv64_arch.patch b/depends/patches/qt/fix_riscv64_arch.patch deleted file mode 100644 index e7f29f01f9..0000000000 --- a/depends/patches/qt/fix_riscv64_arch.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -index 20bfd36..93729fa 100644 ---- a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -+++ b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -@@ -65,7 +65,8 @@ - defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ - defined(__SH4__) || defined(__alpha__) || \ - defined(_MIPS_ARCH_MIPS32R2) || \ -- defined(__AARCH64EL__) -+ defined(__AARCH64EL__) || defined(__aarch64__) || \ -+ defined(__riscv) - #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 - #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) - #if defined(_WIN32) diff --git a/depends/patches/qt/freetype_back_compat.patch b/depends/patches/qt/freetype_back_compat.patch deleted file mode 100644 index 1ca55f1ce3..0000000000 --- a/depends/patches/qt/freetype_back_compat.patch +++ /dev/null @@ -1,28 +0,0 @@ -commit 14bc77db61bf9d56f9b6c8b84aa02573605c19c6 -Author: fanquake <fanquake@gmail.com> -Date: Tue Aug 18 15:15:08 2020 +0800 - - Fix backwards compatibility with older Freetype versions at runtime - - A few years ago, libfreetype introduced FT_Get_Font_Format() as an alias - for FT_Get_X11_Font_Format(), but FT_Get_X11_Font_Format() was kept for abi - backwards-compatibility. - - Qt 5.9 introduced a call to FT_Get_Font_Format(). Replace it with FT_Get_X11_Font_Format() - in order to remain compatible with older freetype, which is still used by e.g. Ubuntu Trusty. - - See #14348. - -diff --git a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -index 3f543755..8ecc1c8c 100644 ---- a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -+++ b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -@@ -898,7 +898,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - } - } - #if defined(FT_FONT_FORMATS_H) -- const char *fmt = FT_Get_Font_Format(face); -+ const char *fmt = FT_Get_X11_Font_Format(face); - if (fmt && qstrncmp(fmt, "CFF", 4) == 0) { - FT_Bool no_stem_darkening = true; - FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening); diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch index fe82c2c73c..f4a6f09ee4 100644 --- a/depends/patches/qt/no-xlib.patch +++ b/depends/patches/qt/no-xlib.patch @@ -22,15 +22,15 @@ index 7c62c2e2b3..c05c6c0a07 100644 #include <xcb/xfixes.h> #include <xcb/xcb_image.h> -@@ -384,6 +386,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget) - w->setCursor(c, isBitmapCursor); +@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window) + xcb_flush(xcb_connection()); } +#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) static int cursorIdForShape(int cshape) { int cursorId = 0; -@@ -437,6 +440,7 @@ static int cursorIdForShape(int cshape) +@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape) } return cursorId; } @@ -38,7 +38,7 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) { -@@ -558,7 +562,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) +@@ -556,7 +560,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { xcb_connection_t *conn = xcb_connection(); @@ -48,22 +48,23 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t cursor = XCB_NONE; // Try Xcursor first -@@ -589,6 +595,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +@@ -585,7 +591,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); -@@ -596,6 +603,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +@@ -593,6 +599,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) cursorId, cursorId + 1, 0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0); } +#endif if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) { - const char *name = cursorNames[cshape]; + const char *name = cursorNames[cshape].front(); -- 2.22.0 diff --git a/depends/patches/qt/no_sdk_version_check.patch b/depends/patches/qt/no_sdk_version_check.patch new file mode 100644 index 0000000000..b16635b572 --- /dev/null +++ b/depends/patches/qt/no_sdk_version_check.patch @@ -0,0 +1,20 @@ +commit f5eb142cd04be2bc4ca610ed3b5b7e8ce3520ee3 +Author: fanquake <fanquake@gmail.com> +Date: Tue Jan 5 16:08:49 2021 +0800 + + Don't invoke macOS SDK version checking + + This tries to use xcrun which is not available when cross-compiling. + +diff --git a/qtbase/mkspecs/features/mac/default_post.prf b/qtbase/mkspecs/features/mac/default_post.prf +index 92a9112bca6..447e186eb26 100644 +--- a/qtbase/mkspecs/features/mac/default_post.prf ++++ b/qtbase/mkspecs/features/mac/default_post.prf +@@ -8,7 +8,6 @@ contains(TEMPLATE, .*app) { + !macx-xcode:if(isEmpty(BUILDS)|build_pass) { + # Detect changes to the platform SDK + QMAKE_EXTRA_VARIABLES += QMAKE_MAC_SDK QMAKE_MAC_SDK_VERSION QMAKE_XCODE_DEVELOPER_PATH +- QMAKE_EXTRA_INCLUDES += $$shell_quote($$PWD/sdk.mk) + } + + # Detect incompatible SDK versions diff --git a/depends/patches/qt/xkb-default.patch b/depends/patches/qt/xkb-default.patch deleted file mode 100644 index 165abf3e2e..0000000000 --- a/depends/patches/qt/xkb-default.patch +++ /dev/null @@ -1,26 +0,0 @@ ---- old/qtbase/src/gui/configure.pri 2018-06-06 17:28:10.000000000 -0400 -+++ new/qtbase/src/gui/configure.pri 2018-08-17 18:43:01.589384567 -0400 -@@ -43,18 +43,11 @@ - } - - defineTest(qtConfTest_xkbConfigRoot) { -- qtConfTest_getPkgConfigVariable($${1}): return(true) -- -- for (dir, $$list("/usr/share/X11/xkb", "/usr/local/share/X11/xkb")) { -- exists($$dir) { -- $${1}.value = $$dir -- export($${1}.value) -- $${1}.cache += value -- export($${1}.cache) -- return(true) -- } -- } -- return(false) -+ $${1}.value = "/usr/share/X11/xkb" -+ export($${1}.value) -+ $${1}.cache += value -+ export($${1}.cache) -+ return(true) - } - - defineTest(qtConfTest_qpaDefaultPlatform) { diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 8f2d7af089..0a78cdff20 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -595,11 +595,6 @@ Common misconceptions are clarified in those sections: - *Rationale*: This avoids memory and resource leaks, and ensures exception safety. -- Use `MakeUnique()` to construct objects owned by `unique_ptr`s. - - - *Rationale*: `MakeUnique` is concise and ensures exception safety in complex expressions. - `MakeUnique` is a temporary project local implementation of `std::make_unique` (C++14). - C++ data structures -------------------- diff --git a/doc/fuzzing.md b/doc/fuzzing.md index 87df2bbbb9..4d8825f4c2 100644 --- a/doc/fuzzing.md +++ b/doc/fuzzing.md @@ -108,33 +108,32 @@ Full configure that was tested on macOS Catalina with `brew` installed `llvm`: Read the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html) for more information. This [libFuzzer tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) might also be of interest. -# Fuzzing Bitcoin Core using american fuzzy lop (`afl-fuzz`) +# Fuzzing Bitcoin Core using afl++ ## Quickstart guide -To quickly get started fuzzing Bitcoin Core using [`afl-fuzz`](https://github.com/google/afl): +To quickly get started fuzzing Bitcoin Core using [afl++](https://github.com/AFLplusplus/AFLplusplus): ```sh $ git clone https://github.com/bitcoin/bitcoin $ cd bitcoin/ -$ git clone https://github.com/google/afl -$ make -C afl/ -$ make -C afl/llvm_mode/ +$ git clone https://github.com/AFLplusplus/AFLplusplus +$ make -C AFLplusplus/ source-only $ ./autogen.sh -# It is possible to compile with afl-gcc and afl-g++ instead of afl-clang. However, running afl-fuzz -# may require more memory via the -m flag. -$ CC=$(pwd)/afl/afl-clang-fast CXX=$(pwd)/afl/afl-clang-fast++ ./configure --enable-fuzz +# If afl-clang-lto is not available, see +# https://github.com/AFLplusplus/AFLplusplus#a-selecting-the-best-afl-compiler-for-instrumenting-the-target +$ CC=$(pwd)/AFLplusplus/afl-clang-lto CXX=$(pwd)/AFLplusplus/afl-clang-lto++ ./configure --enable-fuzz $ make # For macOS you may need to ignore x86 compilation checks when running "make". If so, # try compiling using: AFL_NO_X86=1 make $ mkdir -p inputs/ outputs/ $ echo A > inputs/thin-air-input -$ FUZZ=bech32 afl/afl-fuzz -i inputs/ -o outputs/ -- src/test/fuzz/fuzz +$ FUZZ=bech32 AFLplusplus/afl-fuzz -i inputs/ -o outputs/ -- src/test/fuzz/fuzz # You may have to change a few kernel parameters to test optimally - afl-fuzz # will print an error and suggestion if so. ``` -Read the [`afl-fuzz` documentation](https://github.com/google/afl) for more information. +Read the [afl++ documentation](https://github.com/AFLplusplus/AFLplusplus) for more information. # Fuzzing Bitcoin Core using Honggfuzz diff --git a/src/Makefile.am b/src/Makefile.am index 56f561a172..a70ef65aa7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -240,7 +240,6 @@ BITCOIN_CORE_H = \ util/golombrice.h \ util/hasher.h \ util/macros.h \ - util/memory.h \ util/message.h \ util/moneystr.h \ util/rbf.h \ @@ -253,6 +252,7 @@ BITCOIN_CORE_H = \ util/system.h \ util/threadnames.h \ util/time.h \ + util/tokenpipe.h \ util/trace.h \ util/translation.h \ util/ui_change_type.h \ @@ -553,7 +553,6 @@ libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ - compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ @@ -584,6 +583,7 @@ libbitcoin_util_a_SOURCES = \ util/strencodings.cpp \ util/string.cpp \ util/time.cpp \ + util/tokenpipe.cpp \ $(BITCOIN_CORE_H) if USE_LIBEVENT diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 133277baa6..24b2879789 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -252,6 +252,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/net.cpp \ test/fuzz/net_permissions.cpp \ test/fuzz/netaddress.cpp \ + test/fuzz/netbase_dns_lookup.cpp \ test/fuzz/node_eviction.cpp \ test/fuzz/p2p_transport_deserializer.cpp \ test/fuzz/parse_hd_keypath.cpp \ @@ -296,7 +297,8 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ test/fuzz/tx_out.cpp \ - test/fuzz/txrequest.cpp + test/fuzz/txrequest.cpp \ + test/fuzz/validation_load_mempool.cpp endif # ENABLE_FUZZ_BINARY nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 3abfbfd784..376e70ffba 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -17,7 +17,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.vout.resize(1); tx.vout[0].nValue = nValue; - wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)))); + wtxs.push_back(std::make_unique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)))); } // Simple benchmark for wallet coin selection. Note that it maybe be necessary @@ -73,7 +73,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup> CMutableTransaction tx; tx.vout.resize(nInput + 1); tx.vout[nInput].nValue = nValue; - std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); + std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); set.emplace_back(); set.back().Insert(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false); wtxn.emplace_back(std::move(wtx)); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b7bcb534ef..32f06aec2c 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -20,6 +20,7 @@ #include <util/strencodings.h> #include <util/system.h> #include <util/threadnames.h> +#include <util/tokenpipe.h> #include <util/translation.h> #include <util/url.h> @@ -28,6 +29,79 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = urlDecode; +#if HAVE_DECL_FORK + +/** Custom implementation of daemon(). This implements the same order of operations as glibc. + * Opens a pipe to the child process to be able to wait for an event to occur. + * + * @returns 0 if successful, and in child process. + * >0 if successful, and in parent process. + * -1 in case of error (in parent process). + * + * In case of success, endpoint will be one end of a pipe from the child to parent process, + * which can be used with TokenWrite (in the child) or TokenRead (in the parent). + */ +int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint) +{ + // communication pipe with child process + std::optional<TokenPipe> umbilical = TokenPipe::Make(); + if (!umbilical) { + return -1; // pipe or pipe2 failed. + } + + int pid = fork(); + if (pid < 0) { + return -1; // fork failed. + } + if (pid != 0) { + // Parent process gets read end, closes write end. + endpoint = umbilical->TakeReadEnd(); + umbilical->TakeWriteEnd().Close(); + + int status = endpoint.TokenRead(); + if (status != 0) { // Something went wrong while setting up child process. + endpoint.Close(); + return -1; + } + + return pid; + } + // Child process gets write end, closes read end. + endpoint = umbilical->TakeWriteEnd(); + umbilical->TakeReadEnd().Close(); + +#if HAVE_DECL_SETSID + if (setsid() < 0) { + exit(1); // setsid failed. + } +#endif + + if (!nochdir) { + if (chdir("/") != 0) { + exit(1); // chdir failed. + } + } + if (!noclose) { + // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach + // from terminal. + int fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0; + // Don't close if fd<=2 to try to handle the case where the program was invoked without any file descriptors open. + if (fd > 2) close(fd); + if (err) { + exit(1); // dup2 failed. + } + } else { + exit(1); // open /dev/null failed. + } + } + endpoint.TokenWrite(0); // Success + return 0; +} + +#endif + static bool AppInit(int argc, char* argv[]) { NodeContext node; @@ -59,6 +133,14 @@ static bool AppInit(int argc, char* argv[]) return true; } +#if HAVE_DECL_FORK + // Communication with parent after daemonizing. This is used for signalling in the following ways: + // - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate + // that the parent process can quit, and whether it was successful/unsuccessful. + // - an unexpected shutdown of the child process creates an unexpected end of stream at the parent + // end, which is interpreted as failure to start. + TokenPipeEnd daemon_ep; +#endif util::Ref context{node}; try { @@ -105,24 +187,34 @@ static bool AppInit(int argc, char* argv[]) // InitError will have been called with detailed error, which ends up on console return false; } - if (args.GetBoolArg("-daemon", false)) { -#if HAVE_DECL_DAEMON -#if defined(MAC_OSX) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif + if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) { +#if HAVE_DECL_FORK tfm::format(std::cout, PACKAGE_NAME " starting\n"); // Daemonize - if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) - return InitError(Untranslated(strprintf("daemon() failed: %s\n", strerror(errno)))); + switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0) + case 0: // Child: continue. + // If -daemonwait is not enabled, immediately send a success token the parent. + if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) { + daemon_ep.TokenWrite(1); + daemon_ep.Close(); + } + break; + case -1: // Error happened. + return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", strerror(errno)))); + default: { // Parent: wait and exit. + int token = daemon_ep.TokenRead(); + if (token) { // Success + exit(EXIT_SUCCESS); + } else { // fRet = false or token read error (premature exit). + tfm::format(std::cerr, "Error during initializaton - check debug.log for details\n"); + exit(EXIT_FAILURE); + } + } } -#if defined(MAC_OSX) -#pragma GCC diagnostic pop -#endif #else return InitError(Untranslated("-daemon is not supported on this operating system\n")); -#endif // HAVE_DECL_DAEMON +#endif // HAVE_DECL_FORK } // Lock data directory after daemonization if (!AppInitLockDataDirectory()) @@ -138,6 +230,13 @@ static bool AppInit(int argc, char* argv[]) PrintExceptionContinue(nullptr, "AppInit()"); } +#if HAVE_DECL_FORK + if (daemon_ep.IsOpen()) { + // Signal initialization status to parent, then close pipe. + daemon_ep.TokenWrite(fRet); + daemon_ep.Close(); + } +#endif if (fRet) { WaitForShutdown(); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 2c517b58f8..1631176477 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -7,7 +7,6 @@ #include <tinyformat.h> #include <util/system.h> -#include <util/memory.h> #include <assert.h> @@ -44,13 +43,13 @@ const CBaseChainParams& BaseParams() std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) { - return MakeUnique<CBaseChainParams>("", 8332, 8334); + return std::make_unique<CBaseChainParams>("", 8332, 8334); } else if (chain == CBaseChainParams::TESTNET) { - return MakeUnique<CBaseChainParams>("testnet3", 18332, 18334); + return std::make_unique<CBaseChainParams>("testnet3", 18332, 18334); } else if (chain == CBaseChainParams::SIGNET) { - return MakeUnique<CBaseChainParams>("signet", 38332, 38334); + return std::make_unique<CBaseChainParams>("signet", 38332, 38334); } else if (chain == CBaseChainParams::REGTEST) { - return MakeUnique<CBaseChainParams>("regtest", 18443, 18445); + return std::make_unique<CBaseChainParams>("regtest", 18443, 18445); } throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index 8a51f310f7..ff581d4a9e 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -9,13 +9,6 @@ #include <cstddef> #include <cstdint> -// Prior to GLIBC_2.14, memcpy was aliased to memmove. -extern "C" void* memmove(void* a, const void* b, size_t c); -extern "C" void* memcpy(void* a, const void* b, size_t c) -{ - return memmove(a, b, c); -} - #if defined(__i386__) || defined(__arm__) extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp deleted file mode 100644 index 06d0dd6fba..0000000000 --- a/src/compat/glibc_sanity.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2009-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - -#include <cstddef> - -extern "C" void* memcpy(void* a, const void* b, size_t c); -void* memcpy_int(void* a, const void* b, size_t c) -{ - return memcpy(a, b, c); -} - -namespace -{ -// trigger: Use the memcpy_int wrapper which calls our internal memcpy. -// A direct call to memcpy may be optimized away by the compiler. -// test: Fill an array with a sequence of integers. memcpy to a new empty array. -// Verify that the arrays are equal. Use an odd size to decrease the odds of -// the call being optimized away. -template <unsigned int T> -bool sanity_test_memcpy() -{ - unsigned int memcpy_test[T]; - unsigned int memcpy_verify[T] = {}; - for (unsigned int i = 0; i != T; ++i) - memcpy_test[i] = i; - - memcpy_int(memcpy_verify, memcpy_test, sizeof(memcpy_test)); - - for (unsigned int i = 0; i != T; ++i) { - if (memcpy_verify[i] != i) - return false; - } - return true; -} -} // namespace - -bool glibc_sanity_test() -{ - return sanity_test_memcpy<1025>(); -} diff --git a/src/compat/sanity.h b/src/compat/sanity.h index 909c4f6da8..8efa416102 100644 --- a/src/compat/sanity.h +++ b/src/compat/sanity.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_COMPAT_SANITY_H #define BITCOIN_COMPAT_SANITY_H -bool glibc_sanity_test(); bool glibcxx_sanity_test(); #endif // BITCOIN_COMPAT_SANITY_H diff --git a/src/httprpc.cpp b/src/httprpc.cpp index cb8b220895..867ddb090e 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -301,7 +301,7 @@ bool StartHTTPRPC(const util::Ref& context) } struct event_base* eventBase = EventBase(); assert(eventBase); - httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase); + httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase); RPCSetTimerInterface(httpRPCTimerInterface.get()); return true; } diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 4f61bbeabd..32271fb7ab 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -102,8 +102,8 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type, fs::create_directories(path); m_name = filter_name + " block filter index"; - m_db = MakeUnique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe); - m_filter_fileseq = MakeUnique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE); + m_db = std::make_unique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe); + m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE); } bool BlockFilterIndex::Init() diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 6398b7edc8..f41985c344 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -192,7 +192,7 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& } TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe) - : m_db(MakeUnique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) + : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) {} TxIndex::~TxIndex() {} diff --git a/src/init.cpp b/src/init.cpp index 8aa80eacca..67ca98ee6f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -580,8 +580,9 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); -#if HAVE_DECL_DAEMON - argsman.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); +#if HAVE_DECL_FORK + argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); + argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); #else hidden_args.emplace_back("-daemon"); #endif @@ -771,7 +772,7 @@ static bool InitSanityCheck() return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")); } - if (!glibc_sanity_test() || !glibcxx_sanity_test()) + if (!glibcxx_sanity_test()) return false; if (!Random_SanityCheck()) { @@ -1349,7 +1350,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA } assert(!node.scheduler); - node.scheduler = MakeUnique<CScheduler>(); + node.scheduler = std::make_unique<CScheduler>(); // Start the lightweight task scheduler thread node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); }); @@ -1401,9 +1402,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)}; assert(!node.banman); - node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); assert(!node.connman); - node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true)); + node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true)); assert(!node.fee_estimator); // Don't initialize fee estimation with old data if we don't relay transactions, @@ -1799,7 +1800,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex); + g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex); g_txindex->Start(); } diff --git a/src/init.h b/src/init.h index c04d966d06..34bca09dd1 100644 --- a/src/init.h +++ b/src/init.h @@ -9,6 +9,11 @@ #include <memory> #include <string> +//! Default value for -daemon option +static constexpr bool DEFAULT_DAEMON = false; +//! Default value for -daemonwait option +static constexpr bool DEFAULT_DAEMONWAIT = false; + class ArgsManager; struct NodeContext; namespace interfaces { diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp index 4134a4527f..c6bbbed445 100644 --- a/src/interfaces/handler.cpp +++ b/src/interfaces/handler.cpp @@ -4,7 +4,6 @@ #include <interfaces/handler.h> -#include <util/memory.h> #include <boost/signals2/connection.hpp> #include <utility> @@ -35,12 +34,12 @@ public: std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection) { - return MakeUnique<HandlerImpl>(std::move(connection)); + return std::make_unique<HandlerImpl>(std::move(connection)); } std::unique_ptr<Handler> MakeHandler(std::function<void()> cleanup) { - return MakeUnique<CleanupHandler>(std::move(cleanup)); + return std::make_unique<CleanupHandler>(std::move(cleanup)); } } // namespace interfaces diff --git a/src/miner.cpp b/src/miner.cpp index 076d43c951..fbaef0f224 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -39,13 +39,13 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam return nNewTime - nOldTime; } -void RegenerateCommitments(CBlock& block) +void RegenerateCommitments(CBlock& block, BlockManager& blockman) { CMutableTransaction tx{*block.vtx.at(0)}; tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block)); block.vtx.at(0) = MakeTransactionRef(tx); - GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus()); + GenerateCoinbaseCommitment(block, WITH_LOCK(::cs_main, assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); return blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus()); block.hashMerkleRoot = BlockMerkleRoot(block); } @@ -99,7 +99,7 @@ void BlockAssembler::resetBlock() Optional<int64_t> BlockAssembler::m_last_block_num_txs{nullopt}; Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt}; -std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn) { int64_t nTimeStart = GetTimeMicros(); @@ -117,7 +117,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end LOCK2(cs_main, m_mempool.cs); - CBlockIndex* pindexPrev = ::ChainActive().Tip(); + assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*chainstate.m_chain.Tip())); + CBlockIndex* pindexPrev = chainstate.m_chain.Tip(); assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; @@ -176,7 +177,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); BlockValidationState state; - if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) { + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + if (!TestBlockValidity(state, chainparams, chainstate, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); } int64_t nTime2 = GetTimeMicros(); diff --git a/src/miner.h b/src/miner.h index 9a2b7063f4..a67fec6dd8 100644 --- a/src/miner.h +++ b/src/miner.h @@ -158,7 +158,7 @@ public: explicit BlockAssembler(const CTxMemPool& mempool, const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn); + std::unique_ptr<CBlockTemplate> CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn); static Optional<int64_t> m_last_block_num_txs; static Optional<int64_t> m_last_block_weight; @@ -202,6 +202,6 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); /** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */ -void RegenerateCommitments(CBlock& block); +void RegenerateCommitments(CBlock& block, BlockManager& blockman); #endif // BITCOIN_MINER_H diff --git a/src/net.cpp b/src/net.cpp index cf7d3e50ba..1e4a6a9aa7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2466,11 +2466,11 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (semOutbound == nullptr) { // initialize semaphore - semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections)); + semOutbound = std::make_unique<CSemaphore>(std::min(m_max_outbound, nMaxConnections)); } if (semAddnode == nullptr) { // initialize semaphore - semAddnode = MakeUnique<CSemaphore>(nMaxAddnode); + semAddnode = std::make_unique<CSemaphore>(nMaxAddnode); } // @@ -2906,11 +2906,11 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; if (conn_type_in != ConnectionType::BLOCK_RELAY) { - m_tx_relay = MakeUnique<TxRelay>(); + m_tx_relay = std::make_unique<TxRelay>(); } if (RelayAddrsWithConn()) { - m_addr_known = MakeUnique<CRollingBloomFilter>(5000, 0.001); + m_addr_known = std::make_unique<CRollingBloomFilter>(5000, 0.001); } for (const std::string &msg : getAllNetMessageTypes()) @@ -2923,8 +2923,8 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const LogPrint(BCLog::NET, "Added connection peer=%d\n", id); } - m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION)); - m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer()); + m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION)); + m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer()); } CNode::~CNode() diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 86bafe6d27..6ce984348c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -472,6 +472,24 @@ private: std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans); /** Offset into vExtraTxnForCompact to insert the next tx */ size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; + + void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman); + bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params, + BlockFilterType filter_type, uint32_t start_height, + const uint256& stop_hash, uint32_t max_height_diff, + const CBlockIndex*& stop_index, + BlockFilterIndex*& filter_index); + void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, + CConnman& connman); + void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, + CConnman& connman); + void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, + CConnman& connman); }; } // namespace @@ -539,7 +557,7 @@ struct CNodeState { * - its connection type is IsBlockOnlyConn() == false * - it gave us a valid connecting header * - we haven't reached MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT yet - * - it has a better chain than we have + * - its chain tip has at least as much work as ours * * CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip, * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future: @@ -557,16 +575,16 @@ struct CNodeState { */ struct ChainSyncTimeoutState { //! A timeout used for checking whether our peer has sufficiently synced - int64_t m_timeout; + int64_t m_timeout{0}; //! A header with the work we require on our peer's chain - const CBlockIndex * m_work_header; + const CBlockIndex* m_work_header{nullptr}; //! After timeout is reached, set to true after sending getheaders - bool m_sent_getheaders; + bool m_sent_getheaders{false}; //! Whether this peer is protected from disconnection due to a bad/slow chain - bool m_protect; + bool m_protect{false}; }; - ChainSyncTimeoutState m_chain_sync{0, nullptr, false, false}; + ChainSyncTimeoutState m_chain_sync; //! Time of last new block announcement int64_t m_last_block_announcement{0}; @@ -580,11 +598,7 @@ struct CNodeState { //! Whether this peer relays txs via wtxid bool m_wtxid_relay{false}; - CNodeState(bool is_inbound) - : m_is_inbound(is_inbound) - { - m_recently_announced_invs.reset(); - } + CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {} }; /** Map maintaining per-node state. */ @@ -665,41 +679,6 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co return true; } -/** Check whether the last unknown block a peer advertised is not yet known. */ -static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - CNodeState *state = State(nodeid); - assert(state != nullptr); - - if (!state->hashLastUnknownBlock.IsNull()) { - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock); - if (pindex && pindex->nChainWork > 0) { - if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { - state->pindexBestKnownBlock = pindex; - } - state->hashLastUnknownBlock.SetNull(); - } - } -} - -/** Update tracking information about which blocks a peer is assumed to have. */ -static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - CNodeState *state = State(nodeid); - assert(state != nullptr); - - ProcessBlockAvailability(nodeid); - - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); - if (pindex && pindex->nChainWork > 0) { - // An actually better block was announced. - if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { - state->pindexBestKnownBlock = pindex; - } - } else { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; - } -} - void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) { AssertLockHeld(cs_main); @@ -749,9 +728,9 @@ bool PeerManagerImpl::TipMayBeStale() return m_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty(); } -static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool PeerManagerImpl::CanDirectFetch(const Consensus::Params &consensusParams) { - return ::ChainActive().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; + return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; } static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -763,6 +742,41 @@ static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIV return false; } +/** Check whether the last unknown block a peer advertised is not yet known. */ +void PeerManagerImpl::ProcessBlockAvailability(NodeId nodeid) { + CNodeState *state = State(nodeid); + assert(state != nullptr); + + if (!state->hashLastUnknownBlock.IsNull()) { + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock); + if (pindex && pindex->nChainWork > 0) { + if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { + state->pindexBestKnownBlock = pindex; + } + state->hashLastUnknownBlock.SetNull(); + } + } +} + +/** Update tracking information about which blocks a peer is assumed to have. */ +void PeerManagerImpl::UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { + CNodeState *state = State(nodeid); + assert(state != nullptr); + + ProcessBlockAvailability(nodeid); + + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash); + if (pindex && pindex->nChainWork > 0) { + // An actually better block was announced. + if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { + state->pindexBestKnownBlock = pindex; + } + } else { + // An unknown block was announced; just assume that the latest one is the best one. + state->hashLastUnknownBlock = hash; + } +} + void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller) { if (count == 0) @@ -775,7 +789,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count // Make sure pindexBestKnownBlock is up to date, we'll need it. ProcessBlockAvailability(nodeid); - if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < ::ChainActive().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { // This peer has nothing interesting. return; } @@ -783,7 +797,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count if (state->pindexLastCommonBlock == nullptr) { // Bootstrap quickly by guessing a parent of our best tip is the forking point. // Guessing wrong in either direction is not a problem. - state->pindexLastCommonBlock = ::ChainActive()[std::min(state->pindexBestKnownBlock->nHeight, ::ChainActive().Height())]; + state->pindexLastCommonBlock = m_chainman.ActiveChain()[std::min(state->pindexBestKnownBlock->nHeight, m_chainman.ActiveChain().Height())]; } // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor @@ -826,7 +840,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count // We wouldn't download this block or its descendants from this peer. return; } - if (pindex->nStatus & BLOCK_HAVE_DATA || ::ChainActive().Contains(pindex)) { + if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) { if (pindex->HaveTxsDownloaded()) state->pindexLastCommonBlock = pindex; } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { @@ -1179,10 +1193,10 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat // active chain if they are no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about and // we fully-validated them at some point. -static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) { AssertLockHeld(cs_main); - if (::ChainActive().Contains(pindex)) return true; + if (m_chainman.ActiveChain().Contains(pindex)) return true; return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); @@ -1206,6 +1220,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn m_stale_tip_check_time(0), m_ignore_incoming_txs(ignore_incoming_txs) { + assert(std::addressof(g_chainman) == std::addressof(m_chainman)); // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); @@ -1331,7 +1346,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha /** * Update our best height and announce any block hashes which weren't previously - * in ::ChainActive() to our peers. + * in m_chainman.ActiveChain() to our peers. */ void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { @@ -1393,7 +1408,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta // the tip yet so we have no way to check this directly here. Instead we // just check that there are currently no other blocks in flight. else if (state.IsValid() && - !::ChainstateActive().IsInitialBlockDownload() && + !m_chainman.ActiveChainstate().IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first); @@ -1412,12 +1427,12 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid) { assert(recentRejects); - if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) { + if (m_chainman.ActiveChain().Tip()->GetBlockHash() != hashRecentRejectsChainTip) { // If the chain tip has changed previously rejected transactions // might be now valid, e.g. due to a nLockTime'd tx becoming valid, // or a double-spend. Reset the rejects filter and give those // txs a second chance. - hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash(); + hashRecentRejectsChainTip = m_chainman.ActiveChain().Tip()->GetBlockHash(); recentRejects->reset(); } @@ -1433,9 +1448,9 @@ bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid) return recentRejects->contains(hash) || m_mempool.exists(gtxid); } -bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool PeerManagerImpl::AlreadyHaveBlock(const uint256& block_hash) { - return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; + return m_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; } void PeerManagerImpl::SendPings() @@ -1514,7 +1529,7 @@ static void RelayAddress(const CNode& originator, connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman) +void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman) { bool send = false; std::shared_ptr<const CBlock> a_recent_block; @@ -1531,7 +1546,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch bool need_activate_chain = false; { LOCK(cs_main); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && pindex->IsValid(BLOCK_VALID_TREE)) { @@ -1546,13 +1561,13 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch } // release cs_main before calling ActivateBestChain if (need_activate_chain) { BlockValidationState state; - if (!::ChainstateActive().ActivateBestChain(state, chainparams, a_recent_block)) { + if (!m_chainman.ActiveChainstate().ActivateBestChain(state, chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } LOCK(cs_main); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { send = BlockRequestAllowed(pindex, consensusParams); if (!send) { @@ -1574,7 +1589,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch } // Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold if (send && !pfrom.HasPermission(PF_NOBAN) && ( - (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) + (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) )) { LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId()); @@ -1641,7 +1656,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch // instead we respond with the full, non-compact block. bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness; int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) { + if (CanDirectFetch(consensusParams) && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) { if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); } else { @@ -1662,7 +1677,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch // and we want it right after the last block so they don't // wait for other stuff first. std::vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash())); + vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash())); connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); peer.m_continuation_block.SetNull(); } @@ -1841,9 +1856,9 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. - if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -1871,7 +1886,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // If we don't have the last header, then they'll have given us // something new (if these headers are valid). - if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) { + if (!m_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) { received_new_header = true; } } @@ -1899,27 +1914,27 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // because it is set in UpdateBlockAvailability. Some nullptr checks // are still present, however, as belt-and-suspenders. - if (received_new_header && pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) { + if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) { nodestate->m_last_block_announcement = GetTime(); } if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more headers. - // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue + // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue // from there instead. LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height); - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256())); } bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus()); // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. - if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) { + if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) { std::vector<const CBlockIndex*> vToFetch; const CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. - while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) { @@ -1932,7 +1947,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // very large reorg at a time we think we're close to caught up to // the main chain -- this shouldn't really happen. Bail out on the // direct fetch and rely on parallel download instead. - if (!::ChainActive().Contains(pindexWalk)) { + if (!m_chainman.ActiveChain().Contains(pindexWalk)) { LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); @@ -1965,7 +1980,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, } // If we're in IBD, we want outbound peers that will serve us a useful // chain. Disconnect peers that are on chains with insufficient work. - if (::ChainstateActive().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { + if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { // When nCount < MAX_HEADERS_RESULTS, we know we have no more // headers to fetch from this peer. if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { @@ -1973,7 +1988,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // us sync -- disconnect if it is an outbound disconnection // candidate. // Note: We compare their tip to nMinimumChainWork (rather than - // ::ChainActive().Tip()) because we won't start block download + // m_chainman.ActiveChain().Tip()) because we won't start block download // until we have a headers chain that has at least // nMinimumChainWork, even if a peer has a chain past our tip, // as an anti-DoS measure. @@ -1990,7 +2005,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, // thus always subject to eviction under the bad/lagging chain logic. // See ChainSyncTimeoutState. if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) { - if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { + if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId()); nodestate->m_chain_sync.m_protect = true; ++m_outbound_peers_with_protect_from_disconnect; @@ -2021,7 +2036,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash); if (porphanTx == nullptr) continue; - const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), m_mempool, porphanTx, false /* bypass_limits */); + const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */); const TxValidationState& state = result.m_state; if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { @@ -2097,7 +2112,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) * @param[out] filter_index The filter index, if the request can be serviced. * @return True if the request can be serviced. */ -static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params, +bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params, BlockFilterType filter_type, uint32_t start_height, const uint256& stop_hash, uint32_t max_height_diff, const CBlockIndex*& stop_index, @@ -2115,7 +2130,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par { LOCK(cs_main); - stop_index = g_chainman.m_blockman.LookupBlockIndex(stop_hash); + stop_index = m_chainman.m_blockman.LookupBlockIndex(stop_hash); // Check that the stop block exists and the peer would be allowed to fetch it. if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) { @@ -2160,7 +2175,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par * @param[in] chain_params Chain parameters * @param[in] connman Pointer to the connection manager */ -static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, +void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, CConnman& connman) { uint8_t filter_type_ser; @@ -2202,7 +2217,7 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara * @param[in] chain_params Chain parameters * @param[in] connman Pointer to the connection manager */ -static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, +void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, CConnman& connman) { uint8_t filter_type_ser; @@ -2257,7 +2272,7 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar * @param[in] chain_params Chain parameters * @param[in] connman Pointer to the connection manager */ -static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, +void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, CConnman& connman) { uint8_t filter_type_ser; @@ -2440,7 +2455,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // // We skip this for block-relay-only peers to avoid potentially leaking // information about our block-relay-only connections via address relay. - if (fListen && !::ChainstateActive().IsInitialBlockDownload()) + if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices()); FastRandomContext insecure_rand; @@ -2757,7 +2772,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } if (best_block != nullptr) { - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block)); LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId()); } @@ -2813,7 +2828,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, a_recent_block = most_recent_block; } BlockValidationState state; - if (!::ChainstateActive().ActivateBestChain(state, m_chainparams, a_recent_block)) { + if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } @@ -2821,14 +2836,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, LOCK(cs_main); // Find the last block the caller has in the main chain - const CBlockIndex* pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); + const CBlockIndex* pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator); // Send the rest of the chain if (pindex) - pindex = ::ChainActive().Next(pindex); + pindex = m_chainman.ActiveChain().Next(pindex); int nLimit = 500; LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId()); - for (; pindex; pindex = ::ChainActive().Next(pindex)) + for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { @@ -2838,7 +2853,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // If pruning, don't inv blocks unless we have on disk and are likely to still have // for some reasonable time window (1 hour) that block relay might require. const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing; - if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave)) + if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave)) { LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; @@ -2874,13 +2889,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, { LOCK(cs_main); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(req.blockhash); + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(req.blockhash); if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) { LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId()); return; } - if (pindex->nHeight >= ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) { + if (pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_BLOCKTXN_DEPTH) { CBlock block; bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus()); assert(ret); @@ -2918,7 +2933,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } LOCK(cs_main); - if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) { + if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) { LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId()); return; } @@ -2928,7 +2943,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, if (locator.IsNull()) { // If locator is null, return the hashStop block - pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop); + pindex = m_chainman.m_blockman.LookupBlockIndex(hashStop); if (!pindex) { return; } @@ -2941,23 +2956,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, else { // Find the last block the caller has in the main chain - pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); + pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator); if (pindex) - pindex = ::ChainActive().Next(pindex); + pindex = m_chainman.ActiveChain().Next(pindex); } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end std::vector<CBlock> vHeaders; int nLimit = MAX_HEADERS_RESULTS; LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId()); - for (; pindex; pindex = ::ChainActive().Next(pindex)) + for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } - // pindex can be nullptr either if we sent ::ChainActive().Tip() OR - // if our peer has ::ChainActive().Tip() (and thus we are sending an empty + // pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR + // if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. // @@ -2968,7 +2983,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // without the new block. By resetting the BestHeaderSent, we ensure we // will re-announce the new block via headers (or compact blocks again) // in the SendMessages logic. - nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip(); + nodestate->pindexBestHeaderSent = pindex ? pindex : m_chainman.ActiveChain().Tip(); m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); return; } @@ -3036,7 +3051,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, return; } - const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), m_mempool, ptx, false /* bypass_limits */); + const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */); const TxValidationState& state = result.m_state; if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { @@ -3201,14 +3216,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, { LOCK(cs_main); - if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { + if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers - if (!::ChainstateActive().IsInitialBlockDownload()) - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); + if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256())); return; } - if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) { + if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) { received_new_header = true; } } @@ -3248,7 +3263,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // If this was a new header with more work than our tip, update the // peer's last block announcement time - if (received_new_header && pindex->nChainWork > ::ChainActive().Tip()->nChainWork) { + if (received_new_header && pindex->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) { nodestate->m_last_block_announcement = GetTime(); } @@ -3258,7 +3273,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here return; - if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better + if (pindex->nChainWork <= m_chainman.ActiveChain().Tip()->nChainWork || // We know something better pindex->nTx != 0) { // We had this block at some point, but pruned it if (fAlreadyInFlight) { // We requested this block for some reason, but our mempool will probably be useless @@ -3282,7 +3297,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // We want to be a bit conservative just to be extra careful about DoS // possibilities in compact block processing... - if (pindex->nHeight <= ::ChainActive().Height() + 2) { + if (pindex->nHeight <= m_chainman.ActiveChain().Height() + 2) { if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || (fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) { std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr; @@ -3930,7 +3945,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) // their chain has more work than ours, we should sync to it, // unless it's invalid, in which case we should find that out and // disconnect from them elsewhere). - if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork) { + if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) { if (state.m_chain_sync.m_timeout != 0) { state.m_chain_sync.m_timeout = 0; state.m_chain_sync.m_work_header = nullptr; @@ -3942,7 +3957,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) // where we checked against our tip. // Either way, set a new timeout based on current tip. state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT; - state.m_chain_sync.m_work_header = ::ChainActive().Tip(); + state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip(); state.m_chain_sync.m_sent_getheaders = false; } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) { // No evidence yet that our peer has synced to a chain with work equal to that @@ -3955,7 +3970,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) } else { assert(state.m_chain_sync.m_work_header); LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString()); - m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); + m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); state.m_chain_sync.m_sent_getheaders = true; constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes // Bump the timeout to allow a response, which could clear the timeout @@ -4192,7 +4207,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) auto current_time = GetTime<std::chrono::microseconds>(); if (fListen && pto->RelayAddrsWithConn() && - !::ChainstateActive().IsInitialBlockDownload() && + !m_chainman.ActiveChainstate().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) { // If we've sent before, clear the bloom filter for the peer, so that our // self-announcement will actually go out. @@ -4253,7 +4268,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Start block sync if (pindexBestHeader == nullptr) - pindexBestHeader = ::ChainActive().Tip(); + pindexBestHeader = m_chainman.ActiveChain().Tip(); bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { // Only actively request headers from a single peer, unless we're close to today. @@ -4278,7 +4293,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height); - m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256())); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexStart), uint256())); } } @@ -4305,11 +4320,11 @@ bool PeerManagerImpl::SendMessages(CNode* pto) bool fFoundStartingHeader = false; // Try to find first header that our peer doesn't have, and // then send all headers past that one. If we come across any - // headers that aren't on ::ChainActive(), give up. + // headers that aren't on m_chainman.ActiveChain(), give up. for (const uint256& hash : peer->m_blocks_for_headers_relay) { - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash); assert(pindex); - if (::ChainActive()[pindex->nHeight] != pindex) { + if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; break; @@ -4399,15 +4414,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // in the past. if (!peer->m_blocks_for_headers_relay.empty()) { const uint256& hashToAnnounce = peer->m_blocks_for_headers_relay.back(); - const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce); + const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hashToAnnounce); assert(pindex); // Warn if we're announcing a block that is not on the main chain. // This should be very rare and could be optimized out. // Just log for now. - if (::ChainActive()[pindex->nHeight] != pindex) { + if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) { LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n", - hashToAnnounce.ToString(), ::ChainActive().Tip()->GetBlockHash().ToString()); + hashToAnnounce.ToString(), m_chainman.ActiveChain().Tip()->GetBlockHash().ToString()); } // If the peer's chain has this block, don't inv it back. @@ -4637,7 +4652,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Message: getdata (blocks) // std::vector<CInv> vGetData; - if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector<const CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); @@ -4726,4 +4741,3 @@ bool PeerManagerImpl::SendMessages(CNode* pto) } // release cs_main return true; } - diff --git a/src/netbase.cpp b/src/netbase.cpp index 88c36ed86c..ac2392ebed 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -42,6 +42,50 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP; int g_socks5_recv_timeout = 20 * 1000; static std::atomic<bool> interruptSocks5Recv(false); +std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup) +{ + addrinfo ai_hint{}; + // We want a TCP port, which is a streaming socket type + ai_hint.ai_socktype = SOCK_STREAM; + ai_hint.ai_protocol = IPPROTO_TCP; + // We don't care which address family (IPv4 or IPv6) is returned + ai_hint.ai_family = AF_UNSPEC; + // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only + // return addresses whose family we have an address configured for. + // + // If we don't allow lookups, then use the AI_NUMERICHOST flag for + // getaddrinfo to only decode numerical network addresses and suppress + // hostname lookups. + ai_hint.ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST; + + addrinfo* ai_res{nullptr}; + const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)}; + if (n_err != 0) { + return {}; + } + + // Traverse the linked list starting with ai_trav. + addrinfo* ai_trav{ai_res}; + std::vector<CNetAddr> resolved_addresses; + while (ai_trav != nullptr) { + if (ai_trav->ai_family == AF_INET) { + assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in)); + resolved_addresses.emplace_back(reinterpret_cast<sockaddr_in*>(ai_trav->ai_addr)->sin_addr); + } + if (ai_trav->ai_family == AF_INET6) { + assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6)); + const sockaddr_in6* s6{reinterpret_cast<sockaddr_in6*>(ai_trav->ai_addr)}; + resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id); + } + ai_trav = ai_trav->ai_next; + } + freeaddrinfo(ai_res); + + return resolved_addresses; +} + +DNSLookupFn g_dns_lookup{WrappedGetAddrInfo}; + enum Network ParseNetwork(const std::string& net_in) { std::string net = ToLower(net_in); if (net == "ipv4") return NET_IPV4; @@ -87,7 +131,7 @@ std::vector<std::string> GetNetworkNames(bool append_unroutable) return names; } -bool static LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +static bool LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function) { vIP.clear(); @@ -109,73 +153,20 @@ bool static LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, un } } - struct addrinfo aiHint; - memset(&aiHint, 0, sizeof(struct addrinfo)); - - // We want a TCP port, which is a streaming socket type - aiHint.ai_socktype = SOCK_STREAM; - aiHint.ai_protocol = IPPROTO_TCP; - // We don't care which address family (IPv4 or IPv6) is returned - aiHint.ai_family = AF_UNSPEC; - // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only - // return addresses whose family we have an address configured for. - // - // If we don't allow lookups, then use the AI_NUMERICHOST flag for - // getaddrinfo to only decode numerical network addresses and suppress - // hostname lookups. - aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; - struct addrinfo *aiRes = nullptr; - int nErr = getaddrinfo(name.c_str(), nullptr, &aiHint, &aiRes); - if (nErr) - return false; - - // Traverse the linked list starting with aiTrav, add all non-internal - // IPv4,v6 addresses to vIP while respecting nMaxSolutions. - struct addrinfo *aiTrav = aiRes; - while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) - { - CNetAddr resolved; - if (aiTrav->ai_family == AF_INET) - { - assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); - resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr); - } - - if (aiTrav->ai_family == AF_INET6) - { - assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); - struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr; - resolved = CNetAddr(s6->sin6_addr, s6->sin6_scope_id); + for (const CNetAddr& resolved : dns_lookup_function(name, fAllowLookup)) { + if (nMaxSolutions > 0 && vIP.size() >= nMaxSolutions) { + break; } /* Never allow resolving to an internal address. Consider any such result invalid */ if (!resolved.IsInternal()) { vIP.push_back(resolved); } - - aiTrav = aiTrav->ai_next; } - freeaddrinfo(aiRes); - return (vIP.size() > 0); } -/** - * Resolve a host string to its corresponding network addresses. - * - * @param name The string representing a host. Could be a name or a numerical - * IP address (IPv6 addresses in their bracketed form are - * allowed). - * @param[out] vIP The resulting network addresses to which the specified host - * string resolved. - * - * @returns Whether or not the specified host string successfully resolved to - * any resulting network addresses. - * - * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int) - * for additional parameter descriptions. - */ -bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return false; @@ -187,49 +178,23 @@ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned in strHost = strHost.substr(1, strHost.size() - 2); } - return LookupIntern(strHost, vIP, nMaxSolutions, fAllowLookup); + return LookupIntern(strHost, vIP, nMaxSolutions, fAllowLookup, dns_lookup_function); } - /** - * Resolve a host string to its first corresponding network address. - * - * @see LookupHost(const std::string&, std::vector<CNetAddr>&, unsigned int, bool) for - * additional parameter descriptions. - */ -bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup) +bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return false; } std::vector<CNetAddr> vIP; - LookupHost(name, vIP, 1, fAllowLookup); + LookupHost(name, vIP, 1, fAllowLookup, dns_lookup_function); if(vIP.empty()) return false; addr = vIP.front(); return true; } -/** - * Resolve a service string to its corresponding service. - * - * @param name The string representing a service. Could be a name or a - * numerical IP address (IPv6 addresses should be in their - * disambiguated bracketed form), optionally followed by a port - * number. (e.g. example.com:8333 or - * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420) - * @param[out] vAddr The resulting services to which the specified service string - * resolved. - * @param portDefault The default port for resulting services if not specified - * by the service string. - * @param fAllowLookup Whether or not hostname lookups are permitted. If yes, - * external queries may be performed. - * @param nMaxSolutions The maximum number of results we want, specifying 0 - * means "as many solutions as we get." - * - * @returns Whether or not the service string successfully resolved to any - * resulting services. - */ -bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions) +bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function) { if (name.empty() || !ValidAsCString(name)) { return false; @@ -239,7 +204,7 @@ bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefau SplitHostPort(name, port, hostname); std::vector<CNetAddr> vIP; - bool fRet = LookupIntern(hostname, vIP, nMaxSolutions, fAllowLookup); + bool fRet = LookupIntern(hostname, vIP, nMaxSolutions, fAllowLookup, dns_lookup_function); if (!fRet) return false; vAddr.resize(vIP.size()); @@ -248,36 +213,20 @@ bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefau return true; } -/** - * Resolve a service string to its first corresponding service. - * - * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int) - * for additional parameter descriptions. - */ -bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup) +bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return false; } std::vector<CService> vService; - bool fRet = Lookup(name, vService, portDefault, fAllowLookup, 1); + bool fRet = Lookup(name, vService, portDefault, fAllowLookup, 1, dns_lookup_function); if (!fRet) return false; addr = vService[0]; return true; } -/** - * Resolve a service string with a numeric IP to its first corresponding - * service. - * - * @returns The resulting CService if the resolution was successful, [::]:0 - * otherwise. - * - * @see Lookup(const char *, CService&, int, bool) for additional parameter - * descriptions. - */ -CService LookupNumeric(const std::string& name, int portDefault) +CService LookupNumeric(const std::string& name, int portDefault, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return {}; @@ -285,7 +234,7 @@ CService LookupNumeric(const std::string& name, int portDefault) CService addr; // "1.2:345" will fail to resolve the ip, but will still set the port. // If the ip fails to resolve, re-init the result. - if(!Lookup(name, addr, portDefault, false)) + if(!Lookup(name, addr, portDefault, false, dns_lookup_function)) addr = CService(); return addr; } @@ -414,24 +363,6 @@ static std::string Socks5ErrorString(uint8_t err) } } -/** - * Connect to a specified destination service through an already connected - * SOCKS5 proxy. - * - * @param strDest The destination fully-qualified domain name. - * @param port The destination port. - * @param auth The credentials with which to authenticate with the specified - * SOCKS5 proxy. - * @param sock The SOCKS5 proxy socket. - * - * @returns Whether or not the operation succeeded. - * - * @note The specified SOCKS5 proxy socket must already be connected to the - * SOCKS5 proxy. - * - * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol - * Version 5</a> - */ bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock) { IntrRecvError recvr; @@ -606,18 +537,6 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg } } -/** - * Try to connect to the specified service on the specified socket. - * - * @param addrConnect The service to which to connect. - * @param hSocket The socket on which to connect. - * @param nTimeout Wait this many milliseconds for the connection to be - * established. - * @param manual_connection Whether or not the connection was manually requested - * (e.g. through the addnode RPC) - * - * @returns Whether or not a connection was successfully made. - */ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection) { // Create a sockaddr from the specified service. @@ -716,22 +635,6 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) { return true; } -/** - * Set the name proxy to use for all connections to nodes specified by a - * hostname. After setting this proxy, connecting to a node specified by a - * hostname won't result in a local lookup of said hostname, rather, connect to - * the node by asking the name proxy for a proxy connection to the hostname, - * effectively delegating the hostname lookup to the specified proxy. - * - * This delegation increases privacy for those who set the name proxy as they no - * longer leak their external hostname queries to their DNS servers. - * - * @returns Whether or not the operation succeeded. - * - * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5 - * server in common use (most notably Tor) actually implements UDP - * support, and a DNS resolver is beyond the scope of this project. - */ bool SetNameProxy(const proxyType &addrProxy) { if (!addrProxy.IsValid()) return false; @@ -762,21 +665,6 @@ bool IsProxy(const CNetAddr &addr) { return false; } -/** - * Connect to a specified destination service through a SOCKS5 proxy by first - * connecting to the SOCKS5 proxy. - * - * @param proxy The SOCKS5 proxy. - * @param strDest The destination service to which to connect. - * @param port The destination port. - * @param sock The socket on which to connect to the SOCKS5 proxy. - * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5 - * proxy to be established. - * @param[out] outProxyConnectionFailed Whether or not the connection to the - * SOCKS5 proxy failed. - * - * @returns Whether or not the operation succeeded. - */ bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed) { // first connect to proxy server @@ -800,18 +688,7 @@ bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int return true; } -/** - * Parse and resolve a specified subnet string into the appropriate internal - * representation. - * - * @param strSubnet A string representation of a subnet of the form `network - * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g. - * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`). - * @param ret The resulting internal representation of a subnet. - * - * @returns Whether the operation succeeded or not. - */ -bool LookupSubNet(const std::string& strSubnet, CSubNet& ret) +bool LookupSubNet(const std::string& strSubnet, CSubNet& ret, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(strSubnet)) { return false; @@ -822,7 +699,7 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret) std::string strAddress = strSubnet.substr(0, slash); // TODO: Use LookupHost(const std::string&, CNetAddr&, bool) instead to just get // one CNetAddr. - if (LookupHost(strAddress, vIP, 1, false)) + if (LookupHost(strAddress, vIP, 1, false, dns_lookup_function)) { CNetAddr network = vIP[0]; if (slash != strSubnet.npos) @@ -837,7 +714,7 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret) else // If not a valid number, try full netmask syntax { // Never allow lookup for netmask - if (LookupHost(strNetmask, vIP, 1, false)) { + if (LookupHost(strNetmask, vIP, 1, false, dns_lookup_function)) { ret = CSubNet(network, vIP[0]); return ret.IsValid(); } diff --git a/src/netbase.h b/src/netbase.h index 751f7eb3f0..e98a21ce1f 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -64,6 +64,11 @@ struct ProxyCredentials std::string password; }; +/** + * Wrapper for getaddrinfo(3). Do not use directly: call Lookup/LookupHost/LookupNumeric/LookupSubNet. + */ +std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup); + enum Network ParseNetwork(const std::string& net); std::string GetNetworkName(enum Network net); /** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */ @@ -71,15 +76,107 @@ std::vector<std::string> GetNetworkNames(bool append_unroutable = false); bool SetProxy(enum Network net, const proxyType &addrProxy); bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); +/** + * Set the name proxy to use for all connections to nodes specified by a + * hostname. After setting this proxy, connecting to a node specified by a + * hostname won't result in a local lookup of said hostname, rather, connect to + * the node by asking the name proxy for a proxy connection to the hostname, + * effectively delegating the hostname lookup to the specified proxy. + * + * This delegation increases privacy for those who set the name proxy as they no + * longer leak their external hostname queries to their DNS servers. + * + * @returns Whether or not the operation succeeded. + * + * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5 + * server in common use (most notably Tor) actually implements UDP + * support, and a DNS resolver is beyond the scope of this project. + */ bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); bool GetNameProxy(proxyType &nameProxyOut); -bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup); -bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup); -bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup); -bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); -CService LookupNumeric(const std::string& name, int portDefault = 0); -bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet); + +using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>; +extern DNSLookupFn g_dns_lookup; + +/** + * Resolve a host string to its corresponding network addresses. + * + * @param name The string representing a host. Could be a name or a numerical + * IP address (IPv6 addresses in their bracketed form are + * allowed). + * @param[out] vIP The resulting network addresses to which the specified host + * string resolved. + * + * @returns Whether or not the specified host string successfully resolved to + * any resulting network addresses. + * + * @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ +bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a host string to its first corresponding network address. + * + * @see LookupHost(const std::string&, std::vector<CNetAddr>&, unsigned int, bool, DNSLookupFn) + * for additional parameter descriptions. + */ +bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string to its corresponding service. + * + * @param name The string representing a service. Could be a name or a + * numerical IP address (IPv6 addresses should be in their + * disambiguated bracketed form), optionally followed by a port + * number. (e.g. example.com:8333 or + * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420) + * @param[out] vAddr The resulting services to which the specified service string + * resolved. + * @param portDefault The default port for resulting services if not specified + * by the service string. + * @param fAllowLookup Whether or not hostname lookups are permitted. If yes, + * external queries may be performed. + * @param nMaxSolutions The maximum number of results we want, specifying 0 + * means "as many solutions as we get." + * + * @returns Whether or not the service string successfully resolved to any + * resulting services. + */ +bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string to its first corresponding service. + * + * @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ +bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string with a numeric IP to its first corresponding + * service. + * + * @returns The resulting CService if the resolution was successful, [::]:0 otherwise. + * + * @see Lookup(const std::string&, std::vector<CService>&, int, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ +CService LookupNumeric(const std::string& name, int portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Parse and resolve a specified subnet string into the appropriate internal + * representation. + * + * @param strSubnet A string representation of a subnet of the form `network + * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g. + * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`). + * @param ret The resulting internal representation of a subnet. + * + * @returns Whether the operation succeeded or not. + */ +bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet, DNSLookupFn dns_lookup_function = g_dns_lookup); /** * Create a TCP socket in the given address family. @@ -93,14 +190,61 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family); */ extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock; -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection); +/** + * Try to connect to the specified service on the specified socket. + * + * @param addrConnect The service to which to connect. + * @param hSocket The socket on which to connect. + * @param nTimeout Wait this many milliseconds for the connection to be + * established. + * @param manual_connection Whether or not the connection was manually requested + * (e.g. through the addnode RPC) + * + * @returns Whether or not a connection was successfully made. + */ +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection); + +/** + * Connect to a specified destination service through a SOCKS5 proxy by first + * connecting to the SOCKS5 proxy. + * + * @param proxy The SOCKS5 proxy. + * @param strDest The destination service to which to connect. + * @param port The destination port. + * @param sock The socket on which to connect to the SOCKS5 proxy. + * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5 + * proxy to be established. + * @param[out] outProxyConnectionFailed Whether or not the connection to the + * SOCKS5 proxy failed. + * + * @returns Whether or not the operation succeeded. + */ bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); + /** Disable or enable blocking-mode for a socket */ bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking); /** Set the TCP_NODELAY flag on a socket */ bool SetSocketNoDelay(const SOCKET& hSocket); void InterruptSocks5(bool interrupt); +/** + * Connect to a specified destination service through an already connected + * SOCKS5 proxy. + * + * @param strDest The destination fully-qualified domain name. + * @param port The destination port. + * @param auth The credentials with which to authenticate with the specified + * SOCKS5 proxy. + * @param sock The SOCKS5 proxy socket. + * + * @returns Whether or not the operation succeeded. + * + * @note The specified SOCKS5 proxy socket must already be connected to the + * SOCKS5 proxy. + * + * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol + * Version 5</a> + */ bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& socket); #endif // BITCOIN_NETBASE_H diff --git a/src/node/coin.cpp b/src/node/coin.cpp index f4f86cdbe9..263dcff657 100644 --- a/src/node/coin.cpp +++ b/src/node/coin.cpp @@ -12,7 +12,8 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins) { assert(node.mempool); LOCK2(cs_main, node.mempool->cs); - CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip(); + assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate())); + CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip(); CCoinsViewMemPool mempool_view(&chain_view, *node.mempool); for (auto& coin : coins) { if (!mempool_view.GetCoin(coin.first, coin.second)) { diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp index 06fcc33725..88bdba5953 100644 --- a/src/node/coinstats.cpp +++ b/src/node/coinstats.cpp @@ -83,7 +83,7 @@ static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, con //! Calculate statistics about the unspent transaction output set template <typename T> -static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point) +static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point) { stats = CCoinsStats(); std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); @@ -92,7 +92,8 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); - stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight; + assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); + stats.nHeight = blockman.LookupBlockIndex(stats.hashBlock)->nHeight; } PrepareHash(hash_obj, stats); @@ -126,19 +127,19 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const return true; } -bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point) +bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point) { switch (hash_type) { case(CoinStatsHashType::HASH_SERIALIZED): { CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - return GetUTXOStats(view, stats, ss, interruption_point); + return GetUTXOStats(view, blockman, stats, ss, interruption_point); } case(CoinStatsHashType::MUHASH): { MuHash3072 muhash; - return GetUTXOStats(view, stats, muhash, interruption_point); + return GetUTXOStats(view, blockman, stats, muhash, interruption_point); } case(CoinStatsHashType::NONE): { - return GetUTXOStats(view, stats, nullptr, interruption_point); + return GetUTXOStats(view, blockman, stats, nullptr, interruption_point); } } // no default case, so the compiler can warn about missing cases assert(false); diff --git a/src/node/coinstats.h b/src/node/coinstats.h index f02b95235f..83f228aa7e 100644 --- a/src/node/coinstats.h +++ b/src/node/coinstats.h @@ -8,6 +8,7 @@ #include <amount.h> #include <uint256.h> +#include <validation.h> #include <cstdint> #include <functional> @@ -36,6 +37,6 @@ struct CCoinsStats }; //! Calculate statistics about the unspent transaction output set -bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {}); +bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {}); #endif // BITCOIN_NODE_COINSTATS_H diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 62ef72ef38..9406ef07c5 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -182,18 +182,21 @@ public: int getNumBlocks() override { LOCK(::cs_main); - return ::ChainActive().Height(); + assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain())); + return m_context->chainman->ActiveChain().Height(); } uint256 getBestBlockHash() override { - const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip()); + assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain())); + const CBlockIndex* tip = WITH_LOCK(::cs_main, return m_context->chainman->ActiveChain().Tip()); return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash(); } int64_t getLastBlockTime() override { LOCK(::cs_main); - if (::ChainActive().Tip()) { - return ::ChainActive().Tip()->GetBlockTime(); + assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain())); + if (m_context->chainman->ActiveChain().Tip()) { + return m_context->chainman->ActiveChain().Tip()->GetBlockTime(); } return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network } @@ -202,11 +205,15 @@ public: const CBlockIndex* tip; { LOCK(::cs_main); - tip = ::ChainActive().Tip(); + assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain())); + tip = m_context->chainman->ActiveChain().Tip(); } return GuessVerificationProgress(Params().TxData(), tip); } - bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } + bool isInitialBlockDownload() override { + assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate())); + return m_context->chainman->ActiveChainstate().IsInitialBlockDownload(); + } bool getReindex() override { return ::fReindex; } bool getImporting() override { return ::fImporting; } void setNetworkActive(bool active) override @@ -231,7 +238,8 @@ public: bool getUnspentOutput(const COutPoint& output, Coin& coin) override { LOCK(::cs_main); - return ::ChainstateActive().CoinsTip().GetCoin(output, coin); + assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate())); + return m_context->chainman->ActiveChainstate().CoinsTip().GetCoin(output, coin); } WalletClient& walletClient() override { @@ -441,13 +449,15 @@ public: bool checkFinalTx(const CTransaction& tx) override { LOCK(cs_main); - return CheckFinalTx(::ChainActive().Tip(), tx); + assert(std::addressof(::ChainActive()) == std::addressof(m_node.chainman->ActiveChain())); + return CheckFinalTx(m_node.chainman->ActiveChain().Tip(), tx); } Optional<int> findLocatorFork(const CBlockLocator& locator) override { LOCK(cs_main); const CChain& active = Assert(m_node.chainman)->ActiveChain(); - if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(active, locator)) { + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) { return fork->nHeight; } return nullopt; @@ -456,7 +466,8 @@ public: { WAIT_LOCK(cs_main, lock); const CChain& active = Assert(m_node.chainman)->ActiveChain(); - return FillBlock(g_chainman.m_blockman.LookupBlockIndex(hash), block, lock, active); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active); } bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override { @@ -468,7 +479,8 @@ public: { WAIT_LOCK(cs_main, lock); const CChain& active = Assert(m_node.chainman)->ActiveChain(); - if (const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) { if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) { return FillBlock(ancestor, ancestor_out, lock, active); } @@ -479,8 +491,10 @@ public: { WAIT_LOCK(cs_main, lock); const CChain& active = Assert(m_node.chainman)->ActiveChain(); - const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash); - const CBlockIndex* ancestor = g_chainman.m_blockman.LookupBlockIndex(ancestor_hash); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash); if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr; return FillBlock(ancestor, ancestor_out, lock, active); } @@ -488,8 +502,10 @@ public: { WAIT_LOCK(cs_main, lock); const CChain& active = Assert(m_node.chainman)->ActiveChain(); - const CBlockIndex* block1 = g_chainman.m_blockman.LookupBlockIndex(block_hash1); - const CBlockIndex* block2 = g_chainman.m_blockman.LookupBlockIndex(block_hash2); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2); const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr; // Using & instead of && below to avoid short circuiting and leaving // output uninitialized. @@ -499,7 +515,8 @@ public: double guessVerificationProgress(const uint256& block_hash) override { LOCK(cs_main); - return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash)); + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + return GuessVerificationProgress(Params().TxData(), m_node.chainman->m_blockman.LookupBlockIndex(block_hash)); } bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override { @@ -511,7 +528,8 @@ public: // used to limit the range, and passing min_height that's too low or // max_height that's too high will not crash or change the result. LOCK(::cs_main); - if (CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { + assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); + if (CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) { if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height); for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) { // Check pprev to not segfault if min_height is too low @@ -601,7 +619,10 @@ public: return ::fHavePruned; } bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); } - bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } + bool isInitialBlockDownload() override { + assert(std::addressof(::ChainstateActive()) == std::addressof(m_node.chainman->ActiveChainstate())); + return m_node.chainman->ActiveChainstate().IsInitialBlockDownload(); + } bool shutdownRequested() override { return ShutdownRequested(); } int64_t getAdjustedTime() override { return GetAdjustedTime(); } void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } @@ -613,7 +634,7 @@ public: } std::unique_ptr<Handler> handleNotifications(std::shared_ptr<Notifications> notifications) override { - return MakeUnique<NotificationsHandlerImpl>(std::move(notifications)); + return std::make_unique<NotificationsHandlerImpl>(std::move(notifications)); } void waitForNotificationsIfTipChanged(const uint256& old_tip) override { @@ -626,7 +647,7 @@ public: } std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override { - return MakeUnique<RpcHandlerImpl>(command); + return std::make_unique<RpcHandlerImpl>(command); } bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); } void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) override @@ -669,6 +690,6 @@ public: } // namespace node namespace interfaces { -std::unique_ptr<Node> MakeNode(NodeContext* context) { return MakeUnique<node::NodeImpl>(context); } -std::unique_ptr<Chain> MakeChain(NodeContext& context) { return MakeUnique<node::ChainImpl>(context); } +std::unique_ptr<Node> MakeNode(NodeContext* context) { return std::make_unique<node::NodeImpl>(context); } +std::unique_ptr<Chain> MakeChain(NodeContext& context) { return std::make_unique<node::ChainImpl>(context); } } // namespace interfaces diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 3b3fab7b6b..97763f4fa2 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -39,9 +39,10 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t { // cs_main scope LOCK(cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate())); // If the transaction is already confirmed in the chain, don't do anything // and return early. - CCoinsViewCache &view = ::ChainstateActive().CoinsTip(); + CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip(); for (size_t o = 0; o < tx->vout.size(); o++) { const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); // IsSpent doesn't mean the coin is spent, it means the output doesn't exist. @@ -53,7 +54,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t if (max_tx_fee > 0) { // First, call ATMP with test_accept and check the fee. If ATMP // fails here, return error immediately. - const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */, + const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */, true /* test_accept */); if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { return HandleATMPError(result.m_state, err_string); @@ -62,7 +63,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t } } // Try to submit the transaction to the mempool. - const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */, + const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */, false /* test_accept */); if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { return HandleATMPError(result.m_state, err_string); diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index ab6168a541..9927e925ac 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -295,7 +295,7 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(), - tr("Comma separated file (*.csv)"), nullptr); + tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ef2f56c2c0..791a29f7a0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -52,7 +52,6 @@ #include <QThread> #include <QTimer> #include <QTranslator> -#include <QtGlobal> #if defined(QT_STATICPLUGIN) #include <QtPlugin> @@ -62,6 +61,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); +Q_IMPORT_PLUGIN(QMacStylePlugin); #endif #endif @@ -466,13 +466,6 @@ int GuiMain(int argc, char* argv[]) QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS) - const auto os_name = QSysInfo::prettyProductName(); - if (os_name.startsWith("macOS 11") || os_name.startsWith("macOS 10.16")) { - QApplication::setStyle("fusion"); - } -#endif - BitcoinApplication app; QFontDatabase::addApplicationFont(":/fonts/monospace"); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 89d6deb70d..32890ed0fe 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -683,13 +683,13 @@ QString formatDurationStr(int secs) int seconds = secs % 60; if (days) - strList.append(QString(QObject::tr("%1 d")).arg(days)); + strList.append(QObject::tr("%1 d").arg(days)); if (hours) - strList.append(QString(QObject::tr("%1 h")).arg(hours)); + strList.append(QObject::tr("%1 h").arg(hours)); if (mins) - strList.append(QString(QObject::tr("%1 m")).arg(mins)); + strList.append(QObject::tr("%1 m").arg(mins)); if (seconds || (!days && !hours && !mins)) - strList.append(QString(QObject::tr("%1 s")).arg(seconds)); + strList.append(QObject::tr("%1 s").arg(seconds)); return strList.join(" "); } @@ -712,12 +712,12 @@ QString formatPingTime(std::chrono::microseconds ping_time) { return (ping_time == std::chrono::microseconds::max() || ping_time == 0us) ? QObject::tr("N/A") : - QString(QObject::tr("%1 ms")).arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10)); + QObject::tr("%1 ms").arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10)); } QString formatTimeOffset(int64_t nTimeOffset) { - return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); + return QObject::tr("%1 s").arg(QString::number((int)nTimeOffset, 10)); } QString formatNiceTimeOffset(qint64 secs) @@ -760,13 +760,13 @@ QString formatNiceTimeOffset(qint64 secs) QString formatBytes(uint64_t bytes) { if(bytes < 1024) - return QString(QObject::tr("%1 B")).arg(bytes); + return QObject::tr("%1 B").arg(bytes); if(bytes < 1024 * 1024) - return QString(QObject::tr("%1 KB")).arg(bytes / 1024); + return QObject::tr("%1 KB").arg(bytes / 1024); if(bytes < 1024 * 1024 * 1024) - return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024); + return QObject::tr("%1 MB").arg(bytes / 1024 / 1024); - return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); + return QObject::tr("%1 GB").arg(bytes / 1024 / 1024 / 1024); } qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) { diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 55ab6046cf..17746b395b 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -141,11 +141,11 @@ void PSBTOperationsDialog::saveTransaction() { filename_suggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), filename_suggestion, - tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter); + tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selected_filter); if (filename.isEmpty()) { return; } - std::ofstream out(filename.toLocal8Bit().data()); + std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary); out << ssTx.str(); out.close(); showStatus(tr("PSBT saved to disk."), StatusLevel::INFO); diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index 490826cbbb..a71c8831e9 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -120,7 +120,9 @@ void QRImageWidget::saveImage() { if (!GUIUtil::HasPixmap(this)) return; - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr); + QString fn = GUIUtil::getSaveFileName( + this, tr("Save QR Code"), QString(), + tr("PNG Image", "Name of PNG file format") + QLatin1String(" (*.png)"), nullptr); if (!fn.isEmpty()) { exportImage().save(fn); diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 0da12c84fd..61cb89d75a 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -81,7 +81,6 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid tableView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH); tableView->horizontalHeader()->setStretchLastSection(true); } - tableView->horizontalHeader()->setSortIndicator(RecentRequestsTableModel::Date, Qt::DescendingOrder); } void ReceiveCoinsDialog::setModel(WalletModel *_model) @@ -96,6 +95,8 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) QTableView* tableView = ui->recentRequestsView; tableView->setModel(_model->getRecentRequestsTableModel()); + tableView->sortByColumn(RecentRequestsTableModel::Date, Qt::DescendingOrder); + connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReceiveCoinsDialog::recentRequestsView_selectionChanged); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 611f3584c3..95e1ce2210 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -266,7 +266,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa } // prepare transaction for getting txFee earlier - m_current_transaction = MakeUnique<WalletModelTransaction>(recipients); + m_current_transaction = std::make_unique<WalletModelTransaction>(recipients); WalletModel::SendCoinsReturn prepareStatus; updateCoinControlState(*m_coin_control); @@ -430,11 +430,11 @@ void SendCoinsDialog::on_sendButton_clicked() fileNameSuggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), fileNameSuggestion, - tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter); + tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selectedFilter); if (filename.isEmpty()) { return; } - std::ofstream out(filename.toLocal8Bit().data()); + std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary); out << ssTx.str(); out.close(); Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index e1ec7b6ed0..42e08c6af7 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -160,7 +160,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa transactionView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH); transactionView->horizontalHeader()->setStretchLastSection(true); } - transactionView->horizontalHeader()->setSortIndicator(TransactionTableModel::Date, Qt::DescendingOrder); // Actions abandonAction = new QAction(tr("Abandon transaction"), this); @@ -236,6 +235,7 @@ void TransactionView::setModel(WalletModel *_model) transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); transactionProxyModel->setSortRole(Qt::EditRole); transactionView->setModel(transactionProxyModel); + transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder); if (_model->getOptionsModel()) { @@ -357,7 +357,7 @@ void TransactionView::exportClicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Transaction History"), QString(), - tr("Comma separated file (*.csv)"), nullptr); + tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index b1e6b43e60..8612893683 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -273,7 +273,7 @@ void WalletView::backupWallet() { QString filename = GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(), - tr("Wallet Data (*.dat)"), nullptr); + tr("Wallet Data", "Name of wallet data file format") + QLatin1String(" (*.dat)"), nullptr); if (filename.isEmpty()) return; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ab0c0b8385..b90dc9bf42 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1073,9 +1073,9 @@ static RPCHelpMan gettxoutsetinfo() const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())}; - CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB()); + CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB()); NodeContext& node = EnsureNodeContext(request.context); - if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) { + if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) { ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", (int64_t)stats.nTransactions); @@ -1100,30 +1100,29 @@ static RPCHelpMan gettxoutsetinfo() static RPCHelpMan gettxout() { return RPCHelpMan{"gettxout", - "\nReturns details about an unspent transaction output.\n", - { - {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"}, - {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"}, - {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, - {RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, - {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT}, - {RPCResult::Type::OBJ, "scriptPubKey", "", - { - {RPCResult::Type::STR_HEX, "asm", ""}, - {RPCResult::Type::STR_HEX, "hex", ""}, - {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"}, - {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"}, - {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", - {{RPCResult::Type::STR, "address", "bitcoin address"}}}, - }}, - {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, - }}, - RPCExamples{ + "\nReturns details about an unspent transaction output.\n", + { + {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"}, + {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"}, + {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, + }, + { + RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""}, + RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, + {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", { + {RPCResult::Type::STR_HEX, "asm", ""}, + {RPCResult::Type::STR_HEX, "hex", ""}, + {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"}, + {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"}, + {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", {{RPCResult::Type::STR, "address", "bitcoin address"}}}, + }}, + {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, + }}, + }, + RPCExamples{ "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nView the details\n" @@ -2137,59 +2136,63 @@ public: static RPCHelpMan scantxoutset() { return RPCHelpMan{"scantxoutset", - "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n" - "\nScans the unspent transaction output set for entries that match certain output descriptors.\n" - "Examples of output descriptors are:\n" - " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n" - " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n" - " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" - " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" - " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" - "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" - "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" - "unhardened or hardened child keys.\n" - "In the latter case, a range needs to be specified by below if different from 1000.\n" - "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n", + "\nScans the unspent transaction output set for entries that match certain output descriptors.\n" + "Examples of output descriptors are:\n" + " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n" + " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n" + " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" + " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" + " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" + "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" + "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" + "unhardened or hardened child keys.\n" + "In the latter case, a range needs to be specified by below if different from 1000.\n" + "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n", + { + {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n" + "\"start\" for starting a scan\n" + "\"abort\" for aborting the current scan (returns true when abort was successful)\n" + "\"status\" for progress report (in %) of the current scan"}, + {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n" + "Every scan object is either a string descriptor or an object:", + { + {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"}, + {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata", { - {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n" - " \"start\" for starting a scan\n" - " \"abort\" for aborting the current scan (returns true when abort was successful)\n" - " \"status\" for progress report (in %) of the current scan"}, - {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n" - " Every scan object is either a string descriptor or an object:", - { - {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"}, - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata", - { - {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"}, - {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"}, - }, - }, - }, + {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"}, + {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"}, + }}, + }, "[scanobjects,...]"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", + }, + { + RPCResult{"When action=='abort'", RPCResult::Type::BOOL, "", ""}, + RPCResult{"When action=='status' and no scan is in progress", RPCResult::Type::NONE, "", ""}, + RPCResult{"When action=='status' and scan is in progress", RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "progress", "The scan progress"}, + }}, + RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, + {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, + {RPCResult::Type::NUM, "height", "The current block height (index)"}, + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::ARR, "unspents", "", + { + {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, - {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, - {RPCResult::Type::NUM, "height", "The current block height (index)"}, - {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, - {RPCResult::Type::ARR, "unspents", "", - { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, - {RPCResult::Type::NUM, "vout", "The vout value"}, - {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"}, - {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"}, - {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"}, - {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"}, - }}, - }}, - {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::NUM, "vout", "The vout value"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"}, + {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"}, + {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"}, }}, - RPCExamples{""}, + }}, + {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, + }}, + }, + RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); @@ -2444,7 +2447,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil chainstate.ForceFlushStateToDisk(); - if (!GetUTXOStats(&chainstate.CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) { + if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f29f556517..fd780ba782 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -150,7 +150,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd && !ShutdownRequested()) { - std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(coinbase_script)); + std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -358,7 +358,7 @@ static RPCHelpMan generateblock() LOCK(cs_main); CTxMemPool empty_mempool; - std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(coinbase_script)); + std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script)); if (!blocktemplate) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); } @@ -369,7 +369,7 @@ static RPCHelpMan generateblock() // Add transactions block.vtx.insert(block.vtx.end(), txs.begin(), txs.end()); - RegenerateCommitments(block); + RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman))); { LOCK(cs_main); @@ -505,83 +505,84 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) { static RPCHelpMan getblocktemplate() { return RPCHelpMan{"getblocktemplate", - "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" - "It returns data needed to construct a block to work on.\n" - "For full specification, see BIPs 22, 23, 9, and 145:\n" - " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" - " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n", + "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" + "It returns data needed to construct a block to work on.\n" + "For full specification, see BIPs 22, 23, 9, and 145:\n" + " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" + " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n", + { + {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template", + { + {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"}, + {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings", { - {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template", - { - {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"}, - {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings", - { - {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"}, - }, - }, - {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings", - { - {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"}, - {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"}, - }, - }, - }, + {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"}, + }}, + {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings", + { + {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"}, + {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"}, + }}, + }, "\"template_request\""}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", + }, + { + RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""}, + RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"}, + RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "version", "The preferred block version"}, + {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced", + { + {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"}, + }}, + {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments", + { + {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"}, + }}, + {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"}, + {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"}, + {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block", + { + {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM, "version", "The preferred block version"}, - {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced", - { - {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"}, - }}, - {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments", - { - {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"}, - }}, - {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"}, - {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"}, - {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block", - { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"}, - {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"}, - {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"}, - {RPCResult::Type::ARR, "depends", "array of numbers", - { - {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"}, - }}, - {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"}, - {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"}, - {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"}, - }}, - }}, - {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content", + {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"}, + {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"}, + {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"}, + {RPCResult::Type::ARR, "depends", "array of numbers", { - {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"}, + {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"}, }}, - {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"}, - {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"}, - {RPCResult::Type::STR, "target", "The hash target"}, - {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, - {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", - { - {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, - }}, - {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, - {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, - {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, - {RPCResult::Type::NUM, "weightlimit", "limit of block weight"}, - {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, - {RPCResult::Type::STR, "bits", "compressed target of next block"}, - {RPCResult::Type::NUM, "height", "The height of the next block"}, - {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"} + {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"}, + {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"}, + {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"}, }}, - RPCExamples{ + }}, + {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content", + { + {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"}, + }}, + {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"}, + {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"}, + {RPCResult::Type::STR, "target", "The hash target"}, + {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", + { + {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, + }}, + {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, + {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, + {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, + {RPCResult::Type::NUM, "weightlimit", "limit of block weight"}, + {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::STR, "bits", "compressed target of next block"}, + {RPCResult::Type::NUM, "height", "The height of the next block"}, + {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}, + }}, + }, + RPCExamples{ HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'") + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") }, @@ -747,7 +748,7 @@ static RPCHelpMan getblocktemplate() // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(scriptDummy); + pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -940,14 +941,17 @@ static RPCHelpMan submitblock() { // We allow 2 arguments for compliance with BIP22. Argument 2 is ignored. return RPCHelpMan{"submitblock", - "\nAttempts to submit new block to network.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n", - { - {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"}, - {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."}, - }, - RPCResult{RPCResult::Type::NONE, "", "Returns JSON Null when valid, a string according to BIP22 otherwise"}, - RPCExamples{ + "\nAttempts to submit new block to network.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n", + { + {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"}, + {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."}, + }, + { + RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""}, + RPCResult{"Otherwise", RPCResult::Type::STR, "", "According to BIP22"}, + }, + RPCExamples{ HelpExampleCli("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"") }, diff --git a/src/rpc/request.h b/src/rpc/request.h index de3a4ae840..27d06f3c92 100644 --- a/src/rpc/request.h +++ b/src/rpc/request.h @@ -34,19 +34,19 @@ public: UniValue id; std::string strMethod; UniValue params; - bool fHelp; + enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE; std::string URI; std::string authUser; std::string peerAddr; const util::Ref& context; - explicit JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {} + explicit JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), context(context) {} //! Initializes request information from another request object and the //! given context. The implementation should be updated if any members are //! added or removed above. JSONRPCRequest(const JSONRPCRequest& other, const util::Ref& context) - : id(other.id), strMethod(other.strMethod), params(other.params), fHelp(other.fHelp), URI(other.URI), + : id(other.id), strMethod(other.strMethod), params(other.params), mode(other.mode), URI(other.URI), authUser(other.authUser), peerAddr(other.peerAddr), context(context) { } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 9a9b3713f3..39938f4eb9 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -88,7 +88,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& sort(vCommands.begin(), vCommands.end()); JSONRPCRequest jreq(helpreq); - jreq.fHelp = true; + jreq.mode = JSONRPCRequest::GET_HELP; jreq.params = UniValue(); for (const std::pair<std::string, const CRPCCommand*>& command : vCommands) @@ -149,7 +149,7 @@ static RPCHelpMan help() } if (strCommand == "dump_all_command_conversions") { // Used for testing only, undocumented - return tableRPC.dumpArgMap(); + return tableRPC.dumpArgMap(jsonRequest); } return tableRPC.help(strCommand, jsonRequest); @@ -437,6 +437,16 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c return out; } +static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result) +{ + for (const auto& command : commands) { + if (ExecuteCommand(*command, request, result, &command == &commands.back())) { + return true; + } + } + return false; +} + UniValue CRPCTable::execute(const JSONRPCRequest &request) const { // Return immediately if in warmup @@ -450,10 +460,8 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const auto it = mapCommands.find(request.strMethod); if (it != mapCommands.end()) { UniValue result; - for (const auto& command : it->second) { - if (ExecuteCommand(*command, request, result, &command == &it->second.back())) { - return result; - } + if (ExecuteCommands(it->second, request, result)) { + return result; } } throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); @@ -484,13 +492,18 @@ std::vector<std::string> CRPCTable::listCommands() const return commandList; } -UniValue CRPCTable::dumpArgMap() const +UniValue CRPCTable::dumpArgMap(const JSONRPCRequest& args_request) const { + JSONRPCRequest request(args_request); + request.mode = JSONRPCRequest::GET_ARGS; + UniValue ret{UniValue::VARR}; for (const auto& cmd : mapCommands) { - for (const auto& c : cmd.second) { - const auto help = RpcMethodFnType(c->unique_id)(); - help.AppendArgMap(ret); + UniValue result; + if (ExecuteCommands(cmd.second, request, result)) { + for (const auto& values : result.getValues()) { + ret.push_back(values); + } } } return ret; diff --git a/src/rpc/server.h b/src/rpc/server.h index fe5a791e1e..03967020c2 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -148,7 +148,7 @@ public: /** * Return all named arguments that need to be converted by the client from string to another JSON type */ - UniValue dumpArgMap() const; + UniValue dumpArgMap(const JSONRPCRequest& request) const; /** * Appends a CRPCCommand to the dispatch table. diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index e890c0108a..44e58cb75f 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -459,6 +459,21 @@ std::string RPCExamples::ToDescriptionString() const return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples; } +UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) +{ + if (request.mode == JSONRPCRequest::GET_ARGS) { + return GetArgMap(); + } + /* + * Check if the given request is valid according to this command or if + * the user is asking for help information, and throw help when appropriate. + */ + if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) { + throw std::runtime_error(ToString()); + } + return m_fun(*this, request); +} + bool RPCHelpMan::IsValidNumArgs(size_t num_args) const { size_t num_required_args = 0; @@ -532,8 +547,9 @@ std::string RPCHelpMan::ToString() const return ret; } -void RPCHelpMan::AppendArgMap(UniValue& arr) const +UniValue RPCHelpMan::GetArgMap() const { + UniValue arr{UniValue::VARR}; for (int i{0}; i < int(m_args.size()); ++i) { const auto& arg = m_args.at(i); std::vector<std::string> arg_names; @@ -548,6 +564,7 @@ void RPCHelpMan::AppendArgMap(UniValue& arr) const arr.push_back(map); } } + return arr; } std::string RPCArg::GetFirstName() const diff --git a/src/rpc/util.h b/src/rpc/util.h index c54ce85f60..94c2d2d626 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -333,26 +333,12 @@ public: using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>; RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); + UniValue HandleRequest(const JSONRPCRequest& request); std::string ToString() const; - /** Append the named args that need to be converted from string to another JSON type */ - void AppendArgMap(UniValue& arr) const; - UniValue HandleRequest(const JSONRPCRequest& request) - { - Check(request); - return m_fun(*this, request); - } + /** Return the named args that need to be converted from string to another JSON type */ + UniValue GetArgMap() const; /** If the supplied number of args is neither too small nor too high */ bool IsValidNumArgs(size_t num_args) const; - /** - * Check if the given request is valid according to this command or if - * the user is asking for help information, and throw help when appropriate. - */ - inline void Check(const JSONRPCRequest& request) const { - if (request.fHelp || !IsValidNumArgs(request.params.size())) { - throw std::runtime_error(ToString()); - } - } - std::vector<std::string> GetArgNames() const; const std::string m_name; diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 6ab01882ac..889cb4f06e 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -853,7 +853,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S CPubKey pubkey(data); if (pubkey.IsFullyValid()) { if (permit_uncompressed || pubkey.IsCompressed()) { - return MakeUnique<ConstPubkeyProvider>(key_exp_index, pubkey); + return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey); } else { error = "Uncompressed keys are not allowed"; return nullptr; @@ -867,7 +867,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S if (permit_uncompressed || key.IsCompressed()) { CPubKey pubkey = key.GetPubKey(); out.keys.emplace(pubkey.GetID(), key); - return MakeUnique<ConstPubkeyProvider>(key_exp_index, pubkey); + return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey); } else { error = "Uncompressed keys are not allowed"; return nullptr; @@ -894,7 +894,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S extpubkey = extkey.Neuter(); out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key); } - return MakeUnique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type); + return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type); } /** Parse a public key including origin information (if enabled). */ @@ -931,7 +931,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c if (!ParseKeyPath(slash_split, info.path, error)) return nullptr; auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], permit_uncompressed, out, error); if (!provider) return nullptr; - return MakeUnique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider)); + return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider)); } /** Parse a script in a particular context. */ @@ -944,17 +944,17 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c if (Func("pk", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error); if (!pubkey) return nullptr; - return MakeUnique<PKDescriptor>(std::move(pubkey)); + return std::make_unique<PKDescriptor>(std::move(pubkey)); } if (Func("pkh", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error); if (!pubkey) return nullptr; - return MakeUnique<PKHDescriptor>(std::move(pubkey)); + return std::make_unique<PKHDescriptor>(std::move(pubkey)); } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, true, out, error); if (!pubkey) return nullptr; - return MakeUnique<ComboDescriptor>(std::move(pubkey)); + return std::make_unique<ComboDescriptor>(std::move(pubkey)); } else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) { error = "Cannot have combo in non-top level"; return nullptr; @@ -1002,12 +1002,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c return nullptr; } } - return MakeUnique<MultisigDescriptor>(thres, std::move(providers), sorted_multi); + return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi); } if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, false, out, error); if (!pubkey) return nullptr; - return MakeUnique<WPKHDescriptor>(std::move(pubkey)); + return std::make_unique<WPKHDescriptor>(std::move(pubkey)); } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) { error = "Cannot have wpkh within wsh"; return nullptr; @@ -1015,7 +1015,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c if (ctx == ParseScriptContext::TOP && Func("sh", expr)) { auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error); if (!desc || expr.size()) return nullptr; - return MakeUnique<SHDescriptor>(std::move(desc)); + return std::make_unique<SHDescriptor>(std::move(desc)); } else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) { error = "Cannot have sh in non-top level"; return nullptr; @@ -1023,7 +1023,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) { auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error); if (!desc || expr.size()) return nullptr; - return MakeUnique<WSHDescriptor>(std::move(desc)); + return std::make_unique<WSHDescriptor>(std::move(desc)); } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) { error = "Cannot have wsh within wsh"; return nullptr; @@ -1034,7 +1034,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c error = "Address is not valid"; return nullptr; } - return MakeUnique<AddressDescriptor>(std::move(dest)); + return std::make_unique<AddressDescriptor>(std::move(dest)); } if (ctx == ParseScriptContext::TOP && Func("raw", expr)) { std::string str(expr.begin(), expr.end()); @@ -1043,7 +1043,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c return nullptr; } auto bytes = ParseHex(str); - return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end())); + return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end())); } if (ctx == ParseScriptContext::P2SH) { error = "A function is needed within P2SH"; @@ -1058,10 +1058,10 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider) { - std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(0, pubkey); + std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey); KeyOriginInfo info; if (provider.GetKeyOrigin(pubkey.GetID(), info)) { - return MakeUnique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider)); + return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider)); } return key_provider; } @@ -1074,7 +1074,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo if (txntype == TxoutType::PUBKEY) { CPubKey pubkey(data[0].begin(), data[0].end()); if (pubkey.IsValid()) { - return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider)); + return std::make_unique<PKDescriptor>(InferPubkey(pubkey, ctx, provider)); } } if (txntype == TxoutType::PUBKEYHASH) { @@ -1082,7 +1082,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CKeyID keyid(hash); CPubKey pubkey; if (provider.GetPubKey(keyid, pubkey)) { - return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider)); + return std::make_unique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider)); } } if (txntype == TxoutType::WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) { @@ -1090,7 +1090,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CKeyID keyid(hash); CPubKey pubkey; if (provider.GetPubKey(keyid, pubkey)) { - return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider)); + return std::make_unique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider)); } } if (txntype == TxoutType::MULTISIG) { @@ -1099,7 +1099,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CPubKey pubkey(data[i].begin(), data[i].end()); providers.push_back(InferPubkey(pubkey, ctx, provider)); } - return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers)); + return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers)); } if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) { uint160 hash(data[0]); @@ -1107,7 +1107,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CScript subscript; if (provider.GetCScript(scriptid, subscript)) { auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider); - if (sub) return MakeUnique<SHDescriptor>(std::move(sub)); + if (sub) return std::make_unique<SHDescriptor>(std::move(sub)); } } if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) { @@ -1116,18 +1116,18 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CScript subscript; if (provider.GetCScript(scriptid, subscript)) { auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider); - if (sub) return MakeUnique<WSHDescriptor>(std::move(sub)); + if (sub) return std::make_unique<WSHDescriptor>(std::move(sub)); } } CTxDestination dest; if (ExtractDestination(script, dest)) { if (GetScriptForDestination(dest) == script) { - return MakeUnique<AddressDescriptor>(std::move(dest)); + return std::make_unique<AddressDescriptor>(std::move(dest)); } } - return MakeUnique<RawDescriptor>(script); + return std::make_unique<RawDescriptor>(script); } diff --git a/src/shutdown.cpp b/src/shutdown.cpp index 6a73e0b2d8..2fc195e2d1 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -5,16 +5,15 @@ #include <shutdown.h> +#include <logging.h> +#include <util/tokenpipe.h> + #include <config/bitcoin-config.h> #include <assert.h> #include <atomic> #ifdef WIN32 #include <condition_variable> -#else -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> #endif static std::atomic<bool> fRequestShutdown(false); @@ -24,25 +23,18 @@ std::mutex g_shutdown_mutex; std::condition_variable g_shutdown_cv; #else /** On UNIX-like operating systems use the self-pipe trick. - * Index 0 will be the read end of the pipe, index 1 the write end. */ -static int g_shutdown_pipe[2] = {-1, -1}; +static TokenPipeEnd g_shutdown_r; +static TokenPipeEnd g_shutdown_w; #endif bool InitShutdownState() { #ifndef WIN32 -#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2 - // If we can, make sure that the file descriptors are closed on exec() - // to prevent interference. - if (pipe2(g_shutdown_pipe, O_CLOEXEC) != 0) { - return false; - } -#else - if (pipe(g_shutdown_pipe) != 0) { - return false; - } -#endif + std::optional<TokenPipe> pipe = TokenPipe::Make(); + if (!pipe) return false; + g_shutdown_r = pipe->TakeReadEnd(); + g_shutdown_w = pipe->TakeWriteEnd(); #endif return true; } @@ -59,17 +51,10 @@ void StartShutdown() // case of a reentrant signal. if (!fRequestShutdown.exchange(true)) { // Write an arbitrary byte to the write end of the shutdown pipe. - const char token = 'x'; - while (true) { - int result = write(g_shutdown_pipe[1], &token, 1); - if (result < 0) { - // Failure. It's possible that the write was interrupted by another signal. - // Other errors are unexpected here. - assert(errno == EINTR); - } else { - assert(result == 1); - break; - } + int res = g_shutdown_w.TokenWrite('x'); + if (res != 0) { + LogPrintf("Sending shutdown token failed\n"); + assert(0); } } #endif @@ -96,17 +81,10 @@ void WaitForShutdown() std::unique_lock<std::mutex> lk(g_shutdown_mutex); g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); }); #else - char token; - while (true) { - int result = read(g_shutdown_pipe[0], &token, 1); - if (result < 0) { - // Failure. Check if the read was interrupted by a signal. - // Other errors are unexpected here. - assert(errno == EINTR); - } else { - assert(result == 1); - break; - } + int res = g_shutdown_r.TokenRead(); + if (res != 'x') { + LogPrintf("Reading shutdown token failed\n"); + assert(0); } #endif } diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index d33d668a04..b523173a45 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <util/memory.h> #include <util/system.h> #include <test/util/setup_common.h> @@ -163,7 +162,7 @@ private: BOOST_AUTO_TEST_CASE(lockedpool_tests_mock) { // Test over three virtual arenas, of which one will succeed being locked - std::unique_ptr<LockedPageAllocator> x = MakeUnique<TestLockedPageAllocator>(3, 1); + std::unique_ptr<LockedPageAllocator> x = std::make_unique<TestLockedPageAllocator>(3, 1); LockedPool pool(std::move(x)); BOOST_CHECK(pool.stats().total == 0); BOOST_CHECK(pool.stats().locked == 0); diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 633a95ce96..04da10715f 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); CBlock& block = pblocktemplate->block; block.hashPrevBlock = prev->GetBlockHash(); block.nTime = prev->nTime + 1; diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 21921375b3..64c6d7f634 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -5,7 +5,6 @@ #include <checkqueue.h> #include <sync.h> #include <test/util/setup_common.h> -#include <util/memory.h> #include <util/system.h> #include <util/time.h> @@ -146,7 +145,7 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue; */ static void Correct_Queue_range(std::vector<size_t> range) { - auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE); + auto small_queue = std::make_unique<Correct_Queue>(QUEUE_BATCH_SIZE); small_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); // Make vChecks here to save on malloc (this test can be slow...) std::vector<FakeCheckCheckCompletion> vChecks; @@ -206,7 +205,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) /** Test that failing checks are caught */ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) { - auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); + auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE); fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1001; ++i) { @@ -234,7 +233,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) // future blocks, ie, the bad state is cleared. BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) { - auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); + auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE); fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (auto times = 0; times < 10; ++times) { @@ -258,7 +257,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) // more than once as well BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { - auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE); + auto queue = std::make_unique<Unique_Queue>(QUEUE_BATCH_SIZE); queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); size_t COUNT = 100000; @@ -293,7 +292,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) // time could leave the data hanging across a sequence of blocks. BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { - auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE); + auto queue = std::make_unique<Memory_Queue>(QUEUE_BATCH_SIZE); queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1000; ++i) { size_t total = i; @@ -320,7 +319,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) // have been destructed BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { - auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE); + auto queue = std::make_unique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE); bool fails = false; queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); std::thread t0([&]() { @@ -360,7 +359,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) /** Test that CCheckQueueControl is threadsafe */ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) { - auto queue = MakeUnique<Standard_Queue>(QUEUE_BATCH_SIZE); + auto queue = std::make_unique<Standard_Queue>(QUEUE_BATCH_SIZE); { std::vector<std::thread> tg; std::atomic<int> nThreads {0}; diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 3d802cbeb3..a6080ad3dd 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -5,7 +5,6 @@ #include <dbwrapper.h> #include <test/util/setup_common.h> #include <uint256.h> -#include <util/memory.h> #include <memory> @@ -207,7 +206,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. - std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false); + std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; @@ -248,7 +247,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. - std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false); + std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 5906913b58..e75982bc6f 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -15,7 +15,6 @@ #include <script/standard.h> #include <serialize.h> #include <txorphanage.h> -#include <util/memory.h> #include <util/string.h> #include <util/system.h> #include <util/time.h> @@ -68,7 +67,7 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup) BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { const CChainParams& chainparams = Params(); - auto connman = MakeUnique<CConnman>(0x1337, 0x1337); + auto connman = std::make_unique<CConnman>(0x1337, 0x1337); auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); @@ -138,7 +137,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { const CChainParams& chainparams = Params(); - auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337); + auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337); auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); @@ -211,8 +210,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) BOOST_AUTO_TEST_CASE(peer_discouragement) { const CChainParams& chainparams = Params(); - auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); - auto connman = MakeUnique<CConnman>(0x1337, 0x1337); + auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + auto connman = std::make_unique<CConnman>(0x1337, 0x1337); auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); @@ -258,8 +257,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) BOOST_AUTO_TEST_CASE(DoS_bantime) { const CChainParams& chainparams = Params(); - auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); - auto connman = MakeUnique<CConnman>(0x1337, 0x1337); + auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + auto connman = std::make_unique<CConnman>(0x1337, 0x1337); auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 328a31f1dc..d951bda20f 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -264,7 +264,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) CCoinsStats stats; bool expected_code_path = false; try { - (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED); + (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED); } catch (const std::logic_error&) { expected_code_path = true; } diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 0d1921f285..ffe4855662 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -6,7 +6,6 @@ #include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/fuzz.h> -#include <util/memory.h> void initialize_descriptor_parse() { diff --git a/src/test/fuzz/eval_script.cpp b/src/test/fuzz/eval_script.cpp index 635288fc36..77ed798923 100644 --- a/src/test/fuzz/eval_script.cpp +++ b/src/test/fuzz/eval_script.cpp @@ -6,7 +6,6 @@ #include <script/interpreter.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> -#include <util/memory.h> #include <limits> diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp index aa8f826e4a..32077b1fe2 100644 --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -17,7 +17,6 @@ #include <script/standard.h> #include <streams.h> #include <test/fuzz/fuzz.h> -#include <util/memory.h> #include <util/strencodings.h> #include <cassert> diff --git a/src/test/fuzz/netbase_dns_lookup.cpp b/src/test/fuzz/netbase_dns_lookup.cpp new file mode 100644 index 0000000000..7be8b13743 --- /dev/null +++ b/src/test/fuzz/netbase_dns_lookup.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 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 <netaddress.h> +#include <netbase.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <string> +#include <vector> + +FUZZ_TARGET(netbase_dns_lookup) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512); + const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); + const bool allow_lookup = fuzzed_data_provider.ConsumeBool(); + const int default_port = fuzzed_data_provider.ConsumeIntegral<int>(); + + auto fuzzed_dns_lookup_function = [&](const std::string&, bool) { + std::vector<CNetAddr> resolved_addresses; + while (fuzzed_data_provider.ConsumeBool()) { + resolved_addresses.push_back(ConsumeNetAddr(fuzzed_data_provider)); + } + return resolved_addresses; + }; + + { + std::vector<CNetAddr> resolved_addresses; + if (LookupHost(name, resolved_addresses, max_results, allow_lookup, fuzzed_dns_lookup_function)) { + for (const CNetAddr& resolved_address : resolved_addresses) { + assert(!resolved_address.IsInternal()); + } + } + assert(resolved_addresses.size() <= max_results || max_results == 0); + } + { + CNetAddr resolved_address; + if (LookupHost(name, resolved_address, allow_lookup, fuzzed_dns_lookup_function)) { + assert(!resolved_address.IsInternal()); + } + } + { + std::vector<CService> resolved_services; + if (Lookup(name, resolved_services, default_port, allow_lookup, max_results, fuzzed_dns_lookup_function)) { + for (const CNetAddr& resolved_service : resolved_services) { + assert(!resolved_service.IsInternal()); + } + } + assert(resolved_services.size() <= max_results || max_results == 0); + } + { + CService resolved_service; + if (Lookup(name, resolved_service, default_port, allow_lookup, fuzzed_dns_lookup_function)) { + assert(!resolved_service.IsInternal()); + } + } + { + CService resolved_service = LookupNumeric(name, default_port, fuzzed_dns_lookup_function); + assert(!resolved_service.IsInternal()); + } + { + CSubNet resolved_subnet; + if (LookupSubNet(name, resolved_subnet, fuzzed_dns_lookup_function)) { + assert(resolved_subnet.IsValid()); + } + } +} diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp index ddd2bcfba3..1ad5fb6a05 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -18,6 +18,9 @@ FUZZ_TARGET(parse_numbers) double d; (void)ParseDouble(random_string, &d); + uint8_t u8; + (void)ParseUInt8(random_string, &u8); + int32_t i32; (void)ParseInt32(random_string, &i32); (void)atoi(random_string); diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp index afe382ba21..3fffaac8d0 100644 --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -7,7 +7,6 @@ #include <rpc/client.h> #include <rpc/util.h> #include <test/fuzz/fuzz.h> -#include <util/memory.h> #include <limits> #include <string> diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 0289d49ccc..04fc6da9b1 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -19,7 +19,6 @@ #include <test/util/setup_common.h> #include <test/util/validation.h> #include <txorphanage.h> -#include <util/memory.h> #include <validationinterface.h> #include <version.h> diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 617a71ea60..ee402dba38 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -14,7 +14,6 @@ #include <test/util/setup_common.h> #include <test/util/validation.h> #include <txorphanage.h> -#include <util/memory.h> #include <validation.h> #include <validationinterface.h> diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp index 0b4588c4ce..eb2754054a 100644 --- a/src/test/fuzz/psbt.cpp +++ b/src/test/fuzz/psbt.cpp @@ -10,7 +10,6 @@ #include <pubkey.h> #include <script/script.h> #include <streams.h> -#include <util/memory.h> #include <version.h> #include <cstdint> diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 193862e847..8219a04e49 100644 --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -20,7 +20,6 @@ #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <univalue.h> -#include <util/memory.h> #include <algorithm> #include <cassert> diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp index ce8915ca2c..561230707c 100644 --- a/src/test/fuzz/script_flags.cpp +++ b/src/test/fuzz/script_flags.cpp @@ -5,7 +5,6 @@ #include <pubkey.h> #include <script/interpreter.h> #include <streams.h> -#include <util/memory.h> #include <version.h> #include <test/fuzz/fuzz.h> diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp index 3e7b72805e..7b57c5dfd8 100644 --- a/src/test/fuzz/signature_checker.cpp +++ b/src/test/fuzz/signature_checker.cpp @@ -6,7 +6,6 @@ #include <script/interpreter.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> -#include <util/memory.h> #include <cstdint> #include <limits> @@ -15,7 +14,7 @@ void initialize_signature_checker() { - static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); + static const auto verify_handle = std::make_unique<ECCVerifyHandle>(); } namespace { diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index d8c536e8b1..cdddad82b3 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -259,6 +259,16 @@ void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array<T errno = fuzzed_data_provider.PickValueInArray(errnos); } +/* + * Sets a fuzzed errno in the range [0, 133 (EHWPOISON)]. Can be used from functions emulating + * standard library functions that set errno, or in other contexts where the value of errno + * might be relevant for the execution path that will be taken. + */ +inline void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + errno = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 133); +} + /** * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. @@ -345,6 +355,7 @@ public: FILE* open() { + SetFuzzedErrNo(m_fuzzed_data_provider); if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } @@ -386,6 +397,7 @@ public: static ssize_t read(void* cookie, char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } @@ -404,6 +416,7 @@ public: static ssize_t write(void* cookie, const char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size); if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; @@ -414,8 +427,9 @@ public: static int seek(void* cookie, int64_t* offset, int whence) { - assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet. + assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; @@ -424,6 +438,12 @@ public: return -1; } new_offset = fuzzed_file->m_offset + *offset; + } else if (whence == SEEK_END) { + const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096); + if (AdditionOverflow(n, *offset)) { + return -1; + } + new_offset = n + *offset; } if (new_offset < 0) { return -1; @@ -436,6 +456,7 @@ public: static int close(void* cookie) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0); } }; diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp new file mode 100644 index 0000000000..e1a21b6c53 --- /dev/null +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2020 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 <chainparamsbase.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/setup_common.h> +#include <txmempool.h> +#include <util/time.h> +#include <validation.h> + +#include <cstdint> +#include <vector> + +void initialize_validation_load_mempool() +{ + static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); +} + +FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); + + CTxMemPool pool{}; + auto fuzzed_fopen = [&](const fs::path&, const char*) { + return fuzzed_file_provider.open(); + }; + (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen); + (void)DumpMempool(pool, fuzzed_fopen, true); +} diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index aa628371e6..9acd17c463 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -122,7 +122,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co uint256 hashHighFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx); @@ -143,7 +143,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; uint256 hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx); @@ -157,7 +157,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx); @@ -179,7 +179,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); // Verify that this tx isn't selected. for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { @@ -192,7 +192,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 110 blocks :) @@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) LOCK(m_node.mempool->cs); // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); const CAmount BLOCKSUBSIDY = 50*COIN; const CAmount LOWFEE = CENT; @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); m_node.mempool->clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // block size > limit @@ -311,13 +311,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // orphan in *m_node.mempool, template creation fails hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); // child with higher feerate than parent @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // coinbase in *m_node.mempool, template creation fails @@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // give it a fee so it'll get mined m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); m_node.mempool->clear(); // double spend txn pair in *m_node.mempool, template creation fails @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); // subsidy changing @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); ::ChainActive().SetTip(next); } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // Extend to a 210000-long block chain. while (::ChainActive().Tip()->nHeight < 210000) { CBlockIndex* prev = ::ChainActive().Tip(); @@ -387,7 +387,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); ::ChainActive().SetTip(next); } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // invalid p2sh txn in *m_node.mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); m_node.mempool->clear(); // Delete the dummy blocks again. @@ -492,7 +492,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1; BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // None of the of the absolute height/time locked tx should have made // it into the template because we still check IsFinalTx in CreateNewBlock, @@ -505,7 +505,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) ::ChainActive().Tip()->nHeight++; SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U); ::ChainActive().Tip()->nHeight--; diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index a29c6a2665..167242a971 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -14,7 +14,6 @@ #include <span.h> #include <streams.h> #include <test/util/setup_common.h> -#include <util/memory.h> #include <util/strencodings.h> #include <util/string.h> #include <util/system.h> @@ -188,7 +187,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); std::string pszDest; - std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>( + std::unique_ptr<CNode> pnode1 = std::make_unique<CNode>( id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 0, /* nLocalHostNonceIn = */ 0, @@ -203,7 +202,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode1->m_inbound_onion == false); BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4); - std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>( + std::unique_ptr<CNode> pnode2 = std::make_unique<CNode>( id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 1, /* nLocalHostNonceIn = */ 1, @@ -218,7 +217,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode2->m_inbound_onion == false); BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4); - std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>( + std::unique_ptr<CNode> pnode3 = std::make_unique<CNode>( id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 0, /* nLocalHostNonceIn = */ 0, @@ -233,7 +232,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode3->m_inbound_onion == false); BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4); - std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>( + std::unique_ptr<CNode> pnode4 = std::make_unique<CNode>( id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 1, /* nLocalHostNonceIn = */ 1, @@ -711,7 +710,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test) in_addr ipv4AddrPeer; ipv4AddrPeer.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK); - std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress{}, /* pszDest */ std::string{}, ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false); + std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress{}, /* pszDest */ std::string{}, ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false); pnode->fSuccessfullyConnected.store(true); // the peer claims to be reaching us via IPv6 diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 810665877d..2e0fc7e48f 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -36,7 +36,6 @@ UniValue RPCTestingSetup::CallRPC(std::string args) JSONRPCRequest request(context); request.strMethod = strMethod; request.params = RPCConvertValues(strMethod, vArgs); - request.fHelp = false; if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished(); try { UniValue result = tableRPC.execute(request); diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index 3e4b963fe3..496292875d 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -13,7 +13,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(basic_sanity) { - BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test"); BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test"); BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test"); BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test"); diff --git a/src/test/util/logging.cpp b/src/test/util/logging.cpp index c1dd47f06c..66f4efc139 100644 --- a/src/test/util/logging.cpp +++ b/src/test/util/logging.cpp @@ -7,7 +7,6 @@ #include <logging.h> #include <noui.h> #include <tinyformat.h> -#include <util/memory.h> #include <stdexcept> diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp index 0c6487fbfa..ba1edba0ae 100644 --- a/src/test/util/mining.cpp +++ b/src/test/util/mining.cpp @@ -42,7 +42,7 @@ std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coi { auto block = std::make_shared<CBlock>( BlockAssembler{*Assert(node.mempool), Params()} - .CreateNewBlock(coinbase_scriptPubKey) + .CreateNewBlock(::ChainstateActive(), coinbase_scriptPubKey) ->block); LOCK(cs_main); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 46b28190d3..f866c2a1f9 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -25,7 +25,6 @@ #include <script/sigcache.h> #include <streams.h> #include <txdb.h> -#include <util/memory.h> #include <util/strencodings.h> #include <util/string.h> #include <util/time.h> @@ -131,7 +130,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve { // We have to run a scheduler thread to prevent ActivateBestChain // from blocking due to queue overrun. - m_node.scheduler = MakeUnique<CScheduler>(); + m_node.scheduler = std::make_unique<CScheduler>(); m_node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); }); GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler); @@ -188,8 +187,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); } - m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); - m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. + m_node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. m_node.peerman = PeerManager::make(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool, false); @@ -245,13 +244,13 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa { const CChainParams& chainparams = Params(); CTxMemPool empty_pool; - CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block; + CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)->block; Assert(block.vtx.size() == 1); for (const CMutableTransaction& tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); } - RegenerateCommitments(block); + RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman))); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 5a46002a79..b1b71ef7c9 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -38,6 +38,7 @@ #include <boost/test/unit_test.hpp> using namespace std::literals; +static const std::string STRING_WITH_EMBEDDED_NULL_CHAR{"1"s "\0" "1"s}; /* defined in logging.cpp */ namespace BCLog { @@ -71,7 +72,7 @@ BOOST_AUTO_TEST_CASE(util_datadir) BOOST_AUTO_TEST_CASE(util_check) { // Check that Assert can forward - const std::unique_ptr<int> p_two = Assert(MakeUnique<int>(2)); + const std::unique_ptr<int> p_two = Assert(std::make_unique<int>(2)); // Check that Assert works on lvalues and rvalues const int two = *Assert(p_two); Assert(two == 2); @@ -1272,7 +1273,7 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney) // Parsing strings with embedded NUL characters should fail BOOST_CHECK(!ParseMoney("\0-1"s, ret)); - BOOST_CHECK(!ParseMoney("\0" "1"s, ret)); + BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR, ret)); BOOST_CHECK(!ParseMoney("1\0"s, ret)); } @@ -1450,9 +1451,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(!ParseInt32("aap", &n)); BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseInt32("-2147483649", nullptr)); BOOST_CHECK(!ParseInt32("2147483648", nullptr)); @@ -1480,9 +1479,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) BOOST_CHECK(!ParseInt64("1a", &n)); BOOST_CHECK(!ParseInt64("aap", &n)); BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr)); BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr)); @@ -1490,6 +1487,42 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr)); } +BOOST_AUTO_TEST_CASE(test_ParseUInt8) +{ + uint8_t n; + // Valid values + BOOST_CHECK(ParseUInt8("255", nullptr)); + BOOST_CHECK(ParseUInt8("0", &n) && n == 0); + BOOST_CHECK(ParseUInt8("255", &n) && n == 255); + BOOST_CHECK(ParseUInt8("0255", &n) && n == 255); // no octal + BOOST_CHECK(ParseUInt8("255", &n) && n == static_cast<uint8_t>(255)); + BOOST_CHECK(ParseUInt8("+255", &n) && n == 255); + BOOST_CHECK(ParseUInt8("00000000000000000012", &n) && n == 12); + BOOST_CHECK(ParseUInt8("00000000000000000000", &n) && n == 0); + // Invalid values + BOOST_CHECK(!ParseUInt8("-00000000000000000000", &n)); + BOOST_CHECK(!ParseUInt8("", &n)); + BOOST_CHECK(!ParseUInt8(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseUInt8(" -1", &n)); + BOOST_CHECK(!ParseUInt8("++1", &n)); + BOOST_CHECK(!ParseUInt8("+-1", &n)); + BOOST_CHECK(!ParseUInt8("-+1", &n)); + BOOST_CHECK(!ParseUInt8("--1", &n)); + BOOST_CHECK(!ParseUInt8("-1", &n)); + BOOST_CHECK(!ParseUInt8("1 ", &n)); + BOOST_CHECK(!ParseUInt8("1a", &n)); + BOOST_CHECK(!ParseUInt8("aap", &n)); + BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex + BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex + BOOST_CHECK(!ParseUInt8(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); + // Overflow and underflow + BOOST_CHECK(!ParseUInt8("-255", &n)); + BOOST_CHECK(!ParseUInt8("256", &n)); + BOOST_CHECK(!ParseUInt8("-123", &n)); + BOOST_CHECK(!ParseUInt8("-123", nullptr)); + BOOST_CHECK(!ParseUInt8("256", nullptr)); +} + BOOST_AUTO_TEST_CASE(test_ParseUInt32) { uint32_t n; @@ -1519,9 +1552,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt32) BOOST_CHECK(!ParseUInt32("aap", &n)); BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseUInt32(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseUInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseUInt32("-2147483648", &n)); BOOST_CHECK(!ParseUInt32("4294967296", &n)); @@ -1550,9 +1581,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt64) BOOST_CHECK(!ParseUInt64("1a", &n)); BOOST_CHECK(!ParseUInt64("aap", &n)); BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseUInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr)); BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr)); @@ -1582,9 +1611,7 @@ BOOST_AUTO_TEST_CASE(test_ParseDouble) BOOST_CHECK(!ParseDouble("1a", &n)); BOOST_CHECK(!ParseDouble("aap", &n)); BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseDouble(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseDouble("-1e10000", nullptr)); BOOST_CHECK(!ParseDouble("1e10000", nullptr)); diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 0c87c4d360..f3fc83078f 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -63,7 +63,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash) static int i = 0; static uint64_t time = Params().GenesisBlock().nTime; - auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(CScript{} << i++ << OP_TRUE); + auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), CScript{} << i++ << OP_TRUE); auto pblock = std::make_shared<CBlock>(ptemplate->block); pblock->hashPrevBlock = prev_hash; pblock->nTime = ++time; @@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index) { CScript pubKey; pubKey << 1 << OP_TRUE; - auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey); + auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), pubKey); CBlock pblock = ptemplate->block; CTxOut witness; diff --git a/src/txdb.cpp b/src/txdb.cpp index 4b4766e1ba..3a08e28c01 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -10,7 +10,6 @@ #include <random.h> #include <shutdown.h> #include <uint256.h> -#include <util/memory.h> #include <util/system.h> #include <util/translation.h> #include <util/vector.h> @@ -41,7 +40,7 @@ struct CoinEntry { } CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) : - m_db(MakeUnique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)), + m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)), m_ldb_path(ldb_path), m_is_memory(fMemory) { } @@ -53,7 +52,7 @@ void CCoinsViewDB::ResizeCache(size_t new_cache_size) // Have to do a reset first to get the original `m_db` state to release its // filesystem lock. m_db.reset(); - m_db = MakeUnique<CDBWrapper>( + m_db = std::make_unique<CDBWrapper>( m_ldb_path, new_cache_size, m_is_memory, /*fWipe*/ false, /*obfuscate*/ true); } } diff --git a/src/txrequest.cpp b/src/txrequest.cpp index e54c073328..58424134b0 100644 --- a/src/txrequest.cpp +++ b/src/txrequest.cpp @@ -9,7 +9,6 @@ #include <primitives/transaction.h> #include <random.h> #include <uint256.h> -#include <util/memory.h> #include <boost/multi_index_container.hpp> #include <boost/multi_index/ordered_index.hpp> @@ -711,7 +710,7 @@ public: }; TxRequestTracker::TxRequestTracker(bool deterministic) : - m_impl{MakeUnique<TxRequestTracker::Impl>(deterministic)} {} + m_impl{std::make_unique<TxRequestTracker::Impl>(deterministic)} {} TxRequestTracker::~TxRequestTracker() = default; diff --git a/src/util/memory.h b/src/util/memory.h deleted file mode 100644 index f21b81bade..0000000000 --- a/src/util/memory.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_UTIL_MEMORY_H -#define BITCOIN_UTIL_MEMORY_H - -#include <memory> -#include <utility> - -//! Substitute for C++14 std::make_unique. -//! DEPRECATED use std::make_unique in new code. -template <typename T, typename... Args> -std::unique_ptr<T> MakeUnique(Args&&... args) -{ - return std::make_unique<T>(std::forward<Args>(args)...); -} - -#endif diff --git a/src/util/system.cpp b/src/util/system.cpp index 71453eed81..63b3c52ee5 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -100,7 +100,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b // Create empty lock file if it doesn't exist. FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); - auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile); + auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile); if (!lock->TryLock()) { return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason()); } diff --git a/src/util/system.h b/src/util/system.h index de47b93b6e..30b8cb1c68 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -22,7 +22,6 @@ #include <optional.h> #include <sync.h> #include <tinyformat.h> -#include <util/memory.h> #include <util/settings.h> #include <util/threadnames.h> #include <util/time.h> diff --git a/src/util/tokenpipe.cpp b/src/util/tokenpipe.cpp new file mode 100644 index 0000000000..79465dd430 --- /dev/null +++ b/src/util/tokenpipe.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 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 <util/tokenpipe.h> + +#include <config/bitcoin-config.h> + +#ifndef WIN32 + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +TokenPipeEnd TokenPipe::TakeReadEnd() +{ + TokenPipeEnd res(m_fds[0]); + m_fds[0] = -1; + return res; +} + +TokenPipeEnd TokenPipe::TakeWriteEnd() +{ + TokenPipeEnd res(m_fds[1]); + m_fds[1] = -1; + return res; +} + +TokenPipeEnd::TokenPipeEnd(int fd) : m_fd(fd) +{ +} + +TokenPipeEnd::~TokenPipeEnd() +{ + Close(); +} + +int TokenPipeEnd::TokenWrite(uint8_t token) +{ + while (true) { + ssize_t result = write(m_fd, &token, 1); + if (result < 0) { + // Failure. It's possible that the write was interrupted by a signal, + // in that case retry. + if (errno != EINTR) { + return TS_ERR; + } + } else if (result == 0) { + return TS_EOS; + } else { // ==1 + return 0; + } + } +} + +int TokenPipeEnd::TokenRead() +{ + uint8_t token; + while (true) { + ssize_t result = read(m_fd, &token, 1); + if (result < 0) { + // Failure. Check if the read was interrupted by a signal, + // in that case retry. + if (errno != EINTR) { + return TS_ERR; + } + } else if (result == 0) { + return TS_EOS; + } else { // ==1 + return token; + } + } + return token; +} + +void TokenPipeEnd::Close() +{ + if (m_fd != -1) close(m_fd); + m_fd = -1; +} + +std::optional<TokenPipe> TokenPipe::Make() +{ + int fds[2] = {-1, -1}; +#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2 + if (pipe2(fds, O_CLOEXEC) != 0) { + return std::nullopt; + } +#else + if (pipe(fds) != 0) { + return std::nullopt; + } +#endif + return TokenPipe(fds); +} + +TokenPipe::~TokenPipe() +{ + Close(); +} + +void TokenPipe::Close() +{ + if (m_fds[0] != -1) close(m_fds[0]); + if (m_fds[1] != -1) close(m_fds[1]); + m_fds[0] = m_fds[1] = -1; +} + +#endif // WIN32 diff --git a/src/util/tokenpipe.h b/src/util/tokenpipe.h new file mode 100644 index 0000000000..f56be93a38 --- /dev/null +++ b/src/util/tokenpipe.h @@ -0,0 +1,127 @@ +// Copyright (c) 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. + +#ifndef BITCOIN_UTIL_TOKENPIPE_H +#define BITCOIN_UTIL_TOKENPIPE_H + +#ifndef WIN32 + +#include <cstdint> +#include <optional> + +/** One end of a token pipe. */ +class TokenPipeEnd +{ +private: + int m_fd = -1; + +public: + TokenPipeEnd(int fd = -1); + ~TokenPipeEnd(); + + /** Return value constants for TokenWrite and TokenRead. */ + enum Status { + TS_ERR = -1, //!< I/O error + TS_EOS = -2, //!< Unexpected end of stream + }; + + /** Write token to endpoint. + * + * @returns 0 If successful. + * <0 if error: + * TS_ERR If an error happened. + * TS_EOS If end of stream happened. + */ + int TokenWrite(uint8_t token); + + /** Read token from endpoint. + * + * @returns >=0 Token value, if successful. + * <0 if error: + * TS_ERR If an error happened. + * TS_EOS If end of stream happened. + */ + int TokenRead(); + + /** Explicit close function. + */ + void Close(); + + /** Return whether endpoint is open. + */ + bool IsOpen() { return m_fd != -1; } + + // Move-only class. + TokenPipeEnd(TokenPipeEnd&& other) + { + m_fd = other.m_fd; + other.m_fd = -1; + } + TokenPipeEnd& operator=(TokenPipeEnd&& other) + { + Close(); + m_fd = other.m_fd; + other.m_fd = -1; + return *this; + } + TokenPipeEnd(const TokenPipeEnd&) = delete; + TokenPipeEnd& operator=(const TokenPipeEnd&) = delete; +}; + +/** An interprocess or interthread pipe for sending tokens (one-byte values) + * over. + */ +class TokenPipe +{ +private: + int m_fds[2] = {-1, -1}; + + TokenPipe(int fds[2]) : m_fds{fds[0], fds[1]} {} + +public: + ~TokenPipe(); + + /** Create a new pipe. + * @returns The created TokenPipe, or an empty std::nullopt in case of error. + */ + static std::optional<TokenPipe> Make(); + + /** Take the read end of this pipe. This can only be called once, + * as the object will be moved out. + */ + TokenPipeEnd TakeReadEnd(); + + /** Take the write end of this pipe. This should only be called once, + * as the object will be moved out. + */ + TokenPipeEnd TakeWriteEnd(); + + /** Close and end of the pipe that hasn't been moved out. + */ + void Close(); + + // Move-only class. + TokenPipe(TokenPipe&& other) + { + for (int i = 0; i < 2; ++i) { + m_fds[i] = other.m_fds[i]; + other.m_fds[i] = -1; + } + } + TokenPipe& operator=(TokenPipe&& other) + { + Close(); + for (int i = 0; i < 2; ++i) { + m_fds[i] = other.m_fds[i]; + other.m_fds[i] = -1; + } + return *this; + } + TokenPipe(const TokenPipe&) = delete; + TokenPipe& operator=(const TokenPipe&) = delete; +}; + +#endif // WIN32 + +#endif // BITCOIN_UTIL_TOKENPIPE_H diff --git a/src/validation.cpp b/src/validation.cpp index e7439a2a1a..a1bd79bdfc 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1287,7 +1287,7 @@ CoinsViews::CoinsViews( void CoinsViews::InitCache() { - m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview); + m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview); } CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash) @@ -1305,7 +1305,7 @@ void CChainState::InitCoinsDB( leveldb_name += "_" + m_from_snapshot_blockhash.ToString(); } - m_coins_views = MakeUnique<CoinsViews>( + m_coins_views = std::make_unique<CoinsViews>( leveldb_name, cache_size_bytes, in_memory, should_wipe); } @@ -5010,11 +5010,11 @@ CBlockFileInfo* GetBlockFileInfo(size_t n) static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function) { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")}; CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); @@ -5095,7 +5095,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) return true; } -bool DumpMempool(const CTxMemPool& pool) +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit) { int64_t start = GetTimeMicros(); @@ -5118,7 +5118,7 @@ bool DumpMempool(const CTxMemPool& pool) int64_t mid = GetTimeMicros(); try { - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")}; if (!filestr) { return false; } @@ -5141,7 +5141,7 @@ bool DumpMempool(const CTxMemPool& pool) LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); file << unbroadcast_txids; - if (!FileCommit(file.Get())) + if (!skip_file_commit && !FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) { @@ -5279,7 +5279,7 @@ bool ChainstateManager::ActivateSnapshot( static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC)); } - auto snapshot_chainstate = WITH_LOCK(::cs_main, return MakeUnique<CChainState>( + auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>( this->ActiveChainstate().m_mempool, m_blockman, base_blockhash)); { @@ -5425,7 +5425,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // about the snapshot_chainstate. CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB()); - if (!GetUTXOStats(snapshot_coinsdb, stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) { + if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) { LogPrintf("[snapshot] failed to generate coins stats\n"); return false; } diff --git a/src/validation.h b/src/validation.h index 74f8541934..ea783ddcd1 100644 --- a/src/validation.h +++ b/src/validation.h @@ -148,8 +148,6 @@ extern const std::vector<std::string> CHECKLEVEL_DOC; FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ fs::path GetBlockPosFilename(const FlatFilePos &pos); -/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ -bool LoadGenesisBlock(const CChainParams& chainparams); /** Unload database information */ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman); /** Run instances of script checking worker threads */ @@ -725,6 +723,7 @@ public: /** Replay blocks that aren't fully applied to the database. */ bool ReplayBlocks(const CChainParams& params); bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main); + /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ bool LoadGenesisBlock(const CChainParams& chainparams); void PruneBlockIndexCandidates(); @@ -1014,11 +1013,13 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); +using FopenFn = std::function<FILE*(const fs::path&, const char*)>; + /** Dump the mempool to disk. */ -bool DumpMempool(const CTxMemPool& pool); +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false); /** Load the mempool from disk. */ -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate); +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen); //! Check whether the block associated with this index entry is pruned or not. inline bool IsBlockPruned(const CBlockIndex* pblockindex) diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index ad40e6da9a..1dc23374e3 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -331,7 +331,7 @@ void BerkeleyDatabase::Open() if (m_db == nullptr) { int ret; - std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0); + std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0); bool fMockDb = env->IsMock(); if (fMockDb) { @@ -462,7 +462,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip) std::string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} BerkeleyBatch db(*this, true); - std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0); + std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer strFileRes.c_str(), // Filename @@ -819,7 +819,7 @@ void BerkeleyDatabase::RemoveRef() std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close) { - return MakeUnique<BerkeleyBatch>(*this, false, flush_on_close); + return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close); } std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error) @@ -835,7 +835,7 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con status = DatabaseStatus::FAILED_ALREADY_LOADED; return nullptr; } - db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename)); + db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename)); } if (options.verify && !db->Verify(error)) { diff --git a/src/wallet/db.h b/src/wallet/db.h index 2c75486a44..b64e9fa533 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -11,7 +11,6 @@ #include <optional.h> #include <streams.h> #include <support/allocators/secure.h> -#include <util/memory.h> #include <atomic> #include <memory> @@ -193,7 +192,7 @@ public: void ReloadDbEnv() override {} std::string Filename() override { return "dummy"; } std::string Format() override { return "dummy"; } - std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); } + std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); } }; enum class DatabaseFormat { diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index f3e24384df..fdeead1fa5 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -70,7 +70,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); #if HAVE_SYSTEM - argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID, %w is replaced by wallet name, %b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included) and %h is replaced by the block height (-1 if not included). %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); #endif argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 1fb789b128..ada586119a 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -588,10 +588,10 @@ public: } // namespace wallet namespace interfaces { -std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<wallet::WalletImpl>(wallet) : nullptr; } +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(wallet) : nullptr; } std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args) { - return MakeUnique<wallet::WalletClientImpl>(chain, args); + return std::make_unique<wallet::WalletClientImpl>(chain, args); } } // namespace interfaces diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 60f76465a6..50423c3e32 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -982,14 +982,14 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string"); } auto parsed_redeemscript = ParseHex(strRedeemScript); - import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end()); + import_data.redeemscript = std::make_unique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end()); } if (witness_script_hex.size()) { if (!IsHex(witness_script_hex)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string"); } auto parsed_witnessscript = ParseHex(witness_script_hex); - import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end()); + import_data.witnessscript = std::make_unique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end()); } for (size_t i = 0; i < pubKeys.size(); ++i) { const auto& str = pubKeys[i].get_str(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8b0962f9ee..1b9071a712 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -96,7 +96,7 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { - CHECK_NONFATAL(!request.fHelp); + CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE); std::string wallet_name; if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name); diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index 09a9ec68cd..6d912be019 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -119,7 +119,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v return false; } - std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0); + std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer filename.c_str(), // Filename "main", // Logical db name diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index efb408c163..a8200981d8 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -378,7 +378,7 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata() return; } - std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(m_storage.GetDatabase()); + std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_storage.GetDatabase()); for (auto& meta_pair : mapKeyMetadata) { CKeyMetadata& meta = meta_pair.second; if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin @@ -551,7 +551,7 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const { - return MakeUnique<LegacySigningProvider>(*this); + return std::make_unique<LegacySigningProvider>(*this); } bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata) @@ -651,14 +651,14 @@ std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestin if (!key_id.IsNull()) { auto it = mapKeyMetadata.find(key_id); if (it != mapKeyMetadata.end()) { - return MakeUnique<CKeyMetadata>(it->second); + return std::make_unique<CKeyMetadata>(it->second); } } CScript scriptPubKey = GetScriptForDestination(dest); auto it = m_script_metadata.find(CScriptID(scriptPubKey)); if (it != m_script_metadata.end()) { - return MakeUnique<CKeyMetadata>(it->second); + return std::make_unique<CKeyMetadata>(it->second); } return nullptr; @@ -2026,7 +2026,7 @@ std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvid { AssertLockHeld(cs_desc_man); // Get the scripts, keys, and key origins for this script - std::unique_ptr<FlatSigningProvider> out_keys = MakeUnique<FlatSigningProvider>(); + std::unique_ptr<FlatSigningProvider> out_keys = std::make_unique<FlatSigningProvider>(); std::vector<CScript> scripts_temp; if (!m_wallet_descriptor.descriptor->ExpandFromCache(index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) return nullptr; @@ -2051,7 +2051,7 @@ bool DescriptorScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { - std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>(); + std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>(); for (const auto& coin_pair : coins) { std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(coin_pair.second.out.scriptPubKey, true); if (!coin_keys) { @@ -2115,13 +2115,13 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& SignatureData sigdata; input.FillSignatureData(sigdata); - std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>(); + std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>(); std::unique_ptr<FlatSigningProvider> script_keys = GetSigningProvider(script, sign); if (script_keys) { *keys = Merge(*keys, *script_keys); } else { // Maybe there are pubkeys listed that we can sign for - script_keys = MakeUnique<FlatSigningProvider>(); + script_keys = std::make_unique<FlatSigningProvider>(); for (const auto& pk_pair : input.hd_keypaths) { const CPubKey& pubkey = pk_pair.first; std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey); @@ -2162,7 +2162,7 @@ std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDe CKeyID key_id = GetKeyForDestination(*provider, dest); if (provider->GetKeyOrigin(key_id, orig)) { LOCK(cs_desc_man); - std::unique_ptr<CKeyMetadata> meta = MakeUnique<CKeyMetadata>(); + std::unique_ptr<CKeyMetadata> meta = std::make_unique<CKeyMetadata>(); meta->key_origin = orig; meta->has_key_origin = true; meta->nCreateTime = m_wallet_descriptor.creation_time; diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 0fb3b1d3c4..975974cb6a 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -8,7 +8,6 @@ #include <crypto/common.h> #include <logging.h> #include <sync.h> -#include <util/memory.h> #include <util/strencodings.h> #include <util/system.h> #include <util/translation.h> @@ -330,7 +329,7 @@ void SQLiteDatabase::Close() std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close) { // We ignore flush_on_close because we don't do manual flushing for SQLite - return MakeUnique<SQLiteBatch>(*this); + return std::make_unique<SQLiteBatch>(*this); } SQLiteBatch::SQLiteBatch(SQLiteDatabase& database) @@ -571,7 +570,7 @@ std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const D { try { fs::path data_file = SQLiteDataFile(path); - auto db = MakeUnique<SQLiteDatabase>(data_file.parent_path(), data_file); + auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file); if (options.verify && !db->Verify(error)) { status = DatabaseStatus::FAILED_VERIFY; return nullptr; diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index ffac78d752..0e9f3819eb 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) // Make sure that can use BnB when there are preset inputs empty_wallet(); { - std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); + std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); bool firstRun; wallet->LoadWallet(firstRun); wallet->SetupLegacyScriptPubKeyMan(); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 5480f3ab22..4598999b3c 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -485,7 +485,7 @@ public: ListCoinsTestingSetup() { CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); - wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); + wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); { LOCK2(wallet->cs_wallet, ::cs_main); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 58ab124061..ac1b2a4c0a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -944,6 +944,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", hash.GetHex()); + if (confirm.status == CWalletTx::Status::CONFIRMED) + { + boost::replace_all(strCmd, "%b", confirm.hashBlock.GetHex()); + boost::replace_all(strCmd, "%h", ToString(confirm.block_height)); + } else { + boost::replace_all(strCmd, "%b", "unconfirmed"); + boost::replace_all(strCmd, "%h", "-1"); + } #ifndef WIN32 // Substituting the wallet name isn't currently supported on windows // because windows shell escaping has not been implemented yet: diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 69854cae05..a5a454f9a8 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1086,15 +1086,15 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas /** Return object for accessing dummy database with no read/write capabilities. */ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase() { - return MakeUnique<DummyDatabase>(); + return std::make_unique<DummyDatabase>(); } /** Return object for accessing temporary in-memory database. */ std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() { #ifdef USE_BDB - return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); + return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); #elif USE_SQLITE - return MakeUnique<SQLiteDatabase>("", "", true); + return std::make_unique<SQLiteDatabase>("", "", true); #endif } diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h index 6f0b202a18..49c1c2a07d 100644 --- a/src/zmq/zmqabstractnotifier.h +++ b/src/zmq/zmqabstractnotifier.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H #define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H -#include <util/memory.h> #include <memory> #include <string> @@ -27,7 +26,7 @@ public: template <typename T> static std::unique_ptr<CZMQAbstractNotifier> Create() { - return MakeUnique<T>(); + return std::make_unique<T>(); } std::string GetType() const { return type; } diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index b068ce612c..4e2de1daf4 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -17,7 +17,7 @@ from test_framework.util import ( FILE_CHAR_START = 32 if os.name == 'nt' else 1 FILE_CHAR_END = 128 FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/' - +UNCONFIRMED_HASH_STRING = 'unconfirmed' def notify_outputname(walletname, txid): return txid if os.name == 'nt' else '{}_{}'.format(walletname, txid) @@ -43,7 +43,7 @@ class NotificationsTest(BitcoinTestFramework): "-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s')), ], [ "-rescan", - "-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))), + "-walletnotify=echo %h_%b > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))), ]] self.wallet_names = [self.default_wallet_name, self.wallet] super().setup_network() @@ -90,11 +90,9 @@ class NotificationsTest(BitcoinTestFramework): self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) # directory content should equal the generated transaction hashes - txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count))) - assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) + tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count))) self.stop_node(1) - for tx_file in os.listdir(self.walletnotify_dir): - os.remove(os.path.join(self.walletnotify_dir, tx_file)) + self.expect_wallet_notify(tx_details) self.log.info("test -walletnotify after rescan") # restart node to rescan to force wallet notifications @@ -104,10 +102,8 @@ class NotificationsTest(BitcoinTestFramework): self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) # directory content should equal the generated transaction hashes - txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count))) - assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) - for tx_file in os.listdir(self.walletnotify_dir): - os.remove(os.path.join(self.walletnotify_dir, tx_file)) + tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count))) + self.expect_wallet_notify(tx_details) # Conflicting transactions tests. # Generate spends from node 0, and check notifications @@ -122,7 +118,7 @@ class NotificationsTest(BitcoinTestFramework): tx1 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True) assert_equal(tx1 in self.nodes[0].getrawmempool(), True) self.sync_mempools() - self.expect_wallet_notify([tx1]) + self.expect_wallet_notify([(tx1, -1, UNCONFIRMED_HASH_STRING)]) # Generate bump transaction, sync mempools, and check for bump1 # notification. In the future, per @@ -131,39 +127,57 @@ class NotificationsTest(BitcoinTestFramework): bump1 = self.nodes[0].bumpfee(tx1)["txid"] assert_equal(bump1 in self.nodes[0].getrawmempool(), True) self.sync_mempools() - self.expect_wallet_notify([bump1]) + self.expect_wallet_notify([(bump1, -1, UNCONFIRMED_HASH_STRING)]) # Add bump1 transaction to new block, checking for a notification # and the correct number of confirmations. - self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) + blockhash1 = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0] + blockheight1 = self.nodes[0].getblockcount() self.sync_blocks() - self.expect_wallet_notify([bump1]) + self.expect_wallet_notify([(bump1, blockheight1, blockhash1)]) assert_equal(self.nodes[1].gettransaction(bump1)["confirmations"], 1) # Generate a second transaction to be bumped. tx2 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True) assert_equal(tx2 in self.nodes[0].getrawmempool(), True) self.sync_mempools() - self.expect_wallet_notify([tx2]) + self.expect_wallet_notify([(tx2, -1, UNCONFIRMED_HASH_STRING)]) # Bump tx2 as bump2 and generate a block on node 0 while # disconnected, then reconnect and check for notifications on node 1 # about newly confirmed bump2 and newly conflicted tx2. self.disconnect_nodes(0, 1) bump2 = self.nodes[0].bumpfee(tx2)["txid"] - self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) + blockhash2 = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0] + blockheight2 = self.nodes[0].getblockcount() assert_equal(self.nodes[0].gettransaction(bump2)["confirmations"], 1) assert_equal(tx2 in self.nodes[1].getrawmempool(), True) self.connect_nodes(0, 1) self.sync_blocks() - self.expect_wallet_notify([bump2, tx2]) + self.expect_wallet_notify([(bump2, blockheight2, blockhash2), (tx2, -1, UNCONFIRMED_HASH_STRING)]) assert_equal(self.nodes[1].gettransaction(bump2)["confirmations"], 1) # TODO: add test for `-alertnotify` large fork notifications - def expect_wallet_notify(self, tx_ids): - self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10) - assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id in tx_ids), sorted(os.listdir(self.walletnotify_dir))) + def expect_wallet_notify(self, tx_details): + self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_details), timeout=10) + # Should have no more and no less files than expected + assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id, _, _ in tx_details), sorted(os.listdir(self.walletnotify_dir))) + # Should now verify contents of each file + for tx_id, blockheight, blockhash in tx_details: + fname = os.path.join(self.walletnotify_dir, notify_outputname(self.wallet, tx_id)) + with open(fname, 'rt', encoding='utf-8') as f: + text = f.read() + # Universal newline ensures '\n' on 'nt' + assert_equal(text[-1], '\n') + text = text[:-1] + if os.name == 'nt': + # On Windows, echo as above will append a whitespace + assert_equal(text[-1], ' ') + text = text[:-1] + expected = str(blockheight) + '_' + blockhash + assert_equal(text, expected) + for tx_file in os.listdir(self.walletnotify_dir): os.remove(os.path.join(self.walletnotify_dir, tx_file)) diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py index e5c4f12f20..c0b76d960f 100755 --- a/test/functional/wallet_groups.py +++ b/test/functional/wallet_groups.py @@ -29,8 +29,9 @@ class WalletGroupTest(BitcoinTestFramework): self.skip_if_no_wallet() def run_test(self): + self.log.info("Setting up") # Mine some coins - self.nodes[0].generate(110) + self.nodes[0].generate(101) # Get some addresses from the two nodes addr1 = [self.nodes[1].getnewaddress() for _ in range(3)] @@ -48,6 +49,7 @@ class WalletGroupTest(BitcoinTestFramework): # - node[1] should pick one 0.5 UTXO and leave the rest # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a # given address, and leave the rest + self.log.info("Test sending transactions picks one UTXO group and leaves the rest") txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) tx1 = self.nodes[1].getrawtransaction(txid1, True) # txid1 should have 1 input and 2 outputs @@ -70,7 +72,7 @@ class WalletGroupTest(BitcoinTestFramework): assert_approx(v[0], vexp=0.2, vspan=0.0001) assert_approx(v[1], vexp=1.3, vspan=0.0001) - # Test 'avoid partial if warranted, even if disabled' + self.log.info("Test avoiding partial spends if warranted, even if avoidpartialspends is disabled") self.sync_all() self.nodes[0].generate(1) # Nodes 1-2 now have confirmed UTXOs (letters denote destinations): @@ -104,7 +106,7 @@ class WalletGroupTest(BitcoinTestFramework): assert_equal(input_addrs[0], input_addrs[1]) # Node 2 enforces avoidpartialspends so needs no checking here - # Test wallet option maxapsfee with Node 3 + self.log.info("Test wallet option maxapsfee") addr_aps = self.nodes[3].getnewaddress() self.nodes[0].sendtoaddress(addr_aps, 1.0) self.nodes[0].sendtoaddress(addr_aps, 1.0) @@ -131,6 +133,7 @@ class WalletGroupTest(BitcoinTestFramework): # Test wallet option maxapsfee with node 4, which sets maxapsfee # 1 sat higher, crossing the threshold from non-grouped to grouped. + self.log.info("Test wallet option maxapsfee threshold from non-grouped to grouped") addr_aps3 = self.nodes[4].getnewaddress() [self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)] self.nodes[0].generate(1) @@ -147,8 +150,7 @@ class WalletGroupTest(BitcoinTestFramework): self.sync_all() self.nodes[0].generate(1) - # Fill node2's wallet with 10000 outputs corresponding to the same - # scriptPubKey + self.log.info("Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey") for _ in range(5): raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}]) tx = FromHex(CTransaction(), raw_tx) @@ -158,12 +160,12 @@ class WalletGroupTest(BitcoinTestFramework): signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex']) self.nodes[0].sendrawtransaction(signed_tx['hex']) self.nodes[0].generate(1) - - self.sync_all() + self.sync_all() # Check that we can create a transaction that only requires ~100 of our # utxos, without pulling in all outputs and creating a transaction that # is way too big. + self.log.info("Test creating txn that only requires ~100 of our UTXOs without pulling in all outputs") assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5) |