aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/m4/l_atomic.m47
-rwxr-xr-xci/test/00_setup_env_native_asan.sh2
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_msan.sh3
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_valgrind.sh7
-rwxr-xr-xci/test/00_setup_env_native_msan.sh3
-rwxr-xr-xci/test/00_setup_env_native_tidy.sh2
-rwxr-xr-xci/test/00_setup_env_native_tsan.sh2
-rwxr-xr-xci/test/00_setup_env_native_valgrind.sh9
-rwxr-xr-xci/test/00_setup_env_s390x.sh4
-rwxr-xr-xci/test/01_base_install.sh2
-rw-r--r--configure.ac98
-rwxr-xr-xcontrib/devtools/gen-manpages.py4
-rwxr-xr-xcontrib/guix/libexec/build.sh5
-rw-r--r--contrib/guix/manifest.scm10
-rw-r--r--contrib/guix/patches/binutils-unaligned-default.patch22
-rw-r--r--contrib/guix/patches/vmov-alignment.patch288
-rw-r--r--contrib/init/bitcoind.service3
-rw-r--r--contrib/valgrind.supp4
-rw-r--r--depends/hosts/linux.mk6
-rw-r--r--depends/packages.md2
-rw-r--r--depends/packages/boost.mk5
-rw-r--r--depends/patches/boost/process_macos_sdk.patch27
-rw-r--r--doc/build-freebsd.md7
-rw-r--r--doc/build-openbsd.md4
-rw-r--r--doc/descriptors.md13
-rw-r--r--doc/developer-notes.md2
-rw-r--r--doc/external-signer.md3
-rw-r--r--doc/managing-wallets.md16
-rw-r--r--doc/release-notes-27679.md2
-rw-r--r--doc/release-notes/release-notes-25.2.md74
-rw-r--r--doc/release-notes/release-notes-27.0.md217
-rw-r--r--src/.clang-tidy1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.minisketch.include4
-rw-r--r--src/bench/bip324_ecdh.cpp2
-rw-r--r--src/bench/ellswift.cpp2
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/blockencodings.cpp15
-rw-r--r--src/blockencodings.h4
-rw-r--r--src/common/run_command.cpp25
-rw-r--r--src/core_read.cpp2
-rw-r--r--src/crypto/chacha20poly1305.cpp13
-rw-r--r--src/crypto/sha256.cpp4
-rw-r--r--src/external_signer.cpp8
-rw-r--r--src/init.cpp45
-rw-r--r--src/interfaces/wallet.h2
-rw-r--r--src/kernel/chainparams.cpp2
-rw-r--r--src/kernel/checks.cpp5
-rw-r--r--src/merkleblock.cpp3
-rw-r--r--src/minisketch/.cirrus.yml57
-rwxr-xr-xsrc/minisketch/ci/cirrus.sh8
-rw-r--r--src/minisketch/ci/linux-debian.Dockerfile6
-rw-r--r--src/minisketch/configure.ac6
-rw-r--r--src/minisketch/src/int_utils.h34
-rw-r--r--src/net.cpp6
-rw-r--r--src/net_processing.cpp4
-rw-r--r--src/netbase.cpp6
-rw-r--r--src/node/interfaces.cpp1
-rw-r--r--src/policy/rbf.cpp8
-rw-r--r--src/qt/guiutil.cpp32
-rw-r--r--src/qt/notificator.cpp8
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/walletmodel.cpp9
-rw-r--r--src/qt/walletmodel.h2
-rw-r--r--src/qt/walletview.cpp2
-rw-r--r--src/randomenv.cpp10
-rw-r--r--src/rpc/node.cpp7
-rw-r--r--src/rpc/util.cpp6
-rw-r--r--src/rpc/util.h2
-rw-r--r--src/script/descriptor.cpp9
-rw-r--r--src/script/sign.cpp2
-rw-r--r--src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml2
-rw-r--r--src/secp256k1/.github/actions/run-in-docker-action/action.yml5
-rw-r--r--src/secp256k1/CMakeLists.txt41
-rw-r--r--src/secp256k1/CONTRIBUTING.md2
-rw-r--r--src/secp256k1/README.md6
-rwxr-xr-xsrc/secp256k1/ci/ci.sh3
-rw-r--r--src/secp256k1/cmake/AllTargetsCompileOptions.cmake12
-rw-r--r--src/secp256k1/configure.ac29
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.h4
-rw-r--r--src/secp256k1/doc/release-process.md72
-rw-r--r--src/secp256k1/include/secp256k1.h66
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h2
-rw-r--r--src/secp256k1/include/secp256k1_ellswift.h4
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h10
-rw-r--r--src/secp256k1/include/secp256k1_preallocated.h14
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h20
-rw-r--r--src/secp256k1/include/secp256k1_schnorrsig.h4
-rw-r--r--src/secp256k1/src/assumptions.h100
-rw-r--r--src/secp256k1/src/checkmem.h7
-rw-r--r--src/secp256k1/src/field.h4
-rw-r--r--src/secp256k1/src/modules/ellswift/tests_impl.h8
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h59
-rw-r--r--src/secp256k1/src/scalar_impl.h4
-rw-r--r--src/secp256k1/src/secp256k1.c39
-rw-r--r--src/secp256k1/src/tests.c67
-rw-r--r--src/secp256k1/src/util.h16
-rwxr-xr-xsrc/secp256k1/tools/check-abi.sh27
-rw-r--r--src/test/argsman_tests.cpp4
-rw-r--r--src/test/blockencodings_tests.cpp10
-rw-r--r--src/test/feefrac_tests.cpp39
-rw-r--r--src/test/fuzz/feeratediagram.cpp16
-rw-r--r--src/test/fuzz/package_eval.cpp8
-rw-r--r--src/test/fuzz/partially_downloaded_block.cpp4
-rw-r--r--src/test/fuzz/rbf.cpp83
-rw-r--r--src/test/fuzz/util/descriptor.cpp2
-rw-r--r--src/test/miniscript_tests.cpp1
-rw-r--r--src/test/rbf_tests.cpp144
-rw-r--r--src/test/rpc_tests.cpp1
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/script_tests.cpp24
-rw-r--r--src/test/system_tests.cpp14
-rw-r--r--src/test/util_tests.cpp5
-rw-r--r--src/test/validation_block_tests.cpp1
-rw-r--r--src/txmempool.cpp6
-rw-r--r--src/txmempool.h4
-rw-r--r--src/univalue/include/univalue.h1
-rw-r--r--src/univalue/lib/univalue_write.cpp3
-rw-r--r--src/univalue/test/object.cpp2
-rw-r--r--src/util/feefrac.cpp40
-rw-r--r--src/util/feefrac.h15
-rw-r--r--src/util/string.h1
-rw-r--r--src/util/subprocess.h1614
-rw-r--r--src/util/time.cpp88
-rw-r--r--src/util/time.h3
-rw-r--r--src/validation.cpp7
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp21
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h8
-rw-r--r--src/wallet/interfaces.cpp4
-rw-r--r--src/wallet/receive.cpp1
-rw-r--r--src/wallet/rpc/addresses.cpp8
-rw-r--r--src/wallet/rpc/backup.cpp1
-rw-r--r--src/wallet/scriptpubkeyman.cpp1
-rw-r--r--src/wallet/scriptpubkeyman.h1
-rw-r--r--src/wallet/wallet.cpp6
-rw-r--r--src/wallet/wallet.h4
-rw-r--r--src/zmq/zmqnotificationinterface.cpp8
-rw-r--r--src/zmq/zmqutil.h3
-rwxr-xr-xtest/functional/feature_assumeutxo.py14
-rwxr-xr-xtest/functional/feature_index_prune.py2
-rwxr-xr-xtest/functional/interface_zmq.py21
-rwxr-xr-xtest/functional/mempool_limit.py54
-rwxr-xr-xtest/functional/mocks/signer.py31
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py11
-rwxr-xr-xtest/functional/p2p_compactblocks_hb.py13
-rwxr-xr-xtest/functional/p2p_disconnect_ban.py8
-rwxr-xr-xtest/functional/p2p_handshake.py1
-rwxr-xr-xtest/functional/p2p_tx_download.py43
-rwxr-xr-xtest/functional/rpc_packages.py73
-rwxr-xr-xtest/functional/rpc_psbt.py21
-rwxr-xr-xtest/functional/rpc_setban.py10
-rwxr-xr-xtest/functional/rpc_signrawtransactionwithkey.py2
-rwxr-xr-xtest/functional/rpc_uptime.py2
-rwxr-xr-xtest/functional/test_framework/test_framework.py2
-rw-r--r--test/functional/test_framework/util.py59
-rwxr-xr-xtest/functional/test_framework/wallet_util.py58
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_groups.py5
-rwxr-xr-xtest/functional/wallet_importdescriptors.py2
-rwxr-xr-xtest/functional/wallet_send.py23
-rwxr-xr-xtest/functional/wallet_signer.py12
-rwxr-xr-xtest/functional/wallet_signrawtransactionwithwallet.py2
-rwxr-xr-xtest/fuzz/test_runner.py24
-rwxr-xr-xtest/lint/lint-includes.py1
164 files changed, 3207 insertions, 1343 deletions
diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4
index aa00168fce..859ddaabbb 100644
--- a/build-aux/m4/l_atomic.m4
+++ b/build-aux/m4/l_atomic.m4
@@ -7,7 +7,7 @@ dnl warranty.
# Clang, when building for 32-bit,
# and linking against libstdc++, requires linking with
# -latomic if using the C++ atomic library.
-# Can be tested with: clang++ test.cpp -m32
+# Can be tested with: clang++ -std=c++20 test.cpp -m32
#
# Sourced from http://bugs.debian.org/797228
@@ -27,8 +27,11 @@ m4_define([_CHECK_ATOMIC_testbody], [[
auto t1 = t.load();
t.compare_exchange_strong(t1, 3s);
- std::atomic<int64_t> a{};
+ std::atomic<double> d{};
+ d.store(3.14);
+ auto d1 = d.load();
+ std::atomic<int64_t> a{};
int64_t v = 5;
int64_t r = a.fetch_add(v);
return static_cast<int>(r);
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index 911b4726ec..668e9ecc8a 100755
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8
export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
# Only install BCC tracing packages in Cirrus CI.
if [[ "${CIRRUS_CI}" == "true" ]]; then
- BPFCC_PACKAGE="" # Temporarily disabled "bpfcc-tools linux-headers-$(uname --kernel-release)"
+ BPFCC_PACKAGE="bpfcc-tools linux-headers-$(uname --kernel-release)"
export CI_CONTAINER_CAP="--privileged -v /sys/kernel:/sys/kernel:rw"
else
BPFCC_PACKAGE=""
diff --git a/ci/test/00_setup_env_native_fuzz_with_msan.sh b/ci/test/00_setup_env_native_fuzz_with_msan.sh
index 0a9dee2ed8..f1c358082d 100755
--- a/ci/test/00_setup_env_native_fuzz_with_msan.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_msan.sh
@@ -17,7 +17,8 @@ export PACKAGES="ninja-build"
# BDB generates false-positives and will be removed in future
export DEP_OPTS="DEBUG=1 NO_BDB=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,memory --disable-hardening --with-asm=no CFLAGS='${MSAN_FLAGS}' CPPFLAGS='-DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
+# _FORTIFY_SOURCE is not compatible with MSAN.
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,memory CPPFLAGS='-DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE -U_FORTIFY_SOURCE'"
export USE_MEMORY_SANITIZER="true"
export RUN_UNIT_TESTS="false"
export RUN_FUNCTIONAL_TESTS="false"
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
index 4f80d7ed42..bf4d1573e3 100755
--- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -6,15 +6,14 @@
export LC_ALL=C.UTF-8
-export CI_IMAGE_NAME_TAG="docker.io/debian:bookworm"
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export CONTAINER_NAME=ci_native_fuzz_valgrind
-export PACKAGES="clang llvm libclang-rt-dev libevent-dev libboost-dev libsqlite3-dev valgrind"
+export PACKAGES="clang-16 llvm-16 libclang-rt-16-dev libevent-dev libboost-dev libsqlite3-dev valgrind"
export NO_DEPENDS=1
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export FUZZ_TESTS_CONFIG="--valgrind"
export GOAL="install"
-# Temporarily pin dwarf 4, until using Valgrind 3.20 or later
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang CXX=clang++ CFLAGS=-gdwarf-4 CXXFLAGS=-gdwarf-4"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang-16 CXX=clang++-16"
export CCACHE_MAXSIZE=200M
diff --git a/ci/test/00_setup_env_native_msan.sh b/ci/test/00_setup_env_native_msan.sh
index cbcd51e218..dd465cac2e 100755
--- a/ci/test/00_setup_env_native_msan.sh
+++ b/ci/test/00_setup_env_native_msan.sh
@@ -17,7 +17,8 @@ export PACKAGES="ninja-build"
# BDB generates false-positives and will be removed in future
export DEP_OPTS="DEBUG=1 NO_BDB=1 NO_QT=1 CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
-export BITCOIN_CONFIG="--with-sanitizers=memory --disable-hardening --with-asm=no CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
+# _FORTIFY_SOURCE is not compatible with MSAN.
+export BITCOIN_CONFIG="--with-sanitizers=memory CPPFLAGS='-U_FORTIFY_SOURCE'"
export USE_MEMORY_SANITIZER="true"
export RUN_FUNCTIONAL_TESTS="false"
export CCACHE_MAXSIZE=250M
diff --git a/ci/test/00_setup_env_native_tidy.sh b/ci/test/00_setup_env_native_tidy.sh
index 4c8658479b..5f422bbdb6 100755
--- a/ci/test/00_setup_env_native_tidy.sh
+++ b/ci/test/00_setup_env_native_tidy.sh
@@ -16,5 +16,5 @@ export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=false
export RUN_TIDY=true
export GOAL="install"
-export BITCOIN_CONFIG="CC=clang-${TIDY_LLVM_V} CXX=clang++-${TIDY_LLVM_V} --with-incompatible-bdb --disable-hardening CFLAGS='-O0 -g0' CXXFLAGS='-O0 -g0 -I/usr/lib/llvm-${TIDY_LLVM_V}/lib/clang/${TIDY_LLVM_V}/include'"
+export BITCOIN_CONFIG="CC=clang-${TIDY_LLVM_V} CXX=clang++-${TIDY_LLVM_V} --with-incompatible-bdb --disable-hardening CFLAGS='-O0 -g0' CXXFLAGS='-O0 -g0'"
export CCACHE_MAXSIZE=200M
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 23ab1ae000..3fcaa8c6c6 100755
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -11,4 +11,4 @@ export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export PACKAGES="clang-18 llvm-18 libclang-rt-18-dev libc++abi-18-dev libc++-18-dev python3-zmq"
export DEP_OPTS="CC=clang-18 CXX='clang++-18 -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER -DDEBUG_LOCKCONTENTION' CXXFLAGS='-g' --with-sanitizers=thread"
+export BITCOIN_CONFIG="--enable-zmq CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER -DDEBUG_LOCKCONTENTION' --with-sanitizers=thread"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index 9bdb2b7860..720e522a6a 100755
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -6,12 +6,11 @@
export LC_ALL=C.UTF-8
-export CI_IMAGE_NAME_TAG="docker.io/debian:bookworm"
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export CONTAINER_NAME=ci_native_valgrind
-export PACKAGES="valgrind clang llvm libclang-rt-dev python3-zmq libevent-dev libboost-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev"
+export PACKAGES="valgrind clang-16 llvm-16 libclang-rt-16-dev python3-zmq libevent-dev libboost-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 feature_init,rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
+export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
export GOAL="install"
-# Temporarily pin dwarf 4, until using Valgrind 3.20 or later
-export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++ CFLAGS=-gdwarf-4 CXXFLAGS=-gdwarf-4" # TODO enable GUI
+export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang-16 CXX=clang++-16" # TODO enable GUI
diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh
index ca84ecce51..2fd94e253c 100755
--- a/ci/test/00_setup_env_s390x.sh
+++ b/ci/test/00_setup_env_s390x.sh
@@ -9,8 +9,8 @@ export LC_ALL=C.UTF-8
export HOST=s390x-linux-gnu
export PACKAGES="python3-zmq"
export CONTAINER_NAME=ci_s390x
-export CI_IMAGE_NAME_TAG="docker.io/s390x/debian:bookworm"
-export TEST_RUNNER_EXTRA="--exclude feature_init,rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
+export CI_IMAGE_NAME_TAG="docker.io/s390x/ubuntu:24.04"
+export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
export RUN_FUNCTIONAL_TESTS=true
export GOAL="install"
export BITCOIN_CONFIG="--enable-reduce-exports"
diff --git a/ci/test/01_base_install.sh b/ci/test/01_base_install.sh
index 6f1498963a..25962a53e5 100755
--- a/ci/test/01_base_install.sh
+++ b/ci/test/01_base_install.sh
@@ -36,7 +36,7 @@ if [ -n "$PIP_PACKAGES" ]; then
fi
if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then
- ${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-18.1.1" /msan/llvm-project
+ ${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-18.1.3" /msan/llvm-project
cmake -G Ninja -B /msan/clang_build/ \
-DLLVM_ENABLE_PROJECTS="clang" \
diff --git a/configure.ac b/configure.ac
index 964b7d0942..febb352cdb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -307,9 +307,9 @@ AC_ARG_ENABLE([werror],
[enable_werror=no])
AC_ARG_ENABLE([external-signer],
- [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is auto, requires Boost::Process)])],
+ [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is yes)])],
[use_external_signer=$enableval],
- [use_external_signer=auto])
+ [use_external_signer=yes])
AC_LANG_PUSH([C++])
@@ -968,24 +968,6 @@ AC_CHECK_DECLS([setsid])
AC_CHECK_DECLS([pipe2])
-AC_CHECK_FUNCS([timingsafe_bcmp])
-
-AC_MSG_CHECKING([for __builtin_clzl])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[
- (void) __builtin_clzl(0);
- ]])],
- [ AC_MSG_RESULT([yes]); have_clzl=yes; AC_DEFINE([HAVE_BUILTIN_CLZL], [1], [Define this symbol if you have __builtin_clzl])],
- [ AC_MSG_RESULT([no]); have_clzl=no;]
-)
-
-AC_MSG_CHECKING([for __builtin_clzll])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[
- (void) __builtin_clzll(0);
- ]])],
- [ AC_MSG_RESULT([yes]); have_clzll=yes; AC_DEFINE([HAVE_BUILTIN_CLZLL], [1], [Define this symbol if you have __builtin_clzll])],
- [ AC_MSG_RESULT([no]); have_clzll=no;]
-)
-
dnl Check for malloc_info (for memory statistics information in getmemoryinfo)
AC_MSG_CHECKING([for getmemoryinfo])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <malloc.h>]],
@@ -1086,22 +1068,6 @@ if test "$use_thread_local" = "yes" || test "$use_thread_local" = "auto"; then
LDFLAGS="$TEMP_LDFLAGS"
fi
-dnl check for gmtime_r(), fallback to gmtime_s() if that is unavailable
-dnl fail if neither are available.
-AC_MSG_CHECKING([for gmtime_r])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <ctime>]],
- [[ gmtime_r((const time_t *) nullptr, (struct tm *) nullptr); ]])],
- [ AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_GMTIME_R], [1], [Define this symbol if gmtime_r is available]) ],
- [ AC_MSG_RESULT([no]);
- AC_MSG_CHECKING([for gmtime_s]);
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <ctime>]],
- [[ gmtime_s((struct tm *) nullptr, (const time_t *) nullptr); ]])],
- [ AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no]); AC_MSG_ERROR([Both gmtime_r and gmtime_s are unavailable]) ]
- )
- ]
-)
-
dnl Check for different ways of gathering OS randomness
AC_MSG_CHECKING([for Linux getrandom function])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -1432,56 +1398,14 @@ if test "$use_boost" = "yes"; then
fi
fi
-if test "$use_external_signer" != "no"; then
- AC_MSG_CHECKING([whether Boost.Process can be used])
- TEMP_CXXFLAGS="$CXXFLAGS"
- dnl Boost 1.78 requires the following workaround.
- dnl See: https://github.com/boostorg/process/issues/235
- CXXFLAGS="$CXXFLAGS -Wno-error=narrowing"
- TEMP_CPPFLAGS="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- TEMP_LDFLAGS="$LDFLAGS"
- dnl Boost 1.73 and older require the following workaround.
- LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS"
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #define BOOST_PROCESS_USE_STD_FS
- #include <boost/process.hpp>
- ]],[[
- namespace bp = boost::process;
- bp::opstream stdin_stream;
- bp::ipstream stdout_stream;
- bp::child c("dummy", bp::std_out > stdout_stream, bp::std_err > stdout_stream, bp::std_in < stdin_stream);
- stdin_stream << std::string{"test"} << std::endl;
- if (c.running()) c.terminate();
- c.wait();
- c.exit_code();
- ]])],
- [have_boost_process="yes"],
- [have_boost_process="no"])
- LDFLAGS="$TEMP_LDFLAGS"
- CPPFLAGS="$TEMP_CPPFLAGS"
- CXXFLAGS="$TEMP_CXXFLAGS"
- AC_MSG_RESULT([$have_boost_process])
- if test "$have_boost_process" = "yes"; then
- case $host in
- dnl Boost Process for Windows uses Boost ASIO. Boost ASIO performs
- dnl pre-main init of Windows networking libraries, which we do not
- dnl want.
- *mingw*)
- use_external_signer="no"
- ;;
- *)
- use_external_signer="yes"
- AC_DEFINE([ENABLE_EXTERNAL_SIGNER], [1], [Define if external signer support is enabled])
- AC_DEFINE([BOOST_PROCESS_USE_STD_FS], [1], [Defined to avoid Boost::Process trying to use Boost Filesystem])
- ;;
- esac
- else
- if test "$use_external_signer" = "yes"; then
- AC_MSG_ERROR([External signing is not supported for this Boost version])
- fi
- use_external_signer="no";
- fi
+case $host in
+ dnl Re-enable it after enabling Windows support in cpp-subprocess.
+ *mingw*)
+ use_external_signer="no"
+ ;;
+esac
+if test "$use_external_signer" = "yes"; then
+ AC_DEFINE([ENABLE_EXTERNAL_SIGNER], [1], [Define if external signer support is enabled])
fi
AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = "yes"])
@@ -1796,7 +1720,6 @@ AM_CONDITIONAL([USE_UPNP], [test "$use_upnp" = "yes"])
dnl for minisketch
AM_CONDITIONAL([ENABLE_CLMUL], [test "$enable_clmul" = "yes"])
-AM_CONDITIONAL([HAVE_CLZ], [test "$have_clzl$have_clzll" = "yesyes"])
AC_DEFINE([CLIENT_VERSION_MAJOR], [_CLIENT_VERSION_MAJOR], [Major version])
AC_DEFINE([CLIENT_VERSION_MINOR], [_CLIENT_VERSION_MINOR], [Minor version])
@@ -1862,7 +1785,6 @@ AC_SUBST(MINIUPNPC_CPPFLAGS)
AC_SUBST(MINIUPNPC_LIBS)
AC_SUBST(NATPMP_CPPFLAGS)
AC_SUBST(NATPMP_LIBS)
-AC_SUBST(HAVE_GMTIME_R)
AC_SUBST(HAVE_FDATASYNC)
AC_SUBST(HAVE_FULLFSYNC)
AC_SUBST(HAVE_O_CLOEXEC)
diff --git a/contrib/devtools/gen-manpages.py b/contrib/devtools/gen-manpages.py
index 2860e7db99..92acd9a403 100755
--- a/contrib/devtools/gen-manpages.py
+++ b/contrib/devtools/gen-manpages.py
@@ -62,6 +62,10 @@ with tempfile.NamedTemporaryFile('w', suffix='.h2m') as footer:
# Copyright is the same for all binaries, so just use the first.
footer.write('[COPYRIGHT]\n')
footer.write('\n'.join(versions[0][2]).strip())
+ # Create SEE ALSO section
+ footer.write('\n[SEE ALSO]\n')
+ footer.write(', '.join(s.rpartition('/')[2] + '(1)' for s in BINARIES))
+ footer.write('\n')
footer.flush()
# Call the binaries through help2man to produce a manual page for each of them.
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 9624221880..1e9b682f3f 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -75,9 +75,8 @@ export OBJC_INCLUDE_PATH="${NATIVE_GCC}/include"
export OBJCPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include"
case "$HOST" in
- *darwin*)
- export LIBRARY_PATH="${NATIVE_GCC}/lib"
- ;;
+ *darwin*) export LIBRARY_PATH="${NATIVE_GCC}/lib" ;;
+ *mingw*) export LIBRARY_PATH="${NATIVE_GCC}/lib" ;;
*)
NATIVE_GCC_STATIC="$(store_path gcc-toolchain static)"
export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC_STATIC}/lib"
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 87dcf49bcb..8f13c642d3 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -110,12 +110,15 @@ desirable for building Bitcoin Core release binaries."
(define (gcc-mingw-patches gcc)
(package-with-extra-patches gcc
- (search-our-patches "gcc-remap-guix-store.patch"
- "vmov-alignment.patch")))
+ (search-our-patches "gcc-remap-guix-store.patch")))
+
+(define (binutils-mingw-patches binutils)
+ (package-with-extra-patches binutils
+ (search-our-patches "binutils-unaligned-default.patch")))
(define (make-mingw-pthreads-cross-toolchain target)
"Create a cross-compilation toolchain package for TARGET"
- (let* ((xbinutils (cross-binutils target))
+ (let* ((xbinutils (binutils-mingw-patches (cross-binutils target)))
(pthreads-xlibc mingw-w64-x86_64-winpthreads)
(pthreads-xgcc (cross-gcc target
#:xgcc (gcc-mingw-patches mingw-w64-base-gcc)
@@ -517,7 +520,6 @@ inspecting signatures in Mach-O binaries.")
(cond ((string-suffix? "-mingw32" target)
(list ;; Native GCC 12 toolchain
gcc-toolchain-12
- (list gcc-toolchain-12 "static")
zip
(make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32")
nsis-x86_64
diff --git a/contrib/guix/patches/binutils-unaligned-default.patch b/contrib/guix/patches/binutils-unaligned-default.patch
new file mode 100644
index 0000000000..d1bc71aee1
--- /dev/null
+++ b/contrib/guix/patches/binutils-unaligned-default.patch
@@ -0,0 +1,22 @@
+commit 6537181f59ed186a341db621812a6bc35e22eaf6
+Author: fanquake <fanquake@gmail.com>
+Date: Wed Apr 10 12:15:52 2024 +0200
+
+ build: turn on -muse-unaligned-vector-move by default
+
+ This allows us to avoid (more invasively) patching GCC, to avoid
+ unaligned instruction use.
+
+diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
+index e0632681477..14a9653abdf 100644
+--- a/gas/config/tc-i386.c
++++ b/gas/config/tc-i386.c
+@@ -801,7 +801,7 @@ static unsigned int no_cond_jump_promotion = 0;
+ static unsigned int sse2avx;
+
+ /* Encode aligned vector move as unaligned vector move. */
+-static unsigned int use_unaligned_vector_move;
++static unsigned int use_unaligned_vector_move = 1;
+
+ /* Encode scalar AVX instructions with specific vector length. */
+ static enum
diff --git a/contrib/guix/patches/vmov-alignment.patch b/contrib/guix/patches/vmov-alignment.patch
deleted file mode 100644
index 96e1cb7cd1..0000000000
--- a/contrib/guix/patches/vmov-alignment.patch
+++ /dev/null
@@ -1,288 +0,0 @@
-Description: Use unaligned VMOV instructions
-Author: Stephen Kitt <skitt@debian.org>
-Bug-Debian: https://bugs.debian.org/939559
-See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
-
-Based on a patch originally by Claude Heiland-Allen <claude@mathr.co.uk>
-
---- a/gcc/config/i386/sse.md
-+++ b/gcc/config/i386/sse.md
-@@ -1058,17 +1058,11 @@
- {
- if (FLOAT_MODE_P (GET_MODE_INNER (<MODE>mode)))
- {
-- if (misaligned_operand (operands[1], <MODE>mode))
-- return "vmovu<ssemodesuffix>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
-- else
-- return "vmova<ssemodesuffix>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
-+ return "vmovu<ssemodesuffix>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
- }
- else
- {
-- if (misaligned_operand (operands[1], <MODE>mode))
-- return "vmovdqu<ssescalarsize>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
-- else
-- return "vmovdqa<ssescalarsize>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
-+ return "vmovdqu<ssescalarsize>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}";
- }
- }
- [(set_attr "type" "ssemov")
-@@ -1184,17 +1178,11 @@
- {
- if (FLOAT_MODE_P (GET_MODE_INNER (<MODE>mode)))
- {
-- if (misaligned_operand (operands[0], <MODE>mode))
-- return "vmovu<ssemodesuffix>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
-- else
-- return "vmova<ssemodesuffix>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
-+ return "vmovu<ssemodesuffix>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
- }
- else
- {
-- if (misaligned_operand (operands[0], <MODE>mode))
-- return "vmovdqu<ssescalarsize>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
-- else
-- return "vmovdqa<ssescalarsize>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
-+ return "vmovdqu<ssescalarsize>\t{%1, %0%{%2%}|%0%{%2%}, %1}";
- }
- }
- [(set_attr "type" "ssemov")
-@@ -7806,7 +7794,7 @@
- "TARGET_SSE && !(MEM_P (operands[0]) && MEM_P (operands[1]))"
- "@
- %vmovlps\t{%1, %0|%q0, %1}
-- %vmovaps\t{%1, %0|%0, %1}
-+ %vmovups\t{%1, %0|%0, %1}
- %vmovlps\t{%1, %d0|%d0, %q1}"
- [(set_attr "type" "ssemov")
- (set_attr "prefix" "maybe_vex")
-@@ -13997,29 +13985,15 @@
- switch (<MODE>mode)
- {
- case E_V8DFmode:
-- if (misaligned_operand (operands[2], <ssequartermode>mode))
-- return "vmovupd\t{%2, %x0|%x0, %2}";
-- else
-- return "vmovapd\t{%2, %x0|%x0, %2}";
-+ return "vmovupd\t{%2, %x0|%x0, %2}";
- case E_V16SFmode:
-- if (misaligned_operand (operands[2], <ssequartermode>mode))
-- return "vmovups\t{%2, %x0|%x0, %2}";
-- else
-- return "vmovaps\t{%2, %x0|%x0, %2}";
-+ return "vmovups\t{%2, %x0|%x0, %2}";
- case E_V8DImode:
-- if (misaligned_operand (operands[2], <ssequartermode>mode))
-- return which_alternative == 2 ? "vmovdqu64\t{%2, %x0|%x0, %2}"
-+ return which_alternative == 2 ? "vmovdqu64\t{%2, %x0|%x0, %2}"
- : "vmovdqu\t{%2, %x0|%x0, %2}";
-- else
-- return which_alternative == 2 ? "vmovdqa64\t{%2, %x0|%x0, %2}"
-- : "vmovdqa\t{%2, %x0|%x0, %2}";
- case E_V16SImode:
-- if (misaligned_operand (operands[2], <ssequartermode>mode))
-- return which_alternative == 2 ? "vmovdqu32\t{%2, %x0|%x0, %2}"
-+ return which_alternative == 2 ? "vmovdqu32\t{%2, %x0|%x0, %2}"
- : "vmovdqu\t{%2, %x0|%x0, %2}";
-- else
-- return which_alternative == 2 ? "vmovdqa32\t{%2, %x0|%x0, %2}"
-- : "vmovdqa\t{%2, %x0|%x0, %2}";
- default:
- gcc_unreachable ();
- }
-@@ -21225,63 +21199,27 @@
- switch (get_attr_mode (insn))
- {
- case MODE_V16SF:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- return "vmovups\t{%1, %t0|%t0, %1}";
-- else
-- return "vmovaps\t{%1, %t0|%t0, %1}";
-+ return "vmovups\t{%1, %t0|%t0, %1}";
- case MODE_V8DF:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- return "vmovupd\t{%1, %t0|%t0, %1}";
-- else
-- return "vmovapd\t{%1, %t0|%t0, %1}";
-+ return "vmovupd\t{%1, %t0|%t0, %1}";
- case MODE_V8SF:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- return "vmovups\t{%1, %x0|%x0, %1}";
-- else
-- return "vmovaps\t{%1, %x0|%x0, %1}";
-+ return "vmovups\t{%1, %x0|%x0, %1}";
- case MODE_V4DF:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- return "vmovupd\t{%1, %x0|%x0, %1}";
-- else
-- return "vmovapd\t{%1, %x0|%x0, %1}";
-+ return "vmovupd\t{%1, %x0|%x0, %1}";
- case MODE_XI:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- {
-- if (which_alternative == 2)
-- return "vmovdqu\t{%1, %t0|%t0, %1}";
-- else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-- return "vmovdqu64\t{%1, %t0|%t0, %1}";
-- else
-- return "vmovdqu32\t{%1, %t0|%t0, %1}";
-- }
-+ if (which_alternative == 2)
-+ return "vmovdqu\t{%1, %t0|%t0, %1}";
-+ else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-+ return "vmovdqu64\t{%1, %t0|%t0, %1}";
- else
-- {
-- if (which_alternative == 2)
-- return "vmovdqa\t{%1, %t0|%t0, %1}";
-- else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-- return "vmovdqa64\t{%1, %t0|%t0, %1}";
-- else
-- return "vmovdqa32\t{%1, %t0|%t0, %1}";
-- }
-+ return "vmovdqu32\t{%1, %t0|%t0, %1}";
- case MODE_OI:
-- if (misaligned_operand (operands[1], <ssehalfvecmode>mode))
-- {
-- if (which_alternative == 2)
-- return "vmovdqu\t{%1, %x0|%x0, %1}";
-- else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-- return "vmovdqu64\t{%1, %x0|%x0, %1}";
-- else
-- return "vmovdqu32\t{%1, %x0|%x0, %1}";
-- }
-+ if (which_alternative == 2)
-+ return "vmovdqu\t{%1, %x0|%x0, %1}";
-+ else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-+ return "vmovdqu64\t{%1, %x0|%x0, %1}";
- else
-- {
-- if (which_alternative == 2)
-- return "vmovdqa\t{%1, %x0|%x0, %1}";
-- else if (GET_MODE_SIZE (<ssescalarmode>mode) == 8)
-- return "vmovdqa64\t{%1, %x0|%x0, %1}";
-- else
-- return "vmovdqa32\t{%1, %x0|%x0, %1}";
-- }
-+ return "vmovdqu32\t{%1, %x0|%x0, %1}";
- default:
- gcc_unreachable ();
- }
---- a/gcc/config/i386/i386.cc
-+++ b/gcc/config/i386/i386.cc
-@@ -5418,17 +5418,15 @@ ix86_get_ssemov (rtx *operands, unsigned size,
- {
- case opcode_int:
- if (scalar_mode == E_HFmode)
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW ? "vmovdqu16" : "vmovdqu64")
-- : "vmovdqa64");
-+ opcode = TARGET_AVX512BW ? "vmovdqu16" : "vmovdqu64";
- else
-- opcode = misaligned_p ? "vmovdqu32" : "vmovdqa32";
-+ opcode = "vmovdqu32";
- break;
- case opcode_float:
-- opcode = misaligned_p ? "vmovups" : "vmovaps";
-+ opcode = "vmovups";
- break;
- case opcode_double:
-- opcode = misaligned_p ? "vmovupd" : "vmovapd";
-+ opcode = "vmovupd";
- break;
- }
- }
-@@ -5438,29 +5436,21 @@ ix86_get_ssemov (rtx *operands, unsigned size,
- {
- case E_HFmode:
- if (evex_reg_p)
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu16"
-- : "vmovdqu64")
-- : "vmovdqa64");
-+ opcode = TARGET_AVX512BW ? "vmovdqu16" : "vmovdqu64";
- else
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu16"
-- : "%vmovdqu")
-- : "%vmovdqa");
-+ opcode = TARGET_AVX512BW ? "vmovdqu16" : "%vmovdqu";
- break;
- case E_SFmode:
-- opcode = misaligned_p ? "%vmovups" : "%vmovaps";
-+ opcode = "%vmovups";
- break;
- case E_DFmode:
-- opcode = misaligned_p ? "%vmovupd" : "%vmovapd";
-+ opcode = "%vmovupd";
- break;
- case E_TFmode:
- if (evex_reg_p)
-- opcode = misaligned_p ? "vmovdqu64" : "vmovdqa64";
-+ opcode = "vmovdqu64";
- else
-- opcode = misaligned_p ? "%vmovdqu" : "%vmovdqa";
-+ opcode = "%vmovdqu";
- break;
- default:
- gcc_unreachable ();
-@@ -5472,48 +5462,32 @@ ix86_get_ssemov (rtx *operands, unsigned size,
- {
- case E_QImode:
- if (evex_reg_p)
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu8"
-- : "vmovdqu64")
-- : "vmovdqa64");
-+ opcode = TARGET_AVX512BW ? "vmovdqu8" : "vmovdqu64";
- else
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu8"
-- : "%vmovdqu")
-- : "%vmovdqa");
-+ opcode = TARGET_AVX512BW ? "vmovdqu8" : "%vmovdqu";
- break;
- case E_HImode:
- if (evex_reg_p)
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu16"
-- : "vmovdqu64")
-- : "vmovdqa64");
-+ opcode = TARGET_AVX512BW ? "vmovdqu16" : "vmovdqu64";
- else
-- opcode = (misaligned_p
-- ? (TARGET_AVX512BW
-- ? "vmovdqu16"
-- : "%vmovdqu")
-- : "%vmovdqa");
-+ opcode = TARGET_AVX512BW ? "vmovdqu16" : "%vmovdqu";
- break;
- case E_SImode:
- if (evex_reg_p)
-- opcode = misaligned_p ? "vmovdqu32" : "vmovdqa32";
-+ opcode = "vmovdqu32";
- else
-- opcode = misaligned_p ? "%vmovdqu" : "%vmovdqa";
-+ opcode = "%vmovdqu";
- break;
- case E_DImode:
- case E_TImode:
- case E_OImode:
- if (evex_reg_p)
-- opcode = misaligned_p ? "vmovdqu64" : "vmovdqa64";
-+ opcode = "vmovdqu64";
- else
-- opcode = misaligned_p ? "%vmovdqu" : "%vmovdqa";
-+ opcode = "%vmovdqu";
- break;
- case E_XImode:
-- opcode = misaligned_p ? "vmovdqu64" : "vmovdqa64";
-+ opcode = "vmovdqu64";
- break;
- default:
- gcc_unreachable ();
diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service
index 87da17f955..ade8a05926 100644
--- a/contrib/init/bitcoind.service
+++ b/contrib/init/bitcoind.service
@@ -81,5 +81,8 @@ PrivateDevices=true
# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true
+# Restrict ABIs to help ensure MemoryDenyWriteExecute is enforced
+SystemCallArchitectures=native
+
[Install]
WantedBy=multi-user.target
diff --git a/contrib/valgrind.supp b/contrib/valgrind.supp
index ee91acd5ef..c537f9e7ec 100644
--- a/contrib/valgrind.supp
+++ b/contrib/valgrind.supp
@@ -13,8 +13,8 @@
#
# Note that suppressions may depend on OS and/or library versions.
# Tested on:
-# * aarch64 (Debian Bookworm system libs, clang, without gui)
-# * x86_64 (Debian Bookworm system libs, clang, without gui)
+# * aarch64 (Ubuntu Noble system libs, clang, without gui)
+# * x86_64 (Ubuntu Noble system libs, clang, without gui)
{
Suppress libdb warning - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=662917
Memcheck:Cond
diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk
index 9cd28f0deb..f5ce2bb0b8 100644
--- a/depends/hosts/linux.mk
+++ b/depends/hosts/linux.mk
@@ -13,7 +13,11 @@ linux_release_CXXFLAGS=$(linux_release_CFLAGS)
linux_debug_CFLAGS=-O1 -g
linux_debug_CXXFLAGS=$(linux_debug_CFLAGS)
-linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_LIBCPP_ENABLE_DEBUG_MODE=1
+# https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
+linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
+
+# https://libcxx.llvm.org/Hardening.html
+linux_debug_CPPFLAGS+=-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG
ifeq (86,$(findstring 86,$(build_arch)))
i686_linux_CC=gcc -m32
diff --git a/depends/packages.md b/depends/packages.md
index 0ffdc66d48..7a7a42afa1 100644
--- a/depends/packages.md
+++ b/depends/packages.md
@@ -162,7 +162,7 @@ From the [Gentoo Wiki entry](https://wiki.gentoo.org/wiki/Project:Quality_Assura
> creates. This leads to massive overlinking, which is toxic to the Gentoo
> ecosystem, as it leads to a massive number of unnecessary rebuilds.
-Where possible, packages are built with Position Independant Code. Either using
+Where possible, packages are built with Position Independent Code. Either using
the Autotools `--with-pic` flag, or `CMAKE_POSITION_INDEPENDENT_CODE` with CMake.
## Secondary dependencies:
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index ab43764b38..ebc097d686 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -3,11 +3,6 @@ $(package)_version=1.81.0
$(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$($(package)_version)/source/
$(package)_file_name=boost_$(subst .,_,$($(package)_version)).tar.bz2
$(package)_sha256_hash=71feeed900fbccca04a3b4f2f84a7c217186f28a940ed8b7ed4725986baf99fa
-$(package)_patches=process_macos_sdk.patch
-
-define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/process_macos_sdk.patch
-endef
define $(package)_stage_cmds
mkdir -p $($(package)_staging_prefix_dir)/include && \
diff --git a/depends/patches/boost/process_macos_sdk.patch b/depends/patches/boost/process_macos_sdk.patch
deleted file mode 100644
index ebc556d972..0000000000
--- a/depends/patches/boost/process_macos_sdk.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-Fix Boost Process compilation with macOS 14 SDK.
-Can be dropped with Boost 1.84.0.
-https://github.com/boostorg/process/pull/343.
-https://github.com/boostorg/process/issues/342.
-
-diff --git a/boost/process/detail/posix/handles.hpp b/boost/process/detail/posix/handles.hpp
-index cd9e1ce5a..304e77b1c 100644
---- a/boost/process/detail/posix/handles.hpp
-+++ b/boost/process/detail/posix/handles.hpp
-@@ -33,7 +33,7 @@ inline std::vector<native_handle_type> get_handles(std::error_code & ec)
- else
- ec.clear();
-
-- auto my_fd = ::dirfd(dir.get());
-+ auto my_fd = dirfd(dir.get());
-
- struct ::dirent * ent_p;
-
-@@ -117,7 +117,7 @@ struct limit_handles_ : handler_base_ext
- return;
- }
-
-- auto my_fd = ::dirfd(dir);
-+ auto my_fd = dirfd(dir);
- struct ::dirent * ent_p;
-
- while ((ent_p = readdir(dir)) != nullptr)
diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md
index aa10e4a891..bf86a0ee4b 100644
--- a/doc/build-freebsd.md
+++ b/doc/build-freebsd.md
@@ -1,6 +1,6 @@
# FreeBSD Build Guide
-**Updated for FreeBSD [12.3](https://www.freebsd.org/releases/12.3R/announce/)**
+**Updated for FreeBSD [14.0](https://www.freebsd.org/releases/14.0R/announce/)**
This guide describes how to build bitcoind, command-line utilities, and GUI on FreeBSD.
@@ -64,10 +64,11 @@ sh/bash: export BDB_PREFIX=[path displayed above]
#### GUI Dependencies
###### Qt5
-Bitcoin Core includes a GUI built with the cross-platform Qt Framework. To compile the GUI, we need to install `qt5`. Skip if you don't intend to use the GUI.
+Bitcoin Core includes a GUI built with the cross-platform Qt Framework. To compile the GUI, we need to install the necessary parts of Qt. Skip if you don't intend to use the GUI.
```bash
-pkg install qt5
+pkg install qt5-buildtools qt5-core qt5-gui qt5-linguisttools qt5-testlib qt5-widgets
```
+
###### libqrencode
The GUI can encode addresses in a QR Code. To build in QR support for the GUI, install `libqrencode`. Skip if not using the GUI or don't want QR code functionality.
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index 7ed83853a8..264d5f2d08 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -1,6 +1,6 @@
# OpenBSD Build Guide
-**Updated for OpenBSD [7.4](https://www.openbsd.org/74.html)**
+**Updated for OpenBSD [7.5](https://www.openbsd.org/75.html)**
This guide describes how to build bitcoind, command-line utilities, and GUI on OpenBSD.
@@ -63,7 +63,7 @@ export BDB_PREFIX="/path/to/bitcoin/depends/x86_64-unknown-openbsd"
Bitcoin Core includes a GUI built with the cross-platform Qt Framework. To compile the GUI, Qt 5 is required.
```bash
-pkg_add qt5
+pkg_add qtbase qttools
```
## Building Bitcoin Core
diff --git a/doc/descriptors.md b/doc/descriptors.md
index 1baf652f30..3b94ec03e4 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -243,7 +243,18 @@ Often it is useful to communicate a description of scripts along with the
necessary private keys. For this reason, anywhere a public key or xpub is
supported, a private key in WIF format or xprv may be provided instead.
This is useful when private keys are necessary for hardened derivation
-steps, or for dumping wallet descriptors including private key material.
+steps, for signing transactions, or for dumping wallet descriptors
+including private key material.
+
+For example, after importing the following 2-of-3 multisig descriptor
+into a wallet, one could use `signrawtransactionwithwallet`
+to sign a transaction with the first key:
+```
+sh(multi(2,xprv.../84'/0'/0'/0/0,xpub1...,xpub2...))
+```
+Note how the first key is an xprv private key with a specific derivation path,
+while the other two are public keys.
+
### Compatibility with old wallets
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 89c13600eb..cc3f0518e5 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -115,6 +115,8 @@ code.
Use `reinterpret_cast` and `const_cast` as appropriate.
- Prefer [`list initialization ({})`](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-list) where possible.
For example `int x{0};` instead of `int x = 0;` or `int x(0);`
+ - Recursion is checked by clang-tidy and thus must be made explicit. Use
+ `NOLINTNEXTLINE(misc-no-recursion)` to suppress the check.
For function calls a namespace should be specified explicitly, unless such functions have been declared within it.
Otherwise, [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl), also known as ADL, could be
diff --git a/doc/external-signer.md b/doc/external-signer.md
index de44cdd880..1468e852ef 100644
--- a/doc/external-signer.md
+++ b/doc/external-signer.md
@@ -150,6 +150,9 @@ Example, display the first native SegWit receive address on Testnet:
The command MUST be able to figure out the address type from the descriptor.
+The command MUST return an object containing `{"address": "[the address]"}`.
+As a sanity check, for devices that support this, it SHOULD ask the device to derive the address.
+
If <descriptor> contains a master key fingerprint, the command MUST fail if it does not match the fingerprint known by the device.
If <descriptor> contains an xpub, the command MUST fail if it does not match the xpub known by the device.
diff --git a/doc/managing-wallets.md b/doc/managing-wallets.md
index 22e006c963..b99d88877b 100644
--- a/doc/managing-wallets.md
+++ b/doc/managing-wallets.md
@@ -122,6 +122,22 @@ $ bitcoin-cli -rpcwallet="restored-wallet" getwalletinfo
The restored wallet can also be loaded in the GUI via `File` ->`Open wallet`.
+## Wallet Passphrase
+
+Understanding wallet security is crucial for safely storing your Bitcoin. A key aspect is the wallet passphrase, used for encryption. Let's explore its nuances, role, encryption process, and limitations.
+
+- **Not the Seed:**
+The wallet passphrase and the seed are two separate components in wallet security. The seed, or HD seed, functions as a master key for deriving private and public keys in a hierarchical deterministic (HD) wallet. In contrast, the passphrase serves as an additional layer of security specifically designed to secure the private keys within the wallet. The passphrase serves as a safeguard, demanding an additional layer of authentication to access funds in the wallet.
+
+- **Protection Against Unauthorized Access:**
+The passphrase serves as a protective measure, securing your funds in situations where an unauthorized user gains access to your unlocked computer or device while your wallet application is active. Without the passphrase, they would be unable to access your wallet's funds or execute transactions. However, it's essential to be aware that someone with access can potentially compromise the security of your passphrase by installing a keylogger.
+
+- **Doesn't Encrypt Metadata or Public Keys:**
+It's important to note that the passphrase primarily secures the private keys and access to funds within the wallet. It does not encrypt metadata associated with transactions or public keys. Information about your transaction history and the public keys involved may still be visible.
+
+- **Risk of Fund Loss if Forgotten or Lost:**
+If the wallet passphrase is too complex and is subsequently forgotten or lost, there is a risk of losing access to the funds permanently. A forgotten passphrase will result in the inability to unlock the wallet and access the funds.
+
## Migrating Legacy Wallets to Descriptor Wallets
Legacy wallets (traditional non-descriptor wallets) can be migrated to become Descriptor wallets
diff --git a/doc/release-notes-27679.md b/doc/release-notes-27679.md
new file mode 100644
index 0000000000..dbbb30428c
--- /dev/null
+++ b/doc/release-notes-27679.md
@@ -0,0 +1,2 @@
+- unix socket paths are now accepted for `-zmqpubrawblock` and `-zmqpubrawtx` with
+the format `-zmqpubrawtx=unix:/path/to/file` \ No newline at end of file
diff --git a/doc/release-notes/release-notes-25.2.md b/doc/release-notes/release-notes-25.2.md
new file mode 100644
index 0000000000..3f050ebaef
--- /dev/null
+++ b/doc/release-notes/release-notes-25.2.md
@@ -0,0 +1,74 @@
+25.2 Release Notes
+==================
+
+Bitcoin Core version 25.2 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-25.2>
+
+This release includes various bug fixes and performance
+improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes in some cases), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on macOS)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the data directory needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.15+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
+
+Notable changes
+===============
+
+### Gui
+
+- gui#774 Fix crash on selecting "Mask values" in transaction view
+
+### RPC
+
+- #29003 rpc: fix getrawtransaction segfault
+
+### Wallet
+
+- #29176 wallet: Fix use-after-free in WalletBatch::EraseRecords
+- #29510 wallet: `getrawchangeaddress` and `getnewaddress` failures should not affect keypools for descriptor wallets
+
+### P2P and network changes
+
+- #29412 p2p: Don't process mutated blocks
+- #29524 p2p: Don't consider blocks mutated if they don't connect to known prev block
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Martin Zumsande
+- Sebastian Falbesoner
+- MarcoFalke
+- UdjinM6
+- dergoegge
+- Greg Sanders
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/doc/release-notes/release-notes-27.0.md b/doc/release-notes/release-notes-27.0.md
new file mode 100644
index 0000000000..5060068328
--- /dev/null
+++ b/doc/release-notes/release-notes-27.0.md
@@ -0,0 +1,217 @@
+Bitcoin Core version 27.0 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-27.0/>
+
+This release includes new features, various bug fixes and performance
+improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes in some cases), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on macOS)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the data directory needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux Kernel 3.17+, macOS 11.0+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
+
+Notable changes
+===============
+
+libbitcoinconsensus
+-------------------
+
+- libbitcoinconsensus is deprecated and will be removed for v28. This library has
+ existed for nearly 10 years with very little known uptake or impact. It has
+ become a maintenance burden.
+
+ The underlying functionality does not change between versions, so any users of
+ the library can continue to use the final release indefinitely, with the
+ understanding that Taproot is its final consensus update.
+
+ In the future, libbitcoinkernel will provide a much more useful API that is
+ aware of the UTXO set, and therefore be able to fully validate transactions and
+ blocks. (#29189)
+
+mempool.dat compatibility
+-------------------------
+
+- The `mempool.dat` file created by -persistmempool or the savemempool RPC will
+ be written in a new format. This new format includes the XOR'ing of transaction
+ contents to mitigate issues where external programs (such as anti-virus) attempt
+ to interpret and potentially modify the file.
+
+ This new format can not be read by previous software releases. To allow for a
+ downgrade, a temporary setting `-persistmempoolv1` has been added to fall back
+ to the legacy format. (#28207)
+
+P2P and network changes
+-----------------------
+
+- BIP324 v2 transport is now enabled by default. It remains possible to disable v2
+ by running with `-v2transport=0`. (#29347)
+- Manual connection options (`-connect`, `-addnode` and `-seednode`) will
+ now follow `-v2transport` to connect with v2 by default. They will retry with
+ v1 on failure. (#29058)
+
+- Network-adjusted time has been removed from consensus code. It is replaced
+ with (unadjusted) system time. The warning for a large median time offset
+ (70 minutes or more) is kept. This removes the implicit security assumption of
+ requiring an honest majority of outbound peers, and increases the importance
+ of the node operator ensuring their system time is (and stays) correct to not
+ fall out of consensus with the network. (#28956)
+
+Mempool Policy Changes
+----------------------
+
+- Opt-in Topologically Restricted Until Confirmation (TRUC) Transactions policy
+ (aka v3 transaction policy) is available for use on test networks when
+ `-acceptnonstdtxn=1` is set. By setting the transaction version number to 3, TRUC transactions
+ request the application of limits on spending of their unconfirmed outputs. These
+ restrictions simplify the assessment of incentive compatibility of accepting or
+ replacing TRUC transactions, thus ensuring any replacements are more profitable for
+ the node and making fee-bumping more reliable. TRUC transactions are currently
+ nonstandard and can only be used on test networks where the standardness rules are
+ relaxed or disabled (e.g. with `-acceptnonstdtxn=1`). (#28948)
+
+External Signing
+----------------
+
+- Support for external signing on Windows has been disabled. It will be re-enabled
+ once the underlying dependency (Boost Process), has been replaced with a different
+ library. (#28967)
+
+Updated RPCs
+------------
+
+- The addnode RPC now follows the `-v2transport` option (now on by default, see above) for making connections.
+ It remains possible to specify the transport type manually with the v2transport argument of addnode. (#29239)
+
+Build System
+------------
+
+- A C++20 capable compiler is now required to build Bitcoin Core. (#28349)
+- MacOS releases are configured to use the hardened runtime libraries (#29127)
+
+Wallet
+------
+
+- The CoinGrinder coin selection algorithm has been introduced to mitigate unnecessary
+ large input sets and lower transaction costs at high feerates. CoinGrinder
+ searches for the input set with minimal weight. Solutions found by
+ CoinGrinder will produce a change output. CoinGrinder is only active at
+ elevated feerates (default: 30+ sat/vB, based on `-consolidatefeerate`×3). (#27877)
+- The Branch And Bound coin selection algorithm will be disabled when the subtract fee
+ from outputs feature is used. (#28994)
+- If the birth time of a descriptor is detected to be later than the first transaction
+ involving that descriptor, the birth time will be reset to the earlier time. (#28920)
+
+Low-level changes
+=================
+
+Pruning
+-------
+
+- When pruning during initial block download, more blocks will be pruned at each
+ flush in order to speed up the syncing of such nodes. (#20827)
+
+Init
+----
+
+- Various fixes to prevent issues where subsequent instances of Bitcoin Core would
+ result in deletion of files in use by an existing instance. (#28784, #28946)
+- Improved handling of empty `settings.json` files. (#29144)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 22388o⚡️
+- Aaron Clauson
+- Amiti Uttarwar
+- Andrew Toth
+- Anthony Towns
+- Antoine Poinsot
+- Ava Chow
+- Brandon Odiwuor
+- brunoerg
+- Chris Stewart
+- Cory Fields
+- dergoegge
+- djschnei21
+- Fabian Jahr
+- fanquake
+- furszy
+- Gloria Zhao
+- Greg Sanders
+- Hennadii Stepanov
+- Hernan Marino
+- iamcarlos94
+- ismaelsadeeq
+- Jameson Lopp
+- Jesse Barton
+- John Moffett
+- Jon Atack
+- josibake
+- jrakibi
+- Justin Dhillon
+- Kashif Smith
+- kevkevin
+- Kristaps Kaupe
+- L0la L33tz
+- Luke Dashjr
+- Lőrinc
+- marco
+- MarcoFalke
+- Mark Friedenbach
+- Marnix
+- Martin Leitner-Ankerl
+- Martin Zumsande
+- Max Edwards
+- Murch
+- muxator
+- naiyoma
+- Nikodemas Tuckus
+- ns-xvrn
+- pablomartin4btc
+- Peter Todd
+- Pieter Wuille
+- Richard Myers
+- Roman Zeyde
+- Russell Yanofsky
+- Ryan Ofsky
+- Sebastian Falbesoner
+- Sergi Delgado Segura
+- Sjors Provoost
+- stickies-v
+- stratospher
+- Supachai Kheawjuy
+- TheCharlatan
+- UdjinM6
+- Vasil Dimov
+- w0xlt
+- willcl-ark
+
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/src/.clang-tidy b/src/.clang-tidy
index e4b789dcaa..a00400f083 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -6,6 +6,7 @@ bugprone-string-constructor,
bugprone-use-after-move,
bugprone-lambda-function-name,
misc-unused-using-decls,
+misc-no-recursion,
modernize-use-default-member-init,
modernize-use-emplace,
modernize-use-noexcept,
diff --git a/src/Makefile.am b/src/Makefile.am
index 11b8f6e4c3..f48ed4d442 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -320,6 +320,7 @@ BITCOIN_CORE_H = \
util/sock.h \
util/spanparsing.h \
util/string.h \
+ util/subprocess.h \
util/syserror.h \
util/task_runner.h \
util/thread.h \
diff --git a/src/Makefile.minisketch.include b/src/Makefile.minisketch.include
index 1363bec34e..c6f894f0ca 100644
--- a/src/Makefile.minisketch.include
+++ b/src/Makefile.minisketch.include
@@ -12,10 +12,6 @@ LIBMINISKETCH_CPPFLAGS += -DHAVE_CLMUL
MINISKETCH_LIBS += $(LIBMINISKETCH_CLMUL)
endif
-if HAVE_CLZ
-LIBMINISKETCH_CPPFLAGS += -DHAVE_CLZ
-endif
-
EXTRA_LIBRARIES += $(MINISKETCH_LIBS)
minisketch_libminisketch_clmul_a_SOURCES = $(MINISKETCH_FIELD_CLMUL_SOURCES_INT) $(MINISKETCH_FIELD_CLMUL_HEADERS_INT)
diff --git a/src/bench/bip324_ecdh.cpp b/src/bench/bip324_ecdh.cpp
index 659da0f08e..fb10c2957e 100644
--- a/src/bench/bip324_ecdh.cpp
+++ b/src/bench/bip324_ecdh.cpp
@@ -27,7 +27,7 @@ static void BIP324_ECDH(benchmark::Bench& bench)
bench.batch(1).unit("ecdh").run([&] {
CKey key;
- key.Set(UCharCast(key_data.data()), UCharCast(key_data.data()) + 32, true);
+ key.Set(key_data.data(), key_data.data() + 32, true);
EllSwiftPubKey our_ellswift(our_ellswift_data);
EllSwiftPubKey their_ellswift(their_ellswift_data);
diff --git a/src/bench/ellswift.cpp b/src/bench/ellswift.cpp
index 82e8dec982..9441b4863e 100644
--- a/src/bench/ellswift.cpp
+++ b/src/bench/ellswift.cpp
@@ -17,7 +17,7 @@ static void EllSwiftCreate(benchmark::Bench& bench)
bench.batch(1).unit("pubkey").run([&] {
auto ret = key.EllSwiftCreate(MakeByteSpan(entropy));
/* Use the first 32 bytes of the ellswift encoded public key as next private key. */
- key.Set(UCharCast(ret.data()), UCharCast(ret.data()) + 32, true);
+ key.Set(ret.data(), ret.data() + 32, true);
assert(key.IsValid());
/* Use the last 32 bytes of the ellswift encoded public key as next entropy. */
std::copy(ret.begin() + 32, ret.begin() + 64, MakeWritableByteSpan(entropy).begin());
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 129deeec60..161d703282 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -551,7 +551,7 @@ public:
peer.is_outbound ? "out" : "in",
ConnectionTypeForNetinfo(peer.conn_type),
peer.network,
- peer.transport_protocol_type.starts_with('v') ? peer.transport_protocol_type[1] : ' ',
+ (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ',
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
peer.last_send ? ToString(time_now - peer.last_send) : "",
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 1e940e8f03..5a4513d281 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -40,14 +40,14 @@ void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
shorttxidk1 = shorttxidhash.GetUint64(1);
}
-uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256& txhash) const {
+uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const {
static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
- return SipHashUint256(shorttxidk0, shorttxidk1, txhash) & 0xffffffffffffL;
+ return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0xffffffffffffL;
}
-ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn) {
+ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<CTransactionRef>& extra_txn) {
if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
return READ_STATUS_INVALID;
if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT)
@@ -134,11 +134,14 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
}
for (size_t i = 0; i < extra_txn.size(); i++) {
- uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first);
+ if (extra_txn[i] == nullptr) {
+ continue;
+ }
+ uint64_t shortid = cmpctblock.GetShortID(extra_txn[i]->GetWitnessHash());
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
if (idit != shorttxids.end()) {
if (!have_txn[idit->second]) {
- txn_available[idit->second] = extra_txn[i].second;
+ txn_available[idit->second] = extra_txn[i];
have_txn[idit->second] = true;
mempool_count++;
extra_count++;
@@ -150,7 +153,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
// Note that we don't want duplication between extra_txn and mempool to
// trigger this case, so we compare witness hashes first
if (txn_available[idit->second] &&
- txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) {
+ txn_available[idit->second]->GetWitnessHash() != extra_txn[i]->GetWitnessHash()) {
txn_available[idit->second].reset();
mempool_count--;
extra_count--;
diff --git a/src/blockencodings.h b/src/blockencodings.h
index fb0f734ff8..2b1fabadd6 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -111,7 +111,7 @@ public:
CBlockHeaderAndShortTxIDs(const CBlock& block);
- uint64_t GetShortID(const uint256& txhash) const;
+ uint64_t GetShortID(const Wtxid& wtxid) const;
size_t BlockTxCount() const { return shorttxids.size() + prefilledtxn.size(); }
@@ -142,7 +142,7 @@ public:
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
// extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
- ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn);
+ ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<CTransactionRef>& extra_txn);
bool IsTxAvailable(size_t index) const;
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing);
};
diff --git a/src/common/run_command.cpp b/src/common/run_command.cpp
index 8bd5febd53..347b486095 100644
--- a/src/common/run_command.cpp
+++ b/src/common/run_command.cpp
@@ -12,39 +12,34 @@
#include <univalue.h>
#ifdef ENABLE_EXTERNAL_SIGNER
-#include <boost/process.hpp>
+#include <util/subprocess.h>
#endif // ENABLE_EXTERNAL_SIGNER
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
{
#ifdef ENABLE_EXTERNAL_SIGNER
- namespace bp = boost::process;
+ namespace sp = subprocess;
UniValue result_json;
- bp::opstream stdin_stream;
- bp::ipstream stdout_stream;
- bp::ipstream stderr_stream;
+ std::istringstream stdout_stream;
+ std::istringstream stderr_stream;
if (str_command.empty()) return UniValue::VNULL;
- bp::child c(
- str_command,
- bp::std_out > stdout_stream,
- bp::std_err > stderr_stream,
- bp::std_in < stdin_stream
- );
+ auto c = sp::Popen(str_command, sp::input{sp::PIPE}, sp::output{sp::PIPE}, sp::error{sp::PIPE});
if (!str_std_in.empty()) {
- stdin_stream << str_std_in << std::endl;
+ c.send(str_std_in);
}
- stdin_stream.pipe().close();
+ auto [out_res, err_res] = c.communicate();
+ stdout_stream.str(std::string{out_res.buf.begin(), out_res.buf.end()});
+ stderr_stream.str(std::string{err_res.buf.begin(), err_res.buf.end()});
std::string result;
std::string error;
std::getline(stdout_stream, result);
std::getline(stderr_stream, error);
- c.wait();
- const int n_error = c.exit_code();
+ const int n_error = c.retcode();
if (n_error) throw std::runtime_error(strprintf("RunCommandParseJSON error: process(%s) returned %d: %s\n", str_command, n_error, error));
if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
diff --git a/src/core_read.cpp b/src/core_read.cpp
index e32e46d1b9..5956d9df5f 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -256,6 +256,6 @@ util::Result<int> SighashFromStr(const std::string& sighash)
if (it != map_sighash_values.end()) {
return it->second;
} else {
- return util::Error{Untranslated(sighash + " is not a valid sighash parameter.")};
+ return util::Error{Untranslated("'" + sighash + "' is not a valid sighash parameter.")};
}
}
diff --git a/src/crypto/chacha20poly1305.cpp b/src/crypto/chacha20poly1305.cpp
index 3e8051c2dc..b969bb1a29 100644
--- a/src/crypto/chacha20poly1305.cpp
+++ b/src/crypto/chacha20poly1305.cpp
@@ -2,10 +2,6 @@
// 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 <crypto/chacha20poly1305.h>
#include <crypto/common.h>
@@ -30,10 +26,7 @@ void AEADChaCha20Poly1305::SetKey(Span<const std::byte> key) noexcept
namespace {
-#ifndef HAVE_TIMINGSAFE_BCMP
-#define HAVE_TIMINGSAFE_BCMP
-
-int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n) noexcept
+int timingsafe_bcmp_internal(const unsigned char* b1, const unsigned char* b2, size_t n) noexcept
{
const unsigned char *p1 = b1, *p2 = b2;
int ret = 0;
@@ -42,8 +35,6 @@ int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n)
return (ret != 0);
}
-#endif
-
/** Compute poly1305 tag. chacha20 must be set to the right nonce, block 0. Will be at block 1 after. */
void ComputeTag(ChaCha20& chacha20, Span<const std::byte> aad, Span<const std::byte> cipher, Span<std::byte> tag) noexcept
{
@@ -97,7 +88,7 @@ bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std:
m_chacha20.Seek(nonce, 0);
std::byte expected_tag[EXPANSION];
ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag);
- if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false;
+ if (timingsafe_bcmp_internal(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false;
// Decrypt (starting at block 1).
m_chacha20.Crypt(cipher.first(plain1.size()), plain1);
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index 4c7bb6f20f..301f22a248 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -20,7 +20,7 @@
#include <asm/hwcap.h>
#endif
-#if defined(MAC_OSX) && defined(ENABLE_ARM_SHANI)
+#if defined(__APPLE__) && defined(ENABLE_ARM_SHANI)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
@@ -670,7 +670,7 @@ std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implem
#endif
#endif
-#if defined(MAC_OSX)
+#if defined(__APPLE__)
int val = 0;
size_t len = sizeof(val);
if (sysctlbyname("hw.optional.arm.FEAT_SHA256", &val, &len, nullptr, 0) == 0) {
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index 749bb5f74f..ff159a2aa5 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -62,12 +62,12 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS
UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
{
- return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
+ return RunCommandParseJSON(m_command + " --fingerprint " + m_fingerprint + NetworkArg() + " displayaddress --desc " + descriptor);
}
UniValue ExternalSigner::GetDescriptors(const int account)
{
- return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
+ return RunCommandParseJSON(m_command + " --fingerprint " + m_fingerprint + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
}
bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error)
@@ -93,8 +93,8 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return false;
}
- const std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
- const std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";
+ const std::string command = m_command + " --stdin --fingerprint " + m_fingerprint + NetworkArg();
+ const std::string stdinStr = "signtx " + EncodeBase64(ssTx.str());
const UniValue signer_result = RunCommandParseJSON(command, stdinStr);
diff --git a/src/init.cpp b/src/init.cpp
index 885c0673dd..c19d596c7f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -553,16 +553,12 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control host and port to use if onion listening enabled (default: %s). If no port is specified, the default port of %i will be used.", DEFAULT_TOR_CONTROL, DEFAULT_TOR_CONTROL_PORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
-#if USE_UPNP
- argsman.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
-#else
- argsman.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
-#endif
+ argsman.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", DEFAULT_UPNP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#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_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %u)", DEFAULT_NATPMP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
hidden_args.emplace_back("-natpmp");
#endif // USE_NATPMP
@@ -1305,30 +1301,33 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
- for (const std::string port_option : {
- "-i2psam",
- "-onion",
- "-proxy",
- "-rpcbind",
- "-torcontrol",
- "-whitebind",
- "-zmqpubhashblock",
- "-zmqpubhashtx",
- "-zmqpubrawblock",
- "-zmqpubrawtx",
- "-zmqpubsequence",
+ for (const auto &port_option : std::vector<std::pair<std::string, bool>>{
+ // arg name UNIX socket support
+ {"-i2psam", false},
+ {"-onion", true},
+ {"-proxy", true},
+ {"-rpcbind", false},
+ {"-torcontrol", false},
+ {"-whitebind", false},
+ {"-zmqpubhashblock", true},
+ {"-zmqpubhashtx", true},
+ {"-zmqpubrawblock", true},
+ {"-zmqpubrawtx", true},
+ {"-zmqpubsequence", true}
}) {
- for (const std::string& socket_addr : args.GetArgs(port_option)) {
+ const std::string arg{port_option.first};
+ const bool unix{port_option.second};
+ for (const std::string& socket_addr : args.GetArgs(arg)) {
std::string host_out;
uint16_t port_out{0};
if (!SplitHostPort(socket_addr, port_out, host_out)) {
#if HAVE_SOCKADDR_UN
- // Allow unix domain sockets for -proxy and -onion e.g. unix:/some/file/path
- if ((port_option != "-proxy" && port_option != "-onion") || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
- return InitError(InvalidPortErrMsg(port_option, socket_addr));
+ // Allow unix domain sockets for some options e.g. unix:/some/file/path
+ if (!unix || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
+ return InitError(InvalidPortErrMsg(arg, socket_addr));
}
#else
- return InitError(InvalidPortErrMsg(port_option, socket_addr));
+ return InitError(InvalidPortErrMsg(arg, socket_addr));
#endif
}
}
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 6114236623..c41f35829d 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -127,7 +127,7 @@ public:
virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0;
//! Display address on external signer
- virtual bool displayAddress(const CTxDestination& dest) = 0;
+ virtual util::Result<void> displayAddress(const CTxDestination& dest) = 0;
//! Lock coin.
virtual bool lockCoin(const COutPoint& output, const bool write_to_db) = 0;
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index 264a2fd681..26c261eba2 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -133,7 +133,7 @@ public:
// release ASAP to avoid it where possible.
vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd
vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9
- vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr
+ vSeeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us."); // Luke Dashjr
vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf
vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.btc.petertodd.net."); // Peter Todd, only supports x1, x5, x9, and xd
diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp
index bf8a2ec74c..45a5e25093 100644
--- a/src/kernel/checks.cpp
+++ b/src/kernel/checks.cpp
@@ -6,7 +6,6 @@
#include <key.h>
#include <random.h>
-#include <util/time.h>
#include <util/translation.h>
#include <memory>
@@ -23,10 +22,6 @@ util::Result<void> SanityChecks(const Context&)
return util::Error{Untranslated("OS cryptographic RNG sanity check failure. Aborting.")};
}
- if (!ChronoSanityCheck()) {
- return util::Error{Untranslated("Clock epoch mismatch. Aborting.")};
- }
-
return {};
}
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index c75f5c5e60..669c6e3b70 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -54,6 +54,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std:
txn = CPartialMerkleTree(vHashes, vMatch);
}
+// NOLINTNEXTLINE(misc-no-recursion)
uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
//we can never have zero txs in a merkle block, we always need the coinbase tx
//if we do not have this assert, we can hit a memory access violation when indexing into vTxid
@@ -74,6 +75,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve
}
}
+// NOLINTNEXTLINE(misc-no-recursion)
void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) {
// determine whether this node is the parent of at least one matched txid
bool fParentOfMatch = false;
@@ -92,6 +94,7 @@ void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const st
}
}
+// NOLINTNEXTLINE(misc-no-recursion)
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
if (nBitsUsed >= vBits.size()) {
// overflowed the bits array - failure
diff --git a/src/minisketch/.cirrus.yml b/src/minisketch/.cirrus.yml
index 4a5353f137..5ceefee2cf 100644
--- a/src/minisketch/.cirrus.yml
+++ b/src/minisketch/.cirrus.yml
@@ -36,17 +36,6 @@ env_matrix_snippet: &ENV_MATRIX_VALGRIND
TESTRUNS: 1
BUILD:
-env_matrix_snippet: &ENV_MATRIX_SAN
- - env:
- ENABLE_FIELDS: 28
- - env:
- BUILD: distcheck
- - env:
- CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
- LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
- UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
- BENCH: no
-
env_matrix_snippet: &ENV_MATRIX_SAN_VALGRIND
- env:
ENABLE_FIELDS: "11,64,37"
@@ -72,9 +61,9 @@ task:
<< : *ENV_MATRIX_SAN_VALGRIND
matrix:
- env:
- CC: gcc
+ CXX: g++
- env:
- CC: clang
+ CXX: clang++ -gdwarf-4
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
@@ -92,30 +81,45 @@ task:
<< : *ENV_MATRIX_VALGRIND
matrix:
- env:
- CC: i686-linux-gnu-gcc
+ CXX: i686-linux-gnu-g++
- env:
- CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include
+ CXX: clang++ --target=i686-linux-gnu -gdwarf-4
+ CXXFLAGS: -g -O2 -isystem /usr/i686-linux-gnu/include -isystem /usr/i686-linux-gnu/include/c++/10/i686-linux-gnu
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
- name: "x86_64: macOS Catalina"
+ name: "arm64: macOS Monterey"
macos_instance:
- image: catalina-base
+ image: ghcr.io/cirruslabs/macos-monterey-base:latest
env:
- # Cirrus gives us a fixed number of 12 virtual CPUs.
- MAKEFLAGS: -j13
- matrix:
- << : *ENV_MATRIX_SAN
+ # Cirrus gives us a fixed number of 4 virtual CPUs.
+ MAKEFLAGS: -j5
matrix:
- env:
- CC: gcc-9
+ CXX: g++-11
+ # Homebrew's gcc for arm64 has no libubsan.
+ matrix:
+ - env:
+ ENABLE_FIELDS: 28
+ - env:
+ BUILD: distcheck
- env:
- CC: clang
+ CXX: clang++
+ matrix:
+ - env:
+ ENABLE_FIELDS: 28
+ - env:
+ BUILD: distcheck
+ - env:
+ CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
+ BENCH: no
brew_script:
- brew update
- - brew install automake libtool gcc@9
+ - brew install automake libtool gcc@11
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
@@ -128,13 +132,11 @@ task:
cpu: 4
memory: 2G
env:
- EXEC_CMD: qemu-s390x -L /usr/s390x-linux-gnu
+ EXEC_CMD: qemu-s390x
HOST: s390x-linux-gnu
BUILD:
<< : *MERGE_BASE
test_script:
- # https://sourceware.org/bugzilla/show_bug.cgi?id=27008
- - rm /etc/ld.so.cache
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -146,6 +148,7 @@ task:
memory: 2G
env:
EXEC_CMD: wine
+ EXEC_EXT: .exe
HOST: x86_64-w64-mingw32
BUILD:
<< : *MERGE_BASE
diff --git a/src/minisketch/ci/cirrus.sh b/src/minisketch/ci/cirrus.sh
index 02f737ca7f..36250d1651 100755
--- a/src/minisketch/ci/cirrus.sh
+++ b/src/minisketch/ci/cirrus.sh
@@ -7,7 +7,7 @@ export LC_ALL=C
env >> test_env.log
-$CC -v || true
+$CXX -v || true
valgrind --version || true
./autogen.sh
@@ -32,10 +32,10 @@ then
fi
if [ -n "$EXEC_CMD" ]; then
- $EXEC_CMD ./test $TESTRUNS
- $EXEC_CMD ./test-verify $TESTRUNS
+ $EXEC_CMD "./test$EXEC_EXT" $TESTRUNS
+ $EXEC_CMD "./test-verify$EXEC_EXT" $TESTRUNS
fi
if [ "$BENCH" = "yes" ]; then
- $EXEC_CMD ./bench
+ $EXEC_CMD "./bench$EXEC_EXT"
fi
diff --git a/src/minisketch/ci/linux-debian.Dockerfile b/src/minisketch/ci/linux-debian.Dockerfile
index 63e5412ee7..122af36e1f 100644
--- a/src/minisketch/ci/linux-debian.Dockerfile
+++ b/src/minisketch/ci/linux-debian.Dockerfile
@@ -8,10 +8,10 @@ RUN apt-get update
RUN apt-get install --no-install-recommends --no-upgrade -y \
git ca-certificates \
make automake libtool pkg-config dpkg-dev valgrind qemu-user \
- gcc g++ clang libc6-dbg \
+ gcc g++ clang libclang-rt-dev libc6-dbg \
gcc-i686-linux-gnu g++-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \
- g++-s390x-linux-gnu gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \
- wine g++-mingw-w64-x86-64
+ g++-s390x-linux-gnu libstdc++6:s390x gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \
+ wine wine64 g++-mingw-w64-x86-64
# Run a dummy command in wine to make it set up configuration
RUN wine true || true
diff --git a/src/minisketch/configure.ac b/src/minisketch/configure.ac
index 83910448a2..cd52d7f412 100644
--- a/src/minisketch/configure.ac
+++ b/src/minisketch/configure.ac
@@ -104,11 +104,6 @@ esac
AX_CHECK_COMPILE_FLAG([-Wall],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],[],[$CXXFLAG_WERROR])
-## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
-## unknown options if any other warning is produced. Test the -Wfoo case, and
-## set the -Wno-foo case if it works.
-AX_CHECK_COMPILE_FLAG([-Wshift-count-overflow],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-shift-count-overflow"],,[[$CXXFLAG_WERROR]])
-
if test "x$use_ccache" != "xno"; then
AC_MSG_CHECKING(if ccache should be used)
if test x$CCACHE = x; then
@@ -119,7 +114,6 @@ if test "x$use_ccache" != "xno"; then
fi
else
use_ccache=yes
- CC="$ac_cv_path_CCACHE $CC"
CXX="$ac_cv_path_CCACHE $CXX"
fi
AC_MSG_RESULT($use_ccache)
diff --git a/src/minisketch/src/int_utils.h b/src/minisketch/src/int_utils.h
index d21ba56f33..2b3d8cb402 100644
--- a/src/minisketch/src/int_utils.h
+++ b/src/minisketch/src/int_utils.h
@@ -7,13 +7,16 @@
#ifndef _MINISKETCH_INT_UTILS_H_
#define _MINISKETCH_INT_UTILS_H_
+#include <stdint.h>
#include <stdlib.h>
#include <limits>
#include <algorithm>
#include <type_traits>
-#ifdef _MSC_VER
+#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
+# include <bit>
+#elif defined(_MSC_VER)
# include <intrin.h>
#endif
@@ -54,11 +57,10 @@ class BitWriter {
int offset = 0;
unsigned char* out;
-public:
- BitWriter(unsigned char* output) : out(output) {}
-
template<int BITS, typename I>
- inline void Write(I val) {
+ inline void WriteInner(I val) {
+ // We right shift by up to 8 bits below. Verify that's well defined for the type I.
+ static_assert(std::numeric_limits<I>::digits > 8, "BitWriter::WriteInner needs I > 8 bits");
int bits = BITS;
if (bits + offset >= 8) {
state |= ((val & ((I(1) << (8 - offset)) - 1)) << offset);
@@ -77,6 +79,19 @@ public:
offset += bits;
}
+
+public:
+ BitWriter(unsigned char* output) : out(output) {}
+
+ template<int BITS, typename I>
+ inline void Write(I val) {
+ // If I is smaller than an unsigned int, invoke WriteInner with argument converted to unsigned.
+ using compute_type = typename std::conditional<
+ (std::numeric_limits<I>::digits < std::numeric_limits<unsigned>::digits),
+ unsigned, I>::type;
+ return WriteInner<BITS, compute_type>(val);
+ }
+
inline void Flush() {
if (offset) {
*(out++) = state;
@@ -129,7 +144,11 @@ constexpr inline I Mask() { return ((I((I(-1)) << (std::numeric_limits<I>::digit
/** Compute the smallest power of two that is larger than val. */
template<typename I>
static inline int CountBits(I val, int max) {
-#ifdef _MSC_VER
+#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
+ // c++20 impl
+ (void)max;
+ return std::bit_width(val);
+#elif defined(_MSC_VER)
(void)max;
unsigned long index;
unsigned char ret;
@@ -175,6 +194,7 @@ public:
}
static constexpr inline bool IsZero(I a) { return a == 0; }
+ static constexpr inline bool IsOne(I a) { return a == 1; }
static constexpr inline I Mask(I val) { return val & MASK; }
static constexpr inline I Shift(I val, int bits) { return ((val << bits) & MASK); }
static constexpr inline I UnsafeShift(I val, int bits) { return (val << bits); }
@@ -233,7 +253,7 @@ template<typename I, int N, typename L, typename F> inline constexpr I GFMul(con
template<typename I, typename F, int BITS, uint32_t MOD>
inline I InvExtGCD(I x)
{
- if (F::IsZero(x)) return x;
+ if (F::IsZero(x) || F::IsOne(x)) return x;
I t(0), newt(1);
I r(MOD), newr = x;
int rlen = BITS + 1, newrlen = F::Bits(newr, BITS);
diff --git a/src/net.cpp b/src/net.cpp
index e388f05b03..3e959c187c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2256,7 +2256,11 @@ void CConnman::ThreadDNSAddressSeed()
if (!resolveSource.SetInternal(host)) {
continue;
}
- unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed
+ // Limit number of IPs learned from a single DNS seed. This limit exists to prevent the results from
+ // one DNS seed from dominating AddrMan. Note that the number of results from a UDP DNS query is
+ // bounded to 33 already, but it is possible for it to use TCP where a larger number of results can be
+ // returned.
+ unsigned int nMaxIPs = 32;
const auto addresses{LookupHost(host, nMaxIPs, true)};
if (!addresses.empty()) {
for (const CNetAddr& ip : addresses) {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 6996af38cb..39ffff97d2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1006,7 +1006,7 @@ private:
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
* these are kept in a ring buffer */
- std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_msgproc_mutex);
+ std::vector<CTransactionRef> vExtraTxnForCompact GUARDED_BY(g_msgproc_mutex);
/** Offset into vExtraTxnForCompact to insert the next tx */
size_t vExtraTxnForCompactIt GUARDED_BY(g_msgproc_mutex) = 0;
@@ -1802,7 +1802,7 @@ void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
return;
if (!vExtraTxnForCompact.size())
vExtraTxnForCompact.resize(m_opts.max_extra_txs);
- vExtraTxnForCompact[vExtraTxnForCompactIt] = std::make_pair(tx->GetWitnessHash(), tx);
+ vExtraTxnForCompact[vExtraTxnForCompactIt] = tx;
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % m_opts.max_extra_txs;
}
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 3ca1a5227a..f0fa298378 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -632,10 +632,7 @@ std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connecti
std::unique_ptr<Sock> Proxy::Connect() const
{
- if (!IsValid()) {
- LogPrintf("Cannot connect to invalid Proxy\n");
- return {};
- }
+ if (!IsValid()) return {};
if (!m_is_unix_socket) return ConnectDirectly(proxy, /*manual_connection=*/true);
@@ -656,7 +653,6 @@ std::unique_ptr<Sock> Proxy::Connect() const
socklen_t len = sizeof(addrun);
if(!ConnectToSocket(*sock, (struct sockaddr*)&addrun, len, path, /*manual_connection=*/true)) {
- LogPrintf("Cannot connect to socket for %s\n", path);
return {};
}
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index f9a372e3de..4d2d83812e 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -406,6 +406,7 @@ public:
NodeContext* m_context{nullptr};
};
+// NOLINTNEXTLINE(misc-no-recursion)
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active, const BlockManager& blockman)
{
if (!index) return false;
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 84c3352b9d..2ad79b6f99 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -191,13 +191,13 @@ std::optional<std::pair<DiagramCheckError, std::string>> ImprovesFeerateDiagram(
int64_t replacement_vsize)
{
// Require that the replacement strictly improves the mempool's feerate diagram.
- const auto diagram_results{pool.CalculateFeerateDiagramsForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
+ const auto chunk_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
- if (!diagram_results.has_value()) {
- return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(diagram_results).original);
+ if (!chunk_results.has_value()) {
+ return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(chunk_results).original);
}
- if (!std::is_gt(CompareFeerateDiagram(diagram_results.value().second, diagram_results.value().first))) {
+ if (!std::is_gt(CompareChunks(chunk_results.value().second, chunk_results.value().first))) {
return std::make_pair(DiagramCheckError::FAILURE, "insufficient feerate: does not improve feerate diagram");
}
return std::nullopt;
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 084b9a6615..1d1a84e375 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -109,22 +109,26 @@ QFont fixedPitchFont(bool use_embedded_font)
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
}
-// Just some dummy data to generate a convincing random-looking (but consistent) address
-static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47};
-
-// Generate a dummy address with invalid CRC, starting with the network prefix.
+// Return a pre-generated dummy bech32m address (P2TR) with invalid checksum.
static std::string DummyAddress(const CChainParams &params)
{
- std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
- sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
- for(int i=0; i<256; ++i) { // Try every trailing byte
- std::string s = EncodeBase58(sourcedata);
- if (!IsValidDestinationString(s)) {
- return s;
- }
- sourcedata[sourcedata.size()-1] += 1;
- }
- return "";
+ std::string addr;
+ switch (params.GetChainType()) {
+ case ChainType::MAIN:
+ addr = "bc1p35yvjel7srp783ztf8v6jdra7dhfzk5jaun8xz2qp6ws7z80n4tq2jku9f";
+ break;
+ case ChainType::SIGNET:
+ case ChainType::TESTNET:
+ addr = "tb1p35yvjel7srp783ztf8v6jdra7dhfzk5jaun8xz2qp6ws7z80n4tqa6qnlg";
+ break;
+ case ChainType::REGTEST:
+ addr = "bcrt1p35yvjel7srp783ztf8v6jdra7dhfzk5jaun8xz2qp6ws7z80n4tqsr2427";
+ break;
+ } // no default case, so the compiler can warn about missing cases
+ assert(!addr.empty());
+
+ if (Assume(!IsValidDestinationString(addr))) return addr;
+ return {};
}
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 2021e5f9dc..551c0ffd13 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -112,10 +112,10 @@ FreedesktopImage::FreedesktopImage(const QImage &img):
for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
{
- image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
- image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
- image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
- image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
+ image[ptr * BYTES_PER_PIXEL + 0] = char(data[ptr] >> 16); // R
+ image[ptr * BYTES_PER_PIXEL + 1] = char(data[ptr] >> 8); // G
+ image[ptr * BYTES_PER_PIXEL + 2] = char(data[ptr]); // B
+ image[ptr * BYTES_PER_PIXEL + 3] = char(data[ptr] >> 24); // A
}
}
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index d816a72ca3..d0f7c64357 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -396,6 +396,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
return successful;
}
+// NOLINTNEXTLINE(misc-no-recursion)
QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) const
{
auto setting = [&]{ return node().getPersistentSetting(SettingName(option) + suffix); };
@@ -508,6 +509,7 @@ QFont OptionsModel::getFontForMoney() const
return getFontForChoice(m_font_money);
}
+// NOLINTNEXTLINE(misc-no-recursion)
bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::string& suffix)
{
auto changed = [&] { return value.isValid() && value != getOption(option, suffix); };
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 1bdf94d3b5..fe000bcbb8 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -569,16 +569,17 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return true;
}
-bool WalletModel::displayAddress(std::string sAddress) const
+void WalletModel::displayAddress(std::string sAddress) const
{
CTxDestination dest = DecodeDestination(sAddress);
- bool res = false;
try {
- res = m_wallet->displayAddress(dest);
+ util::Result<void> result = m_wallet->displayAddress(dest);
+ if (!result) {
+ QMessageBox::warning(nullptr, tr("Signer error"), QString::fromStdString(util::ErrorString(result).translated));
+ }
} catch (const std::runtime_error& e) {
QMessageBox::critical(nullptr, tr("Can't display address"), e.what());
}
- return res;
}
bool WalletModel::isWalletEnabled()
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 503ee16823..ab2096c1fe 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -130,7 +130,7 @@ public:
UnlockContext requestUnlock();
bool bumpFee(uint256 hash, uint256& new_hash);
- bool displayAddress(std::string sAddress) const;
+ void displayAddress(std::string sAddress) const;
static bool isWalletEnabled();
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 09e0771534..4926d1e80b 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -134,7 +134,7 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int
return;
QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
- qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
+ qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toLongLong();
QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
QModelIndex index = ttm->index(start, 0, parent);
QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index da81a61651..123b5cc06c 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -13,6 +13,7 @@
#include <compat/compat.h>
#include <compat/cpuid.h>
#include <crypto/sha512.h>
+#include <span.h>
#include <support/cleanse.h>
#include <util/time.h>
@@ -357,10 +358,19 @@ void RandAddStaticEnv(CSHA512& hasher)
hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ;
// Hostname
+#ifdef WIN32
+ constexpr DWORD max_size = MAX_COMPUTERNAME_LENGTH + 1;
+ char hname[max_size];
+ DWORD size = max_size;
+ if (GetComputerNameA(hname, &size) != 0) {
+ hasher.Write(UCharCast(hname), size);
+ }
+#else
char hname[256];
if (gethostname(hname, 256) == 0) {
hasher.Write((const unsigned char*)hname, strnlen(hname, 256));
}
+#endif
#if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
// Network interfaces
diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp
index b8c0080aef..ffc2ee5ab0 100644
--- a/src/rpc/node.cpp
+++ b/src/rpc/node.cpp
@@ -26,6 +26,7 @@
#include <univalue.h>
#include <util/any.h>
#include <util/check.h>
+#include <util/time.h>
#include <stdint.h>
#ifdef HAVE_MALLOC_INFO
@@ -58,9 +59,11 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
const int64_t time{request.params[0].getInt<int64_t>()};
- if (time < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
+ constexpr int64_t max_time{Ticks<std::chrono::seconds>(std::chrono::nanoseconds::max())};
+ if (time < 0 || time > max_time) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime must be in the range [0, %s], not %s.", max_time, time));
}
+
SetMockTime(time);
const NodeContext& node_context{EnsureAnyNodeContext(request.context)};
for (const auto& chain_client : node_context.chain_clients) {
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 51c88cc1ba..6e332e3855 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -414,6 +414,7 @@ struct Sections {
/**
* Recursive helper to translate an RPCArg into sections
*/
+ // NOLINTNEXTLINE(misc-no-recursion)
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
{
const auto indent = std::string(current_indent, ' ');
@@ -953,6 +954,7 @@ std::string RPCArg::ToDescriptionString(bool is_named_arg) const
return ret;
}
+// NOLINTNEXTLINE(misc-no-recursion)
void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
{
// Indentation
@@ -1086,6 +1088,7 @@ static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
NONFATAL_UNREACHABLE();
}
+// NOLINTNEXTLINE(misc-no-recursion)
UniValue RPCResult::MatchesType(const UniValue& result) const
{
if (m_skip_type_check) {
@@ -1164,6 +1167,7 @@ void RPCResult::CheckInnerDoc() const
CHECK_NONFATAL(inner_needed != m_inner.empty());
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
@@ -1202,6 +1206,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
NONFATAL_UNREACHABLE();
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::string RPCArg::ToString(const bool oneline) const
{
if (oneline && !m_opts.oneline_description.empty()) {
@@ -1228,6 +1233,7 @@ std::string RPCArg::ToString(const bool oneline) const
case Type::OBJ:
case Type::OBJ_NAMED_PARAMS:
case Type::OBJ_USER_KEYS: {
+ // NOLINTNEXTLINE(misc-no-recursion)
const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
if (m_type == Type::OBJ) {
return "{" + res + "}";
diff --git a/src/rpc/util.h b/src/rpc/util.h
index ad3ed97b2e..f6ee6a317a 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -162,6 +162,7 @@ struct RPCArgOptions {
//!< methods set the also_positional flag and read values from both positions.
};
+// NOLINTNEXTLINE(misc-no-recursion)
struct RPCArg {
enum class Type {
OBJ,
@@ -271,6 +272,7 @@ struct RPCArg {
std::string ToDescriptionString(bool is_named_arg) const;
};
+// NOLINTNEXTLINE(misc-no-recursion)
struct RPCResult {
enum class Type {
OBJ,
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index a0e755afac..a11d4dcbd5 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -599,6 +599,7 @@ public:
COMPAT, // string calculation that mustn't change over time to stay compatible with previous software versions
};
+ // NOLINTNEXTLINE(misc-no-recursion)
bool IsSolvable() const override
{
for (const auto& arg : m_subdescriptor_args) {
@@ -607,6 +608,7 @@ public:
return true;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
bool IsRange() const final
{
for (const auto& pubkey : m_pubkey_args) {
@@ -618,6 +620,7 @@ public:
return false;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const
{
size_t pos = 0;
@@ -630,6 +633,7 @@ public:
return true;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
virtual bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr) const
{
std::string extra = ToStringExtra();
@@ -682,6 +686,7 @@ public:
return ret;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
bool ExpandHelper(int pos, const SigningProvider& arg, const DescriptorCache* read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache) const
{
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
@@ -723,6 +728,7 @@ public:
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &read_cache, output_scripts, out, nullptr);
}
+ // NOLINTNEXTLINE(misc-no-recursion)
void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final
{
for (const auto& p : m_pubkey_args) {
@@ -750,6 +756,7 @@ public:
std::optional<int64_t> MaxSatisfactionElems() const override { return {}; }
+ // NOLINTNEXTLINE(misc-no-recursion)
void GetPubKeys(std::set<CPubKey>& pubkeys, std::set<CExtPubKey>& ext_pubs) const override
{
for (const auto& p : m_pubkey_args) {
@@ -1579,6 +1586,7 @@ struct KeyParser {
};
/** Parse a script in a particular context. */
+// NOLINTNEXTLINE(misc-no-recursion)
std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
using namespace spanparsing;
@@ -1886,6 +1894,7 @@ std::unique_ptr<DescriptorImpl> InferMultiA(const CScript& script, ParseScriptCo
return std::make_unique<MultiADescriptor>(match->first, std::move(keys));
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) {
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index be4b357568..22ac062a63 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -295,7 +295,7 @@ struct TapSatisfier: Satisfier<XOnlyPubKey> {
//! Conversion from a raw xonly public key.
template <typename I>
std::optional<XOnlyPubKey> FromPKBytes(I first, I last) const {
- CHECK_NONFATAL(last - first == 32);
+ if (last - first != 32) return {};
XOnlyPubKey pubkey;
std::copy(first, last, pubkey.begin());
return pubkey;
diff --git a/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml b/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml
index 094ff891f7..ce10eb2686 100644
--- a/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml
+++ b/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml
@@ -16,7 +16,7 @@ runs:
cat valgrind_fingerprint
shell: bash
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
id: cache
with:
path: ${{ env.CI_HOMEBREW_CELLAR_VALGRIND }}
diff --git a/src/secp256k1/.github/actions/run-in-docker-action/action.yml b/src/secp256k1/.github/actions/run-in-docker-action/action.yml
index dbfaa4fece..74933686a0 100644
--- a/src/secp256k1/.github/actions/run-in-docker-action/action.yml
+++ b/src/secp256k1/.github/actions/run-in-docker-action/action.yml
@@ -36,6 +36,11 @@ runs:
load: true
cache-from: type=gha
+ - # Workaround for https://github.com/google/sanitizers/issues/1614 .
+ # The underlying issue has been fixed in clang 18.1.3.
+ run: sudo sysctl -w vm.mmap_rnd_bits=28
+ shell: bash
+
- # Tell Docker to pass environment variables in `env` into the container.
run: >
docker run \
diff --git a/src/secp256k1/CMakeLists.txt b/src/secp256k1/CMakeLists.txt
index cf0dc3ba93..9ef7defe51 100644
--- a/src/secp256k1/CMakeLists.txt
+++ b/src/secp256k1/CMakeLists.txt
@@ -51,29 +51,40 @@ endif()
option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL})
-option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
-if(SECP256K1_ENABLE_MODULE_ECDH)
- add_compile_definitions(ENABLE_MODULE_ECDH=1)
-endif()
+## Modules
+# We declare all options before processing them, to make sure we can express
+# dependendencies while processing.
+option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF)
-if(SECP256K1_ENABLE_MODULE_RECOVERY)
- add_compile_definitions(ENABLE_MODULE_RECOVERY=1)
-endif()
-
option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
+option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
+
+# Processing must be done in a topological sorting of the dependency graph
+# (dependent module first).
+if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
+ add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
+endif()
+
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
+ if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
+ message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.")
+ endif()
set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON)
add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1)
endif()
+
if(SECP256K1_ENABLE_MODULE_EXTRAKEYS)
add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1)
endif()
-option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
-if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
- add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
+if(SECP256K1_ENABLE_MODULE_RECOVERY)
+ add_compile_definitions(ENABLE_MODULE_RECOVERY=1)
+endif()
+
+if(SECP256K1_ENABLE_MODULE_ECDH)
+ add_compile_definitions(ENABLE_MODULE_ECDH=1)
endif()
option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF)
@@ -254,9 +265,14 @@ if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUST
enable_testing()
endif()
+set(SECP256K1_LATE_CFLAGS "" CACHE STRING "Compiler flags that are added to the command line after all other flags added by the build system.")
+include(AllTargetsCompileOptions)
+
add_subdirectory(src)
+all_targets_compile_options(src "${SECP256K1_LATE_CFLAGS}")
if(SECP256K1_BUILD_EXAMPLES)
add_subdirectory(examples)
+ all_targets_compile_options(examples "${SECP256K1_LATE_CFLAGS}")
endif()
message("\n")
@@ -330,6 +346,9 @@ else()
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
endif()
+if(SECP256K1_LATE_CFLAGS)
+ message("SECP256K1_LATE_CFLAGS ................. ${SECP256K1_LATE_CFLAGS}")
+endif()
message("\n")
if(SECP256K1_EXPERIMENTAL)
message(
diff --git a/src/secp256k1/CONTRIBUTING.md b/src/secp256k1/CONTRIBUTING.md
index a5e457913a..5fbf7332c9 100644
--- a/src/secp256k1/CONTRIBUTING.md
+++ b/src/secp256k1/CONTRIBUTING.md
@@ -44,7 +44,7 @@ The Contributor Workflow & Peer Review in libsecp256k1 are similar to Bitcoin Co
In addition, libsecp256k1 tries to maintain the following coding conventions:
-* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Morever, it should be possible to use the library without any heap allocations.
+* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations.
* The tests should cover all lines and branches of the library (see [Test coverage](#coverage)).
* Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)).
* Local variables containing secret data should be cleared explicitly to try to delete secrets from memory.
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 4013e6a93b..6e88eb4ecb 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -79,9 +79,9 @@ To maintain a pristine source tree, CMake encourages to perform an out-of-source
$ mkdir build && cd build
$ cmake ..
- $ make
- $ make check # run the test suite
- $ sudo make install # optional
+ $ cmake --build .
+ $ ctest # run the test suite
+ $ sudo cmake --build . --target install # optional
To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags.
diff --git a/src/secp256k1/ci/ci.sh b/src/secp256k1/ci/ci.sh
index 9cc715955e..3999af4f1c 100755
--- a/src/secp256k1/ci/ci.sh
+++ b/src/secp256k1/ci/ci.sh
@@ -17,7 +17,8 @@ print_environment() {
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
EXAMPLES \
HOST WRAPPER_CMD \
- CC CFLAGS CPPFLAGS AR NM
+ CC CFLAGS CPPFLAGS AR NM \
+ UBSAN_OPTIONS ASAN_OPTIONS LSAN_OPTIONS
do
eval "isset=\${$var+x}"
if [ -n "$isset" ]; then
diff --git a/src/secp256k1/cmake/AllTargetsCompileOptions.cmake b/src/secp256k1/cmake/AllTargetsCompileOptions.cmake
new file mode 100644
index 0000000000..6e420e0fde
--- /dev/null
+++ b/src/secp256k1/cmake/AllTargetsCompileOptions.cmake
@@ -0,0 +1,12 @@
+# Add compile options to all targets added in the subdirectory.
+function(all_targets_compile_options dir options)
+ get_directory_property(targets DIRECTORY ${dir} BUILDSYSTEM_TARGETS)
+ separate_arguments(options)
+ set(compiled_target_types STATIC_LIBRARY SHARED_LIBRARY OBJECT_LIBRARY EXECUTABLE)
+ foreach(target ${targets})
+ get_target_property(type ${target} TYPE)
+ if(type IN_LIST compiled_target_types)
+ target_compile_options(${target} PRIVATE ${options})
+ endif()
+ endforeach()
+endfunction()
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 2c1596775e..158ed5d769 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -387,29 +387,32 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
### Handle module options
###
-if test x"$enable_module_ecdh" = x"yes"; then
- SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
-fi
-
-if test x"$enable_module_recovery" = x"yes"; then
- SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1"
+# Processing must be done in a reverse topological sorting of the dependency graph
+# (dependent module first).
+if test x"$enable_module_ellswift" = x"yes"; then
+ SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1"
fi
if test x"$enable_module_schnorrsig" = x"yes"; then
- SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1"
+ if test x"$enable_module_extrakeys" = x"no"; then
+ AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.])
+ fi
enable_module_extrakeys=yes
+ SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1"
fi
-if test x"$enable_module_ellswift" = x"yes"; then
- SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1"
-fi
-
-# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
-# module to set enable_module_extrakeys=yes
if test x"$enable_module_extrakeys" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1"
fi
+if test x"$enable_module_recovery" = x"yes"; then
+ SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1"
+fi
+
+if test x"$enable_module_ecdh" = x"yes"; then
+ SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
+fi
+
if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi
diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h
index 034a38e6a0..37c8c691f2 100644
--- a/src/secp256k1/contrib/lax_der_parsing.h
+++ b/src/secp256k1/contrib/lax_der_parsing.h
@@ -67,8 +67,8 @@ extern "C" {
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
- * Out: sig: a pointer to a signature object
- * In: input: a pointer to the signature to be parsed
+ * Out: sig: pointer to a signature object
+ * In: input: pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
diff --git a/src/secp256k1/doc/release-process.md b/src/secp256k1/doc/release-process.md
index 51e337a5ab..cdf62430df 100644
--- a/src/secp256k1/doc/release-process.md
+++ b/src/secp256k1/doc/release-process.md
@@ -1,4 +1,4 @@
-# Release Process
+# Release process
This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`.
@@ -14,31 +14,30 @@ This process also assumes that there will be no minor releases for old major rel
We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core.
-## Sanity Checks
-Perform these checks before creating a release:
+## Sanity checks
+Perform these checks when reviewing the release PR (see below):
1. Ensure `make distcheck` doesn't fail.
-```shell
-./autogen.sh && ./configure --enable-dev-mode && make distcheck
-```
+ ```shell
+ ./autogen.sh && ./configure --enable-dev-mode && make distcheck
+ ```
2. Check installation with autotools:
-```shell
-dir=$(mktemp -d)
-./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir
-gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa
-```
+ ```shell
+ dir=$(mktemp -d)
+ ./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir
+ gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa
+ ```
3. Check installation with CMake:
-```shell
-dir=$(mktemp -d)
-build=$(mktemp -d)
-cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build --target install && ls -RlAh $dir
-gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa
-```
-4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to ensure there are no unexpected ABI incompatibilities and that the version number and release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required.
-
-```shell
-tools/check-abi.sh
-```
+ ```shell
+ dir=$(mktemp -d)
+ build=$(mktemp -d)
+ cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build --target install && ls -RlAh $dir
+ gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa
+ ```
+4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to verify that there are no unexpected ABI incompatibilities and that the version number and the release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required.
+ ```shell
+ tools/check-abi.sh
+ ```
## Regular release
@@ -47,27 +46,29 @@ tools/check-abi.sh
* adding a section for the release (make sure that the version number is a link to a diff between the previous and new version),
* removing the `[Unreleased]` section header, and
* including an entry for `### ABI Compatibility` if it doesn't exist,
- * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and
- * if this is not a patch release
- * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac` and
+ * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and,
+ * if this is not a patch release,
+ * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and
* updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`.
-2. After the PR is merged, tag the commit and push it:
+2. Perform the [sanity checks](#sanity-checks) on the PR branch.
+3. After the PR is merged, tag the commit, and push the tag:
```
RELEASE_COMMIT=<merge commit of step 1>
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
```
-3. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that
+4. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that
* sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`,
* increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and
* adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md).
If other maintainers are not present to approve the PR, it can be merged without ACKs.
-4. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
+5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
+6. Send an announcement email to the bitcoin-dev mailing list.
## Maintenance release
-Note that bugfixes only need to be backported to releases for which no compatible release without the bug exists.
+Note that bug fixes need to be backported only to releases for which no compatible release without the bug exists.
1. If there's no maintenance branch `$MAJOR.$MINOR`, create one:
```
@@ -75,19 +76,18 @@ Note that bugfixes only need to be backported to releases for which no compatibl
git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR
```
2. Open a pull request to the `$MAJOR.$MINOR` branch that
- * includes the bugfixes,
+ * includes the bug fixes,
* finalizes the release notes similar to a regular release,
* increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`
and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`
(with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example).
-3. After the PRs are merged, update the release branch and tag the commit:
+3. Perform the [sanity checks](#sanity-checks) on the PR branch.
+4. After the PRs are merged, update the release branch, tag the commit, and push the tag:
```
git checkout $MAJOR.$MINOR && git pull
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH"
- ```
-4. Push tag:
- ```
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
```
-5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
-6. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md).
+6. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
+7. Send an announcement email to the bitcoin-dev mailing list.
+8. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md).
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 936f0b42b7..f4053f2a93 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -265,7 +265,7 @@ SECP256K1_API void secp256k1_selftest(void);
* memory allocation entirely, see secp256k1_context_static and the functions in
* secp256k1_preallocated.h.
*
- * Returns: a newly created context object.
+ * Returns: pointer to a newly created context object.
* In: flags: Always set to SECP256K1_CONTEXT_NONE (see below).
*
* The only valid non-deprecated flag in recent library versions is
@@ -296,8 +296,8 @@ SECP256K1_API secp256k1_context *secp256k1_context_create(
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
- * Returns: a newly created context object.
- * Args: ctx: an existing context to copy (not secp256k1_context_static)
+ * Returns: pointer to a newly created context object.
+ * Args: ctx: pointer to a context to copy (not secp256k1_context_static).
*/
SECP256K1_API secp256k1_context *secp256k1_context_clone(
const secp256k1_context *ctx
@@ -313,7 +313,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_clone(
* behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
* be used instead.
*
- * Args: ctx: an existing context to destroy, constructed using
+ * Args: ctx: pointer to a context to destroy, constructed using
* secp256k1_context_create or secp256k1_context_clone
* (i.e., not secp256k1_context_static).
*/
@@ -350,8 +350,8 @@ SECP256K1_API void secp256k1_context_destroy(
* fails. In this case, the corresponding default handler will be called with
* the data pointer argument set to NULL.
*
- * Args: ctx: an existing context object.
- * In: fun: a pointer to a function to call when an illegal argument is
+ * Args: ctx: pointer to a context object.
+ * In: fun: pointer to a function to call when an illegal argument is
* passed to the API, taking a message and an opaque pointer.
* (NULL restores the default handler.)
* data: the opaque pointer to pass to fun above, must be NULL for the default handler.
@@ -377,8 +377,8 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
* for that). After this callback returns, anything may happen, including
* crashing.
*
- * Args: ctx: an existing context object.
- * In: fun: a pointer to a function to call when an internal error occurs,
+ * Args: ctx: pointer to a context object.
+ * In: fun: pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores the
* default handler, see secp256k1_context_set_illegal_callback
* for details).
@@ -395,7 +395,7 @@ SECP256K1_API void secp256k1_context_set_error_callback(
/** Create a secp256k1 scratch space object.
*
* Returns: a newly created scratch space.
- * Args: ctx: an existing context object.
+ * Args: ctx: pointer to a context object.
* In: size: amount of memory to be available as scratch space. Some extra
* (<100 bytes) will be allocated for extra accounting.
*/
@@ -407,7 +407,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space *secp256k1_sc
/** Destroy a secp256k1 scratch space.
*
* The pointer may not be used afterwards.
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object.
* scratch: space to destroy
*/
SECP256K1_API void secp256k1_scratch_space_destroy(
@@ -419,7 +419,7 @@ SECP256K1_API void secp256k1_scratch_space_destroy(
*
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, its value is undefined.
* In: input: pointer to a serialized public key
@@ -439,14 +439,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse(
/** Serialize a pubkey object into a serialized byte sequence.
*
* Returns: 1 always.
- * Args: ctx: a secp256k1 context object.
- * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
+ * Args: ctx: pointer to a context object.
+ * Out: output: pointer to a 65-byte (if compressed==0) or 33-byte (if
* compressed==1) byte array to place the serialized key
* in.
- * In/Out: outputlen: a pointer to an integer which is initially set to the
+ * In/Out: outputlen: pointer to an integer which is initially set to the
* size of output, and is overwritten with the written
* size.
- * In: pubkey: a pointer to a secp256k1_pubkey containing an
+ * In: pubkey: pointer to a secp256k1_pubkey containing an
* initialized public key.
* flags: SECP256K1_EC_COMPRESSED if serialization should be in
* compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
@@ -464,7 +464,7 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
@@ -477,9 +477,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
/** Parse an ECDSA signature in compact (64 bytes) format.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
- * Args: ctx: a secp256k1 context object
- * Out: sig: a pointer to a signature object
- * In: input64: a pointer to the 64-byte array to parse
+ * Args: ctx: pointer to a context object
+ * Out: sig: pointer to a signature object
+ * In: input64: pointer to the 64-byte array to parse
*
* The signature must consist of a 32-byte big endian R value, followed by a
* 32-byte big endian S value. If R or S fall outside of [0..order-1], the
@@ -498,9 +498,9 @@ SECP256K1_API int secp256k1_ecdsa_signature_parse_compact(
/** Parse a DER ECDSA signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
- * Args: ctx: a secp256k1 context object
- * Out: sig: a pointer to a signature object
- * In: input: a pointer to the signature to be parsed
+ * Args: ctx: pointer to a context object
+ * Out: sig: pointer to a signature object
+ * In: input: pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
@@ -520,13 +520,13 @@ SECP256K1_API int secp256k1_ecdsa_signature_parse_der(
/** Serialize an ECDSA signature in DER format.
*
* Returns: 1 if enough space was available to serialize, 0 otherwise
- * Args: ctx: a secp256k1 context object
- * Out: output: a pointer to an array to store the DER serialization
- * In/Out: outputlen: a pointer to a length integer. Initially, this integer
+ * Args: ctx: pointer to a context object
+ * Out: output: pointer to an array to store the DER serialization
+ * In/Out: outputlen: pointer to a length integer. Initially, this integer
* should be set to the length of output. After the call
* it will be set to the length of the serialization (even
* if 0 was returned).
- * In: sig: a pointer to an initialized signature object
+ * In: sig: pointer to an initialized signature object
*/
SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
const secp256k1_context *ctx,
@@ -538,9 +538,9 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
/** Serialize an ECDSA signature in compact (64 byte) format.
*
* Returns: 1
- * Args: ctx: a secp256k1 context object
- * Out: output64: a pointer to a 64-byte array to store the compact serialization
- * In: sig: a pointer to an initialized signature object
+ * Args: ctx: pointer to a context object
+ * Out: output64: pointer to a 64-byte array to store the compact serialization
+ * In: sig: pointer to an initialized signature object
*
* See secp256k1_ecdsa_signature_parse_compact for details about the encoding.
*/
@@ -554,7 +554,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
*
* Returns: 1: correct signature
* 0: incorrect or unparseable signature
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object
* In: sig: the signature being verified.
* msghash32: the 32-byte message hash being verified.
* The verifier must make sure to apply a cryptographic
@@ -585,12 +585,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
/** Convert a signature to a normalized lower-S form.
*
* Returns: 1 if sigin was not normalized, 0 if it already was.
- * Args: ctx: a secp256k1 context object
- * Out: sigout: a pointer to a signature to fill with the normalized form,
+ * Args: ctx: pointer to a context object
+ * Out: sigout: pointer to a signature to fill with the normalized form,
* or copy if the input was already normalized. (can be NULL if
* you're only interested in whether the input was already
* normalized).
- * In: sigin: a pointer to a signature to check/normalize (can be identical to sigout)
+ * In: sigin: pointer to a signature to check/normalize (can be identical to sigout)
*
* With ECDSA a third-party can forge a second distinct signature of the same
* message, given a single initial signature, but without knowing the key. This
diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h
index 515e174299..4d9da3461d 100644
--- a/src/secp256k1/include/secp256k1_ecdh.h
+++ b/src/secp256k1/include/secp256k1_ecdh.h
@@ -39,7 +39,7 @@ SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_de
* 0: scalar was invalid (zero or overflow) or hashfp returned 0
* Args: ctx: pointer to a context object.
* Out: output: pointer to an array to be filled by hashfp.
- * In: pubkey: a pointer to a secp256k1_pubkey containing an initialized public key.
+ * In: pubkey: pointer to a secp256k1_pubkey containing an initialized public key.
* seckey: a 32-byte scalar with which to multiply the point.
* hashfp: pointer to a hash function. If NULL,
* secp256k1_ecdh_hash_function_sha256 is used
diff --git a/src/secp256k1/include/secp256k1_ellswift.h b/src/secp256k1/include/secp256k1_ellswift.h
index f79bd88396..ae37287f82 100644
--- a/src/secp256k1/include/secp256k1_ellswift.h
+++ b/src/secp256k1/include/secp256k1_ellswift.h
@@ -87,7 +87,7 @@ SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_
* Returns: 1 always.
* Args: ctx: pointer to a context object
* Out: ell64: pointer to a 64-byte array to be filled
- * In: pubkey: a pointer to a secp256k1_pubkey containing an
+ * In: pubkey: pointer to a secp256k1_pubkey containing an
* initialized public key
* rnd32: pointer to 32 bytes of randomness
*
@@ -169,7 +169,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create(
* (will not be NULL)
* ell_b64: pointer to the 64-byte encoded public key of party B
* (will not be NULL)
- * seckey32: a pointer to our 32-byte secret key
+ * seckey32: pointer to our 32-byte secret key
* party: boolean indicating which party we are: zero if we are
* party A, non-zero if we are party B. seckey32 must be
* the private key corresponding to that party's ell_?64.
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
index 7fcce68e68..ad70b92f95 100644
--- a/src/secp256k1/include/secp256k1_extrakeys.h
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -39,7 +39,7 @@ typedef struct {
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
*
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, it's set to an invalid value.
* In: input32: pointer to a serialized xonly_pubkey.
@@ -54,9 +54,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse(
*
* Returns: 1 always.
*
- * Args: ctx: a secp256k1 context object.
- * Out: output32: a pointer to a 32-byte array to place the serialized key in.
- * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an initialized public key.
+ * Args: ctx: pointer to a context object.
+ * Out: output32: pointer to a 32-byte array to place the serialized key in.
+ * In: pubkey: pointer to a secp256k1_xonly_pubkey containing an initialized public key.
*/
SECP256K1_API int secp256k1_xonly_pubkey_serialize(
const secp256k1_context *ctx,
@@ -69,7 +69,7 @@ SECP256K1_API int secp256k1_xonly_pubkey_serialize(
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object.
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
diff --git a/src/secp256k1/include/secp256k1_preallocated.h b/src/secp256k1/include/secp256k1_preallocated.h
index f37744777b..f2d95c245e 100644
--- a/src/secp256k1/include/secp256k1_preallocated.h
+++ b/src/secp256k1/include/secp256k1_preallocated.h
@@ -52,8 +52,8 @@ SECP256K1_API size_t secp256k1_context_preallocated_size(
* in the memory. In simpler words, the prealloc pointer (or any pointer derived
* from it) should not be used during the lifetime of the context object.
*
- * Returns: a newly created context object.
- * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * Returns: pointer to newly created context object.
+ * In: prealloc: pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
* flags: which parts of the context to initialize.
@@ -72,7 +72,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_preallocated_create(
* caller-provided memory.
*
* Returns: the required size of the caller-provided memory block.
- * In: ctx: an existing context to copy.
+ * In: ctx: pointer to a context to copy.
*/
SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
const secp256k1_context *ctx
@@ -91,9 +91,9 @@ SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
- * Returns: a newly created context object.
- * Args: ctx: an existing context to copy (not secp256k1_context_static).
- * In: prealloc: a pointer to a rewritable contiguous block of memory of
+ * Returns: pointer to a newly created context object.
+ * Args: ctx: pointer to a context to copy (not secp256k1_context_static).
+ * In: prealloc: pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
*/
@@ -118,7 +118,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_preallocated_clone(
* preallocated pointer given to secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone.
*
- * Args: ctx: an existing context to destroy, constructed using
+ * Args: ctx: pointer to a context to destroy, constructed using
* secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone
* (i.e., not secp256k1_context_static).
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index b12ca4d972..341b8bac63 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -28,9 +28,9 @@ typedef struct {
/** Parse a compact ECDSA signature (64 bytes + recovery id).
*
* Returns: 1 when the signature could be parsed, 0 otherwise
- * Args: ctx: a secp256k1 context object
- * Out: sig: a pointer to a signature object
- * In: input64: a pointer to a 64-byte compact signature
+ * Args: ctx: pointer to a context object
+ * Out: sig: pointer to a signature object
+ * In: input64: pointer to a 64-byte compact signature
* recid: the recovery id (0, 1, 2 or 3)
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact(
@@ -43,9 +43,9 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact(
/** Convert a recoverable signature into a normal signature.
*
* Returns: 1
- * Args: ctx: a secp256k1 context object.
- * Out: sig: a pointer to a normal signature.
- * In: sigin: a pointer to a recoverable signature.
+ * Args: ctx: pointer to a context object.
+ * Out: sig: pointer to a normal signature.
+ * In: sigin: pointer to a recoverable signature.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert(
const secp256k1_context *ctx,
@@ -56,10 +56,10 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert(
/** Serialize an ECDSA signature in compact format (64 bytes + recovery id).
*
* Returns: 1
- * Args: ctx: a secp256k1 context object.
- * Out: output64: a pointer to a 64-byte array of the compact signature.
- * recid: a pointer to an integer to hold the recovery id.
- * In: sig: a pointer to an initialized signature object.
+ * Args: ctx: pointer to a context object.
+ * Out: output64: pointer to a 64-byte array of the compact signature.
+ * recid: pointer to an integer to hold the recovery id.
+ * In: sig: pointer to an initialized signature object.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
const secp256k1_context *ctx,
diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h
index 26358533f6..23163de2fb 100644
--- a/src/secp256k1/include/secp256k1_schnorrsig.h
+++ b/src/secp256k1/include/secp256k1_schnorrsig.h
@@ -169,11 +169,11 @@ SECP256K1_API int secp256k1_schnorrsig_sign_custom(
*
* Returns: 1: correct signature
* 0: incorrect signature
- * Args: ctx: a secp256k1 context object.
+ * Args: ctx: pointer to a context object.
* In: sig64: pointer to the 64-byte signature to verify.
* msg: the message being verified. Can only be NULL if msglen is 0.
* msglen: length of the message
- * pubkey: pointer to an x-only public key to verify with (cannot be NULL)
+ * pubkey: pointer to an x-only public key to verify with
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context *ctx,
diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h
index 8ed04209e9..7961005350 100644
--- a/src/secp256k1/src/assumptions.h
+++ b/src/secp256k1/src/assumptions.h
@@ -19,65 +19,69 @@
reduce the odds of experiencing an unwelcome surprise.
*/
-struct secp256k1_assumption_checker {
- /* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not
- allowed. */
- int dummy_array[(
- /* Bytes are 8 bits. */
- (CHAR_BIT == 8) &&
+#if defined(__has_attribute)
+# if __has_attribute(__unavailable__)
+__attribute__((__unavailable__("Don't call this function. It only exists because STATIC_ASSERT cannot be used outside a function.")))
+# endif
+#endif
+static void secp256k1_assumption_checker(void) {
+ /* Bytes are 8 bits. */
+ STATIC_ASSERT(CHAR_BIT == 8);
- /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32
- without signed overflow, which would be undefined behaviour. */
- (UINT_MAX <= UINT32_MAX) &&
+ /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32
+ without signed overflow, which would be undefined behaviour. */
+ STATIC_ASSERT(UINT_MAX <= UINT32_MAX);
- /* Conversions from unsigned to signed outside of the bounds of the signed type are
- implementation-defined. Verify that they function as reinterpreting the lower
- bits of the input in two's complement notation. Do this for conversions:
- - from uint(N)_t to int(N)_t with negative result
- - from uint(2N)_t to int(N)_t with negative result
- - from int(2N)_t to int(N)_t with negative result
- - from int(2N)_t to int(N)_t with positive result */
+ /* Conversions from unsigned to signed outside of the bounds of the signed type are
+ implementation-defined. Verify that they function as reinterpreting the lower
+ bits of the input in two's complement notation. Do this for conversions:
+ - from uint(N)_t to int(N)_t with negative result
+ - from uint(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with positive result */
- /* To int8_t. */
- ((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) &&
- ((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) &&
- ((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) &&
- ((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) &&
+ /* To int8_t. */
+ STATIC_ASSERT(((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55));
+ STATIC_ASSERT((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33);
+ STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF);
+ STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34);
- /* To int16_t. */
- ((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) &&
- ((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) &&
- ((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) &&
- ((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) &&
+ /* To int16_t. */
+ STATIC_ASSERT((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322);
+ STATIC_ASSERT((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C);
+ STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4);
+ STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678);
- /* To int32_t. */
- ((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) &&
- ((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) &&
- ((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) &&
- ((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) &&
+ /* To int32_t. */
+ STATIC_ASSERT((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B);
+ STATIC_ASSERT((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE);
+ STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8);
+ STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789);
- /* To int64_t. */
- ((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) &&
+ /* To int64_t. */
+ STATIC_ASSERT((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL);
#if defined(SECP256K1_INT128_NATIVE)
- ((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) &&
- (((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) &&
- (((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) &&
+ STATIC_ASSERT((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL);
+ STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL);
+ STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL);
- /* To int128_t. */
- ((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) &&
+ /* To int128_t. */
+ STATIC_ASSERT((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL));
#endif
- /* Right shift on negative signed values is implementation defined. Verify that it
- acts as a right shift in two's complement with sign extension (i.e duplicating
- the top bit into newly added bits). */
- ((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) &&
- ((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) &&
- ((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) &&
- ((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) &&
+ /* Right shift on negative signed values is implementation defined. Verify that it
+ acts as a right shift in two's complement with sign extension (i.e duplicating
+ the top bit into newly added bits). */
+ STATIC_ASSERT((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA);
+ STATIC_ASSERT((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A);
+ STATIC_ASSERT((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48);
+ STATIC_ASSERT((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL);
#if defined(SECP256K1_INT128_NATIVE)
- ((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) &&
+ STATIC_ASSERT((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL));
#endif
- 1) * 2 - 1];
-};
+
+ /* This function is not supposed to be called. */
+ VERIFY_CHECK(0);
+}
#endif /* SECP256K1_ASSUMPTIONS_H */
diff --git a/src/secp256k1/src/checkmem.h b/src/secp256k1/src/checkmem.h
index f2169decfc..7e333ce5f3 100644
--- a/src/secp256k1/src/checkmem.h
+++ b/src/secp256k1/src/checkmem.h
@@ -30,6 +30,8 @@
* - SECP256K1_CHECKMEM_DEFINE(p, len):
* - marks the len-byte memory pointed to by p as defined data (public data, in the
* context of constant-time checking).
+ * - SECP256K1_CHECKMEM_MSAN_DEFINE(p, len):
+ * - Like SECP256K1_CHECKMEM_DEFINE, but applies only to memory_sanitizer.
*
*/
@@ -48,11 +50,16 @@
# define SECP256K1_CHECKMEM_ENABLED 1
# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len))
# define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len))
+# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) __msan_unpoison((p), (len))
# define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len))
# define SECP256K1_CHECKMEM_RUNNING() (1)
# endif
#endif
+#if !defined SECP256K1_CHECKMEM_MSAN_DEFINE
+# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
+#endif
+
/* If valgrind integration is desired (through the VALGRIND define), implement the
* SECP256K1_CHECKMEM_* macros using valgrind. */
#if !defined SECP256K1_CHECKMEM_ENABLED
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index bd589bf8a8..8c65a3aff6 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -255,8 +255,8 @@ static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a);
/** Multiply two field elements.
*
* On input, a and b must be valid field elements; r does not need to be initialized.
- * r and a may point to the same object, but neither can be equal to b. The magnitudes
- * of a and b must not exceed 8.
+ * r and a may point to the same object, but neither may point to the object pointed
+ * to by b. The magnitudes of a and b must not exceed 8.
* Performs {r = a * b}
* On output, r will have magnitude 1, but won't be normalized.
*/
diff --git a/src/secp256k1/src/modules/ellswift/tests_impl.h b/src/secp256k1/src/modules/ellswift/tests_impl.h
index 7d1efbc492..f96e3a1268 100644
--- a/src/secp256k1/src/modules/ellswift/tests_impl.h
+++ b/src/secp256k1/src/modules/ellswift/tests_impl.h
@@ -188,9 +188,9 @@ void run_ellswift_tests(void) {
CHECK(ret == ((testcase->enc_bitmap >> c) & 1));
if (ret) {
secp256k1_fe x2;
- CHECK(check_fe_equal(&t, &testcase->encs[c]));
+ CHECK(fe_equal(&t, &testcase->encs[c]));
secp256k1_ellswift_xswiftec_var(&x2, &testcase->u, &testcase->encs[c]);
- CHECK(check_fe_equal(&testcase->x, &x2));
+ CHECK(fe_equal(&testcase->x, &x2));
}
}
}
@@ -203,7 +203,7 @@ void run_ellswift_tests(void) {
CHECK(ret);
ret = secp256k1_pubkey_load(CTX, &ge, &pubkey);
CHECK(ret);
- CHECK(check_fe_equal(&testcase->x, &ge.x));
+ CHECK(fe_equal(&testcase->x, &ge.x));
CHECK(secp256k1_fe_is_odd(&ge.y) == testcase->odd_y);
}
for (i = 0; (unsigned)i < sizeof(ellswift_xdh_tests_bip324) / sizeof(ellswift_xdh_tests_bip324[0]); ++i) {
@@ -290,7 +290,7 @@ void run_ellswift_tests(void) {
secp256k1_ecmult(&resj, &decj, &sec, NULL);
secp256k1_ge_set_gej(&res, &resj);
/* Compare. */
- CHECK(check_fe_equal(&res.x, &share_x));
+ CHECK(fe_equal(&res.x, &share_x));
}
/* Verify the joint behavior of secp256k1_ellswift_xdh */
for (i = 0; i < 200 * COUNT; i++) {
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 7b9c542f07..82cd957f16 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -462,6 +462,14 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
: "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc");
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m0, sizeof(m0));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m1, sizeof(m1));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m2, sizeof(m2));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m3, sizeof(m3));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m4, sizeof(m4));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m5, sizeof(m5));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&m6, sizeof(m6));
+
/* Reduce 385 bits into 258. */
__asm__ __volatile__(
/* Preload */
@@ -541,6 +549,12 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
: "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc");
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&p0, sizeof(p0));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&p1, sizeof(p1));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&p2, sizeof(p2));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&p3, sizeof(p3));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&p4, sizeof(p4));
+
/* Reduce 258 bits into 256. */
__asm__ __volatile__(
/* Preload */
@@ -586,6 +600,10 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
: "=g"(c)
: "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "cc", "memory");
+
+ SECP256K1_CHECKMEM_MSAN_DEFINE(r, sizeof(*r));
+ SECP256K1_CHECKMEM_MSAN_DEFINE(&c, sizeof(c));
+
#else
secp256k1_uint128 c128;
uint64_t c, c0, c1, c2;
@@ -663,7 +681,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r));
}
-static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) {
+static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, const secp256k1_scalar *b) {
#ifdef USE_ASM_X86_64
const uint64_t *pb = b->d;
__asm__ __volatile__(
@@ -678,7 +696,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
/* (rax,rdx) = a0 * b0 */
"movq %%r15, %%rax\n"
"mulq %%r11\n"
- /* Extract l0 */
+ /* Extract l8[0] */
"movq %%rax, 0(%%rsi)\n"
/* (r8,r9,r10) = (rdx) */
"movq %%rdx, %%r8\n"
@@ -696,7 +714,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
"addq %%rax, %%r8\n"
"adcq %%rdx, %%r9\n"
"adcq $0, %%r10\n"
- /* Extract l1 */
+ /* Extract l8[1] */
"movq %%r8, 8(%%rsi)\n"
"xorq %%r8, %%r8\n"
/* (r9,r10,r8) += a0 * b2 */
@@ -717,7 +735,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
"addq %%rax, %%r9\n"
"adcq %%rdx, %%r10\n"
"adcq $0, %%r8\n"
- /* Extract l2 */
+ /* Extract l8[2] */
"movq %%r9, 16(%%rsi)\n"
"xorq %%r9, %%r9\n"
/* (r10,r8,r9) += a0 * b3 */
@@ -746,7 +764,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
"addq %%rax, %%r10\n"
"adcq %%rdx, %%r8\n"
"adcq $0, %%r9\n"
- /* Extract l3 */
+ /* Extract l8[3] */
"movq %%r10, 24(%%rsi)\n"
"xorq %%r10, %%r10\n"
/* (r8,r9,r10) += a1 * b3 */
@@ -767,7 +785,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
"addq %%rax, %%r8\n"
"adcq %%rdx, %%r9\n"
"adcq $0, %%r10\n"
- /* Extract l4 */
+ /* Extract l8[4] */
"movq %%r8, 32(%%rsi)\n"
"xorq %%r8, %%r8\n"
/* (r9,r10,r8) += a2 * b3 */
@@ -782,51 +800,54 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
"addq %%rax, %%r9\n"
"adcq %%rdx, %%r10\n"
"adcq $0, %%r8\n"
- /* Extract l5 */
+ /* Extract l8[5] */
"movq %%r9, 40(%%rsi)\n"
/* (r10,r8) += a3 * b3 */
"movq %%r15, %%rax\n"
"mulq %%r14\n"
"addq %%rax, %%r10\n"
"adcq %%rdx, %%r8\n"
- /* Extract l6 */
+ /* Extract l8[6] */
"movq %%r10, 48(%%rsi)\n"
- /* Extract l7 */
+ /* Extract l8[7] */
"movq %%r8, 56(%%rsi)\n"
: "+d"(pb)
- : "S"(l), "D"(a->d)
+ : "S"(l8), "D"(a->d)
: "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory");
+
+ SECP256K1_CHECKMEM_MSAN_DEFINE(l8, sizeof(*l8) * 8);
+
#else
/* 160 bit accumulator. */
uint64_t c0 = 0, c1 = 0;
uint32_t c2 = 0;
- /* l[0..7] = a[0..3] * b[0..3]. */
+ /* l8[0..7] = a[0..3] * b[0..3]. */
muladd_fast(a->d[0], b->d[0]);
- extract_fast(l[0]);
+ extract_fast(l8[0]);
muladd(a->d[0], b->d[1]);
muladd(a->d[1], b->d[0]);
- extract(l[1]);
+ extract(l8[1]);
muladd(a->d[0], b->d[2]);
muladd(a->d[1], b->d[1]);
muladd(a->d[2], b->d[0]);
- extract(l[2]);
+ extract(l8[2]);
muladd(a->d[0], b->d[3]);
muladd(a->d[1], b->d[2]);
muladd(a->d[2], b->d[1]);
muladd(a->d[3], b->d[0]);
- extract(l[3]);
+ extract(l8[3]);
muladd(a->d[1], b->d[3]);
muladd(a->d[2], b->d[2]);
muladd(a->d[3], b->d[1]);
- extract(l[4]);
+ extract(l8[4]);
muladd(a->d[2], b->d[3]);
muladd(a->d[3], b->d[2]);
- extract(l[5]);
+ extract(l8[5]);
muladd_fast(a->d[3], b->d[3]);
- extract_fast(l[6]);
+ extract_fast(l8[6]);
VERIFY_CHECK(c1 == 0);
- l[7] = c0;
+ l8[7] = c0;
#endif
}
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index bbba83e937..972d8041b0 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -229,7 +229,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT
* <= {triangle inequality}
* a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2|
* < {Lemma 1 and Lemma 2}
- * a1*(2^-1 + epslion1) + a2*(2^-1 + epsilon2)
+ * a1*(2^-1 + epsilon1) + a2*(2^-1 + epsilon2)
* < {rounding up to an integer}
* (a1 + a2 + 1)/2
* < {rounding up to a power of 2}
@@ -247,7 +247,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT
* <= {triangle inequality}
* (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2|
* < {Lemma 1 and Lemma 2}
- * (-b1)*(2^-1 + epslion1) + b2*(2^-1 + epsilon2)
+ * (-b1)*(2^-1 + epsilon1) + b2*(2^-1 + epsilon2)
* < {rounding up to an integer}
* (-b1 + b2)/2 + 1
* < {rounding up to a power of 2}
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 4c11e7f0b8..15a5eede67 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -237,36 +237,25 @@ static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx,
}
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
- if (sizeof(secp256k1_ge_storage) == 64) {
- /* When the secp256k1_ge_storage type is exactly 64 byte, use its
- * representation inside secp256k1_pubkey, as conversion is very fast.
- * Note that secp256k1_pubkey_save must use the same representation. */
- secp256k1_ge_storage s;
- memcpy(&s, &pubkey->data[0], sizeof(s));
- secp256k1_ge_from_storage(ge, &s);
- } else {
- /* Otherwise, fall back to 32-byte big endian for X and Y. */
- secp256k1_fe x, y;
- ARG_CHECK(secp256k1_fe_set_b32_limit(&x, pubkey->data));
- ARG_CHECK(secp256k1_fe_set_b32_limit(&y, pubkey->data + 32));
- secp256k1_ge_set_xy(ge, &x, &y);
- }
+ secp256k1_ge_storage s;
+
+ /* We require that the secp256k1_ge_storage type is exactly 64 bytes.
+ * This is formally not guaranteed by the C standard, but should hold on any
+ * sane compiler in the real world. */
+ STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64);
+ memcpy(&s, &pubkey->data[0], 64);
+ secp256k1_ge_from_storage(ge, &s);
ARG_CHECK(!secp256k1_fe_is_zero(&ge->x));
return 1;
}
static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
- if (sizeof(secp256k1_ge_storage) == 64) {
- secp256k1_ge_storage s;
- secp256k1_ge_to_storage(&s, ge);
- memcpy(&pubkey->data[0], &s, sizeof(s));
- } else {
- VERIFY_CHECK(!secp256k1_ge_is_infinity(ge));
- secp256k1_fe_normalize_var(&ge->x);
- secp256k1_fe_normalize_var(&ge->y);
- secp256k1_fe_get_b32(pubkey->data, &ge->x);
- secp256k1_fe_get_b32(pubkey->data + 32, &ge->y);
- }
+ secp256k1_ge_storage s;
+
+ STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64);
+ VERIFY_CHECK(!secp256k1_ge_is_infinity(ge));
+ secp256k1_ge_to_storage(&s, ge);
+ memcpy(&pubkey->data[0], &s, 64);
}
int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) {
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index bec1c45585..85b4881295 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -2927,20 +2927,18 @@ static void run_scalar_tests(void) {
secp256k1_scalar_set_b32(&r2, res[i][1], &overflow);
CHECK(!overflow);
secp256k1_scalar_mul(&z, &x, &y);
- CHECK(!secp256k1_scalar_check_overflow(&z));
CHECK(secp256k1_scalar_eq(&r1, &z));
if (!secp256k1_scalar_is_zero(&y)) {
secp256k1_scalar_inverse(&zz, &y);
- CHECK(!secp256k1_scalar_check_overflow(&zz));
secp256k1_scalar_inverse_var(&zzv, &y);
CHECK(secp256k1_scalar_eq(&zzv, &zz));
secp256k1_scalar_mul(&z, &z, &zz);
- CHECK(!secp256k1_scalar_check_overflow(&z));
CHECK(secp256k1_scalar_eq(&x, &z));
secp256k1_scalar_mul(&zz, &zz, &y);
- CHECK(!secp256k1_scalar_check_overflow(&zz));
CHECK(secp256k1_scalar_eq(&secp256k1_scalar_one, &zz));
}
+ secp256k1_scalar_mul(&z, &x, &x);
+ CHECK(secp256k1_scalar_eq(&r2, &z));
}
}
}
@@ -2955,7 +2953,7 @@ static void random_fe_non_square(secp256k1_fe *ns) {
}
}
-static int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
+static int fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
secp256k1_fe an = *a;
secp256k1_fe bn = *b;
secp256k1_fe_normalize_weak(&an);
@@ -3092,7 +3090,7 @@ static void run_field_half(void) {
#endif
secp256k1_fe_normalize_weak(&u);
secp256k1_fe_add(&u, &u);
- CHECK(check_fe_equal(&t, &u));
+ CHECK(fe_equal(&t, &u));
/* Check worst-case input: ensure the LSB is 1 so that P will be added,
* which will also cause all carries to be 1, since all limbs that can
@@ -3111,7 +3109,7 @@ static void run_field_half(void) {
#endif
secp256k1_fe_normalize_weak(&u);
secp256k1_fe_add(&u, &u);
- CHECK(check_fe_equal(&t, &u));
+ CHECK(fe_equal(&t, &u));
}
}
@@ -3138,7 +3136,7 @@ static void run_field_misc(void) {
secp256k1_fe_add(&z, &q); /* z = x+v */
q = x; /* q = x */
secp256k1_fe_add_int(&q, v); /* q = x+v */
- CHECK(check_fe_equal(&q, &z));
+ CHECK(fe_equal(&q, &z));
/* Test the fe equality and comparison operations. */
CHECK(secp256k1_fe_cmp_var(&x, &x) == 0);
CHECK(secp256k1_fe_equal(&x, &x));
@@ -3198,27 +3196,27 @@ static void run_field_misc(void) {
secp256k1_fe_add(&y, &x);
z = x;
secp256k1_fe_mul_int(&z, 3);
- CHECK(check_fe_equal(&y, &z));
+ CHECK(fe_equal(&y, &z));
secp256k1_fe_add(&y, &x);
secp256k1_fe_add(&z, &x);
- CHECK(check_fe_equal(&z, &y));
+ CHECK(fe_equal(&z, &y));
z = x;
secp256k1_fe_mul_int(&z, 5);
secp256k1_fe_mul(&q, &x, &fe5);
- CHECK(check_fe_equal(&z, &q));
+ CHECK(fe_equal(&z, &q));
secp256k1_fe_negate(&x, &x, 1);
secp256k1_fe_add(&z, &x);
secp256k1_fe_add(&q, &x);
- CHECK(check_fe_equal(&y, &z));
- CHECK(check_fe_equal(&q, &y));
+ CHECK(fe_equal(&y, &z));
+ CHECK(fe_equal(&q, &y));
/* Check secp256k1_fe_half. */
z = x;
secp256k1_fe_half(&z);
secp256k1_fe_add(&z, &z);
- CHECK(check_fe_equal(&x, &z));
+ CHECK(fe_equal(&x, &z));
secp256k1_fe_add(&z, &z);
secp256k1_fe_half(&z);
- CHECK(check_fe_equal(&x, &z));
+ CHECK(fe_equal(&x, &z));
}
}
@@ -3287,18 +3285,31 @@ static void run_fe_mul(void) {
}
static void run_sqr(void) {
- secp256k1_fe x, s;
+ int i;
+ secp256k1_fe x, y, lhs, rhs, tmp;
- {
- int i;
- secp256k1_fe_set_int(&x, 1);
- secp256k1_fe_negate(&x, &x, 1);
+ secp256k1_fe_set_int(&x, 1);
+ secp256k1_fe_negate(&x, &x, 1);
- for (i = 1; i <= 512; ++i) {
- secp256k1_fe_mul_int(&x, 2);
- secp256k1_fe_normalize(&x);
- secp256k1_fe_sqr(&s, &x);
- }
+ for (i = 1; i <= 512; ++i) {
+ secp256k1_fe_mul_int(&x, 2);
+ secp256k1_fe_normalize(&x);
+
+ /* Check that (x+y)*(x-y) = x^2 - y*2 for some random values y */
+ random_fe_test(&y);
+
+ lhs = x;
+ secp256k1_fe_add(&lhs, &y); /* lhs = x+y */
+ secp256k1_fe_negate(&tmp, &y, 1); /* tmp = -y */
+ secp256k1_fe_add(&tmp, &x); /* tmp = x-y */
+ secp256k1_fe_mul(&lhs, &lhs, &tmp); /* lhs = (x+y)*(x-y) */
+
+ secp256k1_fe_sqr(&rhs, &x); /* rhs = x^2 */
+ secp256k1_fe_sqr(&tmp, &y); /* tmp = y^2 */
+ secp256k1_fe_negate(&tmp, &tmp, 1); /* tmp = -y^2 */
+ secp256k1_fe_add(&rhs, &tmp); /* rhs = x^2 - y^2 */
+
+ CHECK(fe_equal(&lhs, &rhs));
}
}
@@ -3620,9 +3631,9 @@ static void run_inverse_tests(void)
for (i = 0; (size_t)i < sizeof(fe_cases)/sizeof(fe_cases[0]); ++i) {
for (var = 0; var <= 1; ++var) {
test_inverse_field(&x_fe, &fe_cases[i][0], var);
- check_fe_equal(&x_fe, &fe_cases[i][1]);
+ CHECK(fe_equal(&x_fe, &fe_cases[i][1]));
test_inverse_field(&x_fe, &fe_cases[i][1], var);
- check_fe_equal(&x_fe, &fe_cases[i][0]);
+ CHECK(fe_equal(&x_fe, &fe_cases[i][0]));
}
}
for (i = 0; (size_t)i < sizeof(scalar_cases)/sizeof(scalar_cases[0]); ++i) {
@@ -4558,7 +4569,7 @@ static void ecmult_const_mult_xonly(void) {
/* Check that resj's X coordinate corresponds with resx. */
secp256k1_fe_sqr(&v, &resj.z);
secp256k1_fe_mul(&v, &v, &resx);
- CHECK(check_fe_equal(&v, &resj.x));
+ CHECK(fe_equal(&v, &resj.x));
}
/* Test that secp256k1_ecmult_const_xonly correctly rejects X coordinates not on curve. */
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 187bf1c5e0..154d9ebcf1 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -51,13 +51,27 @@ static void print_buf_plain(const unsigned char *buf, size_t len) {
# define SECP256K1_INLINE inline
# endif
+/** Assert statically that expr is true.
+ *
+ * This is a statement-like macro and can only be used inside functions.
+ */
+#define STATIC_ASSERT(expr) do { \
+ switch(0) { \
+ case 0: \
+ /* If expr evaluates to 0, we have two case labels "0", which is illegal. */ \
+ case /* ERROR: static assertion failed */ (expr): \
+ ; \
+ } \
+} while(0)
+
/** Assert statically that expr is an integer constant expression, and run stmt.
*
* Useful for example to enforce that magnitude arguments are constant.
*/
#define ASSERT_INT_CONST_AND_DO(expr, stmt) do { \
switch(42) { \
- case /* ERROR: integer argument is not constant */ expr: \
+ /* C allows only integer constant expressions as case labels. */ \
+ case /* ERROR: integer argument is not constant */ (expr): \
break; \
default: ; \
} \
diff --git a/src/secp256k1/tools/check-abi.sh b/src/secp256k1/tools/check-abi.sh
index 8f6119cd8e..55c945ac16 100755
--- a/src/secp256k1/tools/check-abi.sh
+++ b/src/secp256k1/tools/check-abi.sh
@@ -3,17 +3,19 @@
set -eu
default_base_version="$(git describe --match "v*.*.*" --abbrev=0)"
-default_new_version="master"
+default_new_version="HEAD"
display_help_and_exit() {
- echo "Usage: $0 <base_ver> <new_ver>"
+ echo "Usage: $0 [<base_ver> [<new_ver>]]"
echo ""
echo "Description: This script uses the ABI Compliance Checker tool to determine if the ABI"
echo " of a new version of libsecp256k1 has changed in a backward-incompatible way."
echo ""
echo "Options:"
- echo " base_ver Specify the base version (default: $default_base_version)"
- echo " new_ver Specify the new version (default: $default_new_version)"
+ echo " base_ver Specify the base version as a git commit-ish"
+ echo " (default: most recent reachable tag matching \"v.*.*\", currently \"$default_base_version\")"
+ echo " new_ver Specify the new version as a git commit-ish"
+ echo " (default: $default_new_version)"
echo " -h, --help Display this help message"
exit 0
}
@@ -23,9 +25,11 @@ if [ "$#" -eq 0 ]; then
new_version="$default_new_version"
elif [ "$#" -eq 1 ] && { [ "$1" = "-h" ] || [ "$1" = "--help" ]; }; then
display_help_and_exit
-elif [ "$#" -eq 2 ]; then
+elif [ "$#" -eq 1 ] || [ "$#" -eq 2 ]; then
base_version="$1"
- new_version="$2"
+ if [ "$#" -eq 2 ]; then
+ new_version="$2"
+ fi
else
echo "Invalid usage. See help:"
echo ""
@@ -33,7 +37,8 @@ else
fi
checkout_and_build() {
- git worktree add -d "$1" "$2"
+ _orig_dir="$(pwd)"
+ git worktree add --detach "$1" "$2"
cd "$1"
mkdir build && cd build
cmake -S .. --preset dev-mode \
@@ -45,20 +50,18 @@ checkout_and_build() {
-DSECP256K1_BUILD_EXAMPLES=OFF
cmake --build . -j "$(nproc)"
abi-dumper src/libsecp256k1.so -o ABI.dump -lver "$2"
+ cd "$_orig_dir"
}
echo "Comparing $base_version (base version) to $new_version (new version)"
echo
-original_dir="$(pwd)"
-
-base_source_dir=$(mktemp -d)
+base_source_dir="$(mktemp -d)"
checkout_and_build "$base_source_dir" "$base_version"
-new_source_dir=$(mktemp -d)
+new_source_dir="$(mktemp -d)"
checkout_and_build "$new_source_dir" "$new_version"
-cd "$original_dir"
abi-compliance-checker -lib libsecp256k1 -old "${base_source_dir}/build/ABI.dump" -new "${new_source_dir}/build/ABI.dump"
git worktree remove "$base_source_dir"
git worktree remove "$new_source_dir"
diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp
index 1f46efe464..340208a1c9 100644
--- a/src/test/argsman_tests.cpp
+++ b/src/test/argsman_tests.cpp
@@ -904,7 +904,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
// If check below fails, should manually dump the results with:
//
- // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge
+ // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=argsman_tests/util_ArgsMerge
//
// And verify diff against previous results to make sure the changes are expected.
//
@@ -1007,7 +1007,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// If check below fails, should manually dump the results with:
//
- // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge
+ // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=argsman_tests/util_ChainMerge
//
// And verify diff against previous results to make sure the changes are expected.
//
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 763f0f897e..05355fb21d 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -14,7 +14,7 @@
#include <boost/test/unit_test.hpp>
-std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
+std::vector<CTransactionRef> extra_txn;
BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
@@ -126,7 +126,7 @@ public:
explicit TestHeaderAndShortIDs(const CBlock& block) :
TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block}) {}
- uint64_t GetShortID(const uint256& txhash) const {
+ uint64_t GetShortID(const Wtxid& txhash) const {
DataStream stream{};
stream << *this;
CBlockHeaderAndShortTxIDs base;
@@ -155,8 +155,8 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
shortIDs.prefilledtxn.resize(1);
shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
shortIDs.shorttxids.resize(2);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
- shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
+ shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
DataStream stream{};
stream << shortIDs;
@@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
shortIDs.shorttxids.resize(1);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
DataStream stream{};
stream << shortIDs;
diff --git a/src/test/feefrac_tests.cpp b/src/test/feefrac_tests.cpp
index 2e015b382e..5af3c3d7ed 100644
--- a/src/test/feefrac_tests.cpp
+++ b/src/test/feefrac_tests.cpp
@@ -82,43 +82,4 @@ BOOST_AUTO_TEST_CASE(feefrac_operators)
}
-BOOST_AUTO_TEST_CASE(build_diagram_test)
-{
- FeeFrac p1{1000, 100};
- FeeFrac empty{0, 0};
- FeeFrac zero_fee{0, 1};
- FeeFrac oversized_1{4611686000000, 4000000};
- FeeFrac oversized_2{184467440000000, 100000};
-
- // Diagram-building will reorder chunks
- std::vector<FeeFrac> chunks;
- chunks.push_back(p1);
- chunks.push_back(zero_fee);
- chunks.push_back(empty);
- chunks.push_back(oversized_1);
- chunks.push_back(oversized_2);
-
- // Caller in charge of sorting chunks in case chunk size limit
- // differs from cluster size limit
- std::sort(chunks.begin(), chunks.end(), [](const FeeFrac& a, const FeeFrac& b) { return a > b; });
-
- // Chunks are now sorted in reverse order (largest first)
- BOOST_CHECK(chunks[0] == empty); // empty is considered "highest" fee
- BOOST_CHECK(chunks[1] == oversized_2);
- BOOST_CHECK(chunks[2] == oversized_1);
- BOOST_CHECK(chunks[3] == p1);
- BOOST_CHECK(chunks[4] == zero_fee);
-
- std::vector<FeeFrac> generated_diagram{BuildDiagramFromChunks(chunks)};
- BOOST_CHECK(generated_diagram.size() == 1 + chunks.size());
-
- // Prepended with an empty, then the chunks summed in order as above
- BOOST_CHECK(generated_diagram[0] == empty);
- BOOST_CHECK(generated_diagram[1] == empty);
- BOOST_CHECK(generated_diagram[2] == oversized_2);
- BOOST_CHECK(generated_diagram[3] == oversized_2 + oversized_1);
- BOOST_CHECK(generated_diagram[4] == oversized_2 + oversized_1 + p1);
- BOOST_CHECK(generated_diagram[5] == oversized_2 + oversized_1 + p1 + zero_fee);
-}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/feeratediagram.cpp b/src/test/fuzz/feeratediagram.cpp
index 6d710093cb..1a9c5ee946 100644
--- a/src/test/fuzz/feeratediagram.cpp
+++ b/src/test/fuzz/feeratediagram.cpp
@@ -16,6 +16,20 @@
namespace {
+/** Takes the pre-computed and topologically-valid chunks and generates a fee diagram which starts at FeeFrac of (0, 0) */
+std::vector<FeeFrac> BuildDiagramFromChunks(const Span<const FeeFrac> chunks)
+{
+ std::vector<FeeFrac> diagram;
+ diagram.reserve(chunks.size() + 1);
+
+ diagram.emplace_back(0, 0);
+ for (auto& chunk : chunks) {
+ diagram.emplace_back(diagram.back() + chunk);
+ }
+ return diagram;
+}
+
+
/** Evaluate a diagram at a specific size, returning the fee as a fraction.
*
* Fees in diagram cannot exceed 2^32, as the returned evaluation could overflow
@@ -103,7 +117,7 @@ FUZZ_TARGET(build_and_compare_feerate_diagram)
assert(diagram1.front() == empty);
assert(diagram2.front() == empty);
- auto real = CompareFeerateDiagram(diagram1, diagram2);
+ auto real = CompareChunks(chunks1, chunks2);
auto sim = CompareDiagrams(diagram1, diagram2);
assert(real == sim);
diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index c801c2e325..c201118bce 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -276,8 +276,14 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
// (the package is a test accept and ATMP is a submission).
auto single_submit = txs.size() == 1 && fuzzed_data_provider.ConsumeBool();
+ // Exercise client_maxfeerate logic
+ std::optional<CFeeRate> client_maxfeerate{};
+ if (fuzzed_data_provider.ConsumeBool()) {
+ client_maxfeerate = CFeeRate(fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1, 50 * COIN), 100);
+ }
+
const auto result_package = WITH_LOCK(::cs_main,
- return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
+ return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, client_maxfeerate));
// Always set bypass_limits to false because it is not supported in ProcessNewPackage and
// can be a source of divergence.
diff --git a/src/test/fuzz/partially_downloaded_block.cpp b/src/test/fuzz/partially_downloaded_block.cpp
index 4a4b46da60..ab75afe066 100644
--- a/src/test/fuzz/partially_downloaded_block.cpp
+++ b/src/test/fuzz/partially_downloaded_block.cpp
@@ -60,7 +60,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
// The coinbase is always available
available.insert(0);
- std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
+ std::vector<CTransactionRef> extra_txn;
for (size_t i = 1; i < block->vtx.size(); ++i) {
auto tx{block->vtx[i]};
@@ -68,7 +68,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
bool add_to_mempool{fuzzed_data_provider.ConsumeBool()};
if (add_to_extra_txn) {
- extra_txn.emplace_back(tx->GetWitnessHash(), tx);
+ extra_txn.emplace_back(tx);
available.insert(i);
}
diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp
index 754aff4e70..64785948f6 100644
--- a/src/test/fuzz/rbf.cpp
+++ b/src/test/fuzz/rbf.cpp
@@ -82,17 +82,6 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
}
}
-void CheckDiagramConcave(std::vector<FeeFrac>& diagram)
-{
- // Diagrams are in monotonically-decreasing feerate order.
- FeeFrac last_chunk = diagram.front();
- for (size_t i = 1; i<diagram.size(); ++i) {
- FeeFrac next_chunk = diagram[i] - diagram[i-1];
- assert(next_chunk <= last_chunk);
- last_chunk = next_chunk;
- }
-}
-
FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -107,6 +96,12 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
std::vector<CTransaction> mempool_txs;
size_t iter{0};
+ int32_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000);
+
+ // Keep track of the total vsize of CTxMemPoolEntry's being added to the mempool to avoid overflow
+ // Add replacement_vsize since this is added to new diagram during RBF check
+ int64_t running_vsize_total{replacement_vsize};
+
LOCK2(cs_main, pool.cs);
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
@@ -114,19 +109,33 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
// Make sure txns only have one input, and that a unique input is given to avoid circular references
std::optional<CMutableTransaction> parent = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
if (!parent) {
- continue;
+ return;
}
assert(iter <= g_outpoints.size());
parent->vin.resize(1);
parent->vin[0].prevout = g_outpoints[iter++];
mempool_txs.emplace_back(*parent);
- pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back()));
+ const auto parent_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
+ running_vsize_total += parent_entry.GetTxSize();
+ if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
+ // We aren't adding this final tx to mempool, so we don't want to conflict with it
+ mempool_txs.pop_back();
+ break;
+ }
+ pool.addUnchecked(parent_entry);
if (fuzzed_data_provider.ConsumeBool() && !child->vin.empty()) {
child->vin[0].prevout = COutPoint{mempool_txs.back().GetHash(), 0};
}
mempool_txs.emplace_back(*child);
- pool.addUnchecked(ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back()));
+ const auto child_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
+ running_vsize_total += child_entry.GetTxSize();
+ if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
+ // We aren't adding this final tx to mempool, so we don't want to conflict with it
+ mempool_txs.pop_back();
+ break;
+ }
+ pool.addUnchecked(child_entry);
if (fuzzed_data_provider.ConsumeBool()) {
pool.PrioritiseTransaction(mempool_txs.back().GetHash().ToUint256(), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(-100000, 100000));
@@ -147,33 +156,33 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
pool.CalculateDescendants(txiter, all_conflicts);
}
- // Calculate the feerate diagrams for a replacement.
+ // Calculate the chunks for a replacement.
CAmount replacement_fees = ConsumeMoney(fuzzed_data_provider);
- int64_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 1000000);
- auto calc_results{pool.CalculateFeerateDiagramsForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
+ auto calc_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
if (calc_results.has_value()) {
- // Sanity checks on the diagrams.
+ // Sanity checks on the chunks.
- // Diagrams start at 0.
- assert(calc_results->first.front().size == 0);
- assert(calc_results->first.front().fee == 0);
- assert(calc_results->second.front().size == 0);
- assert(calc_results->second.front().fee == 0);
-
- CheckDiagramConcave(calc_results->first);
- CheckDiagramConcave(calc_results->second);
+ // Feerates are monotonically decreasing.
+ FeeFrac first_sum;
+ for (size_t i = 0; i < calc_results->first.size(); ++i) {
+ first_sum += calc_results->first[i];
+ if (i) assert(!(calc_results->first[i - 1] << calc_results->first[i]));
+ }
+ FeeFrac second_sum;
+ for (size_t i = 0; i < calc_results->second.size(); ++i) {
+ second_sum += calc_results->second[i];
+ if (i) assert(!(calc_results->second[i - 1] << calc_results->second[i]));
+ }
- CAmount replaced_fee{0};
- int64_t replaced_size{0};
+ FeeFrac replaced;
for (auto txiter : all_conflicts) {
- replaced_fee += txiter->GetModifiedFee();
- replaced_size += txiter->GetTxSize();
+ replaced.fee += txiter->GetModifiedFee();
+ replaced.size += txiter->GetTxSize();
}
- // The total fee of the new diagram should be the total fee of the old
- // diagram - replaced_fee + replacement_fees
- assert(calc_results->first.back().fee - replaced_fee + replacement_fees == calc_results->second.back().fee);
- assert(calc_results->first.back().size - replaced_size + replacement_vsize == calc_results->second.back().size);
+ // The total fee & size of the new diagram minus replaced fee & size should be the total
+ // fee & size of the old diagram minus replacement fee & size.
+ assert((first_sum - replaced) == (second_sum - FeeFrac{replacement_fees, replacement_vsize}));
}
// If internals report error, wrapper should too
@@ -182,10 +191,12 @@ FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
assert(err_tuple.value().first == DiagramCheckError::UNCALCULABLE);
} else {
// Diagram check succeeded
+ auto old_sum = std::accumulate(calc_results->first.begin(), calc_results->first.end(), FeeFrac{});
+ auto new_sum = std::accumulate(calc_results->second.begin(), calc_results->second.end(), FeeFrac{});
if (!err_tuple.has_value()) {
// New diagram's final fee should always match or exceed old diagram's
- assert(calc_results->first.back().fee <= calc_results->second.back().fee);
- } else if (calc_results->first.back().fee > calc_results->second.back().fee) {
+ assert(old_sum.fee <= new_sum.fee);
+ } else if (old_sum.fee > new_sum.fee) {
// Or it failed, and if old diagram had higher fees, it should be a failure
assert(err_tuple.value().first == DiagramCheckError::FAILURE);
}
diff --git a/src/test/fuzz/util/descriptor.cpp b/src/test/fuzz/util/descriptor.cpp
index df78bdf314..0fed2bc5e1 100644
--- a/src/test/fuzz/util/descriptor.cpp
+++ b/src/test/fuzz/util/descriptor.cpp
@@ -15,7 +15,7 @@ void MockedDescriptorConverter::Init() {
// an extended one.
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
CKey privkey;
- privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
+ privkey.Set(key_data.begin(), key_data.end(), !IdIsUnCompPubKey(i));
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
CPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 1f28e61bc9..a3699f84b6 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -297,6 +297,7 @@ using miniscript::operator"" _mst;
using Node = miniscript::Node<CPubKey>;
/** Compute all challenges (pubkeys, hashes, timelocks) that occur in a given Miniscript. */
+// NOLINTNEXTLINE(misc-no-recursion)
std::set<Challenge> FindChallenges(const NodeRef& ref) {
std::set<Challenge> chal;
for (const auto& key : ref->keys) {
diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp
index ed33969710..19e45c550a 100644
--- a/src/test/rbf_tests.cpp
+++ b/src/test/rbf_tests.cpp
@@ -426,21 +426,21 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
// Replacement of size 1
{
- const auto replace_one{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/0, /*replacement_vsize=*/1, {entry_low}, {entry_low})};
+ const auto replace_one{pool.CalculateChunksForRBF(/*replacement_fees=*/0, /*replacement_vsize=*/1, {entry_low}, {entry_low})};
BOOST_CHECK(replace_one.has_value());
- std::vector<FeeFrac> expected_old_diagram{FeeFrac(0, 0), FeeFrac(low_fee, low_size)};
- BOOST_CHECK(replace_one->first == expected_old_diagram);
- std::vector<FeeFrac> expected_new_diagram{FeeFrac(0, 0), FeeFrac(0, 1)};
- BOOST_CHECK(replace_one->second == expected_new_diagram);
+ std::vector<FeeFrac> expected_old_chunks{{low_fee, low_size}};
+ BOOST_CHECK(replace_one->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{0, 1}};
+ BOOST_CHECK(replace_one->second == expected_new_chunks);
}
// Non-zero replacement fee/size
{
- const auto replace_one_fee{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low})};
+ const auto replace_one_fee{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low})};
BOOST_CHECK(replace_one_fee.has_value());
- std::vector<FeeFrac> expected_old_diagram{FeeFrac(0, 0), FeeFrac(low_fee, low_size)};
+ std::vector<FeeFrac> expected_old_diagram{{low_fee, low_size}};
BOOST_CHECK(replace_one_fee->first == expected_old_diagram);
- std::vector<FeeFrac> expected_new_diagram{FeeFrac(0, 0), FeeFrac(high_fee, low_size)};
+ std::vector<FeeFrac> expected_new_diagram{{high_fee, low_size}};
BOOST_CHECK(replace_one_fee->second == expected_new_diagram);
}
@@ -451,22 +451,22 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto high_size = entry_high->GetTxSize();
{
- const auto replace_single_chunk{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low, entry_high})};
+ const auto replace_single_chunk{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low, entry_high})};
BOOST_CHECK(replace_single_chunk.has_value());
- std::vector<FeeFrac> expected_old_diagram{FeeFrac(0, 0), FeeFrac(low_fee + high_fee, low_size + high_size)};
- BOOST_CHECK(replace_single_chunk->first == expected_old_diagram);
- std::vector<FeeFrac> expected_new_diagram{FeeFrac(0, 0), FeeFrac(high_fee, low_size)};
- BOOST_CHECK(replace_single_chunk->second == expected_new_diagram);
+ std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
+ BOOST_CHECK(replace_single_chunk->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size}};
+ BOOST_CHECK(replace_single_chunk->second == expected_new_chunks);
}
// Conflict with the 2nd tx, resulting in new diagram with three entries
{
- const auto replace_cpfp_child{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high}, {entry_high})};
+ const auto replace_cpfp_child{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high}, {entry_high})};
BOOST_CHECK(replace_cpfp_child.has_value());
- std::vector<FeeFrac> expected_old_diagram{FeeFrac(0, 0), FeeFrac(low_fee + high_fee, low_size + high_size)};
- BOOST_CHECK(replace_cpfp_child->first == expected_old_diagram);
- std::vector<FeeFrac> expected_new_diagram{FeeFrac(0, 0), FeeFrac(high_fee, low_size), FeeFrac(low_fee + high_fee, low_size + low_size)};
- BOOST_CHECK(replace_cpfp_child->second == expected_new_diagram);
+ std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
+ BOOST_CHECK(replace_cpfp_child->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size}, {low_fee, low_size}};
+ BOOST_CHECK(replace_cpfp_child->second == expected_new_chunks);
}
// third transaction causes the topology check to fail
@@ -476,7 +476,7 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto normal_size = entry_normal->GetTxSize();
{
- const auto replace_too_large{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/normal_fee, /*replacement_vsize=*/normal_size, {entry_low}, {entry_low, entry_high, entry_normal})};
+ const auto replace_too_large{pool.CalculateChunksForRBF(/*replacement_fees=*/normal_fee, /*replacement_vsize=*/normal_size, {entry_low}, {entry_low, entry_high, entry_normal})};
BOOST_CHECK(!replace_too_large.has_value());
BOOST_CHECK_EQUAL(util::ErrorString(replace_too_large).original, strprintf("%s has 2 descendants, max 1 allowed", low_tx->GetHash().GetHex()));
}
@@ -493,12 +493,12 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto low_size_2 = entry_low_2->GetTxSize();
{
- const auto replace_two_chunks_single_cluster{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high_2}, {entry_high_2, entry_low_2})};
+ const auto replace_two_chunks_single_cluster{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high_2}, {entry_high_2, entry_low_2})};
BOOST_CHECK(replace_two_chunks_single_cluster.has_value());
- std::vector<FeeFrac> expected_old_diagram{FeeFrac(0, 0), FeeFrac(high_fee, high_size_2), FeeFrac(low_fee + high_fee, low_size_2 + high_size_2)};
- BOOST_CHECK(replace_two_chunks_single_cluster->first == expected_old_diagram);
- std::vector<FeeFrac> expected_new_diagram{FeeFrac(0, 0), FeeFrac(high_fee, low_size_2)};
- BOOST_CHECK(replace_two_chunks_single_cluster->second == expected_new_diagram);
+ std::vector<FeeFrac> expected_old_chunks{{high_fee, high_size_2}, {low_fee, low_size_2}};
+ BOOST_CHECK(replace_two_chunks_single_cluster->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size_2}};
+ BOOST_CHECK(replace_two_chunks_single_cluster->second == expected_new_chunks);
}
// You can have more than two direct conflicts if the there are multiple affected clusters, all of size 2 or less
@@ -515,10 +515,10 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto conflict_3_entry = pool.GetIter(conflict_3->GetHash()).value();
{
- const auto replace_multiple_clusters{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry})};
+ const auto replace_multiple_clusters{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry})};
BOOST_CHECK(replace_multiple_clusters.has_value());
- BOOST_CHECK(replace_multiple_clusters->first.size() == 4);
- BOOST_CHECK(replace_multiple_clusters->second.size() == 2);
+ BOOST_CHECK(replace_multiple_clusters->first.size() == 3);
+ BOOST_CHECK(replace_multiple_clusters->second.size() == 1);
}
// Add a child transaction to conflict_1 and make it cluster size 2, two chunks due to same feerate
@@ -527,11 +527,11 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto conflict_1_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
{
- const auto replace_multiple_clusters_2{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry})};
+ const auto replace_multiple_clusters_2{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry})};
BOOST_CHECK(replace_multiple_clusters_2.has_value());
- BOOST_CHECK(replace_multiple_clusters_2->first.size() == 5);
- BOOST_CHECK(replace_multiple_clusters_2->second.size() == 2);
+ BOOST_CHECK(replace_multiple_clusters_2->first.size() == 4);
+ BOOST_CHECK(replace_multiple_clusters_2->second.size() == 1);
}
// Add another descendant to conflict_1, making the cluster size > 2 should fail at this point.
@@ -540,86 +540,86 @@ BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
const auto conflict_1_grand_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
{
- const auto replace_cluster_size_3{pool.CalculateFeerateDiagramsForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry, conflict_1_grand_child_entry})};
+ const auto replace_cluster_size_3{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry, conflict_1_grand_child_entry})};
BOOST_CHECK(!replace_cluster_size_3.has_value());
BOOST_CHECK_EQUAL(util::ErrorString(replace_cluster_size_3).original, strprintf("%s has 2 descendants, max 1 allowed", conflict_1->GetHash().GetHex()));
}
}
-BOOST_AUTO_TEST_CASE(feerate_diagram_utilities)
+BOOST_AUTO_TEST_CASE(feerate_chunks_utilities)
{
- // Sanity check the correctness of the feerate diagram comparison.
+ // Sanity check the correctness of the feerate chunks comparison.
// A strictly better case.
- std::vector<FeeFrac> old_diagram{{FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}}};
- std::vector<FeeFrac> new_diagram{{FeeFrac{0, 0}, FeeFrac{1000, 300}, FeeFrac{1050, 400}}};
+ std::vector<FeeFrac> old_chunks{{{950, 300}, {100, 100}}};
+ std::vector<FeeFrac> new_chunks{{{1000, 300}, {50, 100}}};
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
// Incomparable diagrams
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{1000, 300}, FeeFrac{1000, 400}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1000, 300}, {0, 100}};
- BOOST_CHECK(CompareFeerateDiagram(old_diagram, new_diagram) == std::partial_ordering::unordered);
- BOOST_CHECK(CompareFeerateDiagram(new_diagram, old_diagram) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
// Strictly better but smaller size.
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{1100, 300}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1100, 300}};
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
// New diagram is strictly better due to the first chunk, even though
// second chunk contributes no fees
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{1100, 100}, FeeFrac{1100, 200}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1100, 100}, {0, 100}};
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
// Feerate of first new chunk is better with, but second chunk is worse
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{750, 100}, FeeFrac{999, 350}, FeeFrac{1150, 1000}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{750, 100}, {249, 250}, {151, 650}};
- BOOST_CHECK(CompareFeerateDiagram(old_diagram, new_diagram) == std::partial_ordering::unordered);
- BOOST_CHECK(CompareFeerateDiagram(new_diagram, old_diagram) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
// If we make the second chunk slightly better, the new diagram now wins.
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{750, 100}, FeeFrac{1000, 350}, FeeFrac{1150, 500}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{750, 100}, {250, 250}, {150, 150}};
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
// Identical diagrams, cannot be strictly better
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{950, 300}, {100, 100}};
- BOOST_CHECK(std::is_eq(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_eq(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_eq(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_eq(CompareChunks(new_chunks, old_chunks)));
// Same aggregate fee, but different total size (trigger single tail fee check step)
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 399}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}};
+ old_chunks = {{950, 300}, {100, 99}};
+ new_chunks = {{950, 300}, {100, 100}};
// No change in evaluation when tail check needed.
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_gt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_lt(CompareChunks(new_chunks, old_chunks)));
// Trigger multiple tail fee check steps
- old_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 399}};
- new_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}, FeeFrac{1050, 401}, FeeFrac{1050, 402}};
+ old_chunks = {{950, 300}, {100, 99}};
+ new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}};
- BOOST_CHECK(std::is_gt(CompareFeerateDiagram(old_diagram, new_diagram)));
- BOOST_CHECK(std::is_lt(CompareFeerateDiagram(new_diagram, old_diagram)));
+ BOOST_CHECK(std::is_gt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_lt(CompareChunks(new_chunks, old_chunks)));
// Multiple tail fee check steps, unordered result
- new_diagram = {FeeFrac{0, 0}, FeeFrac{950, 300}, FeeFrac{1050, 400}, FeeFrac{1050, 401}, FeeFrac{1050, 402}, FeeFrac{1051, 403}};
- BOOST_CHECK(CompareFeerateDiagram(old_diagram, new_diagram) == std::partial_ordering::unordered);
- BOOST_CHECK(CompareFeerateDiagram(new_diagram, old_diagram) == std::partial_ordering::unordered);
+ new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}, {1, 1}};
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 0d2460c606..3a1cb45e8d 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -291,6 +291,7 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1e-8")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.1e-7")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.01e-6")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000000000000000000000000000000000001e+30")), 1);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.0000000000000000000000000000000000000000000000000000000000000000000000000001e+68")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("10000000000000000000000000000000000000000000000000000000000000000e-64")), COIN);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000e64")), COIN);
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 451bc99d44..68f82b760c 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -4,7 +4,6 @@
#include <key.h>
#include <test/util/setup_common.h>
-#include <util/time.h>
#include <boost/test/unit_test.hpp>
@@ -13,7 +12,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(basic_sanity)
{
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
- BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 0af2fdce08..e4142e203c 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1254,6 +1254,30 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(combined.scriptSig == partial3c);
}
+/**
+ * Reproduction of an exception incorrectly raised when parsing a public key inside a TapMiniscript.
+ */
+BOOST_AUTO_TEST_CASE(sign_invalid_miniscript)
+{
+ FillableSigningProvider keystore;
+ SignatureData sig_data;
+ CMutableTransaction prev, curr;
+
+ // Create a Taproot output which contains a leaf in which a non-32 bytes push is used where a public key is expected
+ // by the Miniscript parser. This offending Script was found by the RPC fuzzer.
+ const auto invalid_pubkey{ParseHex("173d36c8c9c9c9ffffffffffff0200000000021e1e37373721361818181818181e1e1e1e19000000000000000000b19292929292926b006c9b9b9292")};
+ TaprootBuilder builder;
+ builder.Add(0, {invalid_pubkey}, 0xc0);
+ XOnlyPubKey nums{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
+ builder.Finalize(nums);
+ prev.vout.emplace_back(0, GetScriptForDestination(builder.GetOutput()));
+ curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
+ sig_data.tr_spenddata = builder.GetSpendData();
+
+ // SignSignature can fail but it shouldn't raise an exception (nor crash).
+ BOOST_CHECK(!SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
+}
+
BOOST_AUTO_TEST_CASE(script_standard_push)
{
ScriptError err;
diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp
index 90fce9adf9..2de147deea 100644
--- a/src/test/system_tests.cpp
+++ b/src/test/system_tests.cpp
@@ -11,7 +11,7 @@
#include <univalue.h>
#ifdef ENABLE_EXTERNAL_SIGNER
-#include <boost/process.hpp>
+#include <util/subprocess.h>
#endif // ENABLE_EXTERNAL_SIGNER
#include <boost/test/unit_test.hpp>
@@ -34,20 +34,16 @@ BOOST_AUTO_TEST_CASE(run_command)
BOOST_CHECK(result.isNull());
}
{
- const UniValue result = RunCommandParseJSON("echo \"{\"success\": true}\"");
+ const UniValue result = RunCommandParseJSON("echo {\"success\": true}");
BOOST_CHECK(result.isObject());
const UniValue& success = result.find_value("success");
BOOST_CHECK(!success.isNull());
BOOST_CHECK_EQUAL(success.get_bool(), true);
}
{
- // An invalid command is handled by Boost
- const int expected_error{2};
- BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), boost::process::process_error, [&](const boost::process::process_error& e) {
- BOOST_CHECK(std::string(e.what()).find("RunCommandParseJSON error:") == std::string::npos);
- BOOST_CHECK_EQUAL(e.code().value(), expected_error);
- return true;
- });
+ // An invalid command is handled by cpp-subprocess
+ const std::string expected{"execve failed: "};
+ BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), subprocess::CalledProcessError, HasReason(expected));
}
{
// Return non-zero exit code, no output to stderr
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 47808a2a58..9a2add748e 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -290,13 +290,18 @@ BOOST_AUTO_TEST_CASE(util_TrimString)
BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
{
+ BOOST_CHECK_EQUAL(FormatISO8601DateTime(971890963199), "32767-12-31T23:59:59Z");
+ BOOST_CHECK_EQUAL(FormatISO8601DateTime(971890876800), "32767-12-31T00:00:00Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(0), "1970-01-01T00:00:00Z");
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
{
+ BOOST_CHECK_EQUAL(FormatISO8601Date(971890963199), "32767-12-31");
+ BOOST_CHECK_EQUAL(FormatISO8601Date(971890876800), "32767-12-31");
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
+ BOOST_CHECK_EQUAL(FormatISO8601Date(0), "1970-01-01");
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 316ab86c2b..69f4e305ab 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -127,6 +127,7 @@ std::shared_ptr<const CBlock> MinerTestingSetup::BadBlock(const uint256& prev_ha
return ret;
}
+// NOLINTNEXTLINE(misc-no-recursion)
void MinerTestingSetup::BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
{
if (height <= 0 || blocks.size() >= max_size) return;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 82eec6241f..06066e38b2 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1280,7 +1280,7 @@ std::optional<std::string> CTxMemPool::CheckConflictTopology(const setEntries& d
return std::nullopt;
}
-util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::CalculateFeerateDiagramsForRBF(CAmount replacement_fees, int64_t replacement_vsize, const setEntries& direct_conflicts, const setEntries& all_conflicts)
+util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::CalculateChunksForRBF(CAmount replacement_fees, int64_t replacement_vsize, const setEntries& direct_conflicts, const setEntries& all_conflicts)
{
Assume(replacement_vsize > 0);
@@ -1335,7 +1335,6 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
// No topology restrictions post-chunking; sort
std::sort(old_chunks.begin(), old_chunks.end(), std::greater());
- std::vector<FeeFrac> old_diagram = BuildDiagramFromChunks(old_chunks);
std::vector<FeeFrac> new_chunks;
@@ -1365,6 +1364,5 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
// No topology restrictions post-chunking; sort
std::sort(new_chunks.begin(), new_chunks.end(), std::greater());
- std::vector<FeeFrac> new_diagram = BuildDiagramFromChunks(new_chunks);
- return std::make_pair(old_diagram, new_diagram);
+ return std::make_pair(old_chunks, new_chunks);
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 9dd4d56da7..52a3dc2d7d 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -738,7 +738,7 @@ public:
}
/**
- * Calculate the old and new mempool feerate diagrams relating to the
+ * Calculate the sorted chunks for the old and new mempool relating to the
* clusters that would be affected by a potential replacement transaction.
* (replacement_fees, replacement_vsize) values are gathered from a
* proposed set of replacement transactions that are considered as a single
@@ -752,7 +752,7 @@ public:
* @param[in] all_conflicts All transactions that would be removed
* @return old and new diagram pair respectively, or an error string if the conflicts don't match a calculable topology
*/
- util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CalculateFeerateDiagramsForRBF(CAmount replacement_fees, int64_t replacement_vsize, const setEntries& direct_conflicts, const setEntries& all_conflicts) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CalculateChunksForRBF(CAmount replacement_fees, int64_t replacement_vsize, const setEntries& direct_conflicts, const setEntries& all_conflicts) EXCLUSIVE_LOCKS_REQUIRED(cs);
/* Check that all direct conflicts are in a cluster size of two or less. Each
* direct conflict may be in a separate cluster.
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index acbce25741..da12157555 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -18,6 +18,7 @@
#include <utility>
#include <vector>
+// NOLINTNEXTLINE(misc-no-recursion)
class UniValue {
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
index 4a3cbba20f..4a2219061a 100644
--- a/src/univalue/lib/univalue_write.cpp
+++ b/src/univalue/lib/univalue_write.cpp
@@ -27,6 +27,7 @@ static std::string json_escape(const std::string& inS)
return outS;
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::string UniValue::write(unsigned int prettyIndent,
unsigned int indentLevel) const
{
@@ -66,6 +67,7 @@ static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, std::
s.append(prettyIndent * indentLevel, ' ');
}
+// NOLINTNEXTLINE(misc-no-recursion)
void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const
{
s += "[";
@@ -88,6 +90,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s
s += "]";
}
+// NOLINTNEXTLINE(misc-no-recursion)
void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const
{
s += "{";
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
index 8b90448b36..1c724555f3 100644
--- a/src/univalue/test/object.cpp
+++ b/src/univalue/test/object.cpp
@@ -421,7 +421,7 @@ void univalue_readwrite()
// Valid, with leading or trailing whitespace
BOOST_CHECK(v.read(" 1.0") && (v.get_real() == 1.0));
BOOST_CHECK(v.read("1.0 ") && (v.get_real() == 1.0));
- BOOST_CHECK(v.read("0.00000000000000000000000000000000000001e+30 ") && v.get_real() == 1e-8);
+ BOOST_CHECK(v.read("0.00000000000000000000000000000000000001e+30 "));
BOOST_CHECK(!v.read(".19e-6")); //should fail, missing leading 0, therefore invalid JSON
// Invalid, initial garbage
diff --git a/src/util/feefrac.cpp b/src/util/feefrac.cpp
index 68fb836936..5b6173835c 100644
--- a/src/util/feefrac.cpp
+++ b/src/util/feefrac.cpp
@@ -7,39 +7,26 @@
#include <array>
#include <vector>
-std::vector<FeeFrac> BuildDiagramFromChunks(const Span<const FeeFrac> chunks)
-{
- std::vector<FeeFrac> diagram;
- diagram.reserve(chunks.size() + 1);
-
- diagram.emplace_back(0, 0);
- for (auto& chunk : chunks) {
- diagram.emplace_back(diagram.back() + chunk);
- }
- return diagram;
-}
-
-std::partial_ordering CompareFeerateDiagram(Span<const FeeFrac> dia0, Span<const FeeFrac> dia1)
+std::partial_ordering CompareChunks(Span<const FeeFrac> chunks0, Span<const FeeFrac> chunks1)
{
/** Array to allow indexed access to input diagrams. */
- const std::array<Span<const FeeFrac>, 2> dias = {dia0, dia1};
+ const std::array<Span<const FeeFrac>, 2> chunk = {chunks0, chunks1};
/** How many elements we have processed in each input. */
- size_t next_index[2] = {1, 1};
+ size_t next_index[2] = {0, 0};
+ /** Accumulated fee/sizes in diagrams, up to next_index[i] - 1. */
+ FeeFrac accum[2];
/** Whether the corresponding input is strictly better than the other at least in one place. */
bool better_somewhere[2] = {false, false};
/** Get the first unprocessed point in diagram number dia. */
- const auto next_point = [&](int dia) { return dias[dia][next_index[dia]]; };
+ const auto next_point = [&](int dia) { return chunk[dia][next_index[dia]] + accum[dia]; };
/** Get the last processed point in diagram number dia. */
- const auto prev_point = [&](int dia) { return dias[dia][next_index[dia] - 1]; };
-
- // Diagrams should be non-empty, and first elements zero in size and fee
- Assert(!dia0.empty() && !dia1.empty());
- Assert(prev_point(0).IsEmpty());
- Assert(prev_point(1).IsEmpty());
+ const auto prev_point = [&](int dia) { return accum[dia]; };
+ /** Move to the next point in diagram number dia. */
+ const auto advance = [&](int dia) { accum[dia] += chunk[dia][next_index[dia]++]; };
do {
- bool done_0 = next_index[0] == dias[0].size();
- bool done_1 = next_index[1] == dias[1].size();
+ bool done_0 = next_index[0] == chunk[0].size();
+ bool done_1 = next_index[1] == chunk[1].size();
if (done_0 && done_1) break;
// Determine which diagram has the first unprocessed point. If a single side is finished, use the
@@ -69,17 +56,16 @@ std::partial_ordering CompareFeerateDiagram(Span<const FeeFrac> dia0, Span<const
// If B and P have the same size, B can be marked as processed (in addition to P, see
// below), as we've already performed a comparison at this size.
- if (point_b.size == point_p.size) ++next_index[!unproc_side];
+ if (point_b.size == point_p.size) advance(!unproc_side);
}
// If P lies above AB, unproc_side is better in P. If P lies below AB, then !unproc_side is
// better in P.
if (std::is_gt(cmp)) better_somewhere[unproc_side] = true;
if (std::is_lt(cmp)) better_somewhere[!unproc_side] = true;
- ++next_index[unproc_side];
+ advance(unproc_side);
// If both diagrams are better somewhere, they are incomparable.
if (better_somewhere[0] && better_somewhere[1]) return std::partial_ordering::unordered;
-
} while(true);
// Otherwise compare the better_somewhere values.
diff --git a/src/util/feefrac.h b/src/util/feefrac.h
index 7102f85f88..9772162010 100644
--- a/src/util/feefrac.h
+++ b/src/util/feefrac.h
@@ -146,15 +146,14 @@ struct FeeFrac
}
};
-/** Takes the pre-computed and topologically-valid chunks and generates a fee diagram which starts at FeeFrac of (0, 0) */
-std::vector<FeeFrac> BuildDiagramFromChunks(Span<const FeeFrac> chunks);
-
-/** Compares two feerate diagrams. The shorter one is implicitly
- * extended with a horizontal straight line.
+/** Compare the feerate diagrams implied by the provided sorted chunks data.
+ *
+ * The implied diagram for each starts at (0, 0), then contains for each chunk the cumulative fee
+ * and size up to that chunk, and then extends infinitely to the right with a horizontal line.
*
- * A feerate diagram consists of a list of (fee, size) points with the property that size
- * is strictly increasing and that the first entry is (0, 0).
+ * The caller must guarantee that the sum of the FeeFracs in either of the chunks' data set do not
+ * overflow (so sum fees < 2^63, and sum sizes < 2^31).
*/
-std::partial_ordering CompareFeerateDiagram(Span<const FeeFrac> dia0, Span<const FeeFrac> dia1);
+std::partial_ordering CompareChunks(Span<const FeeFrac> chunks0, Span<const FeeFrac> chunks1);
#endif // BITCOIN_UTIL_FEEFRAC_H
diff --git a/src/util/string.h b/src/util/string.h
index 8b69d6ccae..dab92942fb 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -65,6 +65,7 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin
* @param unary_op Apply this operator to each item
*/
template <typename C, typename S, typename UnaryOp>
+// NOLINTNEXTLINE(misc-no-recursion)
auto Join(const C& container, const S& separator, UnaryOp unary_op)
{
decltype(unary_op(*container.begin())) ret;
diff --git a/src/util/subprocess.h b/src/util/subprocess.h
new file mode 100644
index 0000000000..4acfa8ff83
--- /dev/null
+++ b/src/util/subprocess.h
@@ -0,0 +1,1614 @@
+// Based on the https://github.com/arun11299/cpp-subprocess project.
+
+/*!
+
+Documentation for C++ subprocessing library.
+
+@copyright The code is licensed under the [MIT
+ License](http://opensource.org/licenses/MIT):
+ <br>
+ Copyright &copy; 2016-2018 Arun Muralidharan.
+ <br>
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ <br>
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ <br>
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+@author [Arun Muralidharan]
+@see https://github.com/arun11299/cpp-subprocess to download the source code
+
+@version 1.0.0
+*/
+
+#ifndef BITCOIN_UTIL_SUBPROCESS_H
+#define BITCOIN_UTIL_SUBPROCESS_H
+
+#include <util/syserror.h>
+
+#include <algorithm>
+#include <cassert>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include <future>
+#include <initializer_list>
+#include <iostream>
+#include <locale>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if (defined _MSC_VER) || (defined __MINGW32__)
+ #define __USING_WINDOWS__
+#endif
+
+#ifdef __USING_WINDOWS__
+ #include <codecvt>
+#endif
+
+extern "C" {
+#ifdef __USING_WINDOWS__
+ #include <Windows.h>
+ #include <io.h>
+ #include <cwchar>
+
+ #define close _close
+ #define open _open
+ #define fileno _fileno
+#else
+ #include <sys/wait.h>
+ #include <unistd.h>
+#endif
+ #include <csignal>
+ #include <fcntl.h>
+ #include <sys/types.h>
+}
+
+/*!
+ * Getting started with reading this source code.
+ * The source is mainly divided into four parts:
+ * 1. Exception Classes:
+ * These are very basic exception classes derived from
+ * runtime_error exception.
+ * There are two types of exception thrown from subprocess
+ * library: OSError and CalledProcessError
+ *
+ * 2. Popen Class
+ * This is the main class the users will deal with. It
+ * provides with all the API's to deal with processes.
+ *
+ * 3. Util namespace
+ * It includes some helper functions to split/join a string,
+ * reading from file descriptors, waiting on a process, fcntl
+ * options on file descriptors etc.
+ *
+ * 4. Detail namespace
+ * This includes some metaprogramming and helper classes.
+ */
+
+
+namespace subprocess {
+
+// Max buffer size allocated on stack for read error
+// from pipe
+static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
+
+// Default buffer capacity for OutBuffer and ErrBuffer.
+// If the data exceeds this capacity, the buffer size is grown
+// by 1.5 times its previous capacity
+static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
+
+
+/*-----------------------------------------------
+ * EXCEPTION CLASSES
+ *-----------------------------------------------
+ */
+
+/*!
+ * class: CalledProcessError
+ * Thrown when there was error executing the command.
+ * Check Popen class API's to know when this exception
+ * can be thrown.
+ *
+ */
+class CalledProcessError: public std::runtime_error
+{
+public:
+ int retcode;
+ CalledProcessError(const std::string& error_msg, int retcode):
+ std::runtime_error(error_msg), retcode(retcode)
+ {}
+};
+
+
+/*!
+ * class: OSError
+ * Thrown when some system call fails to execute or give result.
+ * The exception message contains the name of the failed system call
+ * with the stringisized errno code.
+ * Check Popen class API's to know when this exception would be
+ * thrown.
+ * Its usual that the API exception specification would have
+ * this exception together with CalledProcessError.
+ */
+class OSError: public std::runtime_error
+{
+public:
+ OSError(const std::string& err_msg, int err_code):
+ std::runtime_error(err_msg + ": " + SysErrorString(err_code))
+ {}
+};
+
+//--------------------------------------------------------------------
+namespace util
+{
+ template <typename R>
+ inline bool is_ready(std::shared_future<R> const &f)
+ {
+ return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+ }
+
+ inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
+ bool force)
+ {
+ //
+ // Unless we're told otherwise, don't quote unless we actually
+ // need to do so --- hopefully avoid problems if programs won't
+ // parse quotes properly
+ //
+
+ if (force == false && argument.empty() == false &&
+ argument.find_first_of(L" \t\n\v\"") == argument.npos) {
+ command_line.append(argument);
+ }
+ else {
+ command_line.push_back(L'"');
+
+ for (auto it = argument.begin();; ++it) {
+ unsigned number_backslashes = 0;
+
+ while (it != argument.end() && *it == L'\\') {
+ ++it;
+ ++number_backslashes;
+ }
+
+ if (it == argument.end()) {
+
+ //
+ // Escape all backslashes, but let the terminating
+ // double quotation mark we add below be interpreted
+ // as a metacharacter.
+ //
+
+ command_line.append(number_backslashes * 2, L'\\');
+ break;
+ }
+ else if (*it == L'"') {
+
+ //
+ // Escape all backslashes and the following
+ // double quotation mark.
+ //
+
+ command_line.append(number_backslashes * 2 + 1, L'\\');
+ command_line.push_back(*it);
+ }
+ else {
+
+ //
+ // Backslashes aren't special here.
+ //
+
+ command_line.append(number_backslashes, L'\\');
+ command_line.push_back(*it);
+ }
+ }
+
+ command_line.push_back(L'"');
+ }
+ }
+
+#ifdef __USING_WINDOWS__
+ inline std::string get_last_error(DWORD errorMessageID)
+ {
+ if (errorMessageID == 0)
+ return std::string();
+
+ LPSTR messageBuffer = nullptr;
+ size_t size = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&messageBuffer, 0, NULL);
+
+ std::string message(messageBuffer, size);
+
+ LocalFree(messageBuffer);
+
+ return message;
+ }
+
+ inline FILE *file_from_handle(HANDLE h, const char *mode)
+ {
+ int md;
+ if (!mode) {
+ throw OSError("invalid_mode", 0);
+ }
+
+ if (mode[0] == 'w') {
+ md = _O_WRONLY;
+ }
+ else if (mode[0] == 'r') {
+ md = _O_RDONLY;
+ }
+ else {
+ throw OSError("file_from_handle", 0);
+ }
+
+ int os_fhandle = _open_osfhandle((intptr_t)h, md);
+ if (os_fhandle == -1) {
+ CloseHandle(h);
+ throw OSError("_open_osfhandle", 0);
+ }
+
+ FILE *fp = _fdopen(os_fhandle, mode);
+ if (fp == 0) {
+ _close(os_fhandle);
+ throw OSError("_fdopen", 0);
+ }
+
+ return fp;
+ }
+
+ inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle)
+ {
+ SECURITY_ATTRIBUTES saAttr;
+
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ // Create a pipe for the child process's STDIN.
+ if (!CreatePipe(read_handle, write_handle, &saAttr,0))
+ throw OSError("CreatePipe", 0);
+
+ // Ensure the write handle to the pipe for STDIN is not inherited.
+ if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
+ throw OSError("SetHandleInformation", 0);
+ }
+#endif
+
+ /*!
+ * Function: split
+ * Parameters:
+ * [in] str : Input string which needs to be split based upon the
+ * delimiters provided.
+ * [in] deleims : Delimiter characters based upon which the string needs
+ * to be split. Default constructed to ' '(space) and '\t'(tab)
+ * [out] vector<string> : Vector of strings split at deleimiter.
+ */
+ static inline std::vector<std::string>
+ split(const std::string& str, const std::string& delims=" \t")
+ {
+ std::vector<std::string> res;
+ size_t init = 0;
+
+ while (true) {
+ auto pos = str.find_first_of(delims, init);
+ if (pos == std::string::npos) {
+ res.emplace_back(str.substr(init, str.length()));
+ break;
+ }
+ res.emplace_back(str.substr(init, pos - init));
+ pos++;
+ init = pos;
+ }
+
+ return res;
+ }
+
+
+#ifndef __USING_WINDOWS__
+ /*!
+ * Function: set_clo_on_exec
+ * Sets/Resets the FD_CLOEXEC flag on the provided file descriptor
+ * based upon the `set` parameter.
+ * Parameters:
+ * [in] fd : The descriptor on which FD_CLOEXEC needs to be set/reset.
+ * [in] set : If 'true', set FD_CLOEXEC.
+ * If 'false' unset FD_CLOEXEC.
+ */
+ static inline
+ void set_clo_on_exec(int fd, bool set = true)
+ {
+ int flags = fcntl(fd, F_GETFD, 0);
+ if (set) flags |= FD_CLOEXEC;
+ else flags &= ~FD_CLOEXEC;
+ //TODO: should check for errors
+ fcntl(fd, F_SETFD, flags);
+ }
+
+
+ /*!
+ * Function: pipe_cloexec
+ * Creates a pipe and sets FD_CLOEXEC flag on both
+ * read and write descriptors of the pipe.
+ * Parameters:
+ * [out] : A pair of file descriptors.
+ * First element of pair is the read descriptor of pipe.
+ * Second element is the write descriptor of pipe.
+ */
+ static inline
+ std::pair<int, int> pipe_cloexec() noexcept(false)
+ {
+ int pipe_fds[2];
+ int res = pipe(pipe_fds);
+ if (res) {
+ throw OSError("pipe failure", errno);
+ }
+
+ set_clo_on_exec(pipe_fds[0]);
+ set_clo_on_exec(pipe_fds[1]);
+
+ return std::make_pair(pipe_fds[0], pipe_fds[1]);
+ }
+#endif
+
+
+ /*!
+ * Function: write_n
+ * Writes `length` bytes to the file descriptor `fd`
+ * from the buffer `buf`.
+ * Parameters:
+ * [in] fd : The file descriptotr to write to.
+ * [in] buf: Buffer from which data needs to be written to fd.
+ * [in] length: The number of bytes that needs to be written from
+ * `buf` to `fd`.
+ * [out] int : Number of bytes written or -1 in case of failure.
+ */
+ static inline
+ int write_n(int fd, const char* buf, size_t length)
+ {
+ size_t nwritten = 0;
+ while (nwritten < length) {
+ int written = write(fd, buf + nwritten, length - nwritten);
+ if (written == -1) return -1;
+ nwritten += written;
+ }
+ return nwritten;
+ }
+
+
+ /*!
+ * Function: read_atmost_n
+ * Reads at the most `read_upto` bytes from the
+ * file object `fp` before returning.
+ * Parameters:
+ * [in] fp : The file object from which it needs to read.
+ * [in] buf : The buffer into which it needs to write the data.
+ * [in] read_upto: Max number of bytes which must be read from `fd`.
+ * [out] int : Number of bytes written to `buf` or read from `fd`
+ * OR -1 in case of error.
+ * NOTE: In case of EINTR while reading from socket, this API
+ * will retry to read from `fd`, but only till the EINTR counter
+ * reaches 50 after which it will return with whatever data it read.
+ */
+ static inline
+ int read_atmost_n(FILE* fp, char* buf, size_t read_upto)
+ {
+#ifdef __USING_WINDOWS__
+ return (int)fread(buf, 1, read_upto, fp);
+#else
+ int fd = fileno(fp);
+ int rbytes = 0;
+ int eintr_cnter = 0;
+
+ while (1) {
+ int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
+ if (read_bytes == -1) {
+ if (errno == EINTR) {
+ if (eintr_cnter >= 50) return -1;
+ eintr_cnter++;
+ continue;
+ }
+ return -1;
+ }
+ if (read_bytes == 0) return rbytes;
+
+ rbytes += read_bytes;
+ }
+ return rbytes;
+#endif
+ }
+
+
+ /*!
+ * Function: read_all
+ * Reads all the available data from `fp` into
+ * `buf`. Internally calls read_atmost_n.
+ * Parameters:
+ * [in] fp : The file object from which to read from.
+ * [in] buf : The buffer of type `class Buffer` into which
+ * the read data is written to.
+ * [out] int: Number of bytes read OR -1 in case of failure.
+ *
+ * NOTE: `class Buffer` is a exposed public class. See below.
+ */
+
+ static inline int read_all(FILE* fp, std::vector<char>& buf)
+ {
+ auto buffer = buf.data();
+ int total_bytes_read = 0;
+ int fill_sz = buf.size();
+
+ while (1) {
+ const int rd_bytes = read_atmost_n(fp, buffer, fill_sz);
+
+ if (rd_bytes == -1) { // Read finished
+ if (total_bytes_read == 0) return -1;
+ break;
+
+ } else if (rd_bytes == fill_sz) { // Buffer full
+ const auto orig_sz = buf.size();
+ const auto new_sz = orig_sz * 2;
+ buf.resize(new_sz);
+ fill_sz = new_sz - orig_sz;
+
+ //update the buffer pointer
+ buffer = buf.data();
+ total_bytes_read += rd_bytes;
+ buffer += total_bytes_read;
+
+ } else { // Partial data ? Continue reading
+ total_bytes_read += rd_bytes;
+ fill_sz -= rd_bytes;
+ break;
+ }
+ }
+ buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls
+ return total_bytes_read;
+ }
+
+#ifndef __USING_WINDOWS__
+ /*!
+ * Function: wait_for_child_exit
+ * Waits for the process with pid `pid` to exit
+ * and returns its status.
+ * Parameters:
+ * [in] pid : The pid of the process.
+ * [out] pair<int, int>:
+ * pair.first : Return code of the waitpid call.
+ * pair.second : Exit status of the process.
+ *
+ * NOTE: This is a blocking call as in, it will loop
+ * till the child is exited.
+ */
+ static inline
+ std::pair<int, int> wait_for_child_exit(int pid)
+ {
+ int status = 0;
+ int ret = -1;
+ while (1) {
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) break;
+ if (ret == 0) continue;
+ return std::make_pair(ret, status);
+ }
+
+ return std::make_pair(ret, status);
+ }
+#endif
+
+} // end namespace util
+
+
+
+/* -------------------------------
+ * Popen Arguments
+ * -------------------------------
+ */
+
+/*!
+ * Base class for all arguments involving string value.
+ */
+struct string_arg
+{
+ string_arg(const char* arg): arg_value(arg) {}
+ string_arg(std::string&& arg): arg_value(std::move(arg)) {}
+ string_arg(std::string arg): arg_value(std::move(arg)) {}
+ std::string arg_value;
+};
+
+/*!
+ * Option to specify the executable name separately
+ * from the args sequence.
+ * In this case the cmd args must only contain the
+ * options required for this executable.
+ *
+ * Eg: executable{"ls"}
+ */
+struct executable: string_arg
+{
+ template <typename T>
+ executable(T&& arg): string_arg(std::forward<T>(arg)) {}
+};
+
+/*!
+ * Used for redirecting input/output/error
+ */
+enum IOTYPE {
+ STDOUT = 1,
+ STDERR,
+ PIPE,
+};
+
+//TODO: A common base/interface for below stream structures ??
+
+/*!
+ * Option to specify the input channel for the child
+ * process. It can be:
+ * 1. An already open file descriptor.
+ * 2. A file name.
+ * 3. IOTYPE. Usual a PIPE
+ *
+ * Eg: input{PIPE}
+ * OR in case of redirection, output of another Popen
+ * input{popen.output()}
+ */
+struct input
+{
+ // For an already existing file descriptor.
+ explicit input(int fd): rd_ch_(fd) {}
+
+ // FILE pointer.
+ explicit input (FILE* fp):input(fileno(fp)) { assert(fp); }
+
+ explicit input(const char* filename) {
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) throw OSError("File not found: ", errno);
+ rd_ch_ = fd;
+ }
+ explicit input(IOTYPE typ) {
+ assert (typ == PIPE && "STDOUT/STDERR not allowed");
+#ifndef __USING_WINDOWS__
+ std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
+#endif
+ }
+
+ int rd_ch_ = -1;
+ int wr_ch_ = -1;
+};
+
+
+/*!
+ * Option to specify the output channel for the child
+ * process. It can be:
+ * 1. An already open file descriptor.
+ * 2. A file name.
+ * 3. IOTYPE. Usually a PIPE.
+ *
+ * Eg: output{PIPE}
+ * OR output{"output.txt"}
+ */
+struct output
+{
+ explicit output(int fd): wr_ch_(fd) {}
+
+ explicit output (FILE* fp):output(fileno(fp)) { assert(fp); }
+
+ explicit output(const char* filename) {
+ int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
+ if (fd == -1) throw OSError("File not found: ", errno);
+ wr_ch_ = fd;
+ }
+ explicit output(IOTYPE typ) {
+ assert (typ == PIPE && "STDOUT/STDERR not allowed");
+#ifndef __USING_WINDOWS__
+ std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
+#endif
+ }
+
+ int rd_ch_ = -1;
+ int wr_ch_ = -1;
+};
+
+
+/*!
+ * Option to specify the error channel for the child
+ * process. It can be:
+ * 1. An already open file descriptor.
+ * 2. A file name.
+ * 3. IOTYPE. Usually a PIPE or STDOUT
+ *
+ */
+struct error
+{
+ explicit error(int fd): wr_ch_(fd) {}
+
+ explicit error(FILE* fp):error(fileno(fp)) { assert(fp); }
+
+ explicit error(const char* filename) {
+ int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
+ if (fd == -1) throw OSError("File not found: ", errno);
+ wr_ch_ = fd;
+ }
+ explicit error(IOTYPE typ) {
+ assert ((typ == PIPE || typ == STDOUT) && "STDERR not allowed");
+ if (typ == PIPE) {
+#ifndef __USING_WINDOWS__
+ std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
+#endif
+ } else {
+ // Need to defer it till we have checked all arguments
+ deferred_ = true;
+ }
+ }
+
+ bool deferred_ = false;
+ int rd_ch_ = -1;
+ int wr_ch_ = -1;
+};
+
+// ~~~~ End Popen Args ~~~~
+
+
+/*!
+ * class: Buffer
+ * This class is a very thin wrapper around std::vector<char>
+ * This is basically used to determine the length of the actual
+ * data stored inside the dynamically resized vector.
+ *
+ * This is what is returned as the output to communicate and check_output
+ * functions, so, users must know about this class.
+ *
+ * OutBuffer and ErrBuffer are just different typedefs to this class.
+ */
+class Buffer
+{
+public:
+ Buffer() {}
+ explicit Buffer(size_t cap) { buf.resize(cap); }
+ void add_cap(size_t cap) { buf.resize(cap); }
+
+#if 0
+ Buffer(const Buffer& other):
+ buf(other.buf),
+ length(other.length)
+ {
+ std::cout << "COPY" << std::endl;
+ }
+
+ Buffer(Buffer&& other):
+ buf(std::move(other.buf)),
+ length(other.length)
+ {
+ std::cout << "MOVE" << std::endl;
+ }
+#endif
+
+public:
+ std::vector<char> buf;
+ size_t length = 0;
+};
+
+// Buffer for storing output written to output fd
+using OutBuffer = Buffer;
+// Buffer for storing output written to error fd
+using ErrBuffer = Buffer;
+
+
+// Fwd Decl.
+class Popen;
+
+/*---------------------------------------------------
+ * DETAIL NAMESPACE
+ *---------------------------------------------------
+ */
+
+namespace detail {
+
+// Metaprogram for searching a type within
+// a variadic parameter pack
+// This is particularly required to do a compile time
+// checking of the arguments provided to 'check_output' function
+// wherein the user is not expected to provide an 'output' option.
+
+template <typename... T> struct param_pack{};
+
+template <typename F, typename T> struct has_type;
+
+template <typename F>
+struct has_type<F, param_pack<>> {
+ static constexpr bool value = false;
+};
+
+template <typename F, typename... T>
+struct has_type<F, param_pack<F, T...>> {
+ static constexpr bool value = true;
+};
+
+template <typename F, typename H, typename... T>
+struct has_type<F, param_pack<H,T...>> {
+ static constexpr bool value =
+ std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, param_pack<T...>>::value;
+};
+
+//----
+
+/*!
+ * A helper class to Popen class for setting
+ * options as provided in the Popen constructor
+ * or in check_output arguments.
+ * This design allows us to _not_ have any fixed position
+ * to any arguments and specify them in a way similar to what
+ * can be done in python.
+ */
+struct ArgumentDeducer
+{
+ ArgumentDeducer(Popen* p): popen_(p) {}
+
+ void set_option(executable&& exe);
+ void set_option(input&& inp);
+ void set_option(output&& out);
+ void set_option(error&& err);
+
+private:
+ Popen* popen_ = nullptr;
+};
+
+/*!
+ * A helper class to Popen.
+ * This takes care of all the fork-exec logic
+ * in the execute_child API.
+ */
+class Child
+{
+public:
+ Child(Popen* p, int err_wr_pipe):
+ parent_(p),
+ err_wr_pipe_(err_wr_pipe)
+ {}
+
+ void execute_child();
+
+private:
+ // Lets call it parent even though
+ // technically a bit incorrect
+ Popen* parent_ = nullptr;
+ int err_wr_pipe_ = -1;
+};
+
+// Fwd Decl.
+class Streams;
+
+/*!
+ * A helper class to Streams.
+ * This takes care of management of communicating
+ * with the child process with the means of the correct
+ * file descriptor.
+ */
+class Communication
+{
+public:
+ Communication(Streams* stream): stream_(stream)
+ {}
+ void operator=(const Communication&) = delete;
+public:
+ int send(const char* msg, size_t length);
+ int send(const std::vector<char>& msg);
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length);
+ std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
+ { return communicate(msg.data(), msg.size()); }
+
+ void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; }
+ void set_err_buf_cap(size_t cap) { err_buf_cap_ = cap; }
+
+private:
+ std::pair<OutBuffer, ErrBuffer> communicate_threaded(
+ const char* msg, size_t length);
+
+private:
+ Streams* stream_;
+ size_t out_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
+ size_t err_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
+};
+
+
+
+/*!
+ * This is a helper class to Popen.
+ * It takes care of management of all the file descriptors
+ * and file pointers.
+ * It dispatches of the communication aspects to the
+ * Communication class.
+ * Read through the data members to understand about the
+ * various file descriptors used.
+ */
+class Streams
+{
+public:
+ Streams():comm_(this) {}
+ void operator=(const Streams&) = delete;
+
+public:
+ void setup_comm_channels();
+
+ void cleanup_fds()
+ {
+ if (write_to_child_ != -1 && read_from_parent_ != -1) {
+ close(write_to_child_);
+ }
+ if (write_to_parent_ != -1 && read_from_child_ != -1) {
+ close(read_from_child_);
+ }
+ if (err_write_ != -1 && err_read_ != -1) {
+ close(err_read_);
+ }
+ }
+
+ void close_parent_fds()
+ {
+ if (write_to_child_ != -1) close(write_to_child_);
+ if (read_from_child_ != -1) close(read_from_child_);
+ if (err_read_ != -1) close(err_read_);
+ }
+
+ void close_child_fds()
+ {
+ if (write_to_parent_ != -1) close(write_to_parent_);
+ if (read_from_parent_ != -1) close(read_from_parent_);
+ if (err_write_ != -1) close(err_write_);
+ }
+
+ FILE* input() { return input_.get(); }
+ FILE* output() { return output_.get(); }
+ FILE* error() { return error_.get(); }
+
+ void input(FILE* fp) { input_.reset(fp, fclose); }
+ void output(FILE* fp) { output_.reset(fp, fclose); }
+ void error(FILE* fp) { error_.reset(fp, fclose); }
+
+ void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); }
+ void set_err_buf_cap(size_t cap) { comm_.set_err_buf_cap(cap); }
+
+public: /* Communication forwarding API's */
+ int send(const char* msg, size_t length)
+ { return comm_.send(msg, length); }
+
+ int send(const std::vector<char>& msg)
+ { return comm_.send(msg); }
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
+ { return comm_.communicate(msg, length); }
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
+ { return comm_.communicate(msg); }
+
+
+public:// Yes they are public
+
+ std::shared_ptr<FILE> input_ = nullptr;
+ std::shared_ptr<FILE> output_ = nullptr;
+ std::shared_ptr<FILE> error_ = nullptr;
+
+#ifdef __USING_WINDOWS__
+ HANDLE g_hChildStd_IN_Rd = nullptr;
+ HANDLE g_hChildStd_IN_Wr = nullptr;
+ HANDLE g_hChildStd_OUT_Rd = nullptr;
+ HANDLE g_hChildStd_OUT_Wr = nullptr;
+ HANDLE g_hChildStd_ERR_Rd = nullptr;
+ HANDLE g_hChildStd_ERR_Wr = nullptr;
+#endif
+
+ // Pipes for communicating with child
+
+ // Emulates stdin
+ int write_to_child_ = -1; // Parent owned descriptor
+ int read_from_parent_ = -1; // Child owned descriptor
+
+ // Emulates stdout
+ int write_to_parent_ = -1; // Child owned descriptor
+ int read_from_child_ = -1; // Parent owned descriptor
+
+ // Emulates stderr
+ int err_write_ = -1; // Write error to parent (Child owned)
+ int err_read_ = -1; // Read error from child (Parent owned)
+
+private:
+ Communication comm_;
+};
+
+} // end namespace detail
+
+
+
+/*!
+ * class: Popen
+ * This is the single most important class in the whole library
+ * and glues together all the helper classes to provide a common
+ * interface to the client.
+ *
+ * API's provided by the class:
+ * 1. Popen({"cmd"}, output{..}, error{..}, ....)
+ * Command provided as a sequence.
+ * 2. Popen("cmd arg1"m output{..}, error{..}, ....)
+ * Command provided in a single string.
+ * 3. wait() - Wait for the child to exit.
+ * 4. retcode() - The return code of the exited child.
+ * 5. pid() - PID of the spawned child.
+ * 6. poll() - Check the status of the running child.
+ * 7. kill(sig_num) - Kill the child. SIGTERM used by default.
+ * 8. send(...) - Send input to the input channel of the child.
+ * 9. communicate(...) - Get the output/error from the child and close the channels
+ * from the parent side.
+ *10. input() - Get the input channel/File pointer. Can be used for
+ * customizing the way of sending input to child.
+ *11. output() - Get the output channel/File pointer. Usually used
+ in case of redirection. See piping examples.
+ *12. error() - Get the error channel/File pointer. Usually used
+ in case of redirection.
+ */
+class Popen
+{
+public:
+ friend struct detail::ArgumentDeducer;
+ friend class detail::Child;
+
+ template <typename... Args>
+ Popen(const std::string& cmd_args, Args&& ...args):
+ args_(cmd_args)
+ {
+ vargs_ = util::split(cmd_args);
+ init_args(std::forward<Args>(args)...);
+
+ // Setup the communication channels of the Popen class
+ stream_.setup_comm_channels();
+
+ execute_process();
+ }
+
+ template <typename... Args>
+ Popen(std::initializer_list<const char*> cmd_args, Args&& ...args)
+ {
+ vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end());
+ init_args(std::forward<Args>(args)...);
+
+ // Setup the communication channels of the Popen class
+ stream_.setup_comm_channels();
+
+ execute_process();
+ }
+
+ template <typename... Args>
+ Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
+ {
+ init_args(std::forward<Args>(args)...);
+
+ // Setup the communication channels of the Popen class
+ stream_.setup_comm_channels();
+
+ execute_process();
+ }
+
+/*
+ ~Popen()
+ {
+#ifdef __USING_WINDOWS__
+ CloseHandle(this->process_handle_);
+#endif
+ }
+*/
+
+ int pid() const noexcept { return child_pid_; }
+
+ int retcode() const noexcept { return retcode_; }
+
+ int wait() noexcept(false);
+
+ int poll() noexcept(false);
+
+ // Does not fail, Caller is expected to recheck the
+ // status with a call to poll()
+ void kill(int sig_num = 9);
+
+ void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
+
+ void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
+
+ int send(const char* msg, size_t length)
+ { return stream_.send(msg, length); }
+
+ int send(const std::string& msg)
+ { return send(msg.c_str(), msg.size()); }
+
+ int send(const std::vector<char>& msg)
+ { return stream_.send(msg); }
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
+ {
+ auto res = stream_.communicate(msg, length);
+ retcode_ = wait();
+ return res;
+ }
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const std::string& msg)
+ {
+ return communicate(msg.c_str(), msg.size());
+ }
+
+ std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
+ {
+ auto res = stream_.communicate(msg);
+ retcode_ = wait();
+ return res;
+ }
+
+ std::pair<OutBuffer, ErrBuffer> communicate()
+ {
+ return communicate(nullptr, 0);
+ }
+
+ FILE* input() { return stream_.input(); }
+ FILE* output() { return stream_.output();}
+ FILE* error() { return stream_.error(); }
+
+ /// Stream close APIs
+ void close_input() { stream_.input_.reset(); }
+ void close_output() { stream_.output_.reset(); }
+ void close_error() { stream_.error_.reset(); }
+
+private:
+ template <typename F, typename... Args>
+ void init_args(F&& farg, Args&&... args);
+ void init_args();
+ void populate_c_argv();
+ void execute_process() noexcept(false);
+
+private:
+ detail::Streams stream_;
+
+#ifdef __USING_WINDOWS__
+ HANDLE process_handle_;
+ std::future<void> cleanup_future_;
+#endif
+
+ std::string exe_name_;
+
+ // Command in string format
+ std::string args_;
+ // Command provided as sequence
+ std::vector<std::string> vargs_;
+ std::vector<char*> cargv_;
+
+ bool child_created_ = false;
+ // Pid of the child process
+ int child_pid_ = -1;
+
+ int retcode_ = -1;
+};
+
+inline void Popen::init_args() {
+ populate_c_argv();
+}
+
+template <typename F, typename... Args>
+inline void Popen::init_args(F&& farg, Args&&... args)
+{
+ detail::ArgumentDeducer argd(this);
+ argd.set_option(std::forward<F>(farg));
+ init_args(std::forward<Args>(args)...);
+}
+
+inline void Popen::populate_c_argv()
+{
+ cargv_.clear();
+ cargv_.reserve(vargs_.size() + 1);
+ for (auto& arg : vargs_) cargv_.push_back(&arg[0]);
+ cargv_.push_back(nullptr);
+}
+
+inline int Popen::wait() noexcept(false)
+{
+#ifdef __USING_WINDOWS__
+ int ret = WaitForSingleObject(process_handle_, INFINITE);
+
+ return 0;
+#else
+ int ret, status;
+ std::tie(ret, status) = util::wait_for_child_exit(pid());
+ if (ret == -1) {
+ if (errno != ECHILD) throw OSError("waitpid failed", errno);
+ return 0;
+ }
+ if (WIFEXITED(status)) return WEXITSTATUS(status);
+ if (WIFSIGNALED(status)) return WTERMSIG(status);
+ else return 255;
+
+ return 0;
+#endif
+}
+
+inline int Popen::poll() noexcept(false)
+{
+#ifdef __USING_WINDOWS__
+ int ret = WaitForSingleObject(process_handle_, 0);
+ if (ret != WAIT_OBJECT_0) return -1;
+
+ DWORD dretcode_;
+ if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
+ throw OSError("GetExitCodeProcess", 0);
+
+ retcode_ = (int)dretcode_;
+ CloseHandle(process_handle_);
+
+ return retcode_;
+#else
+ if (!child_created_) return -1; // TODO: ??
+
+ int status;
+
+ // Returns zero if child is still running
+ int ret = waitpid(child_pid_, &status, WNOHANG);
+ if (ret == 0) return -1;
+
+ if (ret == child_pid_) {
+ if (WIFSIGNALED(status)) {
+ retcode_ = WTERMSIG(status);
+ } else if (WIFEXITED(status)) {
+ retcode_ = WEXITSTATUS(status);
+ } else {
+ retcode_ = 255;
+ }
+ return retcode_;
+ }
+
+ if (ret == -1) {
+ // From subprocess.py
+ // This happens if SIGCHLD is set to be ignored
+ // or waiting for child process has otherwise been disabled
+ // for our process. This child is dead, we cannot get the
+ // status.
+ if (errno == ECHILD) retcode_ = 0;
+ else throw OSError("waitpid failed", errno);
+ } else {
+ retcode_ = ret;
+ }
+
+ return retcode_;
+#endif
+}
+
+inline void Popen::kill(int sig_num)
+{
+#ifdef __USING_WINDOWS__
+ if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
+ throw OSError("TerminateProcess", 0);
+ }
+#else
+ ::kill(child_pid_, sig_num);
+#endif
+}
+
+
+inline void Popen::execute_process() noexcept(false)
+{
+#ifdef __USING_WINDOWS__
+ if (exe_name_.length()) {
+ this->vargs_.insert(this->vargs_.begin(), this->exe_name_);
+ this->populate_c_argv();
+ }
+ this->exe_name_ = vargs_[0];
+
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
+ std::wstring argument;
+ std::wstring command_line;
+
+ for (auto arg : this->vargs_) {
+ argument = converter.from_bytes(arg);
+ util::quote_argument(argument, command_line, false);
+ command_line += L" ";
+ }
+
+ // CreateProcessW can modify szCmdLine so we allocate needed memory
+ wchar_t *szCmdline = new wchar_t[command_line.size() + 1];
+ wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str());
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFOW siStartInfo;
+ BOOL bSuccess = FALSE;
+ DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
+
+ // Set up members of the PROCESS_INFORMATION structure.
+ ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
+
+ // Set up members of the STARTUPINFOW structure.
+ // This structure specifies the STDIN and STDOUT handles for redirection.
+
+ ZeroMemory(&siStartInfo, sizeof(STARTUPINFOW));
+ siStartInfo.cb = sizeof(STARTUPINFOW);
+
+ siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr;
+ siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr;
+ siStartInfo.hStdInput = this->stream_.g_hChildStd_IN_Rd;
+
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ bSuccess = CreateProcessW(NULL,
+ szCmdline, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ creation_flags, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFOW pointer
+ &piProcInfo); // receives PROCESS_INFORMATION
+
+ // If an error occurs, exit the application.
+ if (!bSuccess) {
+ DWORD errorMessageID = ::GetLastError();
+ throw CalledProcessError("CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID);
+ }
+
+ CloseHandle(piProcInfo.hThread);
+
+ /*
+ TODO: use common apis to close linux handles
+ */
+
+ this->process_handle_ = piProcInfo.hProcess;
+
+ this->cleanup_future_ = std::async(std::launch::async, [this] {
+ WaitForSingleObject(this->process_handle_, INFINITE);
+
+ CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
+ CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
+ CloseHandle(this->stream_.g_hChildStd_IN_Rd);
+ });
+
+/*
+ NOTE: In the linux version, there is a check to make sure that the process
+ has been started. Here, we do nothing because CreateProcess will throw
+ if we fail to create the process.
+*/
+
+
+#else
+
+ int err_rd_pipe, err_wr_pipe;
+ std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec();
+
+ if (exe_name_.length()) {
+ vargs_.insert(vargs_.begin(), exe_name_);
+ populate_c_argv();
+ }
+ exe_name_ = vargs_[0];
+
+ child_pid_ = fork();
+
+ if (child_pid_ < 0) {
+ close(err_rd_pipe);
+ close(err_wr_pipe);
+ throw OSError("fork failed", errno);
+ }
+
+ child_created_ = true;
+
+ if (child_pid_ == 0)
+ {
+ // Close descriptors belonging to parent
+ stream_.close_parent_fds();
+
+ //Close the read end of the error pipe
+ close(err_rd_pipe);
+
+ detail::Child chld(this, err_wr_pipe);
+ chld.execute_child();
+ }
+ else
+ {
+ close (err_wr_pipe);// close child side of pipe, else get stuck in read below
+
+ stream_.close_child_fds();
+
+ try {
+ char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
+
+ int read_bytes = util::read_atmost_n(
+ fdopen(err_rd_pipe, "r"),
+ err_buf,
+ SP_MAX_ERR_BUF_SIZ);
+ close(err_rd_pipe);
+
+ if (read_bytes || strlen(err_buf)) {
+ // Call waitpid to reap the child process
+ // waitpid suspends the calling process until the
+ // child terminates.
+ int retcode = wait();
+
+ // Throw whatever information we have about child failure
+ throw CalledProcessError(err_buf, retcode);
+ }
+ } catch (std::exception& exp) {
+ stream_.cleanup_fds();
+ throw;
+ }
+
+ }
+#endif
+}
+
+namespace detail {
+
+ inline void ArgumentDeducer::set_option(executable&& exe) {
+ popen_->exe_name_ = std::move(exe.arg_value);
+ }
+
+ inline void ArgumentDeducer::set_option(input&& inp) {
+ if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_;
+ if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_;
+ }
+
+ inline void ArgumentDeducer::set_option(output&& out) {
+ if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_;
+ if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_;
+ }
+
+ inline void ArgumentDeducer::set_option(error&& err) {
+ if (err.deferred_) {
+ if (popen_->stream_.write_to_parent_) {
+ popen_->stream_.err_write_ = popen_->stream_.write_to_parent_;
+ } else {
+ throw std::runtime_error("Set output before redirecting error to output");
+ }
+ }
+ if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_;
+ if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
+ }
+
+
+ inline void Child::execute_child() {
+#ifndef __USING_WINDOWS__
+ int sys_ret = -1;
+ auto& stream = parent_->stream_;
+
+ try {
+ if (stream.write_to_parent_ == 0)
+ stream.write_to_parent_ = dup(stream.write_to_parent_);
+
+ if (stream.err_write_ == 0 || stream.err_write_ == 1)
+ stream.err_write_ = dup(stream.err_write_);
+
+ // Make the child owned descriptors as the
+ // stdin, stdout and stderr for the child process
+ auto _dup2_ = [](int fd, int to_fd) {
+ if (fd == to_fd) {
+ // dup2 syscall does not reset the
+ // CLOEXEC flag if the descriptors
+ // provided to it are same.
+ // But, we need to reset the CLOEXEC
+ // flag as the provided descriptors
+ // are now going to be the standard
+ // input, output and error
+ util::set_clo_on_exec(fd, false);
+ } else if(fd != -1) {
+ int res = dup2(fd, to_fd);
+ if (res == -1) throw OSError("dup2 failed", errno);
+ }
+ };
+
+ // Create the standard streams
+ _dup2_(stream.read_from_parent_, 0); // Input stream
+ _dup2_(stream.write_to_parent_, 1); // Output stream
+ _dup2_(stream.err_write_, 2); // Error stream
+
+ // Close the duped descriptors
+ if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
+ close(stream.read_from_parent_);
+
+ if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
+ close(stream.write_to_parent_);
+
+ if (stream.err_write_ != -1 && stream.err_write_ > 2)
+ close(stream.err_write_);
+
+ // Replace the current image with the executable
+ sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
+
+ if (sys_ret == -1) throw OSError("execve failed", errno);
+
+ } catch (const OSError& exp) {
+ // Just write the exception message
+ // TODO: Give back stack trace ?
+ std::string err_msg(exp.what());
+ //ATTN: Can we do something on error here ?
+ util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
+ }
+
+ // Calling application would not get this
+ // exit failure
+ _exit (EXIT_FAILURE);
+#endif
+ }
+
+
+ inline void Streams::setup_comm_channels()
+ {
+#ifdef __USING_WINDOWS__
+ util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
+ this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
+ this->write_to_child_ = _fileno(this->input());
+
+ util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
+ this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
+ this->read_from_child_ = _fileno(this->output());
+
+ util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
+ this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
+ this->err_read_ = _fileno(this->error());
+#else
+
+ if (write_to_child_ != -1) input(fdopen(write_to_child_, "wb"));
+ if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
+ if (err_read_ != -1) error(fdopen(err_read_, "rb"));
+
+ auto handles = {input(), output(), error()};
+
+ for (auto& h : handles) {
+ if (h == nullptr) continue;
+ setvbuf(h, nullptr, _IONBF, BUFSIZ);
+ }
+ #endif
+ }
+
+ inline int Communication::send(const char* msg, size_t length)
+ {
+ if (stream_->input() == nullptr) return -1;
+ return std::fwrite(msg, sizeof(char), length, stream_->input());
+ }
+
+ inline int Communication::send(const std::vector<char>& msg)
+ {
+ return send(msg.data(), msg.size());
+ }
+
+ inline std::pair<OutBuffer, ErrBuffer>
+ Communication::communicate(const char* msg, size_t length)
+ {
+ // Optimization from subprocess.py
+ // If we are using one pipe, or no pipe
+ // at all, using select() or threads is unnecessary.
+ auto hndls = {stream_->input(), stream_->output(), stream_->error()};
+ int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
+ const int len_conv = length;
+
+ if (count >= 2) {
+ OutBuffer obuf;
+ ErrBuffer ebuf;
+ if (stream_->input()) {
+ if (msg) {
+ int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
+ if (wbytes < len_conv) {
+ if (errno != EPIPE && errno != EINVAL) {
+ throw OSError("fwrite error", errno);
+ }
+ }
+ }
+ // Close the input stream
+ stream_->input_.reset();
+ } else if (stream_->output()) {
+ // Read till EOF
+ // ATTN: This could be blocking, if the process
+ // at the other end screws up, we get screwed as well
+ obuf.add_cap(out_buf_cap_);
+
+ int rbytes = util::read_all(
+ stream_->output(),
+ obuf.buf);
+
+ if (rbytes == -1) {
+ throw OSError("read to obuf failed", errno);
+ }
+
+ obuf.length = rbytes;
+ // Close the output stream
+ stream_->output_.reset();
+
+ } else if (stream_->error()) {
+ // Same screwness applies here as well
+ ebuf.add_cap(err_buf_cap_);
+
+ int rbytes = util::read_atmost_n(
+ stream_->error(),
+ ebuf.buf.data(),
+ ebuf.buf.size());
+
+ if (rbytes == -1) {
+ throw OSError("read to ebuf failed", errno);
+ }
+
+ ebuf.length = rbytes;
+ // Close the error stream
+ stream_->error_.reset();
+ }
+ return std::make_pair(std::move(obuf), std::move(ebuf));
+ }
+
+ return communicate_threaded(msg, length);
+ }
+
+
+ inline std::pair<OutBuffer, ErrBuffer>
+ Communication::communicate_threaded(const char* msg, size_t length)
+ {
+ OutBuffer obuf;
+ ErrBuffer ebuf;
+ std::future<int> out_fut, err_fut;
+ const int length_conv = length;
+
+ if (stream_->output()) {
+ obuf.add_cap(out_buf_cap_);
+
+ out_fut = std::async(std::launch::async,
+ [&obuf, this] {
+ return util::read_all(this->stream_->output(), obuf.buf);
+ });
+ }
+ if (stream_->error()) {
+ ebuf.add_cap(err_buf_cap_);
+
+ err_fut = std::async(std::launch::async,
+ [&ebuf, this] {
+ return util::read_all(this->stream_->error(), ebuf.buf);
+ });
+ }
+ if (stream_->input()) {
+ if (msg) {
+ int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
+ if (wbytes < length_conv) {
+ if (errno != EPIPE && errno != EINVAL) {
+ throw OSError("fwrite error", errno);
+ }
+ }
+ }
+ stream_->input_.reset();
+ }
+
+ if (out_fut.valid()) {
+ int res = out_fut.get();
+ if (res != -1) obuf.length = res;
+ else obuf.length = 0;
+ }
+ if (err_fut.valid()) {
+ int res = err_fut.get();
+ if (res != -1) ebuf.length = res;
+ else ebuf.length = 0;
+ }
+
+ return std::make_pair(std::move(obuf), std::move(ebuf));
+ }
+
+} // end namespace detail
+
+}
+
+#endif // BITCOIN_UTIL_SUBPROCESS_H
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 5ca9d21f8d..456662bd84 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -3,70 +3,21 @@
// 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 <util/time.h>
#include <compat/compat.h>
#include <tinyformat.h>
-#include <util/time.h>
#include <util/check.h>
#include <atomic>
#include <chrono>
-#include <ctime>
-#include <locale>
-#include <thread>
-#include <sstream>
#include <string>
+#include <thread>
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
static std::atomic<int64_t> nMockTime(0); //!< For testing
-bool ChronoSanityCheck()
-{
- // std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
- // to use the Unix epoch timestamp, prior to C++20, but in practice they almost
- // certainly will. Any differing behavior will be assumed to be an error, unless
- // certain platforms prove to consistently deviate, at which point we'll cope
- // with it by adding offsets.
-
- // Create a new clock from time_t(0) and make sure that it represents 0
- // seconds from the system_clock's time_since_epoch. Then convert that back
- // to a time_t and verify that it's the same as before.
- const time_t time_t_epoch{};
- auto clock = std::chrono::system_clock::from_time_t(time_t_epoch);
- if (std::chrono::duration_cast<std::chrono::seconds>(clock.time_since_epoch()).count() != 0) {
- return false;
- }
-
- time_t time_val = std::chrono::system_clock::to_time_t(clock);
- if (time_val != time_t_epoch) {
- return false;
- }
-
- // Check that the above zero time is actually equal to the known unix timestamp.
- struct tm epoch;
-#ifdef HAVE_GMTIME_R
- if (gmtime_r(&time_val, &epoch) == nullptr) {
-#else
- if (gmtime_s(&epoch, &time_val) != 0) {
-#endif
- return false;
- }
-
- if ((epoch.tm_sec != 0) ||
- (epoch.tm_min != 0) ||
- (epoch.tm_hour != 0) ||
- (epoch.tm_mday != 1) ||
- (epoch.tm_mon != 0) ||
- (epoch.tm_year != 70)) {
- return false;
- }
- return true;
-}
-
NodeClock::time_point NodeClock::now() noexcept
{
const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
@@ -96,30 +47,21 @@ std::chrono::seconds GetMockTime()
int64_t GetTime() { return GetTime<std::chrono::seconds>().count(); }
-std::string FormatISO8601DateTime(int64_t nTime) {
- struct tm ts;
- time_t time_val = nTime;
-#ifdef HAVE_GMTIME_R
- if (gmtime_r(&time_val, &ts) == nullptr) {
-#else
- if (gmtime_s(&ts, &time_val) != 0) {
-#endif
- return {};
- }
- return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
+std::string FormatISO8601DateTime(int64_t nTime)
+{
+ const std::chrono::sys_seconds secs{std::chrono::seconds{nTime}};
+ const auto days{std::chrono::floor<std::chrono::days>(secs)};
+ const std::chrono::year_month_day ymd{days};
+ const std::chrono::hh_mm_ss hms{secs - days};
+ return strprintf("%04i-%02u-%02uT%02i:%02i:%02iZ", signed{ymd.year()}, unsigned{ymd.month()}, unsigned{ymd.day()}, hms.hours().count(), hms.minutes().count(), hms.seconds().count());
}
-std::string FormatISO8601Date(int64_t nTime) {
- struct tm ts;
- time_t time_val = nTime;
-#ifdef HAVE_GMTIME_R
- if (gmtime_r(&time_val, &ts) == nullptr) {
-#else
- if (gmtime_s(&ts, &time_val) != 0) {
-#endif
- return {};
- }
- return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
+std::string FormatISO8601Date(int64_t nTime)
+{
+ const std::chrono::sys_seconds secs{std::chrono::seconds{nTime}};
+ const auto days{std::chrono::floor<std::chrono::days>(secs)};
+ const std::chrono::year_month_day ymd{days};
+ return strprintf("%04i-%02u-%02u", signed{ymd.year()}, unsigned{ymd.month()}, unsigned{ymd.day()});
}
struct timeval MillisToTimeval(int64_t nTimeout)
diff --git a/src/util/time.h b/src/util/time.h
index 6aa776137c..108560e0e0 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -116,7 +116,4 @@ struct timeval MillisToTimeval(int64_t nTimeout);
*/
struct timeval MillisToTimeval(std::chrono::milliseconds ms);
-/** Sanity check epoch match normal Unix epoch */
-bool ChronoSanityCheck();
-
#endif // BITCOIN_UTIL_TIME_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 8a78f2106d..903f9caf13 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1366,7 +1366,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
// Individual modified feerate exceeded caller-defined max; abort
// N.B. this doesn't take into account CPFPs. Chunk-aware validation may be more robust.
if (args.m_client_maxfeerate && CFeeRate(ws.m_modified_fees, ws.m_vsize) > args.m_client_maxfeerate.value()) {
- package_state.Invalid(PackageValidationResult::PCKG_TX, "max feerate exceeded");
+ // Need to set failure here both individually and at package level
+ ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "max feerate exceeded", "");
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
// Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
return PackageMempoolAcceptResult(package_state, std::move(results));
@@ -6140,13 +6142,14 @@ bool ChainstateManager::DeleteSnapshotChainstate()
Assert(m_snapshot_chainstate);
Assert(m_ibd_chainstate);
- fs::path snapshot_datadir = GetSnapshotCoinsDBPath(*m_snapshot_chainstate);
+ fs::path snapshot_datadir = Assert(node::FindSnapshotChainstateDir(m_options.datadir)).value();
if (!DeleteCoinsDBFromDisk(snapshot_datadir, /*is_snapshot=*/ true)) {
LogPrintf("Deletion of %s failed. Please remove it manually to continue reindexing.\n",
fs::PathToString(snapshot_datadir));
return false;
}
m_active_chainstate = m_ibd_chainstate.get();
+ m_active_chainstate->m_mempool = m_snapshot_chainstate->m_mempool;
m_snapshot_chainstate.reset();
return true;
}
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index a71f8f9fbc..b5703fa54a 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -9,9 +9,11 @@
#include <wallet/external_signer_scriptpubkeyman.h>
#include <iostream>
+#include <key_io.h>
#include <memory>
#include <stdexcept>
#include <string>
+#include <univalue.h>
#include <utility>
#include <vector>
@@ -51,15 +53,26 @@ ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
return signers[0];
}
-bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const
+util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const
{
// TODO: avoid the need to infer a descriptor from inside a descriptor wallet
+ const CScript& scriptPubKey = GetScriptForDestination(dest);
auto provider = GetSolvingProvider(scriptPubKey);
auto descriptor = InferDescriptor(scriptPubKey, *provider);
- signer.DisplayAddress(descriptor->ToString());
- // TODO inspect result
- return true;
+ const UniValue& result = signer.DisplayAddress(descriptor->ToString());
+
+ const UniValue& error = result.find_value("error");
+ if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())};
+
+ const UniValue& ret_address = result.find_value("address");
+ if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")};
+
+ if (ret_address.getValStr() != EncodeDestination(dest)) {
+ return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())};
+ }
+
+ return util::Result<void>();
}
// If sign is true, transaction must previously have been filled
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index c052ce6129..44286456b6 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -9,6 +9,8 @@
#include <memory>
+struct bilingual_str;
+
namespace wallet {
class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
{
@@ -27,7 +29,11 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
static ExternalSigner GetExternalSigner();
- bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
+ /**
+ * Display address on the device and verify that the returned value matches.
+ * @returns nothing or an error message
+ */
+ util::Result<void> DisplayAddress(const CTxDestination& dest, const ExternalSigner& signer) const;
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
};
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index d33e6f3873..0c1cae7253 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -247,7 +247,7 @@ public:
return value.empty() ? m_wallet->EraseAddressReceiveRequest(batch, dest, id)
: m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
}
- bool displayAddress(const CTxDestination& dest) override
+ util::Result<void> displayAddress(const CTxDestination& dest) override
{
LOCK(m_wallet->cs_wallet);
return m_wallet->DisplayAddress(dest);
@@ -286,7 +286,7 @@ public:
if (!res) return util::Error{util::ErrorString(res)};
const auto& txr = *res;
fee = txr.fee;
- change_pos = txr.change_pos ? *txr.change_pos : -1;
+ change_pos = txr.change_pos ? int(*txr.change_pos) : -1;
return txr.tx;
}
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index ea3ffff549..c164266f80 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -253,6 +253,7 @@ bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminef
return (CachedTxGetDebit(wallet, wtx, filter) > 0);
}
+// NOLINTNEXTLINE(misc-no-recursion)
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
{
AssertLockHeld(wallet.cs_wallet);
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index acaa2d8b15..bed9ec029a 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -395,6 +395,7 @@ class DescribeWalletAddressVisitor
public:
const SigningProvider * const provider;
+ // NOLINTNEXTLINE(misc-no-recursion)
void ProcessSubScript(const CScript& subscript, UniValue& obj) const
{
// Always present: script type and redeemscript
@@ -445,6 +446,7 @@ public:
return obj;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
UniValue operator()(const ScriptHash& scripthash) const
{
UniValue obj(UniValue::VOBJ);
@@ -465,6 +467,7 @@ public:
return obj;
}
+ // NOLINTNEXTLINE(misc-no-recursion)
UniValue operator()(const WitnessV0ScriptHash& id) const
{
UniValue obj(UniValue::VOBJ);
@@ -786,9 +789,8 @@ RPCHelpMan walletdisplayaddress()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
- if (!pwallet->DisplayAddress(dest)) {
- throw JSONRPCError(RPC_MISC_ERROR, "Failed to display address");
- }
+ util::Result<void> res = pwallet->DisplayAddress(dest);
+ if (!res) throw JSONRPCError(RPC_MISC_ERROR, util::ErrorString(res).original);
UniValue result(UniValue::VOBJ);
result.pushKV("address", request.params[0].get_str());
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 5167e986b1..ae2dfe5795 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -854,6 +854,7 @@ enum class ScriptContext
// Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used.
// Returns an error string, or the empty string for success.
+// NOLINTNEXTLINE(misc-no-recursion)
static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx)
{
// Use Solver to obtain script type and parsed pubkeys or hashes:
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 59171f6db7..b42275fe4b 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -97,6 +97,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
//! scripts or simply treat any script that has been
//! stored in the keystore as spendable
+// NOLINTNEXTLINE(misc-no-recursion)
IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
{
IsMineResult ret = IsMineResult::NO;
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 4575881d96..2c1ab8d44a 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -27,7 +27,6 @@
#include <unordered_map>
enum class OutputType;
-struct bilingual_str;
namespace wallet {
struct MigrationData;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 96c4397504..8f4171eb15 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2667,7 +2667,7 @@ void ReserveDestination::ReturnDestination()
address = CNoDestination();
}
-bool CWallet::DisplayAddress(const CTxDestination& dest)
+util::Result<void> CWallet::DisplayAddress(const CTxDestination& dest)
{
CScript scriptPubKey = GetScriptForDestination(dest);
for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) {
@@ -2676,9 +2676,9 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
continue;
}
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
- return signer_spk_man->DisplayAddress(scriptPubKey, signer);
+ return signer_spk_man->DisplayAddress(dest, signer);
}
- return false;
+ return util::Error{_("There is no ScriptPubKeyManager for this address")};
}
bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b49b5a7d0d..6a998fa398 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -537,8 +537,8 @@ public:
bool IsSpentKey(const CScript& scriptPubKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /** Display address on an external signer. Returns false if external signer support is not compiled */
- bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** Display address on an external signer. */
+ util::Result<void> DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsLockedCoin(const COutPoint& output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool LockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index d10db046f5..536e471053 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -8,6 +8,7 @@
#include <kernel/chain.h>
#include <kernel/mempool_entry.h>
#include <logging.h>
+#include <netbase.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <validationinterface.h>
@@ -57,7 +58,12 @@ std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std
{
std::string arg("-zmq" + entry.first);
const auto& factory = entry.second;
- for (const std::string& address : gArgs.GetArgs(arg)) {
+ for (std::string& address : gArgs.GetArgs(arg)) {
+ // libzmq uses prefix "ipc://" for UNIX domain sockets
+ if (address.substr(0, ADDR_PREFIX_UNIX.length()) == ADDR_PREFIX_UNIX) {
+ address.replace(0, ADDR_PREFIX_UNIX.length(), ADDR_PREFIX_IPC);
+ }
+
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
diff --git a/src/zmq/zmqutil.h b/src/zmq/zmqutil.h
index 334b51aa91..bec48c0a56 100644
--- a/src/zmq/zmqutil.h
+++ b/src/zmq/zmqutil.h
@@ -9,4 +9,7 @@
void zmqError(const std::string& str);
+/** Prefix for unix domain socket addresses (which are local filesystem paths) */
+const std::string ADDR_PREFIX_IPC = "ipc://"; // used by libzmq, example "ipc:///root/path/to/file"
+
#endif // BITCOIN_ZMQ_ZMQUTIL_H
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 3e882f47b8..19cbbcffdb 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -11,9 +11,6 @@ The assumeutxo value generated and used here is committed to in
## Possible test improvements
-- TODO: test what happens with -reindex and -reindex-chainstate before the
- snapshot is validated, and make sure it's deleted successfully.
-
Interesting test cases could be loading an assumeutxo snapshot file with:
- TODO: Valid hash but invalid snapshot file (bad coin height or
@@ -379,6 +376,17 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
+ for reindex_arg in ['-reindex=1', '-reindex-chainstate=1']:
+ self.log.info(f"Check that restarting with {reindex_arg} will delete the snapshot chainstate")
+ self.restart_node(2, extra_args=[reindex_arg, *self.extra_args[2]])
+ assert_equal(1, len(n2.getchainstates()["chainstates"]))
+ for i in range(1, 300):
+ block = n0.getblock(n0.getblockhash(i), 0)
+ n2.submitheader(block)
+ loaded = n2.loadtxoutset(dump_output['path'])
+ assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
+ assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
+
normal, snapshot = n2.getchainstates()['chainstates']
assert_equal(normal['blocks'], START_HEIGHT)
assert_equal(normal.get('snapshot_blockhash'), None)
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
index b3bf35b524..66c0a4f615 100755
--- a/test/functional/feature_index_prune.py
+++ b/test/functional/feature_index_prune.py
@@ -31,7 +31,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
expected_stats = {
'coinstatsindex': {'synced': True, 'best_block_height': height}
}
- self.wait_until(lambda: self.nodes[1].getindexinfo() == expected_stats)
+ self.wait_until(lambda: self.nodes[1].getindexinfo() == expected_stats, timeout=150)
expected = {**expected_filter, **expected_stats}
self.wait_until(lambda: self.nodes[2].getindexinfo() == expected)
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 3c3ff1e4a0..9f6f8919de 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -3,7 +3,9 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the ZMQ notification interface."""
+import os
import struct
+import tempfile
from time import sleep
from io import BytesIO
@@ -30,7 +32,7 @@ from test_framework.util import (
from test_framework.wallet import (
MiniWallet,
)
-from test_framework.netutil import test_ipv6_local
+from test_framework.netutil import test_ipv6_local, test_unix_socket
# Test may be skipped and not have zmq installed
@@ -119,6 +121,10 @@ class ZMQTest (BitcoinTestFramework):
self.ctx = zmq.Context()
try:
self.test_basic()
+ if test_unix_socket():
+ self.test_basic(unix=True)
+ else:
+ self.log.info("Skipping ipc test, because UNIX sockets are not supported.")
self.test_sequence()
self.test_mempool_sync()
self.test_reorg()
@@ -139,7 +145,7 @@ class ZMQTest (BitcoinTestFramework):
socket.setsockopt(zmq.IPV6, 1)
subscribers.append(ZMQSubscriber(socket, topic.encode()))
- self.restart_node(0, [f"-zmqpub{topic}={address}" for topic, address in services])
+ self.restart_node(0, [f"-zmqpub{topic}={address.replace('ipc://', 'unix:')}" for topic, address in services])
for i, sub in enumerate(subscribers):
sub.socket.connect(services[i][1])
@@ -176,12 +182,19 @@ class ZMQTest (BitcoinTestFramework):
return subscribers
- def test_basic(self):
+ def test_basic(self, unix = False):
+ self.log.info(f"Running basic test with {'ipc' if unix else 'tcp'} protocol")
# Invalid zmq arguments don't take down the node, see #17185.
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
address = f"tcp://127.0.0.1:{self.zmq_port_base}"
+
+ if unix:
+ # Use the shortest temp path possible since paths may have as little as 92-char limit
+ socket_path = tempfile.NamedTemporaryFile().name
+ address = f"ipc://{socket_path}"
+
subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]])
hashblock = subs[0]
@@ -247,6 +260,8 @@ class ZMQTest (BitcoinTestFramework):
])
assert_equal(self.nodes[1].getzmqnotifications(), [])
+ if unix:
+ os.unlink(socket_path)
def test_reorg(self):
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index 6215610c31..e8a568f7ab 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -6,7 +6,6 @@
from decimal import Decimal
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -14,8 +13,7 @@ from test_framework.util import (
assert_fee_amount,
assert_greater_than,
assert_raises_rpc_error,
- create_lots_of_big_transactions,
- gen_return_txouts,
+ fill_mempool,
)
from test_framework.wallet import (
COIN,
@@ -34,50 +32,6 @@ class MempoolLimitTest(BitcoinTestFramework):
]]
self.supports_cli = False
- def fill_mempool(self):
- """Fill mempool until eviction."""
- self.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises")
- txouts = gen_return_txouts()
- node = self.nodes[0]
- miniwallet = self.wallet
- relayfee = node.getnetworkinfo()['relayfee']
-
- tx_batch_size = 1
- num_of_batches = 75
- # Generate UTXOs to flood the mempool
- # 1 to create a tx initially that will be evicted from the mempool later
- # 75 transactions each with a fee rate higher than the previous one
- # And 1 more to verify that this tx does not get added to the mempool with a fee rate less than the mempoolminfee
- # And 2 more for the package cpfp test
- self.generate(miniwallet, 1 + (num_of_batches * tx_batch_size))
-
- # Mine 99 blocks so that the UTXOs are allowed to be spent
- self.generate(node, COINBASE_MATURITY - 1)
-
- self.log.debug("Create a mempool tx that will be evicted")
- tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, fee_rate=relayfee)["txid"]
-
- # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
- # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
- # by 130 should result in a fee that corresponds to 2x of that fee rate
- base_fee = relayfee * 130
-
- self.log.debug("Fill up the mempool with txs with higher fee rate")
- with node.assert_debug_log(["rolling minimum fee bumped"]):
- for batch_of_txid in range(num_of_batches):
- fee = (batch_of_txid + 1) * base_fee
- create_lots_of_big_transactions(miniwallet, node, fee, tx_batch_size, txouts)
-
- self.log.debug("The tx should be evicted by now")
- # The number of transactions created should be greater than the ones present in the mempool
- assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
- # Initial tx created should not be present in the mempool anymore as it had a lower fee rate
- assert tx_to_be_evicted_id not in node.getrawmempool()
-
- self.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
- assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
- assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
-
def test_rbf_carveout_disallowed(self):
node = self.nodes[0]
@@ -139,7 +93,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- self.fill_mempool()
+ fill_mempool(self, node, self.wallet)
current_info = node.getmempoolinfo()
mempoolmin_feerate = current_info["mempoolminfee"]
@@ -229,7 +183,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- self.fill_mempool()
+ fill_mempool(self, node, self.wallet)
current_info = node.getmempoolinfo()
mempoolmin_feerate = current_info["mempoolminfee"]
@@ -303,7 +257,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- self.fill_mempool()
+ fill_mempool(self, node, self.wallet)
# Deliberately try to create a tx with a fee less than the minimum mempool fee to assert that it does not get added to the mempool
self.log.info('Create a mempool tx that will not pass mempoolminfee')
diff --git a/test/functional/mocks/signer.py b/test/functional/mocks/signer.py
index 5f4fad6380..23d163aac3 100755
--- a/test/functional/mocks/signer.py
+++ b/test/functional/mocks/signer.py
@@ -25,35 +25,36 @@ def getdescriptors(args):
sys.stdout.write(json.dumps({
"receive": [
- "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j",
- "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#r0grqw5x",
- "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#x30uthjs",
- "tr([00000001/86'/1'/" + args.account + "']" + xpub + "/0/*)#sng9rd4t"
+ "pkh([00000001/44h/1h/" + args.account + "']" + xpub + "/0/*)#aqllu46s",
+ "sh(wpkh([00000001/49h/1h/" + args.account + "']" + xpub + "/0/*))#5dh56mgg",
+ "wpkh([00000001/84h/1h/" + args.account + "']" + xpub + "/0/*)#h62dxaej",
+ "tr([00000001/86h/1h/" + args.account + "']" + xpub + "/0/*)#pcd5w87f"
],
"internal": [
- "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2",
- "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#kwx4c3pe",
- "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#h92akzzg",
- "tr([00000001/86'/1'/" + args.account + "']" + xpub + "/1/*)#p8dy7c9n"
+ "pkh([00000001/44h/1h/" + args.account + "']" + xpub + "/1/*)#v567pq2g",
+ "sh(wpkh([00000001/49h/1h/" + args.account + "']" + xpub + "/1/*))#pvezzyah",
+ "wpkh([00000001/84h/1h/" + args.account + "']" + xpub + "/1/*)#xw0vmgf2",
+ "tr([00000001/86h/1h/" + args.account + "']" + xpub + "/1/*)#svg4njw3"
]
}))
def displayaddress(args):
- # Several descriptor formats are acceptable, so allowing for potential
- # changes to InferDescriptor:
if args.fingerprint != "00000001":
return sys.stdout.write(json.dumps({"error": "Unexpected fingerprint", "fingerprint": args.fingerprint}))
- expected_desc = [
- "wpkh([00000001/84'/1'/0'/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#0yneg42r",
- "tr([00000001/86'/1'/0'/0/0]c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#4vdj9jqk",
- ]
+ expected_desc = {
+ "wpkh([00000001/84h/1h/0h/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#3te6hhy7": "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g",
+ "sh(wpkh([00000001/49h/1h/0h/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7))#kz9y5w82": "2N2gQKzjUe47gM8p1JZxaAkTcoHPXV6YyVp",
+ "pkh([00000001/44h/1h/0h/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#q3pqd8wh": "n1LKejAadN6hg2FrBXoU1KrwX4uK16mco9",
+ "tr([00000001/86h/1h/0h/0/0]c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#puqqa90m": "tb1phw4cgpt6cd30kz9k4wkpwm872cdvhss29jga2xpmftelhqll62mscq0k4g",
+ "wpkh([00000001/84h/1h/0h/0/1]03a20a46308be0b8ded6dff0a22b10b4245c587ccf23f3b4a303885be3a524f172)#aqpjv5xr": "wrong_address",
+ }
if args.desc not in expected_desc:
return sys.stdout.write(json.dumps({"error": "Unexpected descriptor", "desc": args.desc}))
- return sys.stdout.write(json.dumps({"address": "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g"}))
+ return sys.stdout.write(json.dumps({"address": expected_desc[args.desc]}))
def signtx(args):
if args.fingerprint != "00000001":
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
index f9a8c44be2..ea114e7d70 100755
--- a/test/functional/p2p_addrv2_relay.py
+++ b/test/functional/p2p_addrv2_relay.py
@@ -11,6 +11,7 @@ import time
from test_framework.messages import (
CAddress,
msg_addrv2,
+ msg_sendaddrv2,
)
from test_framework.p2p import (
P2PInterface,
@@ -75,6 +76,12 @@ class AddrTest(BitcoinTestFramework):
self.extra_args = [["-whitelist=addr@127.0.0.1"]]
def run_test(self):
+ self.log.info('Check disconnection when sending sendaddrv2 after verack')
+ conn = self.nodes[0].add_p2p_connection(P2PInterface())
+ with self.nodes[0].assert_debug_log(['sendaddrv2 received after verack from peer=0; disconnecting']):
+ conn.send_message(msg_sendaddrv2())
+ conn.wait_for_disconnect()
+
self.log.info('Create connection that sends addrv2 messages')
addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
msg = msg_addrv2()
@@ -89,8 +96,8 @@ class AddrTest(BitcoinTestFramework):
msg.addrs = ADDRS
msg_size = calc_addrv2_msg_size(ADDRS)
with self.nodes[0].assert_debug_log([
- f'received: addrv2 ({msg_size} bytes) peer=0',
- f'sending addrv2 ({msg_size} bytes) peer=1',
+ f'received: addrv2 ({msg_size} bytes) peer=1',
+ f'sending addrv2 ({msg_size} bytes) peer=2',
]):
addr_source.send_and_ping(msg)
self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
diff --git a/test/functional/p2p_compactblocks_hb.py b/test/functional/p2p_compactblocks_hb.py
index c985a1f98d..023b33ff6d 100755
--- a/test/functional/p2p_compactblocks_hb.py
+++ b/test/functional/p2p_compactblocks_hb.py
@@ -32,10 +32,15 @@ class CompactBlocksConnectionTest(BitcoinTestFramework):
self.connect_nodes(peer, 0)
self.generate(self.nodes[0], 1)
self.disconnect_nodes(peer, 0)
- status_to = [self.peer_info(1, i)['bip152_hb_to'] for i in range(2, 6)]
- status_from = [self.peer_info(i, 1)['bip152_hb_from'] for i in range(2, 6)]
- assert_equal(status_to, status_from)
- return status_to
+
+ def status_to():
+ return [self.peer_info(1, i)['bip152_hb_to'] for i in range(2, 6)]
+
+ def status_from():
+ return [self.peer_info(i, 1)['bip152_hb_from'] for i in range(2, 6)]
+
+ self.wait_until(lambda: status_to() == status_from())
+ return status_to()
def run_test(self):
self.log.info("Testing reserved high-bandwidth mode slot for outbound peer...")
diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py
index c389ff732f..678b006886 100755
--- a/test/functional/p2p_disconnect_ban.py
+++ b/test/functional/p2p_disconnect_ban.py
@@ -77,6 +77,7 @@ class DisconnectBanTest(BitcoinTestFramework):
self.nodes[1].setmocktime(old_time)
self.nodes[1].setban("127.0.0.0/32", "add")
self.nodes[1].setban("127.0.0.0/24", "add")
+ self.nodes[1].setban("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion", "add")
self.nodes[1].setban("192.168.0.1", "add", 1) # ban for 1 seconds
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) # ban for 1000 seconds
listBeforeShutdown = self.nodes[1].listbanned()
@@ -85,13 +86,13 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("setban: test banning with absolute timestamp")
self.nodes[1].setban("192.168.0.2", "add", old_time + 120, True)
- # Move time forward by 3 seconds so the third ban has expired
+ # Move time forward by 3 seconds so the fourth ban has expired
self.nodes[1].setmocktime(old_time + 3)
- assert_equal(len(self.nodes[1].listbanned()), 4)
+ assert_equal(len(self.nodes[1].listbanned()), 5)
self.log.info("Test ban_duration and time_remaining")
for ban in self.nodes[1].listbanned():
- if ban["address"] in ["127.0.0.0/32", "127.0.0.0/24"]:
+ if ban["address"] in ["127.0.0.0/32", "127.0.0.0/24", "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"]:
assert_equal(ban["ban_duration"], 86400)
assert_equal(ban["time_remaining"], 86397)
elif ban["address"] == "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19":
@@ -108,6 +109,7 @@ class DisconnectBanTest(BitcoinTestFramework):
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
assert_equal("192.168.0.2/32", listAfterShutdown[2]['address'])
assert_equal("/19" in listAfterShutdown[3]['address'], True)
+ assert_equal("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion", listAfterShutdown[4]['address'])
# Clear ban lists
self.nodes[1].clearbanned()
diff --git a/test/functional/p2p_handshake.py b/test/functional/p2p_handshake.py
index f0b62e291d..dd19fe9333 100755
--- a/test/functional/p2p_handshake.py
+++ b/test/functional/p2p_handshake.py
@@ -41,6 +41,7 @@ class P2PHandshakeTest(BitcoinTestFramework):
peer.sync_with_ping()
peer.peer_disconnect()
peer.wait_for_disconnect()
+ self.wait_until(lambda: len(node.getpeerinfo()) == 0)
def test_desirable_service_flags(self, node, service_flag_tests, desirable_service_flags, expect_disconnect):
"""Check that connecting to a peer either fails or succeeds depending on its offered
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 0e463c5072..7a50f1e605 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -5,6 +5,7 @@
"""
Test transaction download behavior
"""
+from decimal import Decimal
import time
from test_framework.messages import (
@@ -14,6 +15,7 @@ from test_framework.messages import (
MSG_WTX,
msg_inv,
msg_notfound,
+ msg_tx,
)
from test_framework.p2p import (
P2PInterface,
@@ -22,6 +24,7 @@ from test_framework.p2p import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ fill_mempool,
)
from test_framework.wallet import MiniWallet
@@ -54,6 +57,7 @@ MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + INBOUND_PEER_TX_DELAY + TXID_RE
class TxDownloadTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ self.extra_args= [['-datacarriersize=100000', '-maxmempool=5', '-persistmempool=0']] * self.num_nodes
def test_tx_requests(self):
self.log.info("Test that we request transactions from all our peers, eventually")
@@ -241,6 +245,29 @@ class TxDownloadTest(BitcoinTestFramework):
self.log.info('Check that spurious notfound is ignored')
self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(MSG_TX, 1)]))
+ def test_rejects_filter_reset(self):
+ self.log.info('Check that rejected tx is not requested again')
+ node = self.nodes[0]
+ fill_mempool(self, node, self.wallet)
+ self.wallet.rescan_utxos()
+ mempoolminfee = node.getmempoolinfo()['mempoolminfee']
+ peer = node.add_p2p_connection(TestP2PConn())
+ low_fee_tx = self.wallet.create_self_transfer(fee_rate=Decimal("0.9")*mempoolminfee)
+ assert_equal(node.testmempoolaccept([low_fee_tx['hex']])[0]["reject-reason"], "mempool min fee not met")
+ peer.send_and_ping(msg_tx(low_fee_tx['tx']))
+ peer.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=int(low_fee_tx['wtxid'], 16))]))
+ node.setmocktime(int(time.time()))
+ node.bumpmocktime(MAX_GETDATA_INBOUND_WAIT)
+ peer.sync_with_ping()
+ assert_equal(peer.tx_getdata_count, 0)
+
+ self.log.info('Check that rejection filter is cleared after new block comes in')
+ self.generate(self.wallet, 1, sync_fun=self.no_op)
+ peer.sync_with_ping()
+ peer.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=int(low_fee_tx['wtxid'], 16))]))
+ node.bumpmocktime(MAX_GETDATA_INBOUND_WAIT)
+ peer.wait_for_getdata([int(low_fee_tx['wtxid'], 16)])
+
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
@@ -257,16 +284,22 @@ class TxDownloadTest(BitcoinTestFramework):
# Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when
# the next trickle relay event happens.
- for test in [self.test_in_flight_max, self.test_inv_block, self.test_tx_requests]:
+ for test, with_inbounds in [
+ (self.test_in_flight_max, True),
+ (self.test_inv_block, True),
+ (self.test_tx_requests, True),
+ (self.test_rejects_filter_reset, False),
+ ]:
self.stop_nodes()
self.start_nodes()
self.connect_nodes(1, 0)
# Setup the p2p connections
self.peers = []
- for node in self.nodes:
- for _ in range(NUM_INBOUND):
- self.peers.append(node.add_p2p_connection(TestP2PConn()))
- self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
+ if with_inbounds:
+ for node in self.nodes:
+ for _ in range(NUM_INBOUND):
+ self.peers.append(node.add_p2p_connection(TestP2PConn()))
+ self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
test()
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 029e368166..37c42f2533 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -18,6 +18,7 @@ from test_framework.util import (
assert_equal,
assert_fee_amount,
assert_raises_rpc_error,
+ fill_mempool,
)
from test_framework.wallet import (
DEFAULT_FEE,
@@ -82,7 +83,8 @@ class RPCPackagesTest(BitcoinTestFramework):
self.test_conflicting()
self.test_rbf()
self.test_submitpackage()
- self.test_maxfeerate_maxburn_submitpackage()
+ self.test_maxfeerate_submitpackage()
+ self.test_maxburn_submitpackage()
def test_independent(self, coin):
self.log.info("Test multiple independent transactions in a package")
@@ -358,7 +360,7 @@ class RPCPackagesTest(BitcoinTestFramework):
assert_equal(res["tx-results"][sec_wtxid]["error"], "version")
peer.wait_for_broadcast([first_wtxid])
- def test_maxfeerate_maxburn_submitpackage(self):
+ def test_maxfeerate_submitpackage(self):
node = self.nodes[0]
# clear mempool
deterministic_address = node.get_deterministic_priv_key().address
@@ -369,23 +371,78 @@ class RPCPackagesTest(BitcoinTestFramework):
minrate_btc_kvb = min([chained_txn["fee"] / chained_txn["tx"].get_vsize() * 1000 for chained_txn in chained_txns])
chain_hex = [t["hex"] for t in chained_txns]
pkg_result = node.submitpackage(chain_hex, maxfeerate=minrate_btc_kvb - Decimal("0.00000001"))
+
+ # First tx failed in single transaction evaluation, so package message is generic
+ assert_equal(pkg_result["package_msg"], "transaction failed")
assert_equal(pkg_result["tx-results"][chained_txns[0]["wtxid"]]["error"], "max feerate exceeded")
assert_equal(pkg_result["tx-results"][chained_txns[1]["wtxid"]]["error"], "bad-txns-inputs-missingorspent")
assert_equal(node.getrawmempool(), [])
+ # Make chain of two transactions where parent doesn't make minfee threshold
+ # but child is too high fee
+ # Lower mempool limit to make it easier to fill_mempool
+ self.restart_node(0, extra_args=[
+ "-datacarriersize=100000",
+ "-maxmempool=5",
+ "-persistmempool=0",
+ ])
+ self.wallet.rescan_utxos()
+
+ fill_mempool(self, node, self.wallet)
+
+ minrelay = node.getmempoolinfo()["minrelaytxfee"]
+ parent = self.wallet.create_self_transfer(
+ fee_rate=minrelay,
+ confirmed_only=True,
+ )
+
+ child = self.wallet.create_self_transfer(
+ fee_rate=DEFAULT_FEE,
+ utxo_to_spend=parent["new_utxo"],
+ )
+
+ pkg_result = node.submitpackage([parent["hex"], child["hex"]], maxfeerate=DEFAULT_FEE - Decimal("0.00000001"))
+
+ # Child is connected even though parent is invalid and still reports fee exceeded
+ # this implies sub-package evaluation of both entries together.
+ assert_equal(pkg_result["package_msg"], "transaction failed")
+ assert "mempool min fee not met" in pkg_result["tx-results"][parent["wtxid"]]["error"]
+ assert_equal(pkg_result["tx-results"][child["wtxid"]]["error"], "max feerate exceeded")
+ assert parent["txid"] not in node.getrawmempool()
+ assert child["txid"] not in node.getrawmempool()
+
+ # Reset maxmempool, datacarriersize, reset dynamic mempool minimum feerate, and empty mempool.
+ self.restart_node(0)
+ self.wallet.rescan_utxos()
+
+ assert_equal(node.getrawmempool(), [])
+
+ def test_maxburn_submitpackage(self):
+ node = self.nodes[0]
+
+ assert_equal(node.getrawmempool(), [])
+
self.log.info("Submitpackage maxburnamount arg testing")
- tx = tx_from_hex(chain_hex[1])
+ chained_txns_burn = self.wallet.create_self_transfer_chain(
+ chain_length=2,
+ utxo_to_spend=self.wallet.get_utxo(confirmed_only=True),
+ )
+ chained_burn_hex = [t["hex"] for t in chained_txns_burn]
+
+ tx = tx_from_hex(chained_burn_hex[1])
tx.vout[-1].scriptPubKey = b'a' * 10001 # scriptPubKey bigger than 10k IsUnspendable
- chain_hex = [chain_hex[0], tx.serialize().hex()]
+ chained_burn_hex = [chained_burn_hex[0], tx.serialize().hex()]
# burn test is run before any package evaluation; nothing makes it in and we get broader exception
- assert_raises_rpc_error(-25, "Unspendable output exceeds maximum configured by user", node.submitpackage, chain_hex, 0, chained_txns[1]["new_utxo"]["value"] - Decimal("0.00000001"))
+ assert_raises_rpc_error(-25, "Unspendable output exceeds maximum configured by user", node.submitpackage, chained_burn_hex, 0, chained_txns_burn[1]["new_utxo"]["value"] - Decimal("0.00000001"))
assert_equal(node.getrawmempool(), [])
+ minrate_btc_kvb_burn = min([chained_txn_burn["fee"] / chained_txn_burn["tx"].get_vsize() * 1000 for chained_txn_burn in chained_txns_burn])
+
# Relax the restrictions for both and send it; parent gets through as own subpackage
- pkg_result = node.submitpackage(chain_hex, maxfeerate=minrate_btc_kvb, maxburnamount=chained_txns[1]["new_utxo"]["value"])
- assert "error" not in pkg_result["tx-results"][chained_txns[0]["wtxid"]]
+ pkg_result = node.submitpackage(chained_burn_hex, maxfeerate=minrate_btc_kvb_burn, maxburnamount=chained_txns_burn[1]["new_utxo"]["value"])
+ assert "error" not in pkg_result["tx-results"][chained_txns_burn[0]["wtxid"]]
assert_equal(pkg_result["tx-results"][tx.getwtxid()]["error"], "scriptpubkey")
- assert_equal(node.getrawmempool(), [chained_txns[0]["txid"]])
+ assert_equal(node.getrawmempool(), [chained_txns_burn[0]["txid"]])
if __name__ == "__main__":
RPCPackagesTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 016aa3ba11..6ee7e56886 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -16,8 +16,6 @@ from test_framework.messages import (
CTxIn,
CTxOut,
MAX_BIP125_RBF_SEQUENCE,
- WITNESS_SCALE_FACTOR,
- ser_compact_size,
)
from test_framework.psbt import (
PSBT,
@@ -42,6 +40,7 @@ from test_framework.util import (
find_vout_for_address,
)
from test_framework.wallet_util import (
+ calculate_input_weight,
generate_keypair,
get_generate_key,
)
@@ -752,17 +751,9 @@ class PSBTTest(BitcoinTestFramework):
input_idx = i
break
psbt_in = dec["inputs"][input_idx]
- # Calculate the input weight
- # (prevout + sequence + length of scriptSig + scriptsig) * WITNESS_SCALE_FACTOR + len of num scriptWitness stack items + (length of stack item + stack item) * N stack items
- # Note that occasionally this weight estimate may be slightly larger or smaller than the real weight
- # as sometimes ECDSA signatures are one byte shorter than expected with a probability of 1/128
- len_scriptsig = len(psbt_in["final_scriptSig"]["hex"]) // 2 if "final_scriptSig" in psbt_in else 0
- len_scriptsig += len(ser_compact_size(len_scriptsig))
- len_scriptwitness = (sum([(len(x) // 2) + len(ser_compact_size(len(x) // 2)) for x in psbt_in["final_scriptwitness"]]) + len(ser_compact_size(len(psbt_in["final_scriptwitness"])))) if "final_scriptwitness" in psbt_in else 0
- len_prevout_txid = 32
- len_prevout_index = 4
- len_sequence = 4
- input_weight = ((len_prevout_txid + len_prevout_index + len_sequence + len_scriptsig) * WITNESS_SCALE_FACTOR) + len_scriptwitness
+ scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
+ witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
+ input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
low_input_weight = input_weight // 2
high_input_weight = input_weight * 2
@@ -886,7 +877,7 @@ class PSBTTest(BitcoinTestFramework):
assert_equal(comb_psbt, psbt)
self.log.info("Test walletprocesspsbt raises if an invalid sighashtype is passed")
- assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].walletprocesspsbt, psbt, sighashtype="all")
+ assert_raises_rpc_error(-8, "'all' is not a valid sighash parameter.", self.nodes[0].walletprocesspsbt, psbt, sighashtype="all")
self.log.info("Test decoding PSBT with per-input preimage types")
# note that the decodepsbt RPC doesn't check whether preimages and hashes match
@@ -992,7 +983,7 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[2].sendrawtransaction(processed_psbt['hex'])
self.log.info("Test descriptorprocesspsbt raises if an invalid sighashtype is passed")
- assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[2].descriptorprocesspsbt, psbt, [descriptor], sighashtype="all")
+ assert_raises_rpc_error(-8, "'all' is not a valid sighash parameter.", self.nodes[2].descriptorprocesspsbt, psbt, [descriptor], sighashtype="all")
if __name__ == '__main__':
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
index bc426d7371..ba86b278bd 100755
--- a/test/functional/rpc_setban.py
+++ b/test/functional/rpc_setban.py
@@ -64,20 +64,10 @@ class SetBanTests(BitcoinTestFramework):
assert self.is_banned(node, tor_addr)
assert not self.is_banned(node, ip_addr)
- self.log.info("Test the ban list is preserved through restart")
-
- self.restart_node(1)
- assert self.is_banned(node, tor_addr)
- assert not self.is_banned(node, ip_addr)
-
node.setban(tor_addr, "remove")
assert not self.is_banned(self.nodes[1], tor_addr)
assert not self.is_banned(node, ip_addr)
- self.restart_node(1)
- assert not self.is_banned(node, tor_addr)
- assert not self.is_banned(node, ip_addr)
-
self.log.info("Test -bantime")
self.restart_node(1, ["-bantime=1234"])
self.nodes[1].setban("127.0.0.1", "add")
diff --git a/test/functional/rpc_signrawtransactionwithkey.py b/test/functional/rpc_signrawtransactionwithkey.py
index 0913f5057e..268584331e 100755
--- a/test/functional/rpc_signrawtransactionwithkey.py
+++ b/test/functional/rpc_signrawtransactionwithkey.py
@@ -124,7 +124,7 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
self.log.info("Test signing transaction with invalid sighashtype")
tx = self.nodes[0].createrawtransaction(INPUTS, OUTPUTS)
privkeys = [self.nodes[0].get_deterministic_priv_key().key]
- assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithkey, tx, privkeys, sighashtype="all")
+ assert_raises_rpc_error(-8, "'all' is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithkey, tx, privkeys, sighashtype="all")
def run_test(self):
self.successful_signing_test()
diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py
index cb99e483ec..f8df59d02a 100755
--- a/test/functional/rpc_uptime.py
+++ b/test/functional/rpc_uptime.py
@@ -23,7 +23,7 @@ class UptimeTest(BitcoinTestFramework):
self._test_uptime()
def _test_negative_time(self):
- assert_raises_rpc_error(-8, "Mocktime cannot be negative: -1.", self.nodes[0].setmocktime, -1)
+ assert_raises_rpc_error(-8, "Mocktime must be in the range [0, 9223372036], not -1.", self.nodes[0].setmocktime, -1)
def _test_uptime(self):
wait_time = 10
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index c3884270da..a2f767cc98 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -164,7 +164,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="Don't stop bitcoinds after the test execution")
parser.add_argument("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
help="Directory for caching pregenerated datadirs (default: %(default)s)")
- parser.add_argument("--tmpdir", dest="tmpdir", help="Root directory for datadirs")
+ parser.add_argument("--tmpdir", dest="tmpdir", help="Root directory for datadirs (must not exist)")
parser.add_argument("-l", "--loglevel", dest="loglevel", default="INFO",
help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.")
parser.add_argument("--tracerpc", dest="trace_rpc", default=False, action="store_true",
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index c5b69a3954..0de09b6440 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -496,6 +496,65 @@ def check_node_connections(*, node, num_in, num_out):
assert_equal(info["connections_in"], num_in)
assert_equal(info["connections_out"], num_out)
+def fill_mempool(test_framework, node, miniwallet):
+ """Fill mempool until eviction.
+
+ Allows for simpler testing of scenarios with floating mempoolminfee > minrelay
+ Requires -datacarriersize=100000 and
+ -maxmempool=5.
+ It will not ensure mempools become synced as it
+ is based on a single node and assumes -minrelaytxfee
+ is 1 sat/vbyte.
+ To avoid unintentional tx dependencies, it is recommended to use separate miniwallets for
+ mempool filling vs transactions in tests.
+ """
+ test_framework.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises")
+ txouts = gen_return_txouts()
+ relayfee = node.getnetworkinfo()['relayfee']
+
+ assert_equal(relayfee, Decimal('0.00001000'))
+
+ tx_batch_size = 1
+ num_of_batches = 75
+ # Generate UTXOs to flood the mempool
+ # 1 to create a tx initially that will be evicted from the mempool later
+ # 75 transactions each with a fee rate higher than the previous one
+ test_framework.generate(miniwallet, 1 + (num_of_batches * tx_batch_size))
+
+ # Mine COINBASE_MATURITY - 1 blocks so that the UTXOs are allowed to be spent
+ test_framework.generate(node, 100 - 1)
+
+ # Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
+ # change their effective feerate and thus the order in which they are selected for eviction.
+ confirmed_utxos = [miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 1)]
+ assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 1)
+
+ test_framework.log.debug("Create a mempool tx that will be evicted")
+ tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, utxo_to_spend=confirmed_utxos[0], fee_rate=relayfee)["txid"]
+ del confirmed_utxos[0]
+
+ # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
+ # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
+ # by 130 should result in a fee that corresponds to 2x of that fee rate
+ base_fee = relayfee * 130
+
+ test_framework.log.debug("Fill up the mempool with txs with higher fee rate")
+ with node.assert_debug_log(["rolling minimum fee bumped"]):
+ for batch_of_txid in range(num_of_batches):
+ fee = (batch_of_txid + 1) * base_fee
+ utxos = confirmed_utxos[:tx_batch_size]
+ create_lots_of_big_transactions(miniwallet, node, fee, tx_batch_size, txouts, utxos)
+ del confirmed_utxos[:tx_batch_size]
+
+ test_framework.log.debug("The tx should be evicted by now")
+ # The number of transactions created should be greater than the ones present in the mempool
+ assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
+ # Initial tx created should not be present in the mempool anymore as it had a lower fee rate
+ assert tx_to_be_evicted_id not in node.getrawmempool()
+
+ test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
+ assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
# Transaction/Block functions
#############################
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 44811918bf..d30b00f4a7 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Useful util functions for testing the wallet"""
from collections import namedtuple
+import unittest
from test_framework.address import (
byte_to_base58,
@@ -15,6 +16,11 @@ from test_framework.address import (
script_to_p2wsh,
)
from test_framework.key import ECKey
+from test_framework.messages import (
+ CTxIn,
+ CTxInWitness,
+ WITNESS_SCALE_FACTOR,
+)
from test_framework.script_util import (
key_to_p2pkh_script,
key_to_p2wpkh_script,
@@ -123,6 +129,19 @@ def generate_keypair(compressed=True, wif=False):
privkey = bytes_to_wif(privkey.get_bytes(), compressed)
return privkey, pubkey
+def calculate_input_weight(scriptsig_hex, witness_stack_hex=None):
+ """Given a scriptSig and a list of witness stack items for an input in hex format,
+ calculate the total input weight. If the input has no witness data,
+ `witness_stack_hex` can be set to None."""
+ tx_in = CTxIn(scriptSig=bytes.fromhex(scriptsig_hex))
+ witness_size = 0
+ if witness_stack_hex is not None:
+ tx_inwit = CTxInWitness()
+ for witness_item_hex in witness_stack_hex:
+ tx_inwit.scriptWitness.stack.append(bytes.fromhex(witness_item_hex))
+ witness_size = len(tx_inwit.serialize())
+ return len(tx_in.serialize()) * WITNESS_SCALE_FACTOR + witness_size
+
class WalletUnlock():
"""
A context manager for unlocking a wallet with a passphrase and automatically locking it afterward.
@@ -141,3 +160,42 @@ class WalletUnlock():
def __exit__(self, *args):
_ = args
self.wallet.walletlock()
+
+
+class TestFrameworkWalletUtil(unittest.TestCase):
+ def test_calculate_input_weight(self):
+ SKELETON_BYTES = 32 + 4 + 4 # prevout-txid, prevout-index, sequence
+ SMALL_LEN_BYTES = 1 # bytes needed for encoding scriptSig / witness item lenghts < 253
+ LARGE_LEN_BYTES = 3 # bytes needed for encoding scriptSig / witness item lengths >= 253
+
+ # empty scriptSig, no witness
+ self.assertEqual(calculate_input_weight(""),
+ (SKELETON_BYTES + SMALL_LEN_BYTES) * WITNESS_SCALE_FACTOR)
+ self.assertEqual(calculate_input_weight("", None),
+ (SKELETON_BYTES + SMALL_LEN_BYTES) * WITNESS_SCALE_FACTOR)
+ # small scriptSig, no witness
+ scriptSig_small = "00"*252
+ self.assertEqual(calculate_input_weight(scriptSig_small, None),
+ (SKELETON_BYTES + SMALL_LEN_BYTES + 252) * WITNESS_SCALE_FACTOR)
+ # small scriptSig, empty witness stack
+ self.assertEqual(calculate_input_weight(scriptSig_small, []),
+ (SKELETON_BYTES + SMALL_LEN_BYTES + 252) * WITNESS_SCALE_FACTOR + SMALL_LEN_BYTES)
+ # large scriptSig, no witness
+ scriptSig_large = "00"*253
+ self.assertEqual(calculate_input_weight(scriptSig_large, None),
+ (SKELETON_BYTES + LARGE_LEN_BYTES + 253) * WITNESS_SCALE_FACTOR)
+ # large scriptSig, empty witness stack
+ self.assertEqual(calculate_input_weight(scriptSig_large, []),
+ (SKELETON_BYTES + LARGE_LEN_BYTES + 253) * WITNESS_SCALE_FACTOR + SMALL_LEN_BYTES)
+ # empty scriptSig, 5 small witness stack items
+ self.assertEqual(calculate_input_weight("", ["00", "11", "22", "33", "44"]),
+ ((SKELETON_BYTES + SMALL_LEN_BYTES) * WITNESS_SCALE_FACTOR) + SMALL_LEN_BYTES + 5 * SMALL_LEN_BYTES + 5)
+ # empty scriptSig, 253 small witness stack items
+ self.assertEqual(calculate_input_weight("", ["00"]*253),
+ ((SKELETON_BYTES + SMALL_LEN_BYTES) * WITNESS_SCALE_FACTOR) + LARGE_LEN_BYTES + 253 * SMALL_LEN_BYTES + 253)
+ # small scriptSig, 3 large witness stack items
+ self.assertEqual(calculate_input_weight(scriptSig_small, ["00"*253]*3),
+ ((SKELETON_BYTES + SMALL_LEN_BYTES + 252) * WITNESS_SCALE_FACTOR) + SMALL_LEN_BYTES + 3 * LARGE_LEN_BYTES + 3*253)
+ # large scriptSig, 3 large witness stack items
+ self.assertEqual(calculate_input_weight(scriptSig_large, ["00"*253]*3),
+ ((SKELETON_BYTES + LARGE_LEN_BYTES + 253) * WITNESS_SCALE_FACTOR) + SMALL_LEN_BYTES + 3 * LARGE_LEN_BYTES + 3*253)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 3f6e47d410..32b55813a8 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -85,6 +85,7 @@ TEST_FRAMEWORK_MODULES = [
"crypto.ripemd160",
"script",
"segwit_addr",
+ "wallet_util",
]
EXTENDED_SCRIPTS = [
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index 3b407c285d..26477131cf 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -42,11 +42,6 @@ class WalletGroupTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Setting up")
- # To take full use of immediate tx relay, all nodes need to be reachable
- # via inbound peers, i.e. connect first to last to close the circle
- # (the default test network topology looks like this:
- # node0 <-- node1 <-- node2 <-- node3 <-- node4 <-- node5)
- self.connect_nodes(0, self.num_nodes - 1)
# Mine some coins
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 420bdffc49..f9d05a2fe4 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -688,7 +688,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
encrypted_wallet.walletpassphrase("passphrase", 99999)
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
- with self.nodes[0].assert_debug_log(expected_msgs=["Rescan started from block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206... (slow variant inspecting all blocks)"], timeout=5):
+ with self.nodes[0].assert_debug_log(expected_msgs=["Rescan started from block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206... (slow variant inspecting all blocks)"], timeout=10):
importing = thread.submit(encrypted_wallet.importdescriptors, requests=[descriptor])
# Set the passphrase timeout to 1 to test that the wallet remains unlocked during the rescan
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index e4ca341b49..0a0a8dba0d 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -9,10 +9,6 @@ from itertools import product
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create
-from test_framework.messages import (
- ser_compact_size,
- WITNESS_SCALE_FACTOR,
-)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -21,7 +17,10 @@ from test_framework.util import (
assert_raises_rpc_error,
count_bytes,
)
-from test_framework.wallet_util import generate_keypair
+from test_framework.wallet_util import (
+ calculate_input_weight,
+ generate_keypair,
+)
class WalletSendTest(BitcoinTestFramework):
@@ -543,17 +542,9 @@ class WalletSendTest(BitcoinTestFramework):
input_idx = i
break
psbt_in = dec["inputs"][input_idx]
- # Calculate the input weight
- # (prevout + sequence + length of scriptSig + scriptsig) * WITNESS_SCALE_FACTOR + len of num scriptWitness stack items + (length of stack item + stack item) * N stack items
- # Note that occasionally this weight estimate may be slightly larger or smaller than the real weight
- # as sometimes ECDSA signatures are one byte shorter than expected with a probability of 1/128
- len_scriptsig = len(psbt_in["final_scriptSig"]["hex"]) // 2 if "final_scriptSig" in psbt_in else 0
- len_scriptsig += len(ser_compact_size(len_scriptsig))
- len_scriptwitness = (sum([(len(x) // 2) + len(ser_compact_size(len(x) // 2)) for x in psbt_in["final_scriptwitness"]]) + len(ser_compact_size(len(psbt_in["final_scriptwitness"])))) if "final_scriptwitness" in psbt_in else 0
- len_prevout_txid = 32
- len_prevout_index = 4
- len_sequence = 4
- input_weight = ((len_prevout_txid + len_prevout_index + len_sequence + len_scriptsig) * WITNESS_SCALE_FACTOR) + len_scriptwitness
+ scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
+ witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
+ input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
# Input weight error conditions
assert_raises_rpc_error(
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index 32a1887153..abfc3c1ba1 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -130,8 +130,9 @@ class WalletSignerTest(BitcoinTestFramework):
assert_equal(address_info['hdkeypath'], "m/86h/1h/0h/0/0")
self.log.info('Test walletdisplayaddress')
- result = hww.walletdisplayaddress(address1)
- assert_equal(result, {"address": address1})
+ for address in [address1, address2, address3]:
+ result = hww.walletdisplayaddress(address)
+ assert_equal(result, {"address": address})
# Handle error thrown by script
self.set_mock_result(self.nodes[1], "2")
@@ -140,6 +141,13 @@ class WalletSignerTest(BitcoinTestFramework):
)
self.clear_mock_result(self.nodes[1])
+ # Returned address MUST match:
+ address_fail = hww.getnewaddress(address_type="bech32")
+ assert_equal(address_fail, "bcrt1ql7zg7ukh3dwr25ex2zn9jse926f27xy2jz58tm")
+ assert_raises_rpc_error(-1, 'Signer echoed unexpected address wrong_address',
+ hww.walletdisplayaddress, address_fail
+ )
+
self.log.info('Prepare mock PSBT')
self.nodes[0].sendtoaddress(address4, 1)
self.generate(self.nodes[0], 1)
diff --git a/test/functional/wallet_signrawtransactionwithwallet.py b/test/functional/wallet_signrawtransactionwithwallet.py
index b0517f951d..612a2542e7 100755
--- a/test/functional/wallet_signrawtransactionwithwallet.py
+++ b/test/functional/wallet_signrawtransactionwithwallet.py
@@ -55,7 +55,7 @@ class SignRawTransactionWithWalletTest(BitcoinTestFramework):
def test_with_invalid_sighashtype(self):
self.log.info("Test signrawtransactionwithwallet raises if an invalid sighashtype is passed")
- assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithwallet, hexstring=RAW_TX, sighashtype="all")
+ assert_raises_rpc_error(-8, "'all' is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithwallet, hexstring=RAW_TX, sighashtype="all")
def script_verification_error_test(self):
"""Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script.
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index b3edb0e253..558d63e85c 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -104,9 +104,11 @@ def main():
logging.error("Must have fuzz executable built")
sys.exit(1)
+ fuzz_bin=os.getenv("BITCOINFUZZ", default=os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', 'fuzz'))
+
# Build list of tests
test_list_all = parse_test_list(
- fuzz_bin=os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', 'fuzz'),
+ fuzz_bin=fuzz_bin,
source_dir=config['environment']['SRCDIR'],
)
@@ -151,7 +153,7 @@ def main():
try:
help_output = subprocess.run(
args=[
- os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', 'fuzz'),
+ fuzz_bin,
'-help=1',
],
env=get_fuzz_env(target=test_list_selection[0], source_dir=config['environment']['SRCDIR']),
@@ -173,7 +175,7 @@ def main():
return generate_corpus(
fuzz_pool=fuzz_pool,
src_dir=config['environment']['SRCDIR'],
- build_dir=config["environment"]["BUILDDIR"],
+ fuzz_bin=fuzz_bin,
corpus_dir=args.corpus_dir,
targets=test_list_selection,
)
@@ -184,7 +186,7 @@ def main():
corpus=args.corpus_dir,
test_list=test_list_selection,
src_dir=config['environment']['SRCDIR'],
- build_dir=config["environment"]["BUILDDIR"],
+ fuzz_bin=fuzz_bin,
merge_dirs=[Path(m_dir) for m_dir in args.m_dir],
)
return
@@ -194,7 +196,7 @@ def main():
corpus=args.corpus_dir,
test_list=test_list_selection,
src_dir=config['environment']['SRCDIR'],
- build_dir=config["environment"]["BUILDDIR"],
+ fuzz_bin=fuzz_bin,
using_libfuzzer=using_libfuzzer,
use_valgrind=args.valgrind,
empty_min_time=args.empty_min_time,
@@ -237,7 +239,7 @@ def transform_rpc_target(targets, src_dir):
return targets
-def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
+def generate_corpus(*, fuzz_pool, src_dir, fuzz_bin, corpus_dir, targets):
"""Generates new corpus.
Run {targets} without input, and outputs the generated corpus to
@@ -270,7 +272,7 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
os.makedirs(target_corpus_dir, exist_ok=True)
use_value_profile = int(random.random() < .3)
command = [
- os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
+ fuzz_bin,
"-rss_limit_mb=8000",
"-max_total_time=6000",
"-reload=0",
@@ -283,12 +285,12 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
future.result()
-def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dirs):
+def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, fuzz_bin, merge_dirs):
logging.info(f"Merge the inputs from the passed dir into the corpus_dir. Passed dirs {merge_dirs}")
jobs = []
for t in test_list:
args = [
- os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
+ fuzz_bin,
'-rss_limit_mb=8000',
'-set_cover_merge=1',
# set_cover_merge is used instead of -merge=1 to reduce the overall
@@ -325,13 +327,13 @@ def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dirs
future.result()
-def run_once(*, fuzz_pool, corpus, test_list, src_dir, build_dir, using_libfuzzer, use_valgrind, empty_min_time):
+def run_once(*, fuzz_pool, corpus, test_list, src_dir, fuzz_bin, using_libfuzzer, use_valgrind, empty_min_time):
jobs = []
for t in test_list:
corpus_path = corpus / t
os.makedirs(corpus_path, exist_ok=True)
args = [
- os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
+ fuzz_bin,
]
empty_dir = not any(corpus_path.iterdir())
if using_libfuzzer:
diff --git a/test/lint/lint-includes.py b/test/lint/lint-includes.py
index 81ed4c0840..90884299d5 100755
--- a/test/lint/lint-includes.py
+++ b/test/lint/lint-includes.py
@@ -30,7 +30,6 @@ EXPECTED_BOOST_INCLUDES = ["boost/date_time/posix_time/posix_time.hpp",
"boost/multi_index/tag.hpp",
"boost/multi_index_container.hpp",
"boost/operators.hpp",
- "boost/process.hpp",
"boost/signals2/connection.hpp",
"boost/signals2/optional_last_value.hpp",
"boost/signals2/signal.hpp",