diff options
79 files changed, 1096 insertions, 479 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index e7a50d931a..b915cfabc7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -170,7 +170,7 @@ task: task: name: 'macOS 10.15 native [gui] [no depends]' macos_brew_addon_script: - - brew install boost libevent berkeley-db4 qt miniupnpc ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt + - brew install boost libevent berkeley-db4 qt miniupnpc libnatpmp ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt << : *GLOBAL_TASK_TEMPLATE osx_instance: # Use latest image, but hardcode version to avoid silent upgrades (and breaks) diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index 191b8049b0..f682486088 100644 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_asan -export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev libsqlite3-dev" +export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev" export DOCKER_NAME_TAG=ubuntu:20.04 export NO_DEPENDS=1 export GOAL="install" diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh index dc6b2aecb5..567145fe47 100644 --- a/ci/test/00_setup_env_native_qt5.sh +++ b/ci/test/00_setup_env_native_qt5.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_qt5 export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic gcc-7 can compile our c++17 and run our functional tests in python3, see doc/dependencies.md export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev" -export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" +export DEP_OPTS="NO_QT=1 NO_UPNP=1 NO_NATPMP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash export RUN_SECURITY_TESTS="true" export RUN_UNIT_TESTS_SEQUENTIAL="true" diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index bfaea13a25..f0c153158b 100644 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_valgrind -export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libsqlite3-dev" +export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev" export USE_VALGRIND=1 export NO_DEPENDS=1 export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 diff --git a/configure.ac b/configure.ac index 0a8d5ef8f8..0fa99cfc47 100644 --- a/configure.ac +++ b/configure.ac @@ -143,6 +143,18 @@ AC_ARG_ENABLE([upnp-default], [use_upnp_default=$enableval], [use_upnp_default=no]) +AC_ARG_WITH([natpmp], + [AS_HELP_STRING([--with-natpmp], + [enable NAT-PMP (default is yes if libnatpmp is found)])], + [use_natpmp=$withval], + [use_natpmp=auto]) + +AC_ARG_ENABLE([natpmp-default], + [AS_HELP_STRING([--enable-natpmp-default], + [if NAT-PMP is enabled, turn it on at startup (default is no)])], + [use_natpmp_default=$enableval], + [use_natpmp_default=no]) + AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), [use_tests=$enableval], @@ -1206,6 +1218,7 @@ if test "x$enable_fuzz" = "xyes"; then enable_wallet=no use_bench=no use_upnp=no + use_natpmp=no use_zmq=no AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]]) @@ -1303,6 +1316,13 @@ if test x$have_miniupnpc != xno; then fi fi +dnl Check for libnatpmp (optional). +if test "x$use_natpmp" != xno; then + AC_CHECK_HEADERS([natpmp.h], + [AC_CHECK_LIB([natpmp], [initnatpmp], [NATPMP_LIBS=-lnatpmp], [have_natpmp=no])], + [have_natpmp=no]) +fi + if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then use_boost=no else @@ -1561,6 +1581,31 @@ else fi fi +dnl Enable NAT-PMP support. +AC_MSG_CHECKING([whether to build with support for NAT-PMP]) +if test "x$have_natpmp" = xno; then + if test "x$use_natpmp" = xyes; then + AC_MSG_ERROR([NAT-PMP requested but cannot be built. Use --without-natpmp]) + fi + AC_MSG_RESULT([no]) + use_natpmp=no +else + if test "x$use_natpmp" != xno; then + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([whether to build with NAT-PMP enabled by default]) + use_natpmp=yes + natpmp_setting=0 + if test "x$use_natpmp_default" != xno; then + use_natpmp_default=yes + natpmp_setting=1 + fi + AC_MSG_RESULT($use_natpmp_default) + AC_DEFINE_UNQUOTED([USE_NATPMP], [$natpmp_setting], [NAT-PMP support not compiled if undefined, otherwise value (0 or 1) determines default state]) + else + AC_MSG_RESULT([no]) + fi +fi + dnl these are only used when qt is enabled BUILD_TEST_QT="" if test x$bitcoin_enable_qt != xno; then @@ -1707,6 +1752,7 @@ AC_SUBST(SQLITE_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) +AC_SUBST(NATPMP_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) @@ -1794,6 +1840,7 @@ else fi echo " with bench = $use_bench" echo " with upnp = $use_upnp" +echo " with natpmp = $use_natpmp" echo " use asm = $use_asm" echo " sanitizers = $use_sanitizers" echo " debug enabled = $enable_debug" diff --git a/depends/Makefile b/depends/Makefile index 1ad21f6821..596a46d4a2 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -37,6 +37,7 @@ NO_QR ?= NO_WALLET ?= NO_ZMQ ?= NO_UPNP ?= +NO_NATPMP ?= MULTIPROCESS ?= FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources @@ -126,6 +127,8 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) ifneq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +# Make sure that cache is invalidated when switching between system and +# depends-managed, pinned clang build_id_string+=system_clang $(host_arch)_$(host_os)_id_string+=system_clang endif @@ -139,10 +142,12 @@ sqlite_packages_$(NO_SQLITE) = $(sqlite_packages) wallet_packages_$(NO_WALLET) = $(bdb_packages_) $(sqlite_packages_) upnp_packages_$(NO_UPNP) = $(upnp_packages) +natpmp_packages_$(NO_NATPMP) = $(natpmp_packages) + zmq_packages_$(NO_ZMQ) = $(zmq_packages) multiprocess_packages_$(MULTIPROCESS) = $(multiprocess_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) $(natpmp_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) ifneq ($(zmq_packages_),) @@ -163,12 +168,6 @@ $(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain) include funcs.mk -binutils_path=$($($(host_arch)_$(host_os)_native_binutils)_prefixbin) -ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) -toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) -else -toolchain_path= -endif final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) final_build_id+=$(shell echo -n "$(final_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) $(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) @@ -179,15 +178,39 @@ $(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) $(AT)cd $(@D); $(foreach package,$^, tar xf $($(package)_cached); ) $(AT)touch $@ +# $PATH is not preserved between ./configure and make by convention. Its +# modification and overriding at ./configure time is (as I understand it) +# supposed to be captured by the AC_{PROG_{,OBJ}CXX,PATH_{PROG,TOOL}} macros, +# which will expand the program names to their full absolute paths. The notable +# exception is command line overriding: ./configure CC=clang, which skips the +# program name expansion step, and works because the user implicitly indicates +# with CC=clang that clang will be available in $PATH at all times, and is most +# likely part of the user's system. +# +# Therefore, when we "seed the autoconf cache"/"override well-known program +# vars" by setting AR=<blah> in our config.site, either one of two things needs +# to be true for the build system to work correctly: +# +# 1. If we refer to the program by name (e.g. AR=riscv64-gnu-linux-ar), the +# tool needs to be available in $PATH at all times. +# +# 2. If the tool is _**not**_ expected to be available in $PATH at all times +# (such as is the case for our native_cctools binutils tools), it needs to +# be referred to by its absolute path, such as would be output by the +# AC_PATH_{PROG,TOOL} macros. +# +# Minor note: it is also okay to refer to tools by their absolute path even if +# we expect them to be available in $PATH at all times, more specificity does +# not hurt. $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_build_id) $(AT)@mkdir -p $(@D) $(AT)sed -e 's|@HOST@|$(host)|' \ - -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ - -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ - -e 's|@AR@|$(binutils_path)$(host_AR)|' \ - -e 's|@RANLIB@|$(binutils_path)$(host_RANLIB)|' \ - -e 's|@NM@|$(binutils_path)$(host_NM)|' \ - -e 's|@STRIP@|$(binutils_path)$(host_STRIP)|' \ + -e 's|@CC@|$(host_CC)|' \ + -e 's|@CXX@|$(host_CXX)|' \ + -e 's|@AR@|$(host_AR)|' \ + -e 's|@RANLIB@|$(host_RANLIB)|' \ + -e 's|@NM@|$(host_NM)|' \ + -e 's|@STRIP@|$(host_STRIP)|' \ -e 's|@build_os@|$(build_os)|' \ -e 's|@host_os@|$(host_os)|' \ -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ @@ -200,6 +223,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@no_zmq@|$(NO_ZMQ)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \ + -e 's|@no_natpmp@|$(NO_NATPMP)|' \ -e 's|@multiprocess@|$(MULTIPROCESS)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ diff --git a/depends/README.md b/depends/README.md index 085e0e6054..9bc9b6714b 100644 --- a/depends/README.md +++ b/depends/README.md @@ -94,6 +94,7 @@ The following can be set when running make: `make FOO=bar` - `NO_BDB`: Don't download/build/cache BerkeleyDB - `NO_SQLITE`: Don't download/build/cache SQLite - `NO_UPNP`: Don't download/build/cache packages needed for enabling UPnP +- `NO_NATPMP`: Don't download/build/cache packages needed for enabling NAT-PMP</dd> - `ALLOW_HOST_PACKAGES`: Packages that are missed in dependencies (due to `NO_*` option or build script logic) are searched for among the host system packages using `pkg-config`. It allows building with packages of other (newer) versions diff --git a/depends/config.site.in b/depends/config.site.in index f4531830c8..391767357a 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -8,70 +8,74 @@ true # Dummy command because shellcheck treats all directives before first # See: https://github.com/koalaman/shellcheck/wiki/Directive # shellcheck disable=SC2154 -depends_prefix="$(cd "$(dirname ${ac_site_file})/.." && pwd)" +depends_prefix="$(cd "$(dirname "$ac_site_file")/.." && pwd)" cross_compiling=maybe -host_alias=@HOST@ -ac_tool_prefix=${host_alias}- +host_alias="@HOST@" +ac_tool_prefix="${host_alias}-" -if test -z $with_boost; then - with_boost=$depends_prefix +if test -z "$with_boost"; then + with_boost="$depends_prefix" fi -if test -z $with_qt_plugindir; then - with_qt_plugindir=$depends_prefix/plugins +if test -z "$with_qt_plugindir"; then + with_qt_plugindir="${depends_prefix}/plugins" fi -if test -z $with_qt_translationdir; then - with_qt_translationdir=$depends_prefix/translations +if test -z "$with_qt_translationdir"; then + with_qt_translationdir="${depends_prefix}/translations" fi -if test -z $with_qt_bindir && test -z "@no_qt@"; then - with_qt_bindir=$depends_prefix/native/bin +if test -z "$with_qt_bindir" && test -z "@no_qt@"; then + with_qt_bindir="${depends_prefix}/native/bin" fi -if test -z $with_mpgen && test -n "@multiprocess@"; then - with_mpgen=$depends_prefix/native +if test -z "$with_mpgen" && test -n "@multiprocess@"; then + with_mpgen="${depends_prefix}/native" fi -if test -z $with_qrencode && test -n "@no_qr@"; then +if test -z "$with_qrencode" && test -n "@no_qr@"; then with_qrencode=no fi -if test -z $enable_wallet && test -n "@no_wallet@"; then +if test -z "$enable_wallet" && test -n "@no_wallet@"; then enable_wallet=no fi -if test -z $enable_multiprocess && test -n "@multiprocess@"; then +if test -z "$enable_multiprocess" && test -n "@multiprocess@"; then enable_multiprocess=yes fi -if test -z $with_miniupnpc && test -n "@no_upnp@"; then +if test -z "$with_miniupnpc" && test -n "@no_upnp@"; then with_miniupnpc=no fi -if test -z $with_gui && test -n "@no_qt@"; then +if test -z "$with_natpmp" && test -n "@no_natpmp@"; then + with_natpmp=no +fi + +if test -z "$with_gui" && test -n "@no_qt@"; then with_gui=no fi -if test -z $enable_zmq && test -n "@no_zmq@"; then +if test -z "$enable_zmq" && test -n "@no_zmq@"; then enable_zmq=no fi -if test x@host_os@ = xdarwin; then +if test "x@host_os@" = xdarwin; then BREW=no PORT=no fi -PATH=$depends_prefix/native/bin:$PATH +PATH="${depends_prefix}/native/bin:${PATH}" PKG_CONFIG="$(which pkg-config) --static" # These two need to remain exported because pkg-config does not see them # otherwise. That means they must be unexported at the end of configure.ac to # avoid ruining the cache. Sigh. -export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig +export PKG_CONFIG_PATH="${depends_prefix}/share/pkgconfig:${depends_prefix}/lib/pkgconfig" if test -z "@allow_host_packages@"; then - export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig + export PKG_CONFIG_LIBDIR="${depends_prefix}/lib/pkgconfig" fi -CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" -LDFLAGS="-L$depends_prefix/lib $LDFLAGS" +CPPFLAGS="-I${depends_prefix}/include/ ${CPPFLAGS}" +LDFLAGS="-L${depends_prefix}/lib ${LDFLAGS}" if test -n "@CC@" -a -z "${CC}"; then CC="@CC@" @@ -82,18 +86,18 @@ fi PYTHONPATH="${depends_prefix}/native/lib/python3/dist-packages${PYTHONPATH:+${PATH_SEPARATOR}}${PYTHONPATH}" if test -n "@AR@"; then - AR=@AR@ - ac_cv_path_ac_pt_AR=${AR} + AR="@AR@" + ac_cv_path_ac_pt_AR="${AR}" fi if test -n "@RANLIB@"; then - RANLIB=@RANLIB@ - ac_cv_path_ac_pt_RANLIB=${RANLIB} + RANLIB="@RANLIB@" + ac_cv_path_ac_pt_RANLIB="${RANLIB}" fi if test -n "@NM@"; then - NM=@NM@ - ac_cv_path_ac_pt_NM=${NM} + NM="@NM@" + ac_cv_path_ac_pt_NM="${NM}" fi if test -n "@debug@"; then @@ -101,14 +105,14 @@ if test -n "@debug@"; then fi if test -n "@CFLAGS@"; then - CFLAGS="@CFLAGS@ $CFLAGS" + CFLAGS="@CFLAGS@ ${CFLAGS}" fi if test -n "@CXXFLAGS@"; then - CXXFLAGS="@CXXFLAGS@ $CXXFLAGS" + CXXFLAGS="@CXXFLAGS@ ${CXXFLAGS}" fi if test -n "@CPPFLAGS@"; then - CPPFLAGS="@CPPFLAGS@ $CPPFLAGS" + CPPFLAGS="@CPPFLAGS@ ${CPPFLAGS}" fi if test -n "@LDFLAGS@"; then - LDFLAGS="@LDFLAGS@ $LDFLAGS" + LDFLAGS="@LDFLAGS@ ${LDFLAGS}" fi diff --git a/depends/funcs.mk b/depends/funcs.mk index 5697bd6f15..c0159f0e38 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -1,17 +1,23 @@ define int_vars #Set defaults for vars which may be overridden per-package -$(1)_cc=$($($(1)_type)_CC) -$(1)_cxx=$($($(1)_type)_CXX) -$(1)_objc=$($($(1)_type)_OBJC) -$(1)_objcxx=$($($(1)_type)_OBJCXX) -$(1)_ar=$($($(1)_type)_AR) -$(1)_ranlib=$($($(1)_type)_RANLIB) -$(1)_libtool=$($($(1)_type)_LIBTOOL) -$(1)_nm=$($($(1)_type)_NM) -$(1)_cflags=$($($(1)_type)_CFLAGS) $($($(1)_type)_$(release_type)_CFLAGS) -$(1)_cxxflags=$($($(1)_type)_CXXFLAGS) $($($(1)_type)_$(release_type)_CXXFLAGS) -$(1)_ldflags=$($($(1)_type)_LDFLAGS) $($($(1)_type)_$(release_type)_LDFLAGS) -L$($($(1)_type)_prefix)/lib -$(1)_cppflags=$($($(1)_type)_CPPFLAGS) $($($(1)_type)_$(release_type)_CPPFLAGS) -I$($($(1)_type)_prefix)/include +$(1)_cc=$$($$($(1)_type)_CC) +$(1)_cxx=$$($$($(1)_type)_CXX) +$(1)_objc=$$($$($(1)_type)_OBJC) +$(1)_objcxx=$$($$($(1)_type)_OBJCXX) +$(1)_ar=$$($$($(1)_type)_AR) +$(1)_ranlib=$$($$($(1)_type)_RANLIB) +$(1)_libtool=$$($$($(1)_type)_LIBTOOL) +$(1)_nm=$$($$($(1)_type)_NM) +$(1)_cflags=$$($$($(1)_type)_CFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CFLAGS) +$(1)_cxxflags=$$($$($(1)_type)_CXXFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CXXFLAGS) +$(1)_ldflags=$$($$($(1)_type)_LDFLAGS) \ + $$($$($(1)_type)_$$(release_type)_LDFLAGS) \ + -L$$($($(1)_type)_prefix)/lib +$(1)_cppflags=$$($$($(1)_type)_CPPFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CPPFLAGS) \ + -I$$($$($(1)_type)_prefix)/include $(1)_recipe_hash:= endef diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index e9faeba336..646e97837a 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -6,6 +6,49 @@ LD64_VERSION=530 OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with-libcxx-headers +darwin_native_binutils=native_cctools + +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +# FORCE_USE_SYSTEM_CLANG is empty, so we use our depends-managed, pinned clang +# from llvm.org + +# The native_cctools package is what provides clang when FORCE_USE_SYSTEM_CLANG +# is empty +darwin_native_toolchain=native_cctools + +clang_prog=$(build_prefix)/bin/clang +clangxx_prog=$(clang_prog)++ + +clang_resource_dir=$(build_prefix)/lib/clang/$(native_cctools_clang_version) +else +# FORCE_USE_SYSTEM_CLANG is non-empty, so we use the clang from the user's +# system + +darwin_native_toolchain= + +# We can't just use $(shell command -v clang) because GNU Make handles builtins +# in a special way and doesn't know that `command` is a POSIX-standard builtin +# prior to 1af314465e5dfe3e8baa839a32a72e83c04f26ef, first released in v4.2.90. +# At the time of writing, GNU Make v4.2.1 is still being used in supported +# distro releases. +# +# Source: https://lists.gnu.org/archive/html/bug-make/2017-11/msg00017.html +clang_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v clang") +clangxx_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v clang++") + +clang_resource_dir=$(shell clang -print-resource-dir) +endif + +cctools_TOOLS=AR RANLIB STRIP NM LIBTOOL OTOOL INSTALL_NAME_TOOL + +# Make-only lowercase function +lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1)))))))))))))))))))))))))) + +# For well-known tools provided by cctools, make sure that their well-known +# variable is set to the full path of the tool, just like how AC_PATH_{TOO,PROG} +# would. +$(foreach TOOL,$(cctools_TOOLS),$(eval darwin_$(TOOL) = $$(build_prefix)/bin/$$(host)-$(call lc,$(TOOL)))) + # Flag explanations: # # -mlinker-version @@ -18,7 +61,7 @@ OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with- # Explicitly point to our binaries (e.g. cctools) so that they are # ensured to be found and preferred over other possibilities. # -# -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 +# -stdlib=libc++ -nostdinc++ -Xclang -cxx-isystem$(OSX_SDK)/usr/include/c++/v1 # # Forces clang to use the libc++ headers from our SDK and completely # forget about the libc++ headers from the standard directories @@ -28,8 +71,49 @@ OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with- # https://reviews.llvm.org/D64089, we should use that instead. Read the # differential summary there for more details. # -darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin -darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -stdlib=libc++ -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 +# -Xclang -*system<path_a> \ +# -Xclang -*system<path_b> \ +# -Xclang -*system<path_c> ... +# +# Adds path_a, path_b, and path_c to the bottom of clang's list of +# include search paths. This is used to explicitly specify the list of +# system include search paths and its ordering, rather than rely on +# clang's autodetection routine. This routine has been shown to: +# 1. Fail to pickup libc++ headers in $SYSROOT/usr/include/c++/v1 +# when clang was built manually (see: https://github.com/bitcoin/bitcoin/pull/17919#issuecomment-656785034) +# 2. Fail to pickup C headers in $SYSROOT/usr/include when +# C_INCLUDE_DIRS was specified at configure time (see: https://gist.github.com/dongcarl/5cdc6990b7599e8a5bf6d2a9c70e82f9) +# +# Talking directly to cc1 with -Xclang here grants us access to specify +# more granular categories for these system include search paths, and we +# can use the correct categories that these search paths would have been +# placed in if the autodetection routine had worked correctly. (see: +# https://gist.github.com/dongcarl/5cdc6990b7599e8a5bf6d2a9c70e82f9#the-treatment) +# +# Furthermore, it places these search paths after any "non-Xclang" +# specified search paths. This prevents any additional clang options or +# environment variables from coming after or in between these system +# include search paths, as that would be wrong in general but would also +# break #include_next's. +# +darwin_CC=env -u C_INCLUDE_PATH -u CPLUS_INCLUDE_PATH \ + -u OBJC_INCLUDE_PATH -u OBJCPLUS_INCLUDE_PATH -u CPATH \ + -u LIBRARY_PATH \ + $(clang_prog) --target=$(host) -mmacosx-version-min=$(OSX_MIN_VERSION) \ + -B$(build_prefix)/bin -mlinker-version=$(LD64_VERSION) \ + --sysroot=$(OSX_SDK) \ + -Xclang -internal-externc-isystem$(clang_resource_dir)/include \ + -Xclang -internal-externc-isystem$(OSX_SDK)/usr/include +darwin_CXX=env -u C_INCLUDE_PATH -u CPLUS_INCLUDE_PATH \ + -u OBJC_INCLUDE_PATH -u OBJCPLUS_INCLUDE_PATH -u CPATH \ + -u LIBRARY_PATH \ + $(clangxx_prog) --target=$(host) -mmacosx-version-min=$(OSX_MIN_VERSION) \ + -B$(build_prefix)/bin -mlinker-version=$(LD64_VERSION) \ + --sysroot=$(OSX_SDK) \ + -stdlib=libc++ -nostdinc++ \ + -Xclang -cxx-isystem$(OSX_SDK)/usr/include/c++/v1 \ + -Xclang -internal-externc-isystem$(clang_resource_dir)/include \ + -Xclang -internal-externc-isystem$(OSX_SDK)/usr/include darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) @@ -40,11 +124,4 @@ darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) darwin_debug_CFLAGS=-O1 darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) -darwin_native_binutils=native_cctools -ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) -darwin_native_toolchain=native_cctools -else -darwin_native_toolchain= -endif - darwin_cmake_system=Darwin diff --git a/depends/packages/libnatpmp.mk b/depends/packages/libnatpmp.mk new file mode 100644 index 0000000000..a24f201859 --- /dev/null +++ b/depends/packages/libnatpmp.mk @@ -0,0 +1,19 @@ +package=libnatpmp +$(package)_version=20150609 +$(package)_download_path=https://miniupnp.tuxfamily.org/files/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=e1aa9c4c4219bc06943d6b2130f664daee213fb262fcb94dd355815b8f4536b0 + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" +endef + +define $(package)_build_cmds + $(MAKE) libnatpmp.a $($(package)_build_opts) +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_prefix_dir)/include $($(package)_staging_prefix_dir)/lib &&\ + install *.h $($(package)_staging_prefix_dir)/include &&\ + install libnatpmp.a $($(package)_staging_prefix_dir)/lib +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index d4fd23a47b..0f35ca0d2d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -16,6 +16,7 @@ sqlite_packages=sqlite zmq_packages=zeromq upnp_packages=miniupnpc +natpmp_packages=libnatpmp multiprocess_packages = libmultiprocess capnp multiprocess_native_packages = native_libmultiprocess native_capnp diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md index c66e79af71..12807bfb86 100644 --- a/doc/JSON-RPC-interface.md +++ b/doc/JSON-RPC-interface.md @@ -88,13 +88,14 @@ RPC interface will be abused. - **Secure string handling:** The RPC interface does not guarantee any escaping of data beyond what's necessary to encode it as JSON, although it does usually provide serialized data using a hex - representation of the bytes. If you use RPC data in your programs or - provide its data to other programs, you must ensure any problem - strings are properly escaped. For example, multiple websites have - been manipulated because they displayed decoded hex strings that - included HTML `<script>` tags. For this reason, and other - non-security reasons, it is recommended to display all serialized data - in hex form only. + representation of the bytes. If you use RPC data in your programs or + provide its data to other programs, you must ensure any problem strings + are properly escaped. For example, the `createwallet` RPC accepts + arguments such as `wallet_name` which is a string and could be used + for a path traversal attack without application level checks. Multiple + websites have been manipulated because they displayed decoded hex strings + that included HTML `<script>` tags. For this reason, and others, it is + recommended to display all serialized data in hex form only. ## RPC consistency guarantees diff --git a/doc/build-osx.md b/doc/build-osx.md index c1d101fde1..04ee43f81d 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh). ## Dependencies ```shell -brew install automake libtool boost miniupnpc pkg-config python qt libevent qrencode +brew install automake libtool boost miniupnpc libnatpmp pkg-config python qt libevent qrencode ``` If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting). @@ -30,6 +30,11 @@ If you want to build the disk image with `make deploy` (.dmg / optional), you ne brew install librsvg ``` +and [`macdeployqtplus`](../contrib/macdeploy/README.md) dependencies: +```shell +pip3 install ds_store mac_alias +``` + The wallet support requires one or both of the dependencies ([*SQLite*](#sqlite) and [*Berkeley DB*](#berkeley-db)) in the sections below. To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode). diff --git a/doc/build-unix.md b/doc/build-unix.md index cfe3328b45..5c24886dbf 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -41,6 +41,7 @@ Optional dependencies: Library | Purpose | Description ------------|------------------|---------------------- miniupnpc | UPnP Support | Firewall-jumping support + libnatpmp | NAT-PMP Support | Firewall-jumping support libdb4.8 | Berkeley DB | Optional, wallet storage (only needed when wallet enabled) qt | GUI | GUI toolkit (only needed when GUI enabled) libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) @@ -99,9 +100,9 @@ SQLite is required for the wallet: To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode) -Optional (see `--with-miniupnpc` and `--enable-upnp-default`): +Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`): - sudo apt-get install libminiupnpc-dev + sudo apt install libminiupnpc-dev libnatpmp-dev ZMQ dependencies (provides ZMQ API): @@ -133,9 +134,9 @@ Build requirements: sudo dnf install gcc-c++ libtool make autoconf automake libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 -Optional (see `--with-miniupnpc` and `--enable-upnp-default`): +Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`): - sudo dnf install miniupnpc-devel + sudo dnf install miniupnpc-devel libnatpmp-devel ZMQ dependencies (provides ZMQ API): @@ -158,18 +159,27 @@ Notes The release is built with GCC and then "strip bitcoind" to strip the debug symbols, which reduces the executable size by about 90%. - miniupnpc --------- [miniupnpc](https://miniupnp.tuxfamily.org) may be used for UPnP port mapping. It can be downloaded from [here]( https://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and -turned off by default. See the configure options for upnp behavior desired: +turned off by default. See the configure options for UPnP behavior desired: - --without-miniupnpc No UPnP support miniupnp not required + --without-miniupnpc No UPnP support, miniupnp not required --disable-upnp-default (the default) UPnP support turned off by default at runtime --enable-upnp-default UPnP support turned on by default at runtime +libnatpmp +--------- + +[libnatpmp](https://miniupnp.tuxfamily.org/libnatpmp.html) may be used for NAT-PMP port mapping. It can be downloaded +from [here](https://miniupnp.tuxfamily.org/files/). NAT-PMP support is compiled in and +turned off by default. See the configure options for NAT-PMP behavior desired: + + --without-natpmp No NAT-PMP support, libnatpmp not required + --disable-natpmp-default (the default) NAT-PMP support turned off by default at runtime + --enable-natpmp-default NAT-PMP support turned on by default at runtime Berkeley DB ----------- diff --git a/doc/dependencies.md b/doc/dependencies.md index 76e8910871..6b2b904632 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -14,6 +14,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | GCC | | [7+](https://gcc.gnu.org/) (C++17 support) | | | | | HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | libevent | [2.1.11-stable](https://github.com/libevent/libevent/releases) | [2.0.21](https://github.com/bitcoin/bitcoin/pull/18676) | No | | | +| libnatpmp | [20150609](https://miniupnp.tuxfamily.org/files) | | No | | | | libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | librsvg | | | | | | | MiniUPnPc | [2.0.20180203](https://miniupnp.tuxfamily.org/files) | | No | | | @@ -32,7 +33,8 @@ Controlling dependencies Some dependencies are not needed in all configurations. The following are some factors that affect the dependency list. #### Options passed to `./configure` -* MiniUPnPc is not needed with `--with-miniupnpc=no`. +* MiniUPnPc is not needed with `--without-miniupnpc`. +* libnatpmp is not needed with `--without-natpmp`. * Berkeley DB is not needed with `--disable-wallet` or `--without-bdb`. * SQLite is not needed with `--disable-wallet` or `--without-sqlite`. * Qt is not needed with `--without-gui`. diff --git a/doc/productivity.md b/doc/productivity.md index 555f0afe3c..a01c6f545d 100644 --- a/doc/productivity.md +++ b/doc/productivity.md @@ -51,6 +51,7 @@ After running `./autogen.sh`, which generates the `./configure` file, use `./con ```sh --without-miniupnpc +--without-natpmp --disable-bench --disable-wallet --without-gui diff --git a/doc/release-notes-18077.md b/doc/release-notes-18077.md new file mode 100644 index 0000000000..4034a4c19c --- /dev/null +++ b/doc/release-notes-18077.md @@ -0,0 +1,10 @@ +P2P and network changes +----------------------- + +- Added NAT-PMP port mapping support via [`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html) + +Command-line options +-------------------- + +- The `-natpmp` option has been added to use NAT-PMP to map the listening port. If both UPnP +and NAT-PMP are enabled, a successful allocation from UPnP prevails over one from NAT-PMP. diff --git a/doc/tor.md b/doc/tor.md index 86e5d9ddf3..1ba7137b8e 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -99,7 +99,7 @@ as well, use `discover` instead: ./bitcoind ... -discover -and open port 8333 on your firewall (or use -upnp). +and open port 8333 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`). If you only want to use Tor to reach .onion addresses, but not use it as a proxy for normal IPv4/IPv6 communication, use: diff --git a/src/Makefile.am b/src/Makefile.am index 02797b19bc..1a0791dccd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,6 +152,7 @@ BITCOIN_CORE_H = \ key_io.h \ logging.h \ logging/timer.h \ + mapport.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -299,6 +300,7 @@ libbitcoin_server_a_SOURCES = \ index/blockfilterindex.cpp \ index/txindex.cpp \ init.cpp \ + mapport.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ @@ -598,7 +600,7 @@ bitcoin_bin_ldadd = \ $(LIBMEMENV) \ $(LIBSECP256K1) -bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) +bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) bitcoind_SOURCES = $(bitcoin_daemon_sources) bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index beb3f8dfd2..56b8ca8ce6 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -74,7 +74,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp endif -bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f46310a603..3d41d203d3 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -320,7 +320,7 @@ if ENABLE_ZMQ bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ - $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS) bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index c05dd38737..a6a857d952 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -55,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ - $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS) qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index bdaddb4f0d..e9f9b73abe 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -167,7 +167,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) +test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static if ENABLE_ZMQ diff --git a/src/flatfile.cpp b/src/flatfile.cpp index 8a8f7b681c..11cf357f3d 100644 --- a/src/flatfile.cpp +++ b/src/flatfile.cpp @@ -92,6 +92,7 @@ bool FlatFileSeq::Flush(const FlatFilePos& pos, bool finalize) fclose(file); return error("%s: failed to commit file %d", __func__, pos.nFile); } + DirectoryCommit(m_dir); fclose(file); return true; diff --git a/src/init.cpp b/src/init.cpp index 42e9925f98..9a63fe0e03 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,6 +26,7 @@ #include <interfaces/chain.h> #include <interfaces/node.h> #include <key.h> +#include <mapport.h> #include <miner.h> #include <net.h> #include <net_permissions.h> @@ -468,6 +469,11 @@ void SetupServerArgs(NodeContext& node) #else hidden_args.emplace_back("-upnp"); #endif +#ifdef USE_NATPMP + argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION); +#else + hidden_args.emplace_back("-natpmp"); +#endif // USE_NATPMP argsman.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. " "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". " "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -812,10 +818,13 @@ void InitParameterInteraction(ArgsManager& args) // to protect privacy, do not listen by default if a default proxy server is specified if (args.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); - // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to protect privacy, do not map ports when a proxy is set. The user may still specify -listen=1 // to listen locally, so don't rely on this happening through -listen below. if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__); + } // to protect privacy, do not discover addresses by default if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); @@ -825,6 +834,9 @@ void InitParameterInteraction(ArgsManager& args) // do not map ports or try to retrieve public IP when not listening (pointless) if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__); + } if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); if (args.SoftSetBoolArg("-listenonion", false)) @@ -1898,10 +1910,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA Discover(); - // Map ports with UPnP - if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) { - StartMapPort(); - } + // Map ports with UPnP or NAT-PMP. + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 97549aeb0e..15f7ef6256 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -82,7 +82,7 @@ public: virtual bool shutdownRequested() = 0; //! Map port. - virtual void mapPort(bool use_upnp) = 0; + virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; //! Get proxy. virtual bool getProxy(Network net, proxyType& proxy_info) = 0; diff --git a/src/key_io.cpp b/src/key_io.cpp index d2f5be93f5..a270ede864 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -8,16 +8,12 @@ #include <bech32.h> #include <util/strencodings.h> -#include <boost/variant/apply_visitor.hpp> -#include <boost/variant/static_visitor.hpp> - +#include <algorithm> #include <assert.h> #include <string.h> -#include <algorithm> -namespace -{ -class DestinationEncoder : public boost::static_visitor<std::string> +namespace { +class DestinationEncoder { private: const CChainParams& m_params; @@ -209,7 +205,7 @@ std::string EncodeExtKey(const CExtKey& key) std::string EncodeDestination(const CTxDestination& dest) { - return boost::apply_visitor(DestinationEncoder(Params()), dest); + return std::visit(DestinationEncoder(Params()), dest); } CTxDestination DecodeDestination(const std::string& str) diff --git a/src/mapport.cpp b/src/mapport.cpp new file mode 100644 index 0000000000..2df4ce45d2 --- /dev/null +++ b/src/mapport.cpp @@ -0,0 +1,336 @@ +// Copyright (c) 2011-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 <mapport.h> + +#include <clientversion.h> +#include <logging.h> +#include <net.h> +#include <netaddress.h> +#include <netbase.h> +#include <threadinterrupt.h> +#include <util/system.h> + +#ifdef USE_NATPMP +#include <compat.h> +#include <natpmp.h> +#endif // USE_NATPMP + +#ifdef USE_UPNP +#include <miniupnpc/miniupnpc.h> +#include <miniupnpc/upnpcommands.h> +#include <miniupnpc/upnperrors.h> +// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility +// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. +static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); +#endif // USE_UPNP + +#include <atomic> +#include <cassert> +#include <chrono> +#include <functional> +#include <string> +#include <thread> + +#if defined(USE_NATPMP) || defined(USE_UPNP) +static CThreadInterrupt g_mapport_interrupt; +static std::thread g_mapport_thread; +static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE}; +static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE}; + +using namespace std::chrono_literals; +static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; +static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; + +#ifdef USE_NATPMP +static uint16_t g_mapport_external_port = 0; +static bool NatpmpInit(natpmp_t* natpmp) +{ + const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0); + if (r_init == 0) return true; + LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init); + return false; +} + +static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr) +{ + const int r_send = sendpublicaddressrequest(natpmp); + if (r_send == 2 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + external_ipv4_addr = response.pnu.publicaddress.addr; + return true; + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered) +{ + const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port; + const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/); + if (r_send == 12 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + auto pm = response.pnu.newportmapping; + if (private_port == pm.privateport && pm.lifetime > 0) { + g_mapport_external_port = pm.mappedpublicport; + const CService external{external_ipv4_addr, pm.mappedpublicport}; + if (!external_ip_discovered && fDiscover) { + AddLocal(external, LOCAL_MAPPED); + external_ip_discovered = true; + } + LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString()); + return true; + } else { + LogPrintf("natpmp: Port mapping failed.\n"); + } + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool ProcessNatpmp() +{ + bool ret = false; + natpmp_t natpmp; + struct in_addr external_ipv4_addr; + if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) { + bool external_ip_discovered = false; + const uint16_t private_port = GetListenPort(); + do { + ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered); + } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); + + const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0); + g_mapport_external_port = 0; + if (r_send == 12 /* OK */) { + LogPrintf("natpmp: Port mapping removed successfully.\n"); + } else { + LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send); + } + } + + closenatpmp(&natpmp); + return ret; +} +#endif // USE_NATPMP + +#ifdef USE_UPNP +static bool ProcessUpnp() +{ + bool ret = false; + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = nullptr; + const char * minissdpdpath = nullptr; + struct UPNPDev * devlist = nullptr; + char lanaddr[64]; + + int error = 0; +#if MINIUPNPC_API_VERSION < 14 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#else + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if (r != UPNPCOMMAND_SUCCESS) { + LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); + } else { + if (externalIPAddress[0]) { + CNetAddr resolved; + if (LookupHost(externalIPAddress, resolved, false)) { + LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); + AddLocal(resolved, LOCAL_MAPPED); + } + } else { + LogPrintf("UPnP: GetExternalIPAddress failed.\n"); + } + } + } + + std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); + + do { + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + + if (r != UPNPCOMMAND_SUCCESS) { + ret = false; + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); + break; + } else { + ret = true; + LogPrintf("UPnP Port Mapping successful.\n"); + } + } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); devlist = nullptr; + FreeUPNPUrls(&urls); + } else { + LogPrintf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = nullptr; + if (r != 0) + FreeUPNPUrls(&urls); + } + + return ret; +} +#endif // USE_UPNP + +static void ThreadMapPort() +{ + bool ok; + do { + ok = false; + +#ifdef USE_UPNP + // High priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) { + g_mapport_current_proto = MapPortProtoFlag::UPNP; + ok = ProcessUpnp(); + if (ok) continue; + } +#endif // USE_UPNP + +#ifdef USE_NATPMP + // Low priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) { + g_mapport_current_proto = MapPortProtoFlag::NAT_PMP; + ok = ProcessNatpmp(); + if (ok) continue; + } +#endif // USE_NATPMP + + g_mapport_current_proto = MapPortProtoFlag::NONE; + if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); +} + +void StartThreadMapPort() +{ + if (!g_mapport_thread.joinable()) { + assert(!g_mapport_interrupt); + g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort)); + } +} + +static void DispatchMapPort() +{ + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) { + StartThreadMapPort(); + return; + } + + if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + InterruptMapPort(); + StopMapPort(); + return; + } + + if (g_mapport_enabled_protos & g_mapport_current_proto) { + // Enabling another protocol does not cause switching from the currently used one. + return; + } + + assert(g_mapport_thread.joinable()); + assert(!g_mapport_interrupt); + // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp() + // to force trying the next protocol in the ThreadMapPort() loop. + g_mapport_interrupt(); +} + +static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) +{ + if (enabled) { + g_mapport_enabled_protos |= proto; + } else { + g_mapport_enabled_protos &= ~proto; + } +} + +void StartMapPort(bool use_upnp, bool use_natpmp) +{ + MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp); + MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp); + DispatchMapPort(); +} + +void InterruptMapPort() +{ + g_mapport_enabled_protos = MapPortProtoFlag::NONE; + if (g_mapport_thread.joinable()) { + g_mapport_interrupt(); + } +} + +void StopMapPort() +{ + if (g_mapport_thread.joinable()) { + g_mapport_thread.join(); + g_mapport_interrupt.reset(); + } +} + +#else // #if defined(USE_NATPMP) || defined(USE_UPNP) +void StartMapPort(bool use_upnp, bool use_natpmp) +{ + // Intentionally left blank. +} +void InterruptMapPort() +{ + // Intentionally left blank. +} +void StopMapPort() +{ + // Intentionally left blank. +} +#endif // #if defined(USE_NATPMP) || defined(USE_UPNP) diff --git a/src/mapport.h b/src/mapport.h new file mode 100644 index 0000000000..279d65167f --- /dev/null +++ b/src/mapport.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-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_MAPPORT_H +#define BITCOIN_MAPPORT_H + +#ifdef USE_UPNP +static constexpr bool DEFAULT_UPNP = USE_UPNP; +#else +static constexpr bool DEFAULT_UPNP = false; +#endif // USE_UPNP + +#ifdef USE_NATPMP +static constexpr bool DEFAULT_NATPMP = USE_NATPMP; +#else +static constexpr bool DEFAULT_NATPMP = false; +#endif // USE_NATPMP + +enum MapPortProtoFlag : unsigned int { + NONE = 0x00, + UPNP = 0x01, + NAT_PMP = 0x02, +}; + +void StartMapPort(bool use_upnp, bool use_natpmp); +void InterruptMapPort(); +void StopMapPort(); + +#endif // BITCOIN_MAPPORT_H diff --git a/src/net.cpp b/src/net.cpp index 6050e1354c..4f74bbede4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -33,15 +33,6 @@ #include <poll.h> #endif -#ifdef USE_UPNP -#include <miniupnpc/miniupnpc.h> -#include <miniupnpc/upnpcommands.h> -#include <miniupnpc/upnperrors.h> -// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility -// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. -static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); -#endif - #include <algorithm> #include <cstdint> #include <unordered_map> @@ -507,9 +498,9 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet } } -std::string CNode::ConnectionTypeAsString() const +std::string ConnectionTypeAsString(ConnectionType conn_type) { - switch (m_conn_type) { + switch (conn_type) { case ConnectionType::INBOUND: return "inbound"; case ConnectionType::MANUAL: @@ -601,7 +592,6 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) } X(m_permissionFlags); if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_feeFilter); stats.minFeeFilter = m_tx_relay->minFeeFilter; } else { stats.minFeeFilter = 0; @@ -627,7 +617,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; - stats.m_conn_type_string = ConnectionTypeAsString(); + X(m_conn_type); } #undef X @@ -1560,121 +1550,6 @@ void CConnman::WakeMessageHandler() condMsgProc.notify_one(); } - - - - - -#ifdef USE_UPNP -static CThreadInterrupt g_upnp_interrupt; -static std::thread g_upnp_thread; -static void ThreadMapPort() -{ - std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = nullptr; - const char * minissdpdpath = nullptr; - struct UPNPDev * devlist = nullptr; - char lanaddr[64]; - - int error = 0; -#if MINIUPNPC_API_VERSION < 14 - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#else - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); -#endif - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - if (fDiscover) { - char externalIPAddress[40]; - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); - } else { - if (externalIPAddress[0]) { - CNetAddr resolved; - if (LookupHost(externalIPAddress, resolved, false)) { - LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); - AddLocal(resolved, LOCAL_UPNP); - } - } else { - LogPrintf("UPnP: GetExternalIPAddress failed.\n"); - } - } - } - - std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); - - do { - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); - - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); - } else { - LogPrintf("UPnP Port Mapping successful.\n"); - } - } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); - - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = nullptr; - FreeUPNPUrls(&urls); - } else { - LogPrintf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = nullptr; - if (r != 0) - FreeUPNPUrls(&urls); - } -} - -void StartMapPort() -{ - if (!g_upnp_thread.joinable()) { - assert(!g_upnp_interrupt); - g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); - } -} - -void InterruptMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_interrupt(); - } -} - -void StopMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_thread.join(); - g_upnp_interrupt.reset(); - } -} - -#else -void StartMapPort() -{ - // Intentionally left blank. -} -void InterruptMapPort() -{ - // Intentionally left blank. -} -void StopMapPort() -{ - // Intentionally left blank. -} -#endif - - - - - - void CConnman::ThreadDNSAddressSeed() { FastRandomContext rng; @@ -67,12 +67,6 @@ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2; static const int MAX_FEELER_CONNECTIONS = 1; /** -listen default */ static const bool DEFAULT_LISTEN = true; -/** -upnp default */ -#ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; -#else -static const bool DEFAULT_UPNP = false; -#endif /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ @@ -180,10 +174,9 @@ enum class ConnectionType { ADDR_FETCH, }; +/** Convert ConnectionType enum to a string value */ +std::string ConnectionTypeAsString(ConnectionType conn_type); void Discover(); -void StartMapPort(); -void InterruptMapPort(); -void StopMapPort(); uint16_t GetListenPort(); enum @@ -191,7 +184,7 @@ enum LOCAL_NONE, // unknown LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to - LOCAL_UPNP, // address reported by UPnP + LOCAL_MAPPED, // address reported by UPnP or NAT-PMP LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX @@ -273,11 +266,10 @@ public: // Network the peer connected through Network m_network; uint32_t m_mapped_as; - std::string m_conn_type_string; + ConnectionType m_conn_type; }; - /** Transport protocol agnostic message container. * Ideally it should only contain receive time, payload, * command and size. @@ -402,7 +394,7 @@ public: std::unique_ptr<TransportDeserializer> m_deserializer; std::unique_ptr<TransportSerializer> m_serializer; - // socket + NetPermissionFlags m_permissionFlags{PF_NONE}; std::atomic<ServiceFlags> nServices{NODE_NONE}; SOCKET hSocket GUARDED_BY(cs_hSocket); /** Total size of all vSendMsg entries */ @@ -536,11 +528,6 @@ public: */ Network ConnectedThroughNetwork() const; -protected: - mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); - mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); - -public: // We selected peer as (compact blocks) high-bandwidth peer (BIP152) std::atomic<bool> m_bip152_highbandwidth_to{false}; // Peer selected us as (compact blocks) high-bandwidth peer (BIP152) @@ -573,9 +560,8 @@ public: std::atomic<std::chrono::seconds> m_last_mempool_req{0s}; std::chrono::microseconds nNextInvSend{0}; - RecursiveMutex cs_feeFilter; - // Minimum fee rate with which to filter inv's to this node - CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0}; + /** Minimum fee rate with which to filter inv's to this node */ + std::atomic<CAmount> minFeeFilter{0}; CAmount lastSentFeeFilter{0}; int64_t nextSendTimeFeeFilter{0}; }; @@ -613,44 +599,6 @@ public: CNode(const CNode&) = delete; CNode& operator=(const CNode&) = delete; -private: - const NodeId id; - const uint64_t nLocalHostNonce; - const ConnectionType m_conn_type; - std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION}; - - //! Services offered to this peer. - //! - //! This is supplied by the parent CConnman during peer connection - //! (CConnman::ConnectNode()) from its attribute of the same name. - //! - //! This is const because there is no protocol defined for renegotiating - //! services initially offered to a peer. The set of local services we - //! offer should not change after initialization. - //! - //! An interesting example of this is NODE_NETWORK and initial block - //! download: a node which starts up from scratch doesn't have any blocks - //! to serve, but still advertises NODE_NETWORK because it will eventually - //! fulfill this role after IBD completes. P2P code is written in such a - //! way that it can gracefully handle peers who don't make good on their - //! service advertisements. - const ServiceFlags nLocalServices; - - NetPermissionFlags m_permissionFlags{ PF_NONE }; - std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread - - mutable RecursiveMutex cs_addrName; - std::string addrName GUARDED_BY(cs_addrName); - - // Our address, as reported by the peer - CService addrLocal GUARDED_BY(cs_addrLocal); - mutable RecursiveMutex cs_addrLocal; - - //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service. - const bool m_inbound_onion{false}; - -public: - NodeId GetId() const { return id; } @@ -701,8 +649,6 @@ public: nRefCount--; } - - void AddAddressKnown(const CAddress& _addr) { assert(m_addr_known); @@ -734,7 +680,6 @@ public: } } - void AddKnownTx(const uint256& hash) { if (m_tx_relay != nullptr) { @@ -765,10 +710,48 @@ public: //! Sets the addrName only if it was not previously set void MaybeSetAddrName(const std::string& addrNameIn); - std::string ConnectionTypeAsString() const; + std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); } /** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */ bool IsInboundOnion() const { return m_inbound_onion; } + +private: + const NodeId id; + const uint64_t nLocalHostNonce; + const ConnectionType m_conn_type; + std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION}; + + //! Services offered to this peer. + //! + //! This is supplied by the parent CConnman during peer connection + //! (CConnman::ConnectNode()) from its attribute of the same name. + //! + //! This is const because there is no protocol defined for renegotiating + //! services initially offered to a peer. The set of local services we + //! offer should not change after initialization. + //! + //! An interesting example of this is NODE_NETWORK and initial block + //! download: a node which starts up from scratch doesn't have any blocks + //! to serve, but still advertises NODE_NETWORK because it will eventually + //! fulfill this role after IBD completes. P2P code is written in such a + //! way that it can gracefully handle peers who don't make good on their + //! service advertisements. + const ServiceFlags nLocalServices; + + std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread + + mutable RecursiveMutex cs_addrName; + std::string addrName GUARDED_BY(cs_addrName); + + // Our address, as reported by the peer + CService addrLocal GUARDED_BY(cs_addrLocal); + mutable RecursiveMutex cs_addrLocal; + + //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service. + const bool m_inbound_onion{false}; + + mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); + mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); }; /** diff --git a/src/net_processing.cpp b/src/net_processing.cpp index dc9b051ddd..8f1cb952f2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3712,7 +3712,6 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { if (pfrom.m_tx_relay != nullptr) { - LOCK(pfrom.m_tx_relay->cs_feeFilter); pfrom.m_tx_relay->minFeeFilter = newFeeFilter; } LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId()); @@ -4388,11 +4387,7 @@ bool PeerManager::SendMessages(CNode* pto) if (fSendTrickle && pto->m_tx_relay->fSendMempool) { auto vtxinfo = m_mempool.infoAll(); pto->m_tx_relay->fSendMempool = false; - CFeeRate filterrate; - { - LOCK(pto->m_tx_relay->cs_feeFilter); - filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter); - } + const CFeeRate filterrate{pto->m_tx_relay->minFeeFilter.load()}; LOCK(pto->m_tx_relay->cs_filter); @@ -4426,11 +4421,7 @@ bool PeerManager::SendMessages(CNode* pto) for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) { vInvTx.push_back(it); } - CFeeRate filterrate; - { - LOCK(pto->m_tx_relay->cs_feeFilter); - filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter); - } + const CFeeRate filterrate{pto->m_tx_relay->minFeeFilter.load()}; // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. // A heap is used so that not all items need sorting if only a few are being sent. CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, state.m_wtxid_relay); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index b1f9d32d34..85e46fd373 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -1068,15 +1068,24 @@ CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet() CSubNet::CSubNet(const CNetAddr& addr) : CSubNet() { - valid = addr.IsIPv4() || addr.IsIPv6(); - if (!valid) { + switch (addr.m_net) { + case NET_IPV4: + case NET_IPV6: + valid = true; + assert(addr.m_addr.size() <= sizeof(netmask)); + memset(netmask, 0xFF, addr.m_addr.size()); + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + valid = true; + break; + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: return; } - assert(addr.m_addr.size() <= sizeof(netmask)); - - memset(netmask, 0xFF, addr.m_addr.size()); - network = addr; } @@ -1088,6 +1097,21 @@ bool CSubNet::Match(const CNetAddr &addr) const { if (!valid || !addr.IsValid() || network.m_net != addr.m_net) return false; + + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + case NET_INTERNAL: + return addr == network; + case NET_UNROUTABLE: + case NET_MAX: + return false; + } + assert(network.m_addr.size() == addr.m_addr.size()); for (size_t x = 0; x < addr.m_addr.size(); ++x) { if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) { @@ -1099,18 +1123,35 @@ bool CSubNet::Match(const CNetAddr &addr) const std::string CSubNet::ToString() const { - assert(network.m_addr.size() <= sizeof(netmask)); + std::string suffix; - uint8_t cidr = 0; + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: { + assert(network.m_addr.size() <= sizeof(netmask)); - for (size_t i = 0; i < network.m_addr.size(); ++i) { - if (netmask[i] == 0x00) { - break; + uint8_t cidr = 0; + + for (size_t i = 0; i < network.m_addr.size(); ++i) { + if (netmask[i] == 0x00) { + break; + } + cidr += NetmaskBits(netmask[i]); } - cidr += NetmaskBits(netmask[i]); + + suffix = strprintf("/%u", cidr); + break; + } + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: + break; } - return network.ToString() + strprintf("/%u", cidr); + return network.ToString() + suffix; } bool CSubNet::IsValid() const @@ -1120,7 +1161,19 @@ bool CSubNet::IsValid() const bool CSubNet::SanityCheck() const { - if (!(network.IsIPv4() || network.IsIPv6())) return false; + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + return true; + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: + return false; + } for (size_t x = 0; x < network.m_addr.size(); ++x) { if (network.m_addr[x] & ~netmask[x]) return false; diff --git a/src/netaddress.h b/src/netaddress.h index cf878fe374..b9eade7fd5 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -462,11 +462,33 @@ class CSubNet bool SanityCheck() const; public: + /** + * Construct an invalid subnet (empty, `Match()` always returns false). + */ CSubNet(); + + /** + * Construct from a given network start and number of bits (CIDR mask). + * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is + * created. + * @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for + * IPv6 addresses. Otherwise an invalid subnet is created. + */ CSubNet(const CNetAddr& addr, uint8_t mask); + + /** + * Construct from a given network start and mask. + * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is + * created. + * @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits + * followed by 1-bits. Otherwise an invalid subnet is created. + */ CSubNet(const CNetAddr& addr, const CNetAddr& mask); - //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128) + /** + * Construct a single-host subnet. + * @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46]. + */ explicit CSubNet(const CNetAddr& addr); bool Match(const CNetAddr &addr) const; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 317a5c7cbe..e07eaa33d8 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -12,6 +12,7 @@ #include <interfaces/handler.h> #include <interfaces/node.h> #include <interfaces/wallet.h> +#include <mapport.h> #include <net.h> #include <net_processing.h> #include <netaddress.h> @@ -93,15 +94,7 @@ public: } } bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp) override - { - if (use_upnp) { - StartMapPort(); - } else { - InterruptMapPort(); - StopMapPort(); - } - } + void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { diff --git a/src/outputtype.cpp b/src/outputtype.cpp index e978852826..d96fb282c5 100644 --- a/src/outputtype.cpp +++ b/src/outputtype.cpp @@ -19,8 +19,6 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy"; static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit"; static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32"; -const std::array<OutputType, 3> OUTPUT_TYPES = {OutputType::LEGACY, OutputType::P2SH_SEGWIT, OutputType::BECH32}; - bool ParseOutputType(const std::string& type, OutputType& output_type) { if (type == OUTPUT_TYPE_STRING_LEGACY) { diff --git a/src/outputtype.h b/src/outputtype.h index 57316b92d6..88422e5824 100644 --- a/src/outputtype.h +++ b/src/outputtype.h @@ -20,7 +20,11 @@ enum class OutputType { BECH32, }; -extern const std::array<OutputType, 3> OUTPUT_TYPES; +static constexpr auto OUTPUT_TYPES = std::array{ + OutputType::LEGACY, + OutputType::P2SH_SEGWIT, + OutputType::BECH32, +}; [[nodiscard]] bool ParseOutputType(const std::string& str, OutputType& output_type); const std::string& FormatOutputType(OutputType type); diff --git a/src/policy/fees.h b/src/policy/fees.h index dd9f530c99..c444d71a31 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -11,6 +11,7 @@ #include <random.h> #include <sync.h> +#include <array> #include <map> #include <memory> #include <string> @@ -25,9 +26,15 @@ class TxConfirmStats; /* Identifier for each of the 3 different TxConfirmStats which will track * history over different time horizons. */ enum class FeeEstimateHorizon { - SHORT_HALFLIFE = 0, - MED_HALFLIFE = 1, - LONG_HALFLIFE = 2 + SHORT_HALFLIFE, + MED_HALFLIFE, + LONG_HALFLIFE, +}; + +static constexpr auto ALL_FEE_ESTIMATE_HORIZONS = std::array{ + FeeEstimateHorizon::SHORT_HALFLIFE, + FeeEstimateHorizon::MED_HALFLIFE, + FeeEstimateHorizon::LONG_HALFLIFE, }; std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 8b1308da5d..bb444f22b3 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -11,7 +11,6 @@ #include <wallet/wallet.h> #include <algorithm> -#include <typeinfo> #include <QFont> #include <QDebug> @@ -82,8 +81,9 @@ public: { for (const auto& address : wallet.getAddresses()) { - if (pk_hash_only && address.dest.type() != typeid(PKHash)) + if (pk_hash_only && !std::holds_alternative<PKHash>(address.dest)) { continue; + } AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(address.purpose), address.is_mine); cachedAddressTable.append(AddressTableEntry(addressType, @@ -261,7 +261,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } else if(index.column() == Address) { CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false - if(boost::get<CNoDestination>(&newAddress)) + if(std::get_if<CNoDestination>(&newAddress)) { editStatus = INVALID_ADDRESS; return false; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 75fa970d37..08abef7866 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -455,7 +455,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * else if(ExtractDestination(out.txout.scriptPubKey, address)) { CPubKey pubkey; - PKHash *pkhash = boost::get<PKHash>(&address); + PKHash* pkhash = std::get_if<PKHash>(&address); if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index d8112117cc..bce578a158 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1077,14 +1077,17 @@ </widget> </item> <item row="1" column="0"> - <widget class="QLabel" name="label_23"> + <widget class="QLabel" name="peerConnectionTypeLabel"> + <property name="toolTip"> + <string>The type of peer connection:<ul><li>Inbound: initiated by peer</li><li>Outbound Full Relay: default</li><li>Outbound Block Relay: does not relay transactions or addresses</li><li>Outbound Manual: added using RPC %1 or %2/%3 configuration options</li><li>Outbound Feeler: short-lived, for testing addresses</li><li>Outbound Address Fetch: short-lived, for soliciting addresses</li></ul></string> + </property> <property name="text"> - <string>Direction</string> + <string>Connection Type</string> </property> </widget> </item> <item row="1" column="1"> - <widget class="QLabel" name="peerDirection"> + <widget class="QLabel" name="peerConnectionType"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 88944a58a6..8181cc47e2 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -260,6 +260,16 @@ </widget> </item> <item> + <widget class="QCheckBox" name="mapPortNatpmp"> + <property name="toolTip"> + <string>Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</string> + </property> + <property name="text"> + <string>Map port using NA&T-PMP</string> + </property> + </widget> + </item> + <item> <widget class="QCheckBox" name="allowIncoming"> <property name="toolTip"> <string>Accept connections from outside.</string> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 8205b0506c..430ecd322f 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -764,6 +764,19 @@ QString NetworkToQString(Network net) assert(false); } +QString ConnectionTypeToQString(ConnectionType conn_type) +{ + switch (conn_type) { + case ConnectionType::INBOUND: return QObject::tr("Inbound"); + case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); + case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); + case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); + case ConnectionType::FEELER: return QObject::tr("Outbound Feeler"); + case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch"); + } // no default case, so the compiler can warn about missing cases + assert(false); +} + QString formatDurationStr(int secs) { QStringList strList; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 4bef13efb5..c471b888f7 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -7,17 +7,18 @@ #include <amount.h> #include <fs.h> +#include <net.h> #include <netaddress.h> #include <QEvent> #include <QHeaderView> #include <QItemDelegate> +#include <QLabel> #include <QMessageBox> #include <QObject> #include <QProgressBar> #include <QString> #include <QTableView> -#include <QLabel> class QValidatedLineEdit; class SendCoinsRecipient; @@ -228,6 +229,9 @@ namespace GUIUtil /** Convert enum Network to QString */ QString NetworkToQString(Network net); + /** Convert enum ConnectionType to QString */ + QString ConnectionTypeToQString(ConnectionType conn_type); + /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(int secs); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 468008693a..416b9c83c9 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -24,6 +24,7 @@ #include <QIntValidator> #include <QLocale> #include <QMessageBox> +#include <QSettings> #include <QSystemTrayIcon> #include <QTimer> @@ -50,6 +51,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #ifndef USE_UPNP ui->mapPortUpnp->setEnabled(false); #endif +#ifndef USE_NATPMP + ui->mapPortNatpmp->setEnabled(false); +#endif + connect(this, &QDialog::accepted, [this](){ + QSettings settings; + model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool()); + }); ui->proxyIp->setEnabled(false); ui->proxyPort->setEnabled(false); @@ -214,6 +222,7 @@ void OptionsDialog::setMapper() /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); + mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 152de6decb..1e0391a35c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -13,11 +13,12 @@ #include <qt/guiutil.h> #include <interfaces/node.h> -#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS +#include <mapport.h> #include <net.h> #include <netbase.h> -#include <txdb.h> // for -dbcache defaults +#include <txdb.h> // for -dbcache defaults #include <util/string.h> +#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS #include <QDebug> #include <QSettings> @@ -123,6 +124,13 @@ void OptionsModel::Init(bool resetSettings) if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); + if (!settings.contains("fUseNatpmp")) { + settings.setValue("fUseNatpmp", DEFAULT_NATPMP); + } + if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) { + addOverriddenOption("-natpmp"); + } + if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) @@ -282,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP"); #else return false; -#endif +#endif // USE_UPNP + case MapPortNatpmp: +#ifdef USE_NATPMP + return settings.value("fUseNatpmp"); +#else + return false; +#endif // USE_NATPMP case MinimizeOnClose: return fMinimizeOnClose; @@ -354,7 +368,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - node().mapPort(value.toBool()); + break; + case MapPortNatpmp: // core option - can be changed on-the-fly + settings.setValue("fUseNatpmp", value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f1929a5e06..f7171951a1 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -48,6 +48,7 @@ public: ShowTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool + MapPortNatpmp, // bool MinimizeOnClose, // bool ProxyUse, // bool ProxyIP, // QString diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index b02e4076e2..bad81d894c 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -185,6 +185,11 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const default: return QVariant(); } + } else if (role == StatsRole) { + switch (index.column()) { + case NetNodeId: return QVariant::fromValue(rec); + default: return QVariant(); + } } return QVariant(); @@ -220,11 +225,6 @@ QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent return QModelIndex(); } -const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) -{ - return priv->index(idx); -} - void PeerTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 61a0132e00..7bff239507 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -28,6 +28,7 @@ struct CNodeCombinedStats { CNodeStateStats nodeStateStats; bool fNodeStateStatsAvailable; }; +Q_DECLARE_METATYPE(CNodeCombinedStats*) class NodeLessThan { @@ -52,7 +53,6 @@ class PeerTableModel : public QAbstractTableModel public: explicit PeerTableModel(interfaces::Node& node, QObject* parent); ~PeerTableModel(); - const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); void startAutoRefresh(); void stopAutoRefresh(); @@ -67,6 +67,10 @@ public: Subversion = 6 }; + enum { + StatsRole = Qt::UserRole, + }; + /** @name Methods overridden from QAbstractTableModel @{*/ int rowCount(const QModelIndex &parent) const override; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 61f47e3ad3..df98dbbc99 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -463,6 +463,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir")); ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir")); ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME)); + ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg("addnode").arg(QString(nonbreaking_hyphen) + "addnode").arg(QString(nonbreaking_hyphen) + "connect")); if (platformStyle->getImagesOnButtons()) { ui->openDebugLogfileButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); @@ -1021,11 +1022,9 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) void RPCConsole::peerLayoutAboutToChange() { - QModelIndexList selected = ui->peerWidget->selectionModel()->selectedIndexes(); cachedNodeids.clear(); - for(int i = 0; i < selected.size(); i++) - { - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.at(i).row()); + for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) { + const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); cachedNodeids.append(stats->nodeStats.nodeid); } } @@ -1084,15 +1083,13 @@ void RPCConsole::peerLayoutChanged() void RPCConsole::updateDetailWidget() { - QModelIndexList selected_rows; - auto selection_model = ui->peerWidget->selectionModel(); - if (selection_model) selected_rows = selection_model->selectedRows(); - if (!clientModel || !clientModel->getPeerTableModel() || selected_rows.size() != 1) { + const QList<QModelIndex> selected_peers = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); + if (!clientModel || !clientModel->getPeerTableModel() || selected_peers.size() != 1) { ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); return; } - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected_rows.first().row()); + const auto stats = selected_peers.first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); // update the detail ui with latest node information QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " "); peerAddrDetails += tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid)); @@ -1111,7 +1108,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == PF_NONE) { ui->peerPermissions->setText(tr("N/A")); @@ -1205,19 +1202,9 @@ void RPCConsole::banSelectedNode(int bantime) if (!clientModel) return; - // Get selected peer addresses - QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); - for(int i = 0; i < nodes.count(); i++) - { - // Get currently selected peer address - NodeId id = nodes.at(i).data().toLongLong(); - - // Get currently selected peer address - int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id); - if (detailNodeRow < 0) return; - + for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) { // Find possible nodes, ban it and clear the selected node - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); + const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); if (stats) { m_node.ban(stats->nodeStats.addr, bantime); m_node.disconnectByAddress(stats->nodeStats.addr); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 4835dd7954..6c110f0688 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -120,7 +120,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - const PKHash* pkhash = boost::get<PKHash>(&destination); + const PKHash* pkhash = std::get_if<PKHash>(&destination); if (!pkhash) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 06108f1d07..77fec93f0f 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -123,7 +123,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface continue; } - if (!boost::get<CNoDestination>(&wtx.txout_address[nOut])) + if (!std::get_if<CNoDestination>(&wtx.txout_address[nOut])) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 965b278bfa..52e033d0cc 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1160,7 +1160,7 @@ static RPCHelpMan estimaterawfee() UniValue result(UniValue::VOBJ); - for (const FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { + for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) { CFeeRate feeRate; EstimationResult buckets; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index a96d9d774b..cfca8b4ad4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -129,9 +129,6 @@ static RPCHelpMan getpeerinfo() {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, - {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" - "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" - "best capture connection behaviors."}, {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"}, {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"}, {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, @@ -157,6 +154,9 @@ static RPCHelpMan getpeerinfo() "Only known message types can appear as keys in the object and all bytes received\n" "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} }}, + {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" + "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" + "best capture connection behaviors."}, }}, }}, }, @@ -250,7 +250,7 @@ static RPCHelpMan getpeerinfo() recvPerMsgCmd.pushKV(i.first, i.second); } obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd); - obj.pushKV("connection_type", stats.m_conn_type_string); + obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type)); ret.push_back(obj); } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index e377a80fbd..31072114da 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -209,7 +209,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto return dest; } -class DescribeAddressVisitor : public boost::static_visitor<UniValue> +class DescribeAddressVisitor { public: explicit DescribeAddressVisitor() {} @@ -267,7 +267,7 @@ public: UniValue DescribeAddress(const CTxDestination& dest) { - return boost::apply_visitor(DescribeAddressVisitor(), dest); + return std::visit(DescribeAddressVisitor(), dest); } unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index e5ba9ba6d2..9e4b8a9dd6 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -565,7 +565,7 @@ public: Optional<OutputType> GetOutputType() const override { - switch (m_destination.which()) { + switch (m_destination.index()) { case 1 /* PKHash */: case 2 /* ScriptHash */: return OutputType::LEGACY; case 3 /* WitnessV0ScriptHash */: @@ -593,7 +593,7 @@ public: { CTxDestination dest; ExtractDestination(m_script, dest); - switch (dest.which()) { + switch (dest.index()) { case 1 /* PKHash */: case 2 /* ScriptHash */: return OutputType::LEGACY; case 3 /* WitnessV0ScriptHash */: diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 26d081f853..9781ec32af 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -179,18 +179,18 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& { // Only supports destinations which map to single public keys, i.e. P2PKH, // P2WPKH, and P2SH-P2WPKH. - if (auto id = boost::get<PKHash>(&dest)) { + if (auto id = std::get_if<PKHash>(&dest)) { return ToKeyID(*id); } - if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) { + if (auto witness_id = std::get_if<WitnessV0KeyHash>(&dest)) { return ToKeyID(*witness_id); } - if (auto script_hash = boost::get<ScriptHash>(&dest)) { + if (auto script_hash = std::get_if<ScriptHash>(&dest)) { CScript script; CScriptID script_id(*script_hash); CTxDestination inner_dest; if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) { - if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) { + if (auto inner_witness_id = std::get_if<WitnessV0KeyHash>(&inner_dest)) { return ToKeyID(*inner_witness_id); } } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 57d68c7ce9..7967c01858 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -261,9 +261,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::v return true; } -namespace -{ -class CScriptVisitor : public boost::static_visitor<CScript> +namespace { +class CScriptVisitor { public: CScript operator()(const CNoDestination& dest) const @@ -300,7 +299,7 @@ public: CScript GetScriptForDestination(const CTxDestination& dest) { - return boost::apply_visitor(CScriptVisitor(), dest); + return std::visit(CScriptVisitor(), dest); } CScript GetScriptForRawPubKey(const CPubKey& pubKey) @@ -320,5 +319,5 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) } bool IsValidDestination(const CTxDestination& dest) { - return dest.which() != 0; + return dest.index() != 0; } diff --git a/src/script/standard.h b/src/script/standard.h index 4d1ef61964..d5d87392ad 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -9,10 +9,8 @@ #include <script/interpreter.h> #include <uint256.h> -#include <boost/variant.hpp> - #include <string> - +#include <variant> static const bool DEFAULT_ACCEPT_DATACARRIER = true; @@ -211,7 +209,7 @@ struct WitnessUnknown * (taproot outputs do not require their own type as long as no wallet support exists) * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination; +using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp index 4dfb2e3da0..fa4024fc38 100644 --- a/src/test/fuzz/kitchen_sink.cpp +++ b/src/test/fuzz/kitchen_sink.cpp @@ -28,12 +28,6 @@ constexpr TransactionError ALL_TRANSACTION_ERROR[] = { TransactionError::SIGHASH_MISMATCH, TransactionError::MAX_FEE_EXCEEDED, }; - -constexpr FeeEstimateHorizon ALL_FEE_EST_HORIZON[] = { - FeeEstimateHorizon::SHORT_HALFLIFE, - FeeEstimateHorizon::MED_HALFLIFE, - FeeEstimateHorizon::LONG_HALFLIFE, -}; }; // namespace // The fuzzing kitchen sink: Fuzzing harness for functions that need to be @@ -48,7 +42,7 @@ FUZZ_TARGET(kitchen_sink) (void)RPCErrorFromTransactionError(transaction_error); (void)TransactionErrorString(transaction_error); - (void)StringForFeeEstimateHorizon(fuzzed_data_provider.PickValueInArray(ALL_FEE_EST_HORIZON)); + (void)StringForFeeEstimateHorizon(fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS)); const OutputType output_type = fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES); const std::string& output_type_string = FormatOutputType(output_type); diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp index 87359c9d39..8f843ca773 100644 --- a/src/test/fuzz/muhash.cpp +++ b/src/test/fuzz/muhash.cpp @@ -9,7 +9,7 @@ #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(muhash) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider); diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index 8a17a4b51b..35cfc5230b 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -66,10 +66,10 @@ FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator) } (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral<int>()); EstimationResult result; - (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); + (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); FeeCalculation fee_calculation; (void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool()); - (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE})); + (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS)); } { FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index b6865c5347..58637662c5 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -65,9 +65,8 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO const bool jump_out_of_ibd{fuzzed_data_provider.ConsumeBool()}; if (jump_out_of_ibd) chainstate.JumpOutOfIbd(); CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release(); + FillNode(fuzzed_data_provider, p2p_node); p2p_node.fSuccessfullyConnected = true; - p2p_node.nVersion = PROTOCOL_VERSION; - p2p_node.SetCommonVersion(PROTOCOL_VERSION); connman.AddTestNode(p2p_node); g_setup->m_node.peerman->InitializeNode(&p2p_node); diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index fcfa623508..db52da2f7e 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -49,11 +49,10 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages) for (int i = 0; i < num_peers_to_add; ++i) { peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release()); CNode& p2p_node = *peers.back(); + FillNode(fuzzed_data_provider, p2p_node); p2p_node.fSuccessfullyConnected = true; p2p_node.fPauseSend = false; - p2p_node.nVersion = PROTOCOL_VERSION; - p2p_node.SetCommonVersion(PROTOCOL_VERSION); g_setup->m_node.peerman->InitializeNode(&p2p_node); connman.AddTestNode(p2p_node); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 94c691936e..4e8e501895 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -296,7 +296,7 @@ inline CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcep } template <bool ReturnUniquePtr = false> -auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = nullopt) noexcept +auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept { const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegral<NodeId>()); const ServiceFlags local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); @@ -316,6 +316,23 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N } inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = nullopt) { return ConsumeNode<true>(fdp, node_id_in); } +inline void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, const std::optional<int32_t>& version_in = std::nullopt) noexcept +{ + const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); + const NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); + const int32_t version = version_in.value_or(fuzzed_data_provider.ConsumeIntegral<int32_t>()); + const bool filter_txs = fuzzed_data_provider.ConsumeBool(); + + node.nServices = remote_services; + node.m_permissionFlags = permission_flags; + node.nVersion = version; + node.SetCommonVersion(version); + if (node.m_tx_relay != nullptr) { + LOCK(node.m_tx_relay->cs_filter); + node.m_tx_relay->fRelayTxes = filter_txs; + } +} + inline void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST) { static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}}; diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index ac4db3a5b6..66ad7bb5ea 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -226,8 +226,22 @@ BOOST_AUTO_TEST_CASE(subnet_test) // IPv4 address with IPv6 netmask or the other way around. BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid()); BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid()); - // Can't subnet TOR (or any other non-IPv4 and non-IPv6 network). - BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid()); + + // Create Non-IP subnets. + + const CNetAddr tor_addr{ + ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")}; + + subnet = CSubNet(tor_addr); + BOOST_CHECK(subnet.IsValid()); + BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToString()); + BOOST_CHECK(subnet.Match(tor_addr)); + BOOST_CHECK( + !subnet.Match(ResolveIP("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"))); + BOOST_CHECK(!subnet.Match(ResolveIP("1.2.3.4"))); + + BOOST_CHECK(!CSubNet(tor_addr, 200).IsValid()); + BOOST_CHECK(!CSubNet(tor_addr, ResolveIP("255.0.0.0")).IsValid()); subnet = ResolveSubNet("1.2.3.4/255.255.255.255"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); @@ -442,8 +456,7 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters) BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret)); - // We only do subnetting for IPv4 and IPv6 - BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret)); + BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret)); diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index fb84b7f2ac..366385b619 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -183,23 +183,23 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) s.clear(); s << ToByteVector(pubkey) << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<PKHash>(&address) && - *boost::get<PKHash>(&address) == PKHash(pubkey)); + BOOST_CHECK(std::get_if<PKHash>(&address) && + *std::get_if<PKHash>(&address) == PKHash(pubkey)); // TxoutType::PUBKEYHASH s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<PKHash>(&address) && - *boost::get<PKHash>(&address) == PKHash(pubkey)); + BOOST_CHECK(std::get_if<PKHash>(&address) && + *std::get_if<PKHash>(&address) == PKHash(pubkey)); // TxoutType::SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script s.clear(); s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<ScriptHash>(&address) && - *boost::get<ScriptHash>(&address) == ScriptHash(redeemScript)); + BOOST_CHECK(std::get_if<ScriptHash>(&address) && + *std::get_if<ScriptHash>(&address) == ScriptHash(redeemScript)); // TxoutType::MULTISIG s.clear(); @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) BOOST_CHECK(ExtractDestination(s, address)); WitnessV0KeyHash keyhash; CHash160().Write(pubkey).Finalize(keyhash); - BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash); + BOOST_CHECK(std::get_if<WitnessV0KeyHash>(&address) && *std::get_if<WitnessV0KeyHash>(&address) == keyhash); // TxoutType::WITNESS_V0_SCRIPTHASH s.clear(); @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); s << OP_0 << ToByteVector(scripthash); BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash); + BOOST_CHECK(std::get_if<WitnessV0ScriptHash>(&address) && *std::get_if<WitnessV0ScriptHash>(&address) == scripthash); // TxoutType::WITNESS_UNKNOWN with unknown version s.clear(); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) unk.length = 33; unk.version = 1; std::copy(pubkey.begin(), pubkey.end(), unk.program); - BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk); + BOOST_CHECK(std::get_if<WitnessUnknown>(&address) && *std::get_if<WitnessUnknown>(&address) == unk); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) @@ -259,8 +259,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); // TxoutType::PUBKEYHASH s.clear(); @@ -269,8 +269,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); // TxoutType::SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script @@ -280,8 +280,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) && - *boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript)); + BOOST_CHECK(std::get_if<ScriptHash>(&addresses[0]) && + *std::get_if<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript)); // TxoutType::MULTISIG s.clear(); @@ -293,10 +293,10 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG); BOOST_CHECK_EQUAL(addresses.size(), 2U); BOOST_CHECK_EQUAL(nRequired, 2); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); - BOOST_CHECK(boost::get<PKHash>(&addresses[1]) && - *boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[1]) && + *std::get_if<PKHash>(&addresses[1]) == PKHash(pubkeys[1])); // TxoutType::NULL_DATA s.clear(); diff --git a/src/util/message.cpp b/src/util/message.cpp index e1d5cff48c..73948e4ff1 100644 --- a/src/util/message.cpp +++ b/src/util/message.cpp @@ -31,7 +31,7 @@ MessageVerificationResult MessageVerify( return MessageVerificationResult::ERR_INVALID_ADDRESS; } - if (boost::get<PKHash>(&destination) == nullptr) { + if (std::get_if<PKHash>(&destination) == nullptr) { return MessageVerificationResult::ERR_ADDRESS_NO_KEY; } diff --git a/src/util/system.cpp b/src/util/system.cpp index 5f30136fa2..917d37ccc7 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1047,27 +1047,36 @@ bool FileCommit(FILE *file) LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); return false; } -#else - #if HAVE_FDATASYNC - if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync - LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); - return false; - } - #elif defined(MAC_OSX) && defined(F_FULLFSYNC) +#elif defined(MAC_OSX) && defined(F_FULLFSYNC) if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); return false; } - #else +#elif HAVE_FDATASYNC + if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync + LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + return false; + } +#else if (fsync(fileno(file)) != 0 && errno != EINVAL) { LogPrintf("%s: fsync failed: %d\n", __func__, errno); return false; } - #endif #endif return true; } +void DirectoryCommit(const fs::path &dirname) +{ +#ifndef WIN32 + FILE* file = fsbridge::fopen(dirname, "r"); + if (file) { + fsync(fileno(file)); + fclose(file); + } +#endif +} + bool TruncateFile(FILE *file, unsigned int length) { #if defined(WIN32) return _chsize(_fileno(file), length) == 0; diff --git a/src/util/system.h b/src/util/system.h index 2be8bb754b..010fc5b49f 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -56,7 +56,19 @@ bool error(const char* fmt, const Args&... args) } void PrintExceptionContinue(const std::exception *pex, const char* pszThread); + +/** + * Ensure file contents are fully committed to disk, using a platform-specific + * feature analogous to fsync(). + */ bool FileCommit(FILE *file); + +/** + * Sync directory contents. This is required on some environments to ensure that + * newly created files are committed to disk. + */ +void DirectoryCommit(const fs::path &dirname); + bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae4e8f2898..7705794c63 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -627,7 +627,7 @@ static RPCHelpMan signmessage() throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } - const PKHash *pkhash = boost::get<PKHash>(&dest); + const PKHash* pkhash = std::get_if<PKHash>(&dest); if (!pkhash) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } @@ -3002,7 +3002,7 @@ static RPCHelpMan listunspent() std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey); if (provider) { if (scriptPubKey.IsPayToScriptHash()) { - const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); + const CScriptID& hash = CScriptID(std::get<ScriptHash>(address)); CScript redeemScript; if (provider->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript)); @@ -3012,7 +3012,7 @@ static RPCHelpMan listunspent() bool extracted = ExtractDestination(redeemScript, witness_destination); CHECK_NONFATAL(extracted); // Also return the witness script - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); + const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination); CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; @@ -3022,7 +3022,7 @@ static RPCHelpMan listunspent() } } } else if (scriptPubKey.IsPayToWitnessScriptHash()) { - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address); + const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address); CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; @@ -3645,7 +3645,7 @@ static RPCHelpMan rescanblockchain() }; } -class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> +class DescribeWalletAddressVisitor { public: const SigningProvider * const provider; @@ -3664,7 +3664,7 @@ public: UniValue subobj(UniValue::VOBJ); UniValue detail = DescribeAddress(embedded); subobj.pushKVs(detail); - UniValue wallet_detail = boost::apply_visitor(*this, embedded); + UniValue wallet_detail = std::visit(*this, embedded); subobj.pushKVs(wallet_detail); subobj.pushKV("address", EncodeDestination(embedded)); subobj.pushKV("scriptPubKey", HexStr(subscript)); @@ -3747,7 +3747,7 @@ static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDes provider = pwallet->GetSolvingProvider(script); } ret.pushKVs(detail); - ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider.get()), dest)); + ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest)); return ret; } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index a6db261914..5480f3ab22 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -550,7 +550,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); // Check initial balance from one mature coinbase transaction. @@ -566,7 +566,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); // Lock both coins. Confirm number of available coins drops to 0. @@ -595,7 +595,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0bcbff3b2f..918946f9e7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2789,7 +2789,7 @@ bool CWallet::CreateTransactionInternal( CScript scriptChange; // coin control: send change to custom address - if (!boost::get<CNoDestination>(&coin_control.destChange)) { + if (!std::get_if<CNoDestination>(&coin_control.destChange)) { scriptChange = GetScriptForDestination(coin_control.destChange); } else { // no coin control: send change to newly generated address // Note: We use a new key here to keep it from being obvious which side is the change. @@ -3724,7 +3724,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value) { - if (boost::get<CNoDestination>(&dest)) + if (std::get_if<CNoDestination>(&dest)) return false; m_address_book[dest].destdata.insert(std::make_pair(key, value)); diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py index 8ac91bd008..eb08765ebf 100755 --- a/test/functional/mempool_compatibility.py +++ b/test/functional/mempool_compatibility.py @@ -16,15 +16,15 @@ Only v0.15.2 is required by this test. The rest is used in other backwards compa import os from test_framework.test_framework import BitcoinTestFramework +from test_framework.wallet import MiniWallet class MempoolCompatibilityTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.wallet_names = [None, self.default_wallet_name] + self.wallet_names = [None] def skip_test_if_missing_module(self): - self.skip_if_no_wallet() self.skip_if_no_previous_releases() def setup_network(self): @@ -38,8 +38,15 @@ class MempoolCompatibilityTest(BitcoinTestFramework): def run_test(self): self.log.info("Test that mempool.dat is compatible between versions") - old_node = self.nodes[0] - new_node = self.nodes[1] + old_node, new_node = self.nodes + new_wallet = MiniWallet(new_node) + new_wallet.generate(1) + new_node.generate(100) + # Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend. + # Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced + # unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`. + self.connect_nodes(0, 1) + self.sync_blocks() recipient = old_node.getnewaddress() self.stop_node(1) @@ -58,7 +65,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework): assert old_tx_hash in new_node.getrawmempool() self.log.info("Add unbroadcasted tx to mempool on new node and shutdown") - unbroadcasted_tx_hash = new_node.sendtoaddress(recipient, 0.0001) + unbroadcasted_tx_hash = new_wallet.send_self_transfer(from_node=new_node)['txid'] assert unbroadcasted_tx_hash in new_node.getrawmempool() mempool = new_node.getrawmempool(True) assert mempool[unbroadcasted_tx_hash]['unbroadcast'] diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py index 523fdc9f63..fd5f8aa098 100755 --- a/test/functional/rpc_setban.py +++ b/test/functional/rpc_setban.py @@ -15,6 +15,9 @@ class SetBanTests(BitcoinTestFramework): self.setup_clean_chain = True self.extra_args = [[],[]] + def is_banned(self, node, addr): + return any(e['address'] == addr for e in node.listbanned()) + def run_test(self): # Node 0 connects to Node 1, check that the noban permission is not granted self.connect_nodes(0, 1) @@ -42,5 +45,18 @@ class SetBanTests(BitcoinTestFramework): peerinfo = self.nodes[1].getpeerinfo()[0] assert(not 'noban' in peerinfo['permissions']) + self.log.info("Test that a non-IP address can be banned/unbanned") + node = self.nodes[1] + tor_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion" + ip_addr = "1.2.3.4" + assert(not self.is_banned(node, tor_addr)) + assert(not self.is_banned(node, ip_addr)) + node.setban(tor_addr, "add") + assert(self.is_banned(node, tor_addr)) + assert(not self.is_banned(node, ip_addr)) + node.setban(tor_addr, "remove") + assert(not self.is_banned(self.nodes[1], tor_addr)) + assert(not self.is_banned(node, ip_addr)) + if __name__ == '__main__': SetBanTests().main() diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 62ff5c6e33..b3eb2d61a7 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -362,6 +362,7 @@ def initialize_datadir(dirname, n, chain): f.write("listenonion=0\n") f.write("printtoconsole=0\n") f.write("upnp=0\n") + f.write("natpmp=0\n") f.write("shrinkdebugfile=0\n") os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index a71f2c69cb..7cb74bdcb3 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -71,9 +71,9 @@ class MiniWallet: tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] tx_hex = tx.serialize().hex() - txid = from_node.sendrawtransaction(tx_hex) - self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value}) - tx_info = from_node.getmempoolentry(txid) + tx_info = from_node.testmempoolaccept([tx_hex])[0] + self._utxos.append({'txid': tx_info['txid'], 'vout': 0, 'value': send_value}) + from_node.sendrawtransaction(tx_hex) assert_equal(tx_info['vsize'], vsize) - assert_equal(tx_info['fee'], fee) - return {'txid': txid, 'wtxid': tx_info['wtxid'], 'hex': tx_hex} + assert_equal(tx_info['fees']['base'], fee) + return {'txid': tx_info['txid'], 'wtxid': from_node.decoderawtransaction(tx_hex)['hash'], 'hex': tx_hex} diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 393f734abe..dc032665e4 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -71,9 +71,6 @@ EXPECTED_BOOST_INCLUDES=( boost/thread/mutex.hpp boost/thread/shared_mutex.hpp boost/thread/thread.hpp - boost/variant.hpp - boost/variant/apply_visitor.hpp - boost/variant/static_visitor.hpp ) for BOOST_INCLUDE in $(git grep '^#include <boost/' -- "*.cpp" "*.h" | cut -f2 -d: | cut -f2 -d'<' | cut -f1 -d'>' | sort -u); do |