aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--ci/test/00_setup_env_native_asan.sh2
-rw-r--r--ci/test/00_setup_env_native_qt5.sh2
-rw-r--r--ci/test/00_setup_env_native_valgrind.sh2
-rw-r--r--configure.ac50
-rw-r--r--depends/Makefile50
-rw-r--r--depends/README.md1
-rw-r--r--depends/config.site.in74
-rw-r--r--depends/funcs.mk30
-rw-r--r--depends/hosts/darwin.mk97
-rw-r--r--depends/packages/libnatpmp.mk19
-rw-r--r--depends/packages/packages.mk1
-rw-r--r--doc/JSON-RPC-interface.md15
-rw-r--r--doc/build-osx.md7
-rw-r--r--doc/build-unix.md24
-rw-r--r--doc/dependencies.md4
-rw-r--r--doc/productivity.md1
-rw-r--r--doc/release-notes-18077.md10
-rw-r--r--doc/tor.md2
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/bench/crypto_hash.cpp54
-rw-r--r--src/crypto/muhash.cpp338
-rw-r--r--src/crypto/muhash.h130
-rw-r--r--src/flatfile.cpp1
-rw-r--r--src/init.cpp20
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/mapport.cpp336
-rw-r--r--src/mapport.h30
-rw-r--r--src/net.cpp130
-rw-r--r--src/net.h21
-rw-r--r--src/node/interfaces.cpp11
-rw-r--r--src/qt/addresstablemodel.cpp8
-rw-r--r--src/qt/bantablemodel.cpp8
-rw-r--r--src/qt/forms/debugwindow.ui9
-rw-r--r--src/qt/forms/optionsdialog.ui10
-rw-r--r--src/qt/guiutil.cpp13
-rw-r--r--src/qt/guiutil.h6
-rw-r--r--src/qt/optionsdialog.cpp9
-rw-r--r--src/qt/optionsmodel.cpp24
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/peertablemodel.cpp8
-rw-r--r--src/qt/recentrequeststablemodel.cpp10
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/transactiontablemodel.cpp8
-rw-r--r--src/rpc/net.cpp8
-rw-r--r--src/test/crypto_tests.cpp90
-rw-r--r--src/test/fuzz/connman.cpp2
-rw-r--r--src/test/fuzz/crypto.cpp16
-rw-r--r--src/test/fuzz/muhash.cpp53
-rw-r--r--src/test/fuzz/net.cpp5
-rw-r--r--src/test/fuzz/net_permissions.cpp13
-rw-r--r--src/test/fuzz/process_message.cpp10
-rw-r--r--src/test/fuzz/process_messages.cpp7
-rw-r--r--src/test/fuzz/util.h44
-rw-r--r--src/test/util/net.h31
-rw-r--r--src/util/system.cpp27
-rw-r--r--src/util/system.h12
-rwxr-xr-xtest/functional/mempool_compatibility.py17
-rw-r--r--test/functional/test_framework/muhash.py14
-rw-r--r--test/functional/test_framework/util.py1
64 files changed, 1618 insertions, 330 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 c69b793d6e..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],
@@ -753,6 +765,9 @@ if test x$use_lcov_branch != xno; then
AC_SUBST(LCOV_OPTS, "$LCOV_OPTS --rc lcov_branch_coverage=1")
fi
+dnl Check for __int128
+AC_CHECK_TYPES([__int128])
+
dnl Check for endianness
AC_C_BIGENDIAN
@@ -1203,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]])
@@ -1300,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
@@ -1558,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
@@ -1704,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)
@@ -1791,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 23d790d552..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 \
@@ -408,6 +410,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/hmac_sha512.h \
crypto/poly1305.h \
crypto/poly1305.cpp \
+ crypto/muhash.h \
+ crypto/muhash.cpp \
crypto/ripemd160.cpp \
crypto/ripemd160.h \
crypto/sha1.cpp \
@@ -596,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 3faa5ac968..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
@@ -230,6 +230,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/locale.cpp \
test/fuzz/merkleblock.cpp \
test/fuzz/message.cpp \
+ test/fuzz/muhash.cpp \
test/fuzz/multiplication_overflow.cpp \
test/fuzz/net.cpp \
test/fuzz/net_permissions.cpp \
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 65d16d47d8..30fe11be6b 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
+#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -105,6 +106,54 @@ static void FastRandom_1bit(benchmark::Bench& bench)
});
}
+static void MuHash(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ unsigned char key[32] = {0};
+ int i = 0;
+ bench.run([&] {
+ key[0] = ++i;
+ acc *= MuHash3072(key);
+ });
+}
+
+static void MuHashMul(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ MuHash3072 muhash{rng.randbytes(32)};
+
+ bench.run([&] {
+ acc *= muhash;
+ });
+}
+
+static void MuHashDiv(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ MuHash3072 muhash{rng.randbytes(32)};
+
+ for (size_t i = 0; i < bench.epochIterations(); ++i) {
+ acc *= muhash;
+ }
+
+ bench.run([&] {
+ acc /= muhash;
+ });
+}
+
+static void MuHashPrecompute(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ std::vector<unsigned char> key{rng.randbytes(32)};
+
+ bench.run([&] {
+ MuHash3072{key};
+ });
+}
+
BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
@@ -116,3 +165,8 @@ BENCHMARK(SipHash_32b);
BENCHMARK(SHA256D64_1024);
BENCHMARK(FastRandom_32bit);
BENCHMARK(FastRandom_1bit);
+
+BENCHMARK(MuHash);
+BENCHMARK(MuHashMul);
+BENCHMARK(MuHashDiv);
+BENCHMARK(MuHashPrecompute);
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
new file mode 100644
index 0000000000..fbd14f9325
--- /dev/null
+++ b/src/crypto/muhash.cpp
@@ -0,0 +1,338 @@
+// Copyright (c) 2017-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/muhash.h>
+
+#include <crypto/chacha20.h>
+#include <crypto/common.h>
+#include <hash.h>
+
+#include <cassert>
+#include <cstdio>
+#include <limits>
+
+namespace {
+
+using limb_t = Num3072::limb_t;
+using double_limb_t = Num3072::double_limb_t;
+constexpr int LIMB_SIZE = Num3072::LIMB_SIZE;
+constexpr int LIMBS = Num3072::LIMBS;
+/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */
+constexpr limb_t MAX_PRIME_DIFF = 1103717;
+
+/** Extract the lowest limb of [c0,c1,c2] into n, and left shift the number by 1 limb. */
+inline void extract3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& n)
+{
+ n = c0;
+ c0 = c1;
+ c1 = c2;
+ c2 = 0;
+}
+
+/** [c0,c1] = a * b */
+inline void mul(limb_t& c0, limb_t& c1, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ c1 = t >> LIMB_SIZE;
+ c0 = t;
+}
+
+/* [c0,c1,c2] += n * [d0,d1,d2]. c2 is 0 initially */
+inline void mulnadd3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& d0, limb_t& d1, limb_t& d2, const limb_t& n)
+{
+ double_limb_t t = (double_limb_t)d0 * n + c0;
+ c0 = t;
+ t >>= LIMB_SIZE;
+ t += (double_limb_t)d1 * n + c1;
+ c1 = t;
+ t >>= LIMB_SIZE;
+ c2 = t + d2 * n;
+}
+
+/* [c0,c1] *= n */
+inline void muln2(limb_t& c0, limb_t& c1, const limb_t& n)
+{
+ double_limb_t t = (double_limb_t)c0 * n;
+ c0 = t;
+ t >>= LIMB_SIZE;
+ t += (double_limb_t)c1 * n;
+ c1 = t;
+}
+
+/** [c0,c1,c2] += a * b */
+inline void muladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ limb_t th = t >> LIMB_SIZE;
+ limb_t tl = t;
+
+ c0 += tl;
+ th += (c0 < tl) ? 1 : 0;
+ c1 += th;
+ c2 += (c1 < th) ? 1 : 0;
+}
+
+/** [c0,c1,c2] += 2 * a * b */
+inline void muldbladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ limb_t th = t >> LIMB_SIZE;
+ limb_t tl = t;
+
+ c0 += tl;
+ limb_t tt = th + ((c0 < tl) ? 1 : 0);
+ c1 += tt;
+ c2 += (c1 < tt) ? 1 : 0;
+ c0 += tl;
+ th += (c0 < tl) ? 1 : 0;
+ c1 += th;
+ c2 += (c1 < th) ? 1 : 0;
+}
+
+/**
+ * Add limb a to [c0,c1]: [c0,c1] += a. Then extract the lowest
+ * limb of [c0,c1] into n, and left shift the number by 1 limb.
+ * */
+inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n)
+{
+ limb_t c2 = 0;
+
+ // add
+ c0 += a;
+ if (c0 < a) {
+ c1 += 1;
+
+ // Handle case when c1 has overflown
+ if (c1 == 0)
+ c2 = 1;
+ }
+
+ // extract
+ n = c0;
+ c0 = c1;
+ c1 = c2;
+}
+
+/** in_out = in_out^(2^sq) * mul */
+inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul)
+{
+ for (int j = 0; j < sq; ++j) in_out.Square();
+ in_out.Multiply(mul);
+}
+
+} // namespace
+
+/** Indicates wether d is larger than the modulus. */
+bool Num3072::IsOverflow() const
+{
+ if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false;
+ for (int i = 1; i < LIMBS; ++i) {
+ if (this->limbs[i] != std::numeric_limits<limb_t>::max()) return false;
+ }
+ return true;
+}
+
+void Num3072::FullReduce()
+{
+ limb_t c0 = MAX_PRIME_DIFF;
+ limb_t c1 = 0;
+ for (int i = 0; i < LIMBS; ++i) {
+ addnextract2(c0, c1, this->limbs[i], this->limbs[i]);
+ }
+}
+
+Num3072 Num3072::GetInverse() const
+{
+ // For fast exponentiation a sliding window exponentiation with repunit
+ // precomputation is utilized. See "Fast Point Decompression for Standard
+ // Elliptic Curves" (Brumley, Järvinen, 2008).
+
+ Num3072 p[12]; // p[i] = a^(2^(2^i)-1)
+ Num3072 out;
+
+ p[0] = *this;
+
+ for (int i = 0; i < 11; ++i) {
+ p[i + 1] = p[i];
+ for (int j = 0; j < (1 << i); ++j) p[i + 1].Square();
+ p[i + 1].Multiply(p[i]);
+ }
+
+ out = p[11];
+
+ square_n_mul(out, 512, p[9]);
+ square_n_mul(out, 256, p[8]);
+ square_n_mul(out, 128, p[7]);
+ square_n_mul(out, 64, p[6]);
+ square_n_mul(out, 32, p[5]);
+ square_n_mul(out, 8, p[3]);
+ square_n_mul(out, 2, p[1]);
+ square_n_mul(out, 1, p[0]);
+ square_n_mul(out, 5, p[2]);
+ square_n_mul(out, 3, p[0]);
+ square_n_mul(out, 2, p[0]);
+ square_n_mul(out, 4, p[0]);
+ square_n_mul(out, 4, p[1]);
+ square_n_mul(out, 3, p[0]);
+
+ return out;
+}
+
+void Num3072::Multiply(const Num3072& a)
+{
+ limb_t c0 = 0, c1 = 0, c2 = 0;
+ Num3072 tmp;
+
+ /* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */
+ for (int j = 0; j < LIMBS - 1; ++j) {
+ limb_t d0 = 0, d1 = 0, d2 = 0;
+ mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]);
+ for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]);
+ mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
+ for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]);
+ extract3(c0, c1, c2, tmp.limbs[j]);
+ }
+
+ /* Compute limb N-1 of a*b into tmp. */
+ assert(c2 == 0);
+ for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]);
+ extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
+
+ /* Perform a second reduction. */
+ muln2(c0, c1, MAX_PRIME_DIFF);
+ for (int j = 0; j < LIMBS; ++j) {
+ addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
+ }
+
+ assert(c1 == 0);
+ assert(c0 == 0 || c0 == 1);
+
+ /* Perform up to two more reductions if the internal state has already
+ * overflown the MAX of Num3072 or if it is larger than the modulus or
+ * if both are the case.
+ * */
+ if (this->IsOverflow()) this->FullReduce();
+ if (c0) this->FullReduce();
+}
+
+void Num3072::Square()
+{
+ limb_t c0 = 0, c1 = 0, c2 = 0;
+ Num3072 tmp;
+
+ /* Compute limbs 0..N-2 of this*this into tmp, including one reduction. */
+ for (int j = 0; j < LIMBS - 1; ++j) {
+ limb_t d0 = 0, d1 = 0, d2 = 0;
+ for (int i = 0; i < (LIMBS - 1 - j) / 2; ++i) muldbladd3(d0, d1, d2, this->limbs[i + j + 1], this->limbs[LIMBS - 1 - i]);
+ if ((j + 1) & 1) muladd3(d0, d1, d2, this->limbs[(LIMBS - 1 - j) / 2 + j + 1], this->limbs[LIMBS - 1 - (LIMBS - 1 - j) / 2]);
+ mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
+ for (int i = 0; i < (j + 1) / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[j - i]);
+ if ((j + 1) & 1) muladd3(c0, c1, c2, this->limbs[(j + 1) / 2], this->limbs[j - (j + 1) / 2]);
+ extract3(c0, c1, c2, tmp.limbs[j]);
+ }
+
+ assert(c2 == 0);
+ for (int i = 0; i < LIMBS / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[LIMBS - 1 - i]);
+ extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
+
+ /* Perform a second reduction. */
+ muln2(c0, c1, MAX_PRIME_DIFF);
+ for (int j = 0; j < LIMBS; ++j) {
+ addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
+ }
+
+ assert(c1 == 0);
+ assert(c0 == 0 || c0 == 1);
+
+ /* Perform up to two more reductions if the internal state has already
+ * overflown the MAX of Num3072 or if it is larger than the modulus or
+ * if both are the case.
+ * */
+ if (this->IsOverflow()) this->FullReduce();
+ if (c0) this->FullReduce();
+}
+
+void Num3072::SetToOne()
+{
+ this->limbs[0] = 1;
+ for (int i = 1; i < LIMBS; ++i) this->limbs[i] = 0;
+}
+
+void Num3072::Divide(const Num3072& a)
+{
+ if (this->IsOverflow()) this->FullReduce();
+
+ Num3072 inv{};
+ if (a.IsOverflow()) {
+ Num3072 b = a;
+ b.FullReduce();
+ inv = b.GetInverse();
+ } else {
+ inv = a.GetInverse();
+ }
+
+ this->Multiply(inv);
+ if (this->IsOverflow()) this->FullReduce();
+}
+
+Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
+ Num3072 out{};
+ uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
+ unsigned char tmp[BYTE_SIZE];
+ ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, BYTE_SIZE);
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ out.limbs[i] = ReadLE32(tmp + 4 * i);
+ } else if (sizeof(limb_t) == 8) {
+ out.limbs[i] = ReadLE64(tmp + 8 * i);
+ }
+ }
+ return out;
+}
+
+MuHash3072::MuHash3072(Span<const unsigned char> in) noexcept
+{
+ m_numerator = ToNum3072(in);
+}
+
+void MuHash3072::Finalize(uint256& out) noexcept
+{
+ m_numerator.Divide(m_denominator);
+ m_denominator.SetToOne(); // Needed to keep the MuHash object valid
+
+ unsigned char data[384];
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ WriteLE32(data + i * 4, m_numerator.limbs[i]);
+ } else if (sizeof(limb_t) == 8) {
+ WriteLE64(data + i * 8, m_numerator.limbs[i]);
+ }
+ }
+
+ out = (CHashWriter(SER_DISK, 0) << data).GetSHA256();
+}
+
+MuHash3072& MuHash3072::operator*=(const MuHash3072& mul) noexcept
+{
+ m_numerator.Multiply(mul.m_numerator);
+ m_denominator.Multiply(mul.m_denominator);
+ return *this;
+}
+
+MuHash3072& MuHash3072::operator/=(const MuHash3072& div) noexcept
+{
+ m_numerator.Multiply(div.m_denominator);
+ m_denominator.Multiply(div.m_numerator);
+ return *this;
+}
+
+MuHash3072& MuHash3072::Insert(Span<const unsigned char> in) noexcept {
+ m_numerator.Multiply(ToNum3072(in));
+ return *this;
+}
+
+MuHash3072& MuHash3072::Remove(Span<const unsigned char> in) noexcept {
+ m_numerator.Divide(ToNum3072(in));
+ return *this;
+}
diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h
new file mode 100644
index 0000000000..0c710007c4
--- /dev/null
+++ b/src/crypto/muhash.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2017-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_CRYPTO_MUHASH_H
+#define BITCOIN_CRYPTO_MUHASH_H
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <serialize.h>
+#include <uint256.h>
+
+#include <stdint.h>
+
+class Num3072
+{
+private:
+ void FullReduce();
+ bool IsOverflow() const;
+ Num3072 GetInverse() const;
+
+public:
+
+#ifdef HAVE___INT128
+ typedef unsigned __int128 double_limb_t;
+ typedef uint64_t limb_t;
+ static constexpr int LIMBS = 48;
+ static constexpr int LIMB_SIZE = 64;
+#else
+ typedef uint64_t double_limb_t;
+ typedef uint32_t limb_t;
+ static constexpr int LIMBS = 96;
+ static constexpr int LIMB_SIZE = 32;
+#endif
+ limb_t limbs[LIMBS];
+
+ // Sanity check for Num3072 constants
+ static_assert(LIMB_SIZE * LIMBS == 3072, "Num3072 isn't 3072 bits");
+ static_assert(sizeof(double_limb_t) == sizeof(limb_t) * 2, "bad size for double_limb_t");
+ static_assert(sizeof(limb_t) * 8 == LIMB_SIZE, "LIMB_SIZE is incorrect");
+
+ // Hard coded values in MuHash3072 constructor and Finalize
+ static_assert(sizeof(limb_t) == 4 || sizeof(limb_t) == 8, "bad size for limb_t");
+
+ void Multiply(const Num3072& a);
+ void Divide(const Num3072& a);
+ void SetToOne();
+ void Square();
+
+ Num3072() { this->SetToOne(); };
+
+ SERIALIZE_METHODS(Num3072, obj)
+ {
+ for (auto& limb : obj.limbs) {
+ READWRITE(limb);
+ }
+ }
+};
+
+/** A class representing MuHash sets
+ *
+ * MuHash is a hashing algorithm that supports adding set elements in any
+ * order but also deleting in any order. As a result, it can maintain a
+ * running sum for a set of data as a whole, and add/remove when data
+ * is added to or removed from it. A downside of MuHash is that computing
+ * an inverse is relatively expensive. This is solved by representing
+ * the running value as a fraction, and multiplying added elements into
+ * the numerator and removed elements into the denominator. Only when the
+ * final hash is desired, a single modular inverse and multiplication is
+ * needed to combine the two. The combination is also run on serialization
+ * to allow for space-efficient storage on disk.
+ *
+ * As the update operations are also associative, H(a)+H(b)+H(c)+H(d) can
+ * in fact be computed as (H(a)+H(b)) + (H(c)+H(d)). This implies that
+ * all of this is perfectly parallellizable: each thread can process an
+ * arbitrary subset of the update operations, allowing them to be
+ * efficiently combined later.
+ *
+ * Muhash does not support checking if an element is already part of the
+ * set. That is why this class does not enforce the use of a set as the
+ * data it represents because there is no efficient way to do so.
+ * It is possible to add elements more than once and also to remove
+ * elements that have not been added before. However, this implementation
+ * is intended to represent a set of elements.
+ *
+ * See also https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and
+ * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html.
+ */
+class MuHash3072
+{
+private:
+ static constexpr size_t BYTE_SIZE = 384;
+
+ Num3072 m_numerator;
+ Num3072 m_denominator;
+
+ Num3072 ToNum3072(Span<const unsigned char> in);
+
+public:
+ /* The empty set. */
+ MuHash3072() noexcept {};
+
+ /* A singleton with variable sized data in it. */
+ explicit MuHash3072(Span<const unsigned char> in) noexcept;
+
+ /* Insert a single piece of data into the set. */
+ MuHash3072& Insert(Span<const unsigned char> in) noexcept;
+
+ /* Remove a single piece of data from the set. */
+ MuHash3072& Remove(Span<const unsigned char> in) noexcept;
+
+ /* Multiply (resulting in a hash for the union of the sets) */
+ MuHash3072& operator*=(const MuHash3072& mul) noexcept;
+
+ /* Divide (resulting in a hash for the difference of the sets) */
+ MuHash3072& operator/=(const MuHash3072& div) noexcept;
+
+ /* Finalize into a 32-byte hash. Does not change this object's value. */
+ void Finalize(uint256& out) noexcept;
+
+ SERIALIZE_METHODS(MuHash3072, obj)
+ {
+ READWRITE(obj.m_numerator);
+ READWRITE(obj.m_denominator);
+ }
+};
+
+#endif // BITCOIN_CRYPTO_MUHASH_H
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/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 14c75b8d06..59835c37fc 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:
@@ -626,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
@@ -1538,121 +1529,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;
diff --git a/src/net.h b/src/net.h
index f402257b94..84b5204f14 100644
--- a/src/net.h
+++ b/src/net.h
@@ -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 */
@@ -635,7 +627,6 @@ private:
//! service advertisements.
const ServiceFlags nLocalServices;
- NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable RecursiveMutex cs_addrName;
@@ -764,7 +755,7 @@ 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; }
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/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 665c8e6053..8b1308da5d 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -177,13 +177,17 @@ AddressTableModel::~AddressTableModel()
int AddressTableModel::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return priv->size();
}
int AddressTableModel::columnCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return columns.length();
}
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 2676de96d7..a01a7bc386 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -98,13 +98,17 @@ BanTableModel::~BanTableModel()
int BanTableModel::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return priv->size();
}
int BanTableModel::columnCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return columns.length();
}
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:&lt;ul&gt;&lt;li&gt;Inbound: initiated by peer&lt;/li&gt;&lt;li&gt;Outbound Full Relay: default&lt;/li&gt;&lt;li&gt;Outbound Block Relay: does not relay transactions or addresses&lt;/li&gt;&lt;li&gt;Outbound Manual: added using RPC %1 or %2/%3 configuration options&lt;/li&gt;&lt;li&gt;Outbound Feeler: short-lived, for testing addresses&lt;/li&gt;&lt;li&gt;Outbound Address Fetch: short-lived, for soliciting addresses&lt;/li&gt;&lt;/ul&gt;</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&amp;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 2d7dbb38a3..b02e4076e2 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -134,13 +134,17 @@ void PeerTableModel::stopAutoRefresh()
int PeerTableModel::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return priv->size();
}
int PeerTableModel::columnCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return columns.length();
}
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 5fa9f7cd96..18b913774b 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -36,15 +36,17 @@ RecentRequestsTableModel::~RecentRequestsTableModel()
int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
-
+ if (parent.isValid()) {
+ return 0;
+ }
return list.length();
}
int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
-
+ if (parent.isValid()) {
+ return 0;
+ }
return columns.length();
}
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 61f47e3ad3..e081706c5a 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"));
@@ -1111,7 +1112,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"));
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index bb59b25d33..05b250eea9 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -289,13 +289,17 @@ void TransactionTableModel::updateConfirmations()
int TransactionTableModel::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return priv->size();
}
int TransactionTableModel::columnCount(const QModelIndex &parent) const
{
- Q_UNUSED(parent);
+ if (parent.isValid()) {
+ return 0;
+ }
return columns.length();
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 89ddfb35cb..c4561fa1b4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -128,9 +128,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"},
@@ -156,6 +153,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."},
}},
}},
},
@@ -249,7 +249,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/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 0ad5066603..7358b246b6 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -14,7 +14,9 @@
#include <crypto/sha256.h>
#include <crypto/sha3.h>
#include <crypto/sha512.h>
+#include <crypto/muhash.h>
#include <random.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
@@ -857,4 +859,92 @@ BOOST_AUTO_TEST_CASE(sha3_256_tests)
TestSHA3_256("72c57c359e10684d0517e46653a02d18d29eff803eb009e4d5eb9e95add9ad1a4ac1f38a70296f3a369a16985ca3c957de2084cdc9bdd8994eb59b8815e0debad4ec1f001feac089820db8becdaf896aaf95721e8674e5d476b43bd2b873a7d135cd685f545b438210f9319e4dcd55986c85303c1ddf18dc746fe63a409df0a998ed376eb683e16c09e6e9018504152b3e7628ef350659fb716e058a5263a18823d2f2f6ee6a8091945a48ae1c5cb1694cf2c1fe76ef9177953afe8899cfa2b7fe0603bfa3180937dadfb66fbbdd119bbf8063338aa4a699075a3bfdbae8db7e5211d0917e9665a702fc9b0a0a901d08bea97654162d82a9f05622b060b634244779c33427eb7a29353a5f48b07cbefa72f3622ac5900bef77b71d6b314296f304c8426f451f32049b1f6af156a9dab702e8907d3cd72bb2c50493f4d593e731b285b70c803b74825b3524cda3205a8897106615260ac93c01c5ec14f5b11127783989d1824527e99e04f6a340e827b559f24db9292fcdd354838f9339a5fa1d7f6b2087f04835828b13463dd40927866f16ae33ed501ec0e6c4e63948768c5aeea3e4f6754985954bea7d61088c44430204ef491b74a64bde1358cecb2cad28ee6a3de5b752ff6a051104d88478653339457ac45ba44cbb65f54d1969d047cda746931d5e6a8b48e211416aefd5729f3d60b56b54e7f85aa2f42de3cb69419240c24e67139a11790a709edef2ac52cf35dd0a08af45926ebe9761f498ff83bfe263d6897ee97943a4b982fe3404ef0b4a45e06113c60340e0664f14799bf59cb4b3934b465fabefd87155905ee5309ba41e9e402973311831ea600b16437f71df39ee77130490c4d0227e5d1757fdc66af3ae6b9953053ed9aafca0160209858a7d4dd38fe10e0cb153672d08633ed6c54977aa0a6e67f9ff2f8c9d22dd7b21de08192960fd0e0da68d77c8d810db11dcaa61c725cd4092cbff76c8e1debd8d0361bb3f2e607911d45716f53067bdc0d89dd4889177765166a424e9fc0cb711201099dda213355e6639ac7eb86eca2ae0ab38b7f674f37ef8a6fcca1a6f52f55d9e1dcd631d2c3c82bba129172feb991d5af51afecd9d61a88b6832e4107480e392aed61a8644f551665ebff6b20953b635737a4f895e429fddcfe801f606fbda74b3bf6f5767d0fac14907fcfd0aa1d4c11b9e91b01d68052399b51a29f1ae6acd965109977c14a555cbcbd21ad8cb9f8853506d4bc21c01e62d61d7b21be1b923be54914e6b0a7ca84dd11f1159193e1184568a6134a6bbadf5b4df986edcf2019390ae841cfaa44435e28ce877d3dae4177992fa5d4e5c005876dbe3d1e63bec7dcc0942762b48b1ecc6c1a918409a8a72812a1e245c0c67be6e729c2b49bc6ee4d24a8f63e78e75db45655c26a9a78aff36fcd67117f26b8f654dca664b9f0e30681874cb749e1a692720078856286c2560b0292cc837933423147569350955c9571bf8941ba128fd339cb4268f46b94bc6ee203eb7026813706ea51c4f24c91866fc23a724bf2501327e6ae89c29f8db315dc28d2c7c719514036367e018f4835f63fdecd71f9bdced7132b6c4f8b13c69a517026fcd3622d67cb632320d5e7308f78f4b7cea11f6291b137851dc6cd6366f2785c71c3f237f81a7658b2a8d512b61e0ad5a4710b7b124151689fcb2116063fbff7e9115fed7b93de834970b838e49f8f8ba5f1f874c354078b5810a55ae289a56da563f1da6cd80a3757d6073fa55e016e45ac6cec1f69d871c92fd0ae9670c74249045e6b464787f9504128736309fed205f8df4d90e332908581298d9c75a3fa36ab0c3c9272e62de53ab290c803d67b696fd615c260a47bffad16746f18ba1a10a061bacbea9369693b3c042eec36bed289d7d12e52bca8aa1c2dff88ca7816498d25626d0f1e106ebb0b4a12138e00f3df5b1c2f49d98b1756e69b641b7c6353d99dbff050f4d76842c6cf1c2a4b062fc8e6336fa689b7c9d5c6b4ab8c15a5c20e514ff070a602d85ae52fa7810c22f8eeffd34a095b93342144f7a98d024216b3d68ed7bea047517bfcd83ec83febd1ba0e5858e2bdc1d8b1f7b0f89e90ccc432a3f930cb8209462e64556c5054c56ca2a85f16b32eb83a10459d13516faa4d23302b7607b9bd38dab2239ac9e9440c314433fdfb3ceadab4b4f87415ed6f240e017221f3b5f7ac196cdf54957bec42fe6893994b46de3d27dc7fb58ca88feb5b9e79cf20053d12530ac524337b22a3629bea52f40b06d3e2128f32060f9105847daed81d35f20e2002817434659baff64494c5b5c7f9216bfda38412a0f70511159dc73bb6bae1f8eaa0ef08d99bcb31f94f6be12c29c83df45926430b366c99fca3270c15fc4056398fdf3135b7779e3066a006961d1ac0ad1c83179ce39e87a96b722ec23aabc065badf3e188347a360772ca6a447abac7e6a44f0d4632d52926332e44a0a86bff5ce699fd063bdda3ffd4c41b53ded49fecec67f40599b934e16e3fd1bc063ad7026f8d71bfd4cbaf56599586774723194b692036f1b6bb242e2ffb9c600b5215b412764599476ce475c9e5b396fbcebd6be323dcf4d0048077400aac7500db41dc95fc7f7edbe7c9c2ec5ea89943fe13b42217eef530bbd023671509e12dfce4e1c1c82955d965e6a68aa66f6967dba48feda572db1f099d9a6dc4bc8edade852b5e824a06890dc48a6a6510ecaf8cf7620d757290e3166d431abecc624fa9ac2234d2eb783308ead45544910c633a94964b2ef5fbc409cb8835ac4147d384e12e0a5e13951f7de0ee13eafcb0ca0c04946d7804040c0a3cd088352424b097adb7aad1ca4495952f3e6c0158c02d2bcec33bfda69301434a84d9027ce02c0b9725dad118", "d894b86261436362e64241e61f6b3e6589daf64dc641f60570c4c0bf3b1f2ca3");
}
+static MuHash3072 FromInt(unsigned char i) {
+ unsigned char tmp[32] = {i, 0};
+ return MuHash3072(tmp);
+}
+
+BOOST_AUTO_TEST_CASE(muhash_tests)
+{
+ uint256 out;
+
+ for (int iter = 0; iter < 10; ++iter) {
+ uint256 res;
+ int table[4];
+ for (int i = 0; i < 4; ++i) {
+ table[i] = g_insecure_rand_ctx.randbits(3);
+ }
+ for (int order = 0; order < 4; ++order) {
+ MuHash3072 acc;
+ for (int i = 0; i < 4; ++i) {
+ int t = table[i ^ order];
+ if (t & 4) {
+ acc /= FromInt(t & 3);
+ } else {
+ acc *= FromInt(t & 3);
+ }
+ }
+ acc.Finalize(out);
+ if (order == 0) {
+ res = out;
+ } else {
+ BOOST_CHECK(res == out);
+ }
+ }
+
+ MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X
+ MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X, y=Y
+ MuHash3072 z; // x=X, y=Y, z=1
+ z *= x; // x=X, y=Y, z=X
+ z *= y; // x=X, y=Y, z=X*Y
+ y *= x; // x=X, y=Y*X, z=X*Y
+ z /= y; // x=X, y=Y*X, z=1
+ z.Finalize(out);
+
+ uint256 out2;
+ MuHash3072 a;
+ a.Finalize(out2);
+
+ BOOST_CHECK_EQUAL(out, out2);
+ }
+
+ MuHash3072 acc = FromInt(0);
+ acc *= FromInt(1);
+ acc /= FromInt(2);
+ acc.Finalize(out);
+ BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+
+ MuHash3072 acc2 = FromInt(0);
+ unsigned char tmp[32] = {1, 0};
+ acc2.Insert(tmp);
+ unsigned char tmp2[32] = {2, 0};
+ acc2.Remove(tmp2);
+ acc2.Finalize(out);
+ BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+
+ // Test MuHash3072 serialization
+ MuHash3072 serchk = FromInt(1); serchk *= FromInt(2);
+ std::string ser_exp = "1fa093295ea30a6a3acdc7b3f770fa538eff537528e990e2910e40bbcfd7f6696b1256901929094694b56316de342f593303dd12ac43e06dce1be1ff8301c845beb15468fff0ef002dbf80c29f26e6452bccc91b5cb9437ad410d2a67ea847887fa3c6a6553309946880fe20db2c73fe0641adbd4e86edfee0d9f8cd0ee1230898873dc13ed8ddcaf045c80faa082774279007a2253f8922ee3ef361d378a6af3ddaf180b190ac97e556888c36b3d1fb1c85aab9ccd46e3deaeb7b7cf5db067a7e9ff86b658cf3acd6662bbcce37232daa753c48b794356c020090c831a8304416e2aa7ad633c0ddb2f11be1be316a81be7f7e472071c042cb68faef549c221ebff209273638b741aba5a81675c45a5fa92fea4ca821d7a324cb1e1a2ccd3b76c4228ec8066dad2a5df6e1bd0de45c7dd5de8070bdb46db6c554cf9aefc9b7b2bbf9f75b1864d9f95005314593905c0109b71f703d49944ae94477b51dac10a816bb6d1c700bafabc8bd86fac8df24be519a2f2836b16392e18036cb13e48c5c010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
+ CDataStream ss_chk(SER_DISK, PROTOCOL_VERSION);
+ ss_chk << serchk;
+ BOOST_CHECK_EQUAL(ser_exp, HexStr(ss_chk.str()));
+
+ // Test MuHash3072 deserialization
+ MuHash3072 deserchk;
+ ss_chk >> deserchk;
+ uint256 out3;
+ serchk.Finalize(out);
+ deserchk.Finalize(out3);
+ BOOST_CHECK_EQUAL(HexStr(out), HexStr(out3));
+
+ // Test MuHash3072 overflow, meaning the internal data is larger than the modulus.
+ CDataStream ss_max(ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), SER_DISK, PROTOCOL_VERSION);
+ MuHash3072 overflowchk;
+ ss_max >> overflowchk;
+
+ uint256 out4;
+ overflowchk.Finalize(out4);
+ BOOST_CHECK_EQUAL(HexStr(out4), "3a31e6903aff0de9f62f9a9f7f8b861de76ce2cda09822b90014319ae5dc2271");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 7621751077..fdf51d8558 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -128,7 +128,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
break;
case 26:
- connman.SetServices(random_service, static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
+ connman.SetServices(random_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
break;
case 27:
connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 4783cc1c43..9668b84e7b 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -4,6 +4,7 @@
#include <crypto/hmac_sha256.h>
#include <crypto/hmac_sha512.h>
+#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -35,6 +36,7 @@ FUZZ_TARGET(crypto)
CSHA512 sha512;
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
+ MuHash3072 muhash;
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
@@ -60,6 +62,12 @@ FUZZ_TARGET(crypto)
(void)Hash(data);
(void)Hash160(data);
(void)sha512.Size();
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ muhash *= MuHash3072(data);
+ } else {
+ muhash /= MuHash3072(data);
+ }
break;
}
case 1: {
@@ -70,10 +78,11 @@ FUZZ_TARGET(crypto)
(void)sha256.Reset();
(void)sha3.Reset();
(void)sha512.Reset();
+ muhash = MuHash3072();
break;
}
case 2: {
- switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10)) {
case 0: {
data.resize(CHash160::OUTPUT_SIZE);
hash160.Finalize(data);
@@ -124,6 +133,11 @@ FUZZ_TARGET(crypto)
sha3.Finalize(data);
break;
}
+ case 10: {
+ uint256 out;
+ muhash.Finalize(out);
+ break;
+ }
}
break;
}
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
new file mode 100644
index 0000000000..8f843ca773
--- /dev/null
+++ b/src/test/fuzz/muhash.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/muhash.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <vector>
+
+FUZZ_TARGET(muhash)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+ if (data2.empty()) {
+ data2.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ MuHash3072 muhash;
+
+ // Test that MuHash result is consistent independent of order of operations
+ muhash.Insert(data);
+ muhash.Insert(data2);
+
+ uint256 out;
+ muhash.Finalize(out);
+
+ muhash = MuHash3072();
+ muhash.Insert(data2);
+ muhash.Insert(data);
+
+ uint256 out2;
+ muhash.Finalize(out2);
+
+ assert(out == out2);
+
+ // Test that removing all added elements brings the object back to it's initial state
+ muhash /= muhash;
+ muhash.Finalize(out);
+
+ MuHash3072 muhash2;
+ muhash2.Finalize(out2);
+
+ assert(out == out2);
+}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index ec02cf94e9..3ca921b5cf 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -13,6 +13,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <cstdint>
@@ -122,9 +123,7 @@ FUZZ_TARGET_INIT(net, initialize_net)
(void)node.GetCommonVersion();
(void)node.RelayAddrsWithConn();
- const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ?
- fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({NetPermissionFlags::PF_NONE, NetPermissionFlags::PF_BLOOMFILTER, NetPermissionFlags::PF_RELAY, NetPermissionFlags::PF_FORCERELAY, NetPermissionFlags::PF_NOBAN, NetPermissionFlags::PF_MEMPOOL, NetPermissionFlags::PF_ISIMPLICIT, NetPermissionFlags::PF_ALL}) :
- static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ const NetPermissionFlags net_permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
(void)node.HasPermission(net_permission_flags);
(void)node.ConnectedThroughNetwork();
}
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index 3620e16d30..544a33047b 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -17,18 +17,7 @@ FUZZ_TARGET(net_permissions)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(32);
- const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ? fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({
- NetPermissionFlags::PF_NONE,
- NetPermissionFlags::PF_BLOOMFILTER,
- NetPermissionFlags::PF_RELAY,
- NetPermissionFlags::PF_FORCERELAY,
- NetPermissionFlags::PF_NOBAN,
- NetPermissionFlags::PF_MEMPOOL,
- NetPermissionFlags::PF_ADDR,
- NetPermissionFlags::PF_ISIMPLICIT,
- NetPermissionFlags::PF_ALL,
- }) :
- static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ const NetPermissionFlags net_permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
NetWhitebindPermissions net_whitebind_permissions;
bilingual_str error_net_whitebind_permissions;
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 97e2b04a7d..58637662c5 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -13,6 +13,7 @@
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
@@ -63,13 +64,14 @@ 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();
- CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
- CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY).release();
+ 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);
+
+ // fuzzed_data_provider is fully consumed after this call, don't use it
+ CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
try {
g_setup->m_node.peerman->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream,
GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 0ff95ea1ae..db52da2f7e 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -47,15 +47,12 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
- const ServiceFlags service_flags = ServiceFlags(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
- const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
- peers.push_back(MakeUnique<CNode>(i, service_flags, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, conn_type).release());
+ 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 ff79dfe5f3..4e8e501895 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -22,6 +22,7 @@
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
#include <uint256.h>
@@ -86,6 +87,14 @@ template <typename T>
return obj;
}
+template <typename WeakEnumType, size_t size>
+[[nodiscard]] WeakEnumType ConsumeWeakEnum(FuzzedDataProvider& fuzzed_data_provider, const WeakEnumType (&all_types)[size]) noexcept
+{
+ return fuzzed_data_provider.ConsumeBool() ?
+ fuzzed_data_provider.PickValueInArray<WeakEnumType>(all_types) :
+ WeakEnumType(fuzzed_data_provider.ConsumeIntegral<typename std::underlying_type<WeakEnumType>::type>());
+}
+
[[nodiscard]] inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
@@ -283,22 +292,45 @@ inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcep
inline CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
- return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
}
-inline CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
+template <bool ReturnUniquePtr = false>
+auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept
{
- const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
- const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ 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);
const SOCKET socket = INVALID_SOCKET;
const CAddress address = ConsumeAddress(fuzzed_data_provider);
const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider);
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
- const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
+ const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
- return {node_id, local_services, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion};
+ if constexpr (ReturnUniquePtr) {
+ return std::make_unique<CNode>(node_id, local_services, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion);
+ } else {
+ return CNode{node_id, local_services, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion};
+ }
+}
+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)
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 1208e92762..e25036be26 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -30,4 +30,35 @@ struct ConnmanTestMsg : public CConnman {
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const;
};
+constexpr ServiceFlags ALL_SERVICE_FLAGS[]{
+ NODE_NONE,
+ NODE_NETWORK,
+ NODE_BLOOM,
+ NODE_WITNESS,
+ NODE_COMPACT_FILTERS,
+ NODE_NETWORK_LIMITED,
+};
+
+constexpr NetPermissionFlags ALL_NET_PERMISSION_FLAGS[]{
+ NetPermissionFlags::PF_NONE,
+ NetPermissionFlags::PF_BLOOMFILTER,
+ NetPermissionFlags::PF_RELAY,
+ NetPermissionFlags::PF_FORCERELAY,
+ NetPermissionFlags::PF_NOBAN,
+ NetPermissionFlags::PF_MEMPOOL,
+ NetPermissionFlags::PF_ADDR,
+ NetPermissionFlags::PF_DOWNLOAD,
+ NetPermissionFlags::PF_ISIMPLICIT,
+ NetPermissionFlags::PF_ALL,
+};
+
+constexpr ConnectionType ALL_CONNECTION_TYPES[]{
+ ConnectionType::INBOUND,
+ ConnectionType::OUTBOUND_FULL_RELAY,
+ ConnectionType::MANUAL,
+ ConnectionType::FEELER,
+ ConnectionType::BLOCK_RELAY,
+ ConnectionType::ADDR_FETCH,
+};
+
#endif // BITCOIN_TEST_UTIL_NET_H
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/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/test_framework/muhash.py b/test/functional/test_framework/muhash.py
index 97d02359cb..183548f71f 100644
--- a/test/functional/test_framework/muhash.py
+++ b/test/functional/test_framework/muhash.py
@@ -78,11 +78,13 @@ class MuHash3072:
def insert(self, data):
"""Insert a byte array data in the set."""
- self.numerator = (self.numerator * data_to_num3072(data)) % self.MODULUS
+ data_hash = hashlib.sha256(data).digest()
+ self.numerator = (self.numerator * data_to_num3072(data_hash)) % self.MODULUS
def remove(self, data):
"""Remove a byte array from the set."""
- self.denominator = (self.denominator * data_to_num3072(data)) % self.MODULUS
+ data_hash = hashlib.sha256(data).digest()
+ self.denominator = (self.denominator * data_to_num3072(data_hash)) % self.MODULUS
def digest(self):
"""Extract the final hash. Does not modify this object."""
@@ -93,12 +95,12 @@ class MuHash3072:
class TestFrameworkMuhash(unittest.TestCase):
def test_muhash(self):
muhash = MuHash3072()
- muhash.insert([0]*32)
- muhash.insert([1] + [0]*31)
- muhash.remove([2] + [0]*31)
+ muhash.insert(b'\x00' * 32)
+ muhash.insert((b'\x01' + b'\x00' * 31))
+ muhash.remove((b'\x02' + b'\x00' * 31))
finalized = muhash.digest()
# This mirrors the result in the C++ MuHash3072 unit test
- self.assertEqual(finalized[::-1].hex(), "a44e16d5e34d259b349af21c06e65d653915d2e208e4e03f389af750dc0bfdc3")
+ self.assertEqual(finalized[::-1].hex(), "10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")
def test_chacha20(self):
def chacha_check(key, result):
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)