aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/test/06_script_b.sh1
-rw-r--r--configure.ac4
-rwxr-xr-xcontrib/devtools/security-check.py2
-rwxr-xr-xcontrib/devtools/symbol-check.py22
-rwxr-xr-xcontrib/devtools/test-symbol-check.py25
-rwxr-xr-xcontrib/guix/libexec/build.sh7
-rw-r--r--contrib/guix/manifest.scm51
-rw-r--r--contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch62
-rw-r--r--contrib/guix/patches/glibc-2.24-guix-prefix.patch25
-rw-r--r--contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch100
-rw-r--r--contrib/guix/patches/glibc-2.27-dont-redefine-nss-database.patch87
-rw-r--r--contrib/guix/patches/glibc-2.27-fcommon.patch (renamed from contrib/guix/patches/glibc-2.24-fcommon.patch)10
-rw-r--r--contrib/guix/patches/glibc-2.27-guix-prefix.patch3
-rw-r--r--contrib/guix/patches/glibc-ldd-x86_64.patch4
-rwxr-xr-xcontrib/signet/getcoins.py2
-rw-r--r--contrib/verify-commits/trusted-git-root2
-rw-r--r--contrib/verify-commits/trusted-keys1
-rw-r--r--doc/dependencies.md2
-rw-r--r--doc/developer-notes.md15
-rw-r--r--doc/release-notes-25574.md13
-rw-r--r--doc/release-notes-25943.md4
-rw-r--r--doc/release-notes-27068.md6
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.test_util.include3
-rw-r--r--src/addrman.cpp22
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/wallet_balance.cpp2
-rw-r--r--src/bench/wallet_create_tx.cpp4
-rw-r--r--src/bitcoin-chainstate.cpp1
-rw-r--r--src/blockencodings.h2
-rw-r--r--src/dbwrapper.cpp32
-rw-r--r--src/dbwrapper.h33
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/httpserver.cpp6
-rw-r--r--src/i2p.cpp17
-rw-r--r--src/index/base.cpp9
-rw-r--r--src/init.cpp14
-rw-r--r--src/kernel/chainstatemanager_opts.h6
-rw-r--r--src/kernel/cs_main.cpp1
-rw-r--r--src/mapport.cpp4
-rw-r--r--src/net.cpp87
-rw-r--r--src/net.h33
-rw-r--r--src/net_processing.cpp10
-rw-r--r--src/netaddress.cpp48
-rw-r--r--src/netaddress.h9
-rw-r--r--src/netbase.cpp16
-rw-r--r--src/node/chainstate.cpp28
-rw-r--r--src/node/chainstate.h9
-rw-r--r--src/node/chainstatemanager_args.cpp7
-rw-r--r--src/node/coins_view_args.cpp16
-rw-r--r--src/node/coins_view_args.h15
-rw-r--r--src/node/database_args.cpp18
-rw-r--r--src/node/database_args.h15
-rw-r--r--src/node/miner.cpp39
-rw-r--r--src/node/miner.h19
-rw-r--r--src/qt/askpassphrasedialog.cpp38
-rw-r--r--src/qt/bitcoin.cpp1
-rw-r--r--src/qt/clientmodel.cpp2
-rw-r--r--src/qt/optionsdialog.cpp31
-rw-r--r--src/qt/optionsmodel.cpp6
-rw-r--r--src/qt/optionsmodel.h3
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/random.cpp19
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mempool.cpp23
-rw-r--r--src/rpc/net.cpp10
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/rawtransaction_util.cpp43
-rw-r--r--src/rpc/rawtransaction_util.h7
-rw-r--r--src/script/interpreter.cpp2
-rw-r--r--src/support/allocators/secure.h1
-rw-r--r--src/support/lockedpool.cpp17
-rw-r--r--src/support/lockedpool.h10
-rw-r--r--src/test/addrman_tests.cpp42
-rw-r--r--src/test/base58_tests.cpp1
-rw-r--r--src/test/blockencodings_tests.cpp1
-rw-r--r--src/test/bloom_tests.cpp1
-rw-r--r--src/test/checkqueue_tests.cpp1
-rw-r--r--src/test/coins_tests.cpp7
-rw-r--r--src/test/crypto_tests.cpp1
-rw-r--r--src/test/cuckoocache_tests.cpp2
-rw-r--r--src/test/dbwrapper_tests.cpp23
-rw-r--r--src/test/fuzz/http_request.cpp2
-rw-r--r--src/test/fuzz/miniscript.cpp5
-rw-r--r--src/test/fuzz/netaddress.cpp7
-rw-r--r--src/test/hash_tests.cpp1
-rw-r--r--src/test/key_tests.cpp1
-rw-r--r--src/test/merkle_tests.cpp1
-rw-r--r--src/test/miner_tests.cpp1
-rw-r--r--src/test/minisketch_tests.cpp1
-rw-r--r--src/test/net_tests.cpp34
-rw-r--r--src/test/netbase_tests.cpp6
-rw-r--r--src/test/orphanage_tests.cpp1
-rw-r--r--src/test/pmt_tests.cpp1
-rw-r--r--src/test/pow_tests.cpp1
-rw-r--r--src/test/prevector_tests.cpp1
-rw-r--r--src/test/script_tests.cpp1
-rw-r--r--src/test/serfloat_tests.cpp1
-rw-r--r--src/test/sighash_tests.cpp3
-rw-r--r--src/test/skiplist_tests.cpp1
-rw-r--r--src/test/streams_tests.cpp1
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/txpackage_tests.cpp1
-rw-r--r--src/test/txrequest_tests.cpp1
-rw-r--r--src/test/util/blockfilter.cpp1
-rw-r--r--src/test/util/coins.cpp27
-rw-r--r--src/test/util/coins.h19
-rw-r--r--src/test/util/random.h45
-rw-r--r--src/test/util/setup_common.cpp7
-rw-r--r--src/test/util/setup_common.h6
-rw-r--r--src/test/util_tests.cpp1
-rw-r--r--src/test/validation_block_tests.cpp1
-rw-r--r--src/test/validation_chainstate_tests.cpp18
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp2
-rw-r--r--src/test/validation_flush_tests.cpp27
-rw-r--r--src/test/versionbits_tests.cpp1
-rw-r--r--src/torcontrol.cpp6
-rw-r--r--src/txdb.cpp26
-rw-r--r--src/txdb.h21
-rw-r--r--src/util/error.cpp2
-rw-r--r--src/util/error.h1
-rw-r--r--src/util/system.cpp41
-rw-r--r--src/util/system.h12
-rw-r--r--src/validation.cpp66
-rw-r--r--src/validation.h12
-rw-r--r--src/wallet/dump.cpp3
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h8
-rw-r--r--src/wallet/feebumper.cpp21
-rw-r--r--src/wallet/feebumper.h3
-rw-r--r--src/wallet/interfaces.cpp3
-rw-r--r--src/wallet/rpc/backup.cpp9
-rw-r--r--src/wallet/rpc/encrypt.cpp63
-rw-r--r--src/wallet/rpc/spend.cpp69
-rw-r--r--src/wallet/rpc/transactions.cpp5
-rw-r--r--src/wallet/rpc/util.cpp1
-rw-r--r--src/wallet/rpc/wallet.cpp33
-rw-r--r--src/wallet/salvage.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.cpp11
-rw-r--r--src/wallet/scriptpubkeyman.h16
-rw-r--r--src/wallet/spend.cpp7
-rw-r--r--src/wallet/test/coinselector_tests.cpp28
-rw-r--r--src/wallet/test/ismine_tests.cpp76
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/spend_tests.cpp4
-rw-r--r--src/wallet/test/util.cpp4
-rw-r--r--src/wallet/test/util.h2
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp1
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp24
-rw-r--r--src/wallet/test/walletload_tests.cpp12
-rw-r--r--src/wallet/wallet.cpp84
-rw-r--r--src/wallet/wallet.h27
-rw-r--r--src/wallet/wallettool.cpp10
-rw-r--r--test/README.md41
-rwxr-xr-xtest/functional/feature_coinstatsindex.py5
-rwxr-xr-xtest/functional/feature_config_args.py32
-rwxr-xr-xtest/functional/feature_dbcrash.py2
-rwxr-xr-xtest/functional/feature_pruning.py4
-rwxr-xr-xtest/functional/feature_taproot.py18
-rwxr-xr-xtest/functional/p2p_disconnect_ban.py4
-rwxr-xr-xtest/functional/p2p_node_network_limited.py2
-rwxr-xr-xtest/functional/rpc_decodescript.py3
-rwxr-xr-xtest/functional/rpc_preciousblock.py2
-rwxr-xr-xtest/functional/rpc_rawtransaction.py59
-rw-r--r--test/functional/test_framework/authproxy.py5
-rwxr-xr-xtest/functional/test_framework/p2p.py2
-rwxr-xr-xtest/functional/test_framework/test_framework.py6
-rwxr-xr-xtest/functional/test_framework/test_node.py2
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_bumpfee.py22
-rwxr-xr-xtest/functional/wallet_change_address.py108
-rwxr-xr-xtest/functional/wallet_encryption.py11
-rwxr-xr-xtest/functional/wallet_importdescriptors.py42
-rwxr-xr-xtest/functional/wallet_migration.py70
-rwxr-xr-xtest/functional/wallet_transactiontime_rescan.py39
-rwxr-xr-xtest/lint/lint-locale-dependence.py2
-rwxr-xr-xtest/lint/lint-python.py1
-rwxr-xr-xtest/util/test_runner.py4
181 files changed, 1643 insertions, 1078 deletions
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 075524741a..115d727ca3 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -60,6 +60,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
" src/rpc/signmessage.cpp"\
" src/test/fuzz/txorphan.cpp"\
" src/test/fuzz/util/"\
+ " src/test/util/coins.cpp"\
" src/uint256.cpp"\
" src/util/bip32.cpp"\
" src/util/bytevectorhash.cpp"\
diff --git a/configure.ac b/configure.ac
index 0f809169fc..72503f2b1c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -963,11 +963,11 @@ if test "$use_hardening" != "no"; then
dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning.
dnl Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes.
if test "$enable_debug" != "yes"; then
- AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[
+ AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=3],[
AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[
HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -U_FORTIFY_SOURCE"
])
- HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=2"
+ HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=3"
])
fi
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 8377b92736..6cd022ef17 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -34,7 +34,7 @@ def check_ELF_RELRO(binary) -> bool:
flags = binary.get(lief.ELF.DYNAMIC_TAGS.FLAGS)
if flags.value & lief.ELF.DYNAMIC_FLAGS.BIND_NOW:
have_bindnow = True
- except:
+ except Exception:
have_bindnow = False
return have_gnu_relro and have_bindnow
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 4b1cceb57c..f26236dd59 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -15,19 +15,19 @@ from typing import List, Dict
import lief #type:ignore
-# Debian 9 (Stretch) EOL: 2022. https://wiki.debian.org/DebianReleases#Production_Releases
+# Debian 10 (Buster) EOL: 2024. https://wiki.debian.org/LTS
#
-# - g++ version 6.3.0 (https://packages.debian.org/search?suite=stretch&arch=any&searchon=names&keywords=g%2B%2B)
-# - libc version 2.24 (https://packages.debian.org/search?suite=stretch&arch=any&searchon=names&keywords=libc6)
+# - libgcc version 8.3.0 (https://packages.debian.org/search?suite=buster&arch=any&searchon=names&keywords=libgcc1)
+# - libc version 2.28 (https://packages.debian.org/search?suite=buster&arch=any&searchon=names&keywords=libc6)
#
-# Ubuntu 16.04 (Xenial) EOL: 2026. https://wiki.ubuntu.com/Releases
+# Ubuntu 18.04 (Bionic) EOL: 2028. https://wiki.ubuntu.com/ReleaseTeam
#
-# - g++ version 5.3.1
-# - libc version 2.23
+# - libgcc version 8.4.0 (https://packages.ubuntu.com/bionic/libgcc1)
+# - libc version 2.27 (https://packages.ubuntu.com/bionic/libc6)
#
# CentOS Stream 8 EOL: 2024. https://wiki.centos.org/About/Product
#
-# - g++ version 8.5.0 (http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/Packages/)
+# - libgcc version 8.5.0 (http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/Packages/)
# - libc version 2.28 (http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/Packages/)
#
# See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for more info.
@@ -35,10 +35,10 @@ import lief #type:ignore
MAX_VERSIONS = {
'GCC': (4,8,0),
'GLIBC': {
- lief.ELF.ARCH.x86_64: (2,18),
- lief.ELF.ARCH.ARM: (2,18),
- lief.ELF.ARCH.AARCH64:(2,18),
- lief.ELF.ARCH.PPC64: (2,18),
+ lief.ELF.ARCH.x86_64: (2,27),
+ lief.ELF.ARCH.ARM: (2,27),
+ lief.ELF.ARCH.AARCH64:(2,27),
+ lief.ELF.ARCH.PPC64: (2,27),
lief.ELF.ARCH.RISCV: (2,27),
},
'LIBATOMIC': (1,0),
diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py
index de73b02090..e304880140 100755
--- a/contrib/devtools/test-symbol-check.py
+++ b/contrib/devtools/test-symbol-check.py
@@ -38,31 +38,6 @@ class TestSymbolChecks(unittest.TestCase):
executable = 'test1'
cc = determine_wellknown_cmd('CC', 'gcc')
- # there's no way to do this test for RISC-V at the moment; we build for
- # RISC-V in a glibc 2.27 environment and we allow all symbols from 2.27.
- if 'riscv' in get_machine(cc):
- self.skipTest("test not available for RISC-V")
-
- # nextup was introduced in GLIBC 2.24, so is newer than our supported
- # glibc (2.18), and available in our release build environment (2.24).
- with open(source, 'w', encoding="utf8") as f:
- f.write('''
- #define _GNU_SOURCE
- #include <math.h>
-
- double nextup(double x);
-
- int main()
- {
- nextup(3.14);
- return 0;
- }
- ''')
-
- self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']),
- (1, executable + ': symbol nextup from unsupported version GLIBC_2.24(3)\n' +
- executable + ': failed IMPORTED_SYMBOLS'))
-
# -lutil is part of the libc6 package so a safe bet that it's installed
# it's also out of context enough that it's unlikely to ever become a real dependency
source = 'test2.c'
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index f2be3677eb..08a6c72a95 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -238,13 +238,6 @@ case "$HOST" in
*mingw*) HOST_LDFLAGS="-Wl,--no-insert-timestamp" ;;
esac
-# Using --no-tls-get-addr-optimize retains compatibility with glibc 2.18, by
-# avoiding a PowerPC64 optimisation available in glibc 2.22 and later.
-# https://sourceware.org/binutils/docs-2.35/ld/PowerPC64-ELF64.html
-case "$HOST" in
- *powerpc64*) HOST_LDFLAGS="${HOST_LDFLAGS} -Wl,--no-tls-get-addr-optimize" ;;
-esac
-
# Make $HOST-specific native binaries from depends available in $PATH
export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
mkdir -p "$DISTSRC"
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 8e5c89cc5e..379ad898c4 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -147,7 +147,7 @@ chain for " target " development."))
#:key
(base-gcc-for-libc base-gcc)
(base-kernel-headers base-linux-kernel-headers)
- (base-libc (make-glibc-with-bind-now (make-glibc-without-werror glibc-2.24)))
+ (base-libc (hardened-glibc (make-glibc-without-werror glibc-2.27)))
(base-gcc (make-gcc-rpath-link (hardened-gcc base-gcc))))
"Convenience wrapper around MAKE-CROSS-TOOLCHAIN with default values
desirable for building Bitcoin Core release binaries."
@@ -537,33 +537,14 @@ inspecting signatures in Mach-O binaries.")
(define (make-glibc-without-werror glibc)
(package-with-extra-configure-variable glibc "enable_werror" "no"))
-(define (make-glibc-with-stack-protector glibc)
- (package-with-extra-configure-variable glibc "--enable-stack-protector" "all"))
-
-(define (make-glibc-with-bind-now glibc)
- (package-with-extra-configure-variable glibc "--enable-bind-now" "yes"))
-
-(define-public glibc-2.24
- (package
- (inherit glibc-2.31)
- (version "2.24")
- (source (origin
- (method git-fetch)
- (uri (git-reference
- (url "https://sourceware.org/git/glibc.git")
- (commit "0d7f1ed30969886c8dde62fbf7d2c79967d4bace")))
- (file-name (git-file-name "glibc" "0d7f1ed30969886c8dde62fbf7d2c79967d4bace"))
- (sha256
- (base32
- "0g5hryia5v1k0qx97qffgwzrz4lr4jw3s5kj04yllhswsxyjbic3"))
- (patches (search-our-patches "glibc-ldd-x86_64.patch"
- "glibc-versioned-locpath.patch"
- "glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch"
- "glibc-2.24-no-build-time-cxx-header-run.patch"
- "glibc-2.24-fcommon.patch"
- "glibc-2.24-guix-prefix.patch"))))))
+;; https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html
+(define (hardened-glibc glibc)
+ (package-with-extra-configure-variable (
+ package-with-extra-configure-variable glibc
+ "--enable-stack-protector" "all")
+ "--enable-bind-now" "yes"))
-(define-public glibc-2.27/bitcoin-patched
+(define-public glibc-2.27
(package
(inherit glibc-2.31)
(version "2.27")
@@ -571,14 +552,15 @@ inspecting signatures in Mach-O binaries.")
(method git-fetch)
(uri (git-reference
(url "https://sourceware.org/git/glibc.git")
- (commit "23158b08a0908f381459f273a984c6fd328363cb")))
- (file-name (git-file-name "glibc" "23158b08a0908f381459f273a984c6fd328363cb"))
+ (commit "73886db6218e613bd6d4edf529f11e008a6c2fa6")))
+ (file-name (git-file-name "glibc" "73886db6218e613bd6d4edf529f11e008a6c2fa6"))
(sha256
(base32
- "1b2n1gxv9f4fd5yy68qjbnarhf8mf4vmlxk10i3328c1w5pmp0ca"))
+ "0azpb9cvnbv25zg8019rqz48h8i2257ngyjg566dlnp74ivrs9vq"))
(patches (search-our-patches "glibc-ldd-x86_64.patch"
+ "glibc-versioned-locpath.patch"
"glibc-2.27-riscv64-Use-__has_include-to-include-asm-syscalls.h.patch"
- "glibc-2.27-dont-redefine-nss-database.patch"
+ "glibc-2.27-fcommon.patch"
"glibc-2.27-guix-prefix.patch"))))))
(packages->manifest
@@ -627,12 +609,7 @@ inspecting signatures in Mach-O binaries.")
(make-nsis-for-gcc-10 nsis-x86_64)
osslsigncode))
((string-contains target "-linux-")
- (list (cond ((string-contains target "riscv64-")
- (make-bitcoin-cross-toolchain target
- #:base-libc (make-glibc-with-stack-protector
- (make-glibc-with-bind-now (make-glibc-without-werror glibc-2.27/bitcoin-patched)))))
- (else
- (make-bitcoin-cross-toolchain target)))))
+ (list (make-bitcoin-cross-toolchain target)))
((string-contains target "darwin")
(list clang-toolchain-10 binutils cmake xorriso python-signapple))
(else '())))))
diff --git a/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch b/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch
deleted file mode 100644
index 5c4d0c6ebe..0000000000
--- a/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-https://sourceware.org/git/?p=glibc.git;a=commit;h=a68ba2f3cd3cbe32c1f31e13c20ed13487727b32
-
-commit 6b02af31e9a721bb15a11380cd22d53b621711f8
-Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
-Date: Wed Oct 18 17:26:23 2017 +0100
-
- [AARCH64] Rewrite elf_machine_load_address using _DYNAMIC symbol
-
- This patch rewrites aarch64 elf_machine_load_address to use special _DYNAMIC
- symbol instead of _dl_start.
-
- The static address of _DYNAMIC symbol is stored in the first GOT entry.
- Here is the change which makes this solution work (part of binutils 2.24):
- https://sourceware.org/ml/binutils/2013-06/msg00248.html
-
- i386, x86_64 targets use the same method to do this as well.
-
- The original implementation relies on a trick that R_AARCH64_ABS32 relocation
- being resolved at link time and the static address fits in the 32bits.
- However, in LP64, normally, the address is defined to be 64 bit.
-
- Here is the C version one which should be portable in all cases.
-
- * sysdeps/aarch64/dl-machine.h (elf_machine_load_address): Use
- _DYNAMIC symbol to calculate load address.
-
-diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
-index e86d8b5b63..5a5b8a5de5 100644
---- a/sysdeps/aarch64/dl-machine.h
-+++ b/sysdeps/aarch64/dl-machine.h
-@@ -49,26 +49,11 @@ elf_machine_load_address (void)
- /* To figure out the load address we use the definition that for any symbol:
- dynamic_addr(symbol) = static_addr(symbol) + load_addr
-
-- The choice of symbol is arbitrary. The static address we obtain
-- by constructing a non GOT reference to the symbol, the dynamic
-- address of the symbol we compute using adrp/add to compute the
-- symbol's address relative to the PC.
-- This depends on 32bit relocations being resolved at link time
-- and that the static address fits in the 32bits. */
--
-- ElfW(Addr) static_addr;
-- ElfW(Addr) dynamic_addr;
--
-- asm (" \n"
--" adrp %1, _dl_start; \n"
--" add %1, %1, #:lo12:_dl_start \n"
--" ldr %w0, 1f \n"
--" b 2f \n"
--"1: \n"
--" .word _dl_start \n"
--"2: \n"
-- : "=r" (static_addr), "=r" (dynamic_addr));
-- return dynamic_addr - static_addr;
-+ _DYNAMIC sysmbol is used here as its link-time address stored in
-+ the special unrelocated first GOT entry. */
-+
-+ extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
-+ return (ElfW(Addr)) &_DYNAMIC - elf_machine_dynamic ();
- }
-
- /* Set up the loaded object described by L so its unrelocated PLT
diff --git a/contrib/guix/patches/glibc-2.24-guix-prefix.patch b/contrib/guix/patches/glibc-2.24-guix-prefix.patch
deleted file mode 100644
index 875e8cd611..0000000000
--- a/contrib/guix/patches/glibc-2.24-guix-prefix.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-Without ffile-prefix-map, the debug symbols will contain paths for the
-guix store which will include the hashes of each package. However, the
-hash for the same package will differ when on different architectures.
-In order to be reproducible regardless of the architecture used to build
-the package, map all guix store prefixes to something fixed, e.g. /usr.
-
-We might be able to drop this in favour of using --with-nonshared-cflags
-when we being using newer versions of glibc.
-
---- a/Makeconfig
-+++ b/Makeconfig
-@@ -950,6 +950,10 @@ object-suffixes-for-libc += .oS
- # shared objects. We don't want to use CFLAGS-os because users may, for
- # example, make that processor-specific.
- CFLAGS-.oS = $(CFLAGS-.o) $(PIC-ccflag)
-+
-+# Map Guix store paths to /usr
-+CFLAGS-.oS += `find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;`
-+
- CPPFLAGS-.oS = $(CPPFLAGS-.o) -DPIC -DLIBC_NONSHARED=1
- libtype.oS = lib%_nonshared.a
- endif
---
-2.35.1
-
diff --git a/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch b/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch
deleted file mode 100644
index 11fe7fdc99..0000000000
--- a/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch
+++ /dev/null
@@ -1,100 +0,0 @@
-https://sourceware.org/git/?p=glibc.git;a=commit;h=fc3e1337be1c6935ab58bd13520f97a535cf70cc
-
-commit dc23a45db566095e83ff0b7a57afc87fb5ca89a1
-Author: Florian Weimer <fweimer@redhat.com>
-Date: Wed Sep 21 10:45:32 2016 +0200
-
- Avoid running $(CXX) during build to obtain header file paths
-
- This reduces the build time somewhat and is particularly noticeable
- during rebuilds with few code changes.
-
-diff --git a/Makerules b/Makerules
-index 7e4077ee50..c338850de5 100644
---- a/Makerules
-+++ b/Makerules
-@@ -121,14 +121,10 @@ ifneq (,$(CXX))
- # will be used instead of /usr/include/stdlib.h and /usr/include/math.h.
- before-compile := $(common-objpfx)cstdlib $(common-objpfx)cmath \
- $(before-compile)
--cstdlib=$(shell echo "\#include <cstdlib>" | $(CXX) -M -MP -x c++ - \
-- | sed -n "/cstdlib:/{s/:$$//;p}")
--$(common-objpfx)cstdlib: $(cstdlib)
-+$(common-objpfx)cstdlib: $(c++-cstdlib-header)
- $(INSTALL_DATA) $< $@T
- $(move-if-change) $@T $@
--cmath=$(shell echo "\#include <cmath>" | $(CXX) -M -MP -x c++ - \
-- | sed -n "/cmath:/{s/:$$//;p}")
--$(common-objpfx)cmath: $(cmath)
-+$(common-objpfx)cmath: $(c++-cmath-header)
- $(INSTALL_DATA) $< $@T
- $(move-if-change) $@T $@
- endif
-diff --git a/config.make.in b/config.make.in
-index 95c6f36876..04a8b3ed7f 100644
---- a/config.make.in
-+++ b/config.make.in
-@@ -45,6 +45,8 @@ defines = @DEFINES@
- sysheaders = @sysheaders@
- sysincludes = @SYSINCLUDES@
- c++-sysincludes = @CXX_SYSINCLUDES@
-+c++-cstdlib-header = @CXX_CSTDLIB_HEADER@
-+c++-cmath-header = @CXX_CMATH_HEADER@
- all-warnings = @all_warnings@
- enable-werror = @enable_werror@
-
-diff --git a/configure b/configure
-index 17625e1041..6ff252744b 100755
---- a/configure
-+++ b/configure
-@@ -635,6 +635,8 @@ BISON
- INSTALL_INFO
- PERL
- BASH_SHELL
-+CXX_CMATH_HEADER
-+CXX_CSTDLIB_HEADER
- CXX_SYSINCLUDES
- SYSINCLUDES
- AUTOCONF
-@@ -5054,6 +5056,18 @@ fi
-
-
-
-+# Obtain some C++ header file paths. This is used to make a local
-+# copy of those headers in Makerules.
-+if test -n "$CXX"; then
-+ find_cxx_header () {
-+ echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
-+ }
-+ CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
-+ CXX_CMATH_HEADER="$(find_cxx_header cmath)"
-+fi
-+
-+
-+
- # Test if LD_LIBRARY_PATH contains the notation for the current directory
- # since this would lead to problems installing/building glibc.
- # LD_LIBRARY_PATH contains the current directory if one of the following
-diff --git a/configure.ac b/configure.ac
-index 33bcd62180..9938ab0dc2 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1039,6 +1039,18 @@ fi
- AC_SUBST(SYSINCLUDES)
- AC_SUBST(CXX_SYSINCLUDES)
-
-+# Obtain some C++ header file paths. This is used to make a local
-+# copy of those headers in Makerules.
-+if test -n "$CXX"; then
-+ find_cxx_header () {
-+ echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
-+ }
-+ CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
-+ CXX_CMATH_HEADER="$(find_cxx_header cmath)"
-+fi
-+AC_SUBST(CXX_CSTDLIB_HEADER)
-+AC_SUBST(CXX_CMATH_HEADER)
-+
- # Test if LD_LIBRARY_PATH contains the notation for the current directory
- # since this would lead to problems installing/building glibc.
- # LD_LIBRARY_PATH contains the current directory if one of the following
diff --git a/contrib/guix/patches/glibc-2.27-dont-redefine-nss-database.patch b/contrib/guix/patches/glibc-2.27-dont-redefine-nss-database.patch
deleted file mode 100644
index 16a595d613..0000000000
--- a/contrib/guix/patches/glibc-2.27-dont-redefine-nss-database.patch
+++ /dev/null
@@ -1,87 +0,0 @@
-commit 78a90c2f74a2012dd3eff302189e47ff6779a757
-Author: Andreas Schwab <schwab@linux-m68k.org>
-Date: Fri Mar 2 23:07:14 2018 +0100
-
- Fix multiple definitions of __nss_*_database (bug 22918)
-
- (cherry picked from commit eaf6753f8aac33a36deb98c1031d1bad7b593d2d)
-
-diff --git a/nscd/gai.c b/nscd/gai.c
-index d081747797..576fd0045b 100644
---- a/nscd/gai.c
-+++ b/nscd/gai.c
-@@ -45,3 +45,6 @@
- #ifdef HAVE_LIBIDN
- # include <libidn/idn-stub.c>
- #endif
-+
-+/* Some variables normally defined in libc. */
-+service_user *__nss_hosts_database attribute_hidden;
-diff --git a/nss/nsswitch.c b/nss/nsswitch.c
-index d5e655974f..b0f0c11a3e 100644
---- a/nss/nsswitch.c
-+++ b/nss/nsswitch.c
-@@ -62,7 +62,7 @@ static service_library *nss_new_service (name_database *database,
-
- /* Declare external database variables. */
- #define DEFINE_DATABASE(name) \
-- extern service_user *__nss_##name##_database attribute_hidden; \
-+ service_user *__nss_##name##_database attribute_hidden; \
- weak_extern (__nss_##name##_database)
- #include "databases.def"
- #undef DEFINE_DATABASE
-diff --git a/nss/nsswitch.h b/nss/nsswitch.h
-index eccb535ef5..63573b9ebc 100644
---- a/nss/nsswitch.h
-+++ b/nss/nsswitch.h
-@@ -226,10 +226,10 @@ libc_hidden_proto (__nss_hostname_digits_dots)
- #define MAX_NR_ADDRS 48
-
- /* Prototypes for __nss_*_lookup2 functions. */
--#define DEFINE_DATABASE(arg) \
-- service_user *__nss_##arg##_database attribute_hidden; \
-- int __nss_##arg##_lookup2 (service_user **, const char *, \
-- const char *, void **); \
-+#define DEFINE_DATABASE(arg) \
-+ extern service_user *__nss_##arg##_database attribute_hidden; \
-+ int __nss_##arg##_lookup2 (service_user **, const char *, \
-+ const char *, void **); \
- libc_hidden_proto (__nss_##arg##_lookup2)
- #include "databases.def"
- #undef DEFINE_DATABASE
-diff --git a/posix/tst-rfc3484-2.c b/posix/tst-rfc3484-2.c
-index f509534ca9..8c64ac59ff 100644
---- a/posix/tst-rfc3484-2.c
-+++ b/posix/tst-rfc3484-2.c
-@@ -58,6 +58,7 @@ _res_hconf_init (void)
- #undef USE_NSCD
- #include "../sysdeps/posix/getaddrinfo.c"
-
-+service_user *__nss_hosts_database attribute_hidden;
-
- /* This is the beginning of the real test code. The above defines
- (among other things) the function rfc3484_sort. */
-diff --git a/posix/tst-rfc3484-3.c b/posix/tst-rfc3484-3.c
-index ae44087a10..1c61aaf844 100644
---- a/posix/tst-rfc3484-3.c
-+++ b/posix/tst-rfc3484-3.c
-@@ -58,6 +58,7 @@ _res_hconf_init (void)
- #undef USE_NSCD
- #include "../sysdeps/posix/getaddrinfo.c"
-
-+service_user *__nss_hosts_database attribute_hidden;
-
- /* This is the beginning of the real test code. The above defines
- (among other things) the function rfc3484_sort. */
-diff --git a/posix/tst-rfc3484.c b/posix/tst-rfc3484.c
-index 7f191abbbc..8f45848e44 100644
---- a/posix/tst-rfc3484.c
-+++ b/posix/tst-rfc3484.c
-@@ -58,6 +58,7 @@ _res_hconf_init (void)
- #undef USE_NSCD
- #include "../sysdeps/posix/getaddrinfo.c"
-
-+service_user *__nss_hosts_database attribute_hidden;
-
- /* This is the beginning of the real test code. The above defines
- (among other things) the function rfc3484_sort. */
diff --git a/contrib/guix/patches/glibc-2.24-fcommon.patch b/contrib/guix/patches/glibc-2.27-fcommon.patch
index 2bc32ede90..f3baacab98 100644
--- a/contrib/guix/patches/glibc-2.24-fcommon.patch
+++ b/contrib/guix/patches/glibc-2.27-fcommon.patch
@@ -18,15 +18,15 @@ Date: Fri May 6 11:03:04 2022 +0100
https://sourceware.org/git/?p=glibc.git;a=commit;h=7650321ce037302bfc2f026aa19e0213b8d02fe6
diff --git a/Makeconfig b/Makeconfig
-index ee379f5852..63c4a2f234 100644
+index 86a71e5802..aa2166be60 100644
--- a/Makeconfig
+++ b/Makeconfig
-@@ -824,7 +824,7 @@ ifeq "$(strip $(+cflags))" ""
- +cflags := $(default_cflags)
+@@ -896,7 +896,7 @@ ifeq "$(strip $(+cflags))" ""
endif # $(+cflags) == ""
--+cflags += $(cflags-cpu) $(+gccwarn) $(+merge-constants) $(+math-flags)
-++cflags += $(cflags-cpu) $(+gccwarn) $(+merge-constants) $(+math-flags) -fcommon
+ +cflags += $(cflags-cpu) $(+gccwarn) $(+merge-constants) $(+math-flags) \
+- $(+stack-protector)
++ $(+stack-protector) -fcommon
+gcc-nowarn := -w
# Don't duplicate options if we inherited variables from the parent.
diff --git a/contrib/guix/patches/glibc-2.27-guix-prefix.patch b/contrib/guix/patches/glibc-2.27-guix-prefix.patch
index d777af74f0..6648bc6c05 100644
--- a/contrib/guix/patches/glibc-2.27-guix-prefix.patch
+++ b/contrib/guix/patches/glibc-2.27-guix-prefix.patch
@@ -20,6 +20,3 @@ when we being using newer versions of glibc.
libtype.o := lib%.a
object-suffixes += .o
ifeq (yes,$(build-shared))
---
-2.35.1
-
diff --git a/contrib/guix/patches/glibc-ldd-x86_64.patch b/contrib/guix/patches/glibc-ldd-x86_64.patch
index b1b6d5a548..a23b095caa 100644
--- a/contrib/guix/patches/glibc-ldd-x86_64.patch
+++ b/contrib/guix/patches/glibc-ldd-x86_64.patch
@@ -1,8 +1,8 @@
By default, 'RTDLLIST' in 'ldd' refers to 'lib64/ld-linux-x86-64.so', whereas
it's in 'lib/' for us. This patch fixes that.
---- glibc-2.17/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed 2012-12-25 04:02:13.000000000 +0100
-+++ glibc-2.17/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed 2013-09-15 23:08:03.000000000 +0200
+--- a/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed
++++ b/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed
@@ -1,3 +1,3 @@
/LD_TRACE_LOADED_OBJECTS=1/a\
add_env="$add_env LD_LIBRARY_VERSION=\\$verify_out"
diff --git a/contrib/signet/getcoins.py b/contrib/signet/getcoins.py
index d4e436626f..19751ae269 100755
--- a/contrib/signet/getcoins.py
+++ b/contrib/signet/getcoins.py
@@ -142,7 +142,7 @@ if args.captcha != '': # Retrieve a captcha
try:
res = session.post(args.faucet, data=data)
-except:
+except Exception:
raise SystemExit(f"Unexpected error when contacting faucet: {sys.exc_info()[0]}")
# Display the output as per the returned status code
diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root
index efb6b9f7b4..7ec318e1ea 100644
--- a/contrib/verify-commits/trusted-git-root
+++ b/contrib/verify-commits/trusted-git-root
@@ -1 +1 @@
-8ef096d4f8e08ac691502e3fd34721a8bdfa9044
+437dfe1c26e752c280014a30f809e62c684ad99e
diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys
index eeafcdf205..94daf28b15 100644
--- a/contrib/verify-commits/trusted-keys
+++ b/contrib/verify-commits/trusted-keys
@@ -1,4 +1,3 @@
-B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B
E777299FC265DD04793070EB944D35F9AC3DB76A
D1DBF2C4B96F2DEBF4C16654410108112E7EA81F
152812300785C96444D3334D17565732E08E5E41
diff --git a/doc/dependencies.md b/doc/dependencies.md
index ec205e4b51..a9ca5b3e7a 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -19,7 +19,7 @@ You can find installation instructions in the `build-*.md` file for your platfor
| --- | --- | --- | --- | --- |
| [Boost](../depends/packages/boost.mk) | [link](https://www.boost.org/users/download/) | [1.81.0](https://github.com/bitcoin/bitcoin/pull/26557) | [1.64.0](https://github.com/bitcoin/bitcoin/pull/22320) | No |
| [libevent](../depends/packages/libevent.mk) | [link](https://github.com/libevent/libevent/releases) | [2.1.12-stable](https://github.com/bitcoin/bitcoin/pull/21991) | [2.1.8](https://github.com/bitcoin/bitcoin/pull/24681) | No |
-| glibc | [link](https://www.gnu.org/software/libc/) | N/A | [2.18](https://github.com/bitcoin/bitcoin/pull/23511) | Yes |
+| glibc | [link](https://www.gnu.org/software/libc/) | N/A | [2.27](https://github.com/bitcoin/bitcoin/pull/27029) | Yes |
| Linux Kernel | [link](https://www.kernel.org/) | N/A | 3.2.0 | Yes |
## Optional
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index e2e54e13d3..f15df1bf73 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -560,8 +560,19 @@ address sanitizer, libtsan for the thread sanitizer, and libubsan for the
undefined sanitizer. If you are missing required libraries, the configure script
will fail with a linker error when testing the sanitizer flags.
-The test suite should pass cleanly with the `thread` and `undefined` sanitizers,
-but there are a number of known problems when using the `address` sanitizer. The
+The test suite should pass cleanly with the `thread` and `undefined` sanitizers. You
+may need to use a suppressions file, see `test/sanitizer_suppressions`. They may be
+used as follows:
+```bash
+export LSAN_OPTIONS="suppressions=$(pwd)/test/sanitizer_suppressions/lsan"
+export TSAN_OPTIONS="suppressions=$(pwd)/test/sanitizer_suppressions/tsan:halt_on_error=1:second_deadlock_stack=1"
+export UBSAN_OPTIONS="suppressions=$(pwd)/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
+```
+
+See the CI config for more examples, and upstream documentation for more information
+about any additional options.
+
+There are a number of known problems when using the `address` sanitizer. The
address sanitizer is known to fail in
[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable
unless you also use `--disable-asm` when running configure. We would like to fix
diff --git a/doc/release-notes-25574.md b/doc/release-notes-25574.md
new file mode 100644
index 0000000000..312a99d95b
--- /dev/null
+++ b/doc/release-notes-25574.md
@@ -0,0 +1,13 @@
+Updated settings
+----------------
+
+If the `-checkblocks` or `-checklevel` options are explicitly provided by the
+user, but the verification checks cannot be completed due to an insufficient
+dbcache, Bitcoin Core will now return an error at startup. (#25574)
+
+RPC
+---
+The `-verifychain` RPC will now return `false` if the checks didn't fail,
+but couldn't be completed at the desired depth and level. This could be due
+to missing data while pruning, due to an insufficient dbcache or due to
+the node being shutdown before the call could finish. (#25574)
diff --git a/doc/release-notes-25943.md b/doc/release-notes-25943.md
new file mode 100644
index 0000000000..81b0a48b5d
--- /dev/null
+++ b/doc/release-notes-25943.md
@@ -0,0 +1,4 @@
+New RPC Argument
+--------
+- `sendrawtransaction` has a new, optional argument, `maxburnamount` with a default value of `0`. Any transaction containing an unspendable output with a value greater than `maxburnamount` will not be submitted. At present, the outputs deemed unspendable are those with scripts that begin with an `OP_RETURN` code (known as 'datacarriers'), scripts that exceed the maximum script size, and scripts that contain invalid opcodes.
+
diff --git a/doc/release-notes-27068.md b/doc/release-notes-27068.md
new file mode 100644
index 0000000000..3f5c5dba37
--- /dev/null
+++ b/doc/release-notes-27068.md
@@ -0,0 +1,6 @@
+Wallet
+------
+
+- Wallet passphrases may now contain null characters.
+ Prior to this change, only characters up to the first
+ null character were recognized and accepted. (#27068) \ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
index 5830090ada..7b9ffe427d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -204,8 +204,10 @@ BITCOIN_CORE_H = \
node/chainstate.h \
node/chainstatemanager_args.h \
node/coin.h \
+ node/coins_view_args.h \
node/connection_types.h \
node/context.h \
+ node/database_args.h \
node/eviction.h \
node/interface_ui.h \
node/mempool_args.h \
@@ -388,8 +390,10 @@ libbitcoin_node_a_SOURCES = \
node/chainstate.cpp \
node/chainstatemanager_args.cpp \
node/coin.cpp \
+ node/coins_view_args.cpp \
node/connection_types.cpp \
node/context.cpp \
+ node/database_args.cpp \
node/eviction.cpp \
node/interface_ui.cpp \
node/interfaces.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index ae77b79b8b..aefefe789a 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -10,10 +10,12 @@ EXTRA_LIBRARIES += \
TEST_UTIL_H = \
test/util/blockfilter.h \
test/util/chainstate.h \
+ test/util/coins.h \
test/util/json.h \
test/util/logging.h \
test/util/mining.h \
test/util/net.h \
+ test/util/random.h \
test/util/script.h \
test/util/setup_common.h \
test/util/str.h \
@@ -30,6 +32,7 @@ libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_util_a_SOURCES = \
test/util/blockfilter.cpp \
+ test/util/coins.cpp \
test/util/json.cpp \
test/util/logging.cpp \
test/util/mining.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index a740760faf..f5ca9a5c34 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -486,7 +486,7 @@ void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
- LogPrint(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToString(), nUBucket, nUBucketPos);
+ LogPrint(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToStringAddrPort(), nUBucket, nUBucketPos);
if (infoDelete.nRefCount == 0) {
Delete(nIdDelete);
}
@@ -542,7 +542,7 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
nNew++;
m_network_counts[infoOld.GetNetwork()].n_new++;
LogPrint(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
- infoOld.ToString(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
+ infoOld.ToStringAddrPort(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
}
assert(vvTried[nKBucket][nKBucketPos] == -1);
@@ -618,7 +618,7 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::c
pinfo->nRefCount++;
vvNew[nUBucket][nUBucketPos] = nId;
LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n",
- addr.ToString(), m_netgroupman.GetMappedAS(addr), nUBucket, nUBucketPos);
+ addr.ToStringAddrPort(), m_netgroupman.GetMappedAS(addr), nUBucket, nUBucketPos);
} else {
if (pinfo->nRefCount == 0) {
Delete(nId);
@@ -669,15 +669,15 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSecond
// Output the entry we'd be colliding with, for debugging purposes
auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
- colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "",
- addr.ToString(),
+ colliding_entry != mapInfo.end() ? colliding_entry->second.ToStringAddrPort() : "",
+ addr.ToStringAddrPort(),
m_tried_collisions.size());
return false;
} else {
// move nId to the tried tables
MakeTried(info, nId);
LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
- addr.ToString(), m_netgroupman.GetMappedAS(addr), tried_bucket, tried_bucket_pos);
+ addr.ToStringAddrPort(), m_netgroupman.GetMappedAS(addr), tried_bucket, tried_bucket_pos);
return true;
}
}
@@ -689,7 +689,7 @@ bool AddrManImpl::Add_(const std::vector<CAddress>& vAddr, const CNetAddr& sourc
added += AddSingle(*it, source, time_penalty) ? 1 : 0;
}
if (added > 0) {
- LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToString(), nTried, nNew);
+ LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew);
}
return added > 0;
}
@@ -746,7 +746,7 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool newOnly) const
const AddrInfo& info{it_found->second};
// With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
- LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToString());
+ LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToStringAddrPort());
return {info, info.m_last_try};
}
// Otherwise start over with a (likely) different bucket, and increased chance factor.
@@ -774,7 +774,7 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool newOnly) const
const AddrInfo& info{it_found->second};
// With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
- LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToString());
+ LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToStringAddrPort());
return {info, info.m_last_try};
}
// Otherwise start over with a (likely) different bucket, and increased chance factor.
@@ -891,7 +891,7 @@ void AddrManImpl::ResolveCollisions_()
// Give address at least 60 seconds to successfully connect
if (current_time - info_old.m_last_try > 60s) {
- LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
+ LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
// Replaces an existing address already in the tried table with the new address
Good_(info_new, false, current_time);
@@ -901,7 +901,7 @@ void AddrManImpl::ResolveCollisions_()
// If the collision hasn't resolved in some reasonable amount of time,
// just evict the old entry -- we must not be able to
// connect to it for some reason.
- LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
+ LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
Good_(info_new, false, current_time);
erase_collision = true;
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 087e1442fe..11b0e0dee2 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -45,7 +45,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), "", gArgs, CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index ea272b2120..d5d057e96d 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -28,7 +28,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
- CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
+ CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
diff --git a/src/bench/wallet_create_tx.cpp b/src/bench/wallet_create_tx.cpp
index 820c9d5d50..bd32a5abdc 100644
--- a/src/bench/wallet_create_tx.cpp
+++ b/src/bench/wallet_create_tx.cpp
@@ -83,7 +83,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
- CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
+ CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -136,7 +136,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
- CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
+ CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index d972b71a65..423fa79c6f 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -82,6 +82,7 @@ int main(int argc, char* argv[])
// SETUP: Chainstate
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
+ .datadir = gArgs.GetDataDirNet(),
.adjusted_time_callback = NodeClock::now,
};
ChainstateManager chainman{chainman_opts};
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 7207ff1ae2..afdfa426f1 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -135,7 +135,7 @@ protected:
public:
CBlockHeader header;
- // Can be overriden for testing
+ // Can be overridden for testing
using CheckBlockFn = std::function<bool(const CBlock&, BlockValidationState&, const Consensus::Params&, bool, bool)>;
CheckBlockFn m_check_block_mock{nullptr};
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 6efaf2ec19..0c6debfa80 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -127,40 +127,40 @@ static leveldb::Options GetOptions(size_t nCacheSize)
return options;
}
-CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
- : m_name{fs::PathToString(path.stem())}, m_path{path}, m_is_memory{fMemory}
+CDBWrapper::CDBWrapper(const DBParams& params)
+ : m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only}
{
penv = nullptr;
readoptions.verify_checksums = true;
iteroptions.verify_checksums = true;
iteroptions.fill_cache = false;
syncoptions.sync = true;
- options = GetOptions(nCacheSize);
+ options = GetOptions(params.cache_bytes);
options.create_if_missing = true;
- if (fMemory) {
+ if (params.memory_only) {
penv = leveldb::NewMemEnv(leveldb::Env::Default());
options.env = penv;
} else {
- if (fWipe) {
- LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(path));
- leveldb::Status result = leveldb::DestroyDB(fs::PathToString(path), options);
+ if (params.wipe_data) {
+ LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(params.path));
+ leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), options);
dbwrapper_private::HandleError(result);
}
- TryCreateDirectories(path);
- LogPrintf("Opening LevelDB in %s\n", fs::PathToString(path));
+ TryCreateDirectories(params.path);
+ LogPrintf("Opening LevelDB in %s\n", fs::PathToString(params.path));
}
// PathToString() return value is safe to pass to leveldb open function,
// because on POSIX leveldb passes the byte string directly to ::open(), and
// on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW
// (see env_posix.cc and env_windows.cc).
- leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(path), &pdb);
+ leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(params.path), &pdb);
dbwrapper_private::HandleError(status);
LogPrintf("Opened LevelDB successfully\n");
- if (gArgs.GetBoolArg("-forcecompactdb", false)) {
- LogPrintf("Starting database compaction of %s\n", fs::PathToString(path));
+ if (params.options.force_compact) {
+ LogPrintf("Starting database compaction of %s\n", fs::PathToString(params.path));
pdb->CompactRange(nullptr, nullptr);
- LogPrintf("Finished database compaction of %s\n", fs::PathToString(path));
+ LogPrintf("Finished database compaction of %s\n", fs::PathToString(params.path));
}
// The base-case obfuscation key, which is a noop.
@@ -168,7 +168,7 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo
bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key);
- if (!key_exists && obfuscate && IsEmpty()) {
+ if (!key_exists && params.obfuscate && IsEmpty()) {
// Initialize non-degenerate obfuscation if it won't upset
// existing, non-obfuscated data.
std::vector<unsigned char> new_key = CreateObfuscateKey();
@@ -177,10 +177,10 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo
Write(OBFUSCATE_KEY_KEY, new_key);
obfuscate_key = new_key;
- LogPrintf("Wrote new obfuscate key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key));
+ LogPrintf("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
}
- LogPrintf("Using obfuscation key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key));
+ LogPrintf("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
}
CDBWrapper::~CDBWrapper()
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index b389d039fb..578d9880ac 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -31,6 +31,29 @@ class Env;
static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
+//! User-controlled performance and debug options.
+struct DBOptions {
+ //! Compact database on startup.
+ bool force_compact = false;
+};
+
+//! Application-specific storage settings.
+struct DBParams {
+ //! Location in the filesystem where leveldb data will be stored.
+ fs::path path;
+ //! Configures various leveldb cache settings.
+ size_t cache_bytes;
+ //! If true, use leveldb's memory environment.
+ bool memory_only = false;
+ //! If true, remove all existing data.
+ bool wipe_data = false;
+ //! If true, store data obfuscated via simple XOR. If false, XOR with a
+ //! zero'd byte array.
+ bool obfuscate = false;
+ //! Passed-through options.
+ DBOptions options{};
+};
+
class dbwrapper_error : public std::runtime_error
{
public:
@@ -230,15 +253,7 @@ private:
bool m_is_memory;
public:
- /**
- * @param[in] path Location in the filesystem where leveldb data will be stored.
- * @param[in] nCacheSize Configures various leveldb cache settings.
- * @param[in] fMemory If true, use leveldb's memory environment.
- * @param[in] fWipe If true, remove all existing data.
- * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
- * with a zero'd byte array.
- */
- CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
+ CDBWrapper(const DBParams& params);
~CDBWrapper();
CDBWrapper(const CDBWrapper&) = delete;
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 33a75df68e..86166a5ca4 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -160,7 +160,7 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
JSONRPCRequest jreq;
jreq.context = context;
- jreq.peerAddr = req->GetPeer().ToString();
+ jreq.peerAddr = req->GetPeer().ToStringAddrPort();
if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 720f5c9353..4e4f27f1be 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -222,7 +222,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
// Early address-based allow check
if (!ClientAllowed(hreq->GetPeer())) {
LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
- hreq->GetPeer().ToString());
+ hreq->GetPeer().ToStringAddrPort());
hreq->WriteReply(HTTP_FORBIDDEN);
return;
}
@@ -230,13 +230,13 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
// Early reject unknown HTTP methods
if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
- hreq->GetPeer().ToString());
+ hreq->GetPeer().ToStringAddrPort());
hreq->WriteReply(HTTP_BAD_METHOD);
return;
}
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
- RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString());
+ RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToStringAddrPort());
// Find registered handler for prefix
std::string strURI = hreq->GetURI();
diff --git a/src/i2p.cpp b/src/i2p.cpp
index 8e98440747..a3bfc23a65 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -206,7 +206,7 @@ bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
}
const Reply& lookup_reply =
- SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
+ SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringAddr()));
const std::string& dest = lookup_reply.Get("VALUE");
@@ -233,7 +233,7 @@ bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
} catch (const std::runtime_error& e) {
- Log("Error connecting to %s: %s", to.ToString(), e.what());
+ Log("Error connecting to %s: %s", to.ToStringAddrPort(), e.what());
CheckControlSock();
return false;
}
@@ -302,7 +302,7 @@ std::unique_ptr<Sock> Session::Hello() const
}
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
- throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
+ throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToStringAddrPort()));
}
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
@@ -371,7 +371,7 @@ void Session::CreateIfNotCreatedAlready()
const auto session_type = m_transient ? "transient" : "persistent";
const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs
- Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());
+ Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToStringAddrPort());
auto sock = Hello();
@@ -380,7 +380,9 @@ void Session::CreateIfNotCreatedAlready()
// in the reply in DESTINATION=.
const Reply& reply = SendRequestAndGetReply(
*sock,
- strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7", session_id));
+ strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7 "
+ "inbound.quantity=1 outbound.quantity=1",
+ session_id));
m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
} else {
@@ -396,7 +398,8 @@ void Session::CreateIfNotCreatedAlready()
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
SendRequestAndGetReply(*sock,
- strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
+ strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s "
+ "inbound.quantity=3 outbound.quantity=3",
session_id,
private_key_b64));
}
@@ -408,7 +411,7 @@ void Session::CreateIfNotCreatedAlready()
Log("%s SAM session %s created, my address=%s",
Capitalize(session_type),
m_session_id,
- m_my_addr.ToString());
+ m_my_addr.ToStringAddrPort());
}
std::unique_ptr<Sock> Session::StreamAccept()
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 1d5c0dbe24..6f2ce2efe4 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -8,6 +8,7 @@
#include <kernel/chain.h>
#include <node/blockstorage.h>
#include <node/context.h>
+#include <node/database_args.h>
#include <node/interface_ui.h>
#include <shutdown.h>
#include <tinyformat.h>
@@ -48,7 +49,13 @@ CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
}
BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) :
- CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate)
+ CDBWrapper{DBParams{
+ .path = path,
+ .cache_bytes = n_cache_size,
+ .memory_only = f_memory,
+ .wipe_data = f_wipe,
+ .obfuscate = f_obfuscate,
+ .options = [] { DBOptions options; node::ReadDatabaseArgs(gArgs, options); return options; }()}}
{}
bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
diff --git a/src/init.cpp b/src/init.cpp
index 73ae36e4f7..4e06d44cb0 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1046,6 +1046,7 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
{
ChainstateManager::Options chainman_opts_dummy{
.chainparams = chainparams,
+ .datadir = args.GetDataDirNet(),
};
if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) {
return InitError(*error);
@@ -1444,6 +1445,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
+ .datadir = args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
};
Assert(!ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
@@ -1493,6 +1495,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
+ options.require_full_verification = args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
options.check_interrupt = ShutdownRequested;
options.coins_error_cb = [] {
uiInterface.ThreadSafeMessageBox(
@@ -1524,7 +1527,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
- if (status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB) {
+ if (status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB || status == node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
return InitError(error);
}
@@ -1798,7 +1801,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (connOptions.onion_binds.size() > 1) {
InitWarning(strprintf(_("More than one onion bind address is provided. Using %s "
"for the automatically created Tor onion service."),
- onion_service_target.ToStringIPPort()));
+ onion_service_target.ToStringAddrPort()));
}
StartTorControl(onion_service_target);
}
@@ -1825,6 +1828,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
+ if (!connOptions.m_specified_outgoing.empty() && !connOptions.vSeedNodes.empty()) {
+ LogPrintf("-seednode is ignored when -connect is used\n");
+ }
+
+ if (args.IsArgSet("-dnsseed") && args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED) && args.IsArgSet("-proxy")) {
+ LogPrintf("-dnsseed is ignored when -connect is used and -proxy is specified\n");
+ }
}
const std::string& i2psam_arg = args.GetArg("-i2psam", "");
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
index 226bb6031e..2395f60164 100644
--- a/src/kernel/chainstatemanager_opts.h
+++ b/src/kernel/chainstatemanager_opts.h
@@ -6,6 +6,8 @@
#define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
#include <arith_uint256.h>
+#include <dbwrapper.h>
+#include <txdb.h>
#include <uint256.h>
#include <util/time.h>
@@ -27,6 +29,7 @@ namespace kernel {
*/
struct ChainstateManagerOpts {
const CChainParams& chainparams;
+ fs::path datadir;
const std::function<NodeClock::time_point()> adjusted_time_callback{nullptr};
std::optional<bool> check_block_index{};
bool checkpoints_enabled{DEFAULT_CHECKPOINTS_ENABLED};
@@ -36,6 +39,9 @@ struct ChainstateManagerOpts {
std::optional<uint256> assumed_valid_block{};
//! If the tip is older than this, the node is considered to be in initial block download.
std::chrono::seconds max_tip_age{DEFAULT_MAX_TIP_AGE};
+ DBOptions block_tree_db{};
+ DBOptions coins_db{};
+ CoinsViewOptions coins_view{};
};
} // namespace kernel
diff --git a/src/kernel/cs_main.cpp b/src/kernel/cs_main.cpp
index c3a08c9695..d27cb7caf3 100644
--- a/src/kernel/cs_main.cpp
+++ b/src/kernel/cs_main.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <kernel/cs_main.h>
#include <sync.h>
RecursiveMutex cs_main;
diff --git a/src/mapport.cpp b/src/mapport.cpp
index 84b889f22d..994fd12cf5 100644
--- a/src/mapport.cpp
+++ b/src/mapport.cpp
@@ -104,7 +104,7 @@ static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_
AddLocal(external, LOCAL_MAPPED);
external_ip_discovered = true;
}
- LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString());
+ LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToStringAddrPort());
return true;
} else {
LogPrintf("natpmp: Port mapping failed.\n");
@@ -177,7 +177,7 @@ static bool ProcessUpnp()
if (externalIPAddress[0]) {
CNetAddr resolved;
if (LookupHost(externalIPAddress, resolved, false)) {
- LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString());
+ LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToStringAddr());
AddLocal(resolved, LOCAL_MAPPED);
}
} else {
diff --git a/src/net.cpp b/src/net.cpp
index 4f4e443976..4e4f2f78be 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -196,7 +196,7 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn)
s >> endpoint;
CAddress addr{endpoint, GetDesirableServiceFlags(NODE_NONE)};
addr.nTime = rng.rand_uniform_delay(Now<NodeSeconds>() - one_week, -one_week);
- LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToString());
+ LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToStringAddrPort());
vSeedsOut.push_back(addr);
}
return vSeedsOut;
@@ -258,7 +258,7 @@ std::optional<CService> GetLocalAddrForPeer(CNode& node)
}
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
- LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), node.GetId());
+ LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToStringAddrPort(), node.GetId());
return addrLocal;
}
// Address is unroutable. Don't advertise.
@@ -295,7 +295,7 @@ bool AddLocal(const CService& addr_, int nScore)
if (!IsReachable(addr))
return false;
- LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore);
+ LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
{
LOCK(g_maplocalhost_mutex);
@@ -318,7 +318,7 @@ bool AddLocal(const CNetAddr &addr, int nScore)
void RemoveLocal(const CService& addr)
{
LOCK(g_maplocalhost_mutex);
- LogPrintf("RemoveLocal(%s)\n", addr.ToString());
+ LogPrintf("RemoveLocal(%s)\n", addr.ToStringAddrPort());
mapLocalHost.erase(addr);
}
@@ -405,7 +405,7 @@ CNode* CConnman::FindNode(const CService& addr)
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
{
- return FindNode(static_cast<CNetAddr>(addr)) || FindNode(addr.ToStringIPPort());
+ return FindNode(static_cast<CNetAddr>(addr)) || FindNode(addr.ToStringAddrPort());
}
bool CConnman::CheckIncomingNonce(uint64_t nonce)
@@ -436,6 +436,7 @@ static CAddress GetBindAddress(const Sock& sock)
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
if (pszDest == nullptr) {
@@ -452,7 +453,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
}
LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying connection %s lastseen=%.1fhrs\n",
- pszDest ? pszDest : addrConnect.ToString(),
+ pszDest ? pszDest : addrConnect.ToStringAddrPort(),
Ticks<HoursDouble>(pszDest ? 0h : Now<NodeSeconds>() - addrConnect.nTime));
// Resolve
@@ -464,7 +465,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
const CService rnd{resolved[GetRand(resolved.size())]};
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE};
if (!addrConnect.IsValid()) {
- LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest);
+ LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
return nullptr;
}
// It is possible that we already have a connection to the IP/port pszDest resolved to.
@@ -496,8 +497,23 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (m_i2p_sam_session) {
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
} else {
- i2p_transient_session = std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
+ {
+ LOCK(m_unused_i2p_sessions_mutex);
+ if (m_unused_i2p_sessions.empty()) {
+ i2p_transient_session =
+ std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
+ } else {
+ i2p_transient_session.swap(m_unused_i2p_sessions.front());
+ m_unused_i2p_sessions.pop();
+ }
+ }
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
+ if (!connected) {
+ LOCK(m_unused_i2p_sessions_mutex);
+ if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
+ m_unused_i2p_sessions.emplace(i2p_transient_session.release());
+ }
+ }
}
if (connected) {
@@ -509,7 +525,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (!sock) {
return nullptr;
}
- connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(),
+ connected = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(),
*sock, nConnectTimeout, proxyConnectionFailed);
} else {
// no proxy needed (none set for target network)
@@ -593,7 +609,7 @@ void CNode::SetAddrLocal(const CService& addrLocalIn) {
AssertLockNotHeld(m_addr_local_mutex);
LOCK(m_addr_local_mutex);
if (addrLocal.IsValid()) {
- error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString());
+ error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToStringAddrPort(), addrLocalIn.ToStringAddrPort());
} else {
addrLocal = addrLocalIn;
}
@@ -644,7 +660,7 @@ void CNode::CopyStats(CNodeStats& stats)
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
- stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
+ stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToStringAddrPort() : "";
X(m_conn_type);
}
@@ -973,12 +989,12 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
}
if (!fNetworkActive) {
- LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString());
+ LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToStringAddrPort());
return;
}
if (!sock->IsSelectable()) {
- LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
+ LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToStringAddrPort());
return;
}
@@ -987,14 +1003,14 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
const int on{1};
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
LogPrint(BCLog::NET, "connection from %s: unable to set TCP_NODELAY, continuing anyway\n",
- addr.ToString());
+ addr.ToStringAddrPort());
}
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
if (!NetPermissions::HasFlag(permission_flags, NetPermissionFlags::NoBan) && banned)
{
- LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
+ LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToStringAddrPort());
return;
}
@@ -1002,7 +1018,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
bool discouraged = m_banman && m_banman->IsDiscouraged(addr);
if (!NetPermissions::HasFlag(permission_flags, NetPermissionFlags::NoBan) && nInbound + 1 >= nMaxInbound && discouraged)
{
- LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
+ LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToStringAddrPort());
return;
}
@@ -1040,7 +1056,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
pnode->AddRef();
m_msgproc->InitializeNode(*pnode, nodeServices);
- LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString());
+ LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToStringAddrPort());
{
LOCK(m_nodes_mutex);
@@ -1053,6 +1069,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::optional<int> max_connections;
switch (conn_type) {
case ConnectionType::INBOUND:
@@ -1471,6 +1488,8 @@ void CConnman::ThreadDNSAddressSeed()
}
LogPrintf("Loading addresses from DNS seed %s\n", seed);
+ // If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address
+ // for the base dns seed domain in chainparams
if (HaveNameProxy()) {
AddAddrFetch(seed);
} else {
@@ -1492,8 +1511,9 @@ void CConnman::ThreadDNSAddressSeed()
}
addrman.Add(vAdd, resolveSource);
} else {
- // We now avoid directly using results from DNS Seeds which do not support service bit filtering,
- // instead using them as a addrfetch to get nodes with our desired service bits.
+ // If the seed does not support a subdomain with our desired service bits,
+ // we make an ADDR_FETCH connection to the DNS resolved peer address for the
+ // base dns seed domain in chainparams
AddAddrFetch(seed);
}
}
@@ -1514,6 +1534,7 @@ void CConnman::DumpAddresses()
void CConnman::ProcessAddrFetch()
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::string strDest;
{
LOCK(m_addr_fetches_mutex);
@@ -1595,6 +1616,7 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
FastRandomContext rng;
// Connect to specific addresses
@@ -1602,7 +1624,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
for (int64_t nLoop = 0;; nLoop++)
{
- ProcessAddrFetch();
for (const std::string& strAddr : connect)
{
CAddress addr(CService(), NODE_NONE);
@@ -1671,7 +1692,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Therefore, we do not add them to addrman in the first place.
// In case previously unreachable networks become reachable
// (e.g. in case of -onlynet changes by the user), fixed seeds will
- // be loaded only for networks for which we have no addressses.
+ // be loaded only for networks for which we have no addresses.
seed_addrs.erase(std::remove_if(seed_addrs.begin(), seed_addrs.end(),
[&fixed_seed_networks](const CAddress& addr) { return fixed_seed_networks.count(addr.GetNetwork()) == 0; }),
seed_addrs.end());
@@ -1788,7 +1809,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
!HasAllDesirableServiceFlags(addr.nServices) ||
setConnected.count(m_netgroupman.GetGroup(addr))) continue;
addrConnect = addr;
- LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
+ LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToStringAddrPort());
break;
}
@@ -1868,7 +1889,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
return;
}
- LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
+ LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
}
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, conn_type);
@@ -1945,6 +1966,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
void CConnman::ThreadOpenAddedConnections()
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
while (true)
{
@@ -1974,6 +1996,7 @@ void CConnman::ThreadOpenAddedConnections()
// if successful, this moves the passed grant to the constructed node
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
//
@@ -2097,7 +2120,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
- strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToString());
+ strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToStringAddrPort());
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2137,13 +2160,13 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
if (sock->Bind(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
- strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
+ strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToStringAddrPort(), PACKAGE_NAME);
else
- strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
+ strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToStringAddrPort(), NetworkErrorString(nErr));
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
- LogPrintf("Bound to %s\n", addrBind.ToString());
+ LogPrintf("Bound to %s\n", addrBind.ToStringAddrPort());
// Listen for incoming connections
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR)
@@ -2173,7 +2196,7 @@ void Discover()
for (const CNetAddr &addr : vaddr)
{
if (AddLocal(addr, LOCAL_IF))
- LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString());
+ LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToStringAddr());
}
}
}
@@ -2193,14 +2216,14 @@ void Discover()
struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
CNetAddr addr(s4->sin_addr);
if (AddLocal(addr, LOCAL_IF))
- LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
+ LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToStringAddr());
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
CNetAddr addr(s6->sin6_addr);
if (AddLocal(addr, LOCAL_IF))
- LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
+ LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToStringAddr());
}
}
freeifaddrs(myaddrs);
@@ -2759,7 +2782,7 @@ CNode::CNode(NodeId idIn,
m_connected{GetTime<std::chrono::seconds>()},
addr{addrIn},
addrBind{addrBindIn},
- m_addr_name{addrNameIn.empty() ? addr.ToStringIPPort() : addrNameIn},
+ m_addr_name{addrNameIn.empty() ? addr.ToStringAddrPort() : addrNameIn},
m_inbound_onion{inbound_onion},
m_prefer_evict{node_opts.prefer_evict},
nKeyedNetGroup{nKeyedNetGroupIn},
@@ -2865,7 +2888,7 @@ void CaptureMessageToFile(const CAddress& addr,
auto now = GetTime<std::chrono::microseconds>();
// Windows folder names cannot include a colon
- std::string clean_addr = addr.ToString();
+ std::string clean_addr = addr.ToStringAddrPort();
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr);
diff --git a/src/net.h b/src/net.h
index 666e0f3b8a..2025dfdb05 100644
--- a/src/net.h
+++ b/src/net.h
@@ -38,6 +38,7 @@
#include <map>
#include <memory>
#include <optional>
+#include <queue>
#include <thread>
#include <unordered_set>
#include <vector>
@@ -744,7 +745,7 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -820,7 +821,7 @@ public:
* - Max total outbound connection capacity filled
* - Max connection capacity for type is filled
*/
- bool AddConnection(const std::string& address, ConnectionType conn_type);
+ bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
size_t GetNodeCount(ConnectionDirection) const;
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
@@ -886,10 +887,10 @@ private:
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
bool InitBinds(const Options& options);
- void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
+ void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_unused_i2p_sessions_mutex);
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
- void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
- void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex);
+ void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_unused_i2p_sessions_mutex);
+ void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex, !m_unused_i2p_sessions_mutex);
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
void ThreadI2PAcceptIncoming();
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -956,7 +957,7 @@ private:
bool AlreadyConnectedToAddress(const CAddress& addr);
bool AttemptToEvictConnection();
- CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
+ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void DeleteNode(CNode* pnode);
@@ -1134,6 +1135,26 @@ private:
std::vector<CService> m_onion_binds;
/**
+ * Mutex protecting m_i2p_sam_sessions.
+ */
+ Mutex m_unused_i2p_sessions_mutex;
+
+ /**
+ * A pool of created I2P SAM transient sessions that should be used instead
+ * of creating new ones in order to reduce the load on the I2P network.
+ * Creating a session in I2P is not cheap, thus if this is not empty, then
+ * pick an entry from it instead of creating a new session. If connecting to
+ * a host fails, then the created session is put to this pool for reuse.
+ */
+ std::queue<std::unique_ptr<i2p::sam::Session>> m_unused_i2p_sessions GUARDED_BY(m_unused_i2p_sessions_mutex);
+
+ /**
+ * Cap on the size of `m_unused_i2p_sessions`, to ensure it does not
+ * unexpectedly use too much memory.
+ */
+ static constexpr size_t MAX_UNUSED_I2P_SESSIONS_SIZE{10};
+
+ /**
* RAII helper to atomically create a copy of `m_nodes` and add a reference
* to each of the nodes. The nodes are released when this object is destroyed.
*/
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index a659300a0d..25c65c7090 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1397,7 +1397,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
nonce, strSubVersion, nNodeStartingHeight, tx_relay));
if (fLogIPs) {
- LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, them=%s, txrelay=%d, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addr_you.ToString(), tx_relay, nodeid);
+ LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, them=%s, txrelay=%d, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addr_you.ToStringAddrPort(), tx_relay, nodeid);
} else {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, txrelay=%d, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, tx_relay, nodeid);
}
@@ -3231,7 +3231,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Disconnect if we connected to ourself
if (pfrom.IsInboundConn() && !m_connman.CheckIncomingNonce(nNonce))
{
- LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
+ LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToStringAddrPort());
pfrom.fDisconnect = true;
return;
}
@@ -3358,11 +3358,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
std::string remoteAddr;
if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom.addr.ToString();
+ remoteAddr = ", peeraddr=" + pfrom.addr.ToStringAddrPort();
LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, txrelay=%d, peer=%d%s\n",
cleanSubVer, pfrom.nVersion,
- peer->m_starting_height, addrMe.ToString(), fRelay, pfrom.GetId(),
+ peer->m_starting_height, addrMe.ToStringAddrPort(), fRelay, pfrom.GetId(),
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
@@ -3405,7 +3405,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (!pfrom.IsInboundConn()) {
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
pfrom.nVersion.load(), peer->m_starting_height,
- pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""),
+ pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToStringAddrPort()) : ""),
pfrom.ConnectionTypeAsString());
}
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 782b692d30..85ae8fab36 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -599,7 +599,7 @@ std::string OnionToString(Span<const uint8_t> addr)
return EncodeBase32(address) + ".onion";
}
-std::string CNetAddr::ToStringIP() const
+std::string CNetAddr::ToStringAddr() const
{
switch (m_net) {
case NET_IPV4:
@@ -622,11 +622,6 @@ std::string CNetAddr::ToStringIP() const
assert(false);
}
-std::string CNetAddr::ToString() const
-{
- return ToStringIP();
-}
-
bool operator==(const CNetAddr& a, const CNetAddr& b)
{
return a.m_net == b.m_net && a.m_addr == b.m_addr;
@@ -916,25 +911,17 @@ std::vector<unsigned char> CService::GetKey() const
return key;
}
-std::string CService::ToStringPort() const
+std::string CService::ToStringAddrPort() const
{
- return strprintf("%u", port);
-}
+ const auto port_str = strprintf("%u", port);
-std::string CService::ToStringIPPort() const
-{
if (IsIPv4() || IsTor() || IsI2P() || IsInternal()) {
- return ToStringIP() + ":" + ToStringPort();
+ return ToStringAddr() + ":" + port_str;
} else {
- return "[" + ToStringIP() + "]:" + ToStringPort();
+ return "[" + ToStringAddr() + "]:" + port_str;
}
}
-std::string CService::ToString() const
-{
- return ToStringIPPort();
-}
-
CSubNet::CSubNet():
valid(false)
{
@@ -1098,7 +1085,7 @@ std::string CSubNet::ToString() const
break;
}
- return network.ToString() + suffix;
+ return network.ToStringAddr() + suffix;
}
bool CSubNet::IsValid() const
@@ -1106,29 +1093,6 @@ bool CSubNet::IsValid() const
return valid;
}
-bool CSubNet::SanityCheck() const
-{
- switch (network.m_net) {
- case NET_IPV4:
- case NET_IPV6:
- break;
- case NET_ONION:
- case NET_I2P:
- case NET_CJDNS:
- return true;
- case NET_INTERNAL:
- case NET_UNROUTABLE:
- case NET_MAX:
- return false;
- }
-
- for (size_t x = 0; x < network.m_addr.size(); ++x) {
- if (network.m_addr[x] & ~netmask[x]) return false;
- }
-
- return true;
-}
-
bool operator==(const CSubNet& a, const CSubNet& b)
{
return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
diff --git a/src/netaddress.h b/src/netaddress.h
index 7f782674d3..3d15b0b123 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -193,8 +193,7 @@ public:
bool IsAddrV1Compatible() const;
enum Network GetNetwork() const;
- std::string ToString() const;
- std::string ToStringIP() const;
+ std::string ToStringAddr() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
Network GetNetClass() const;
@@ -476,8 +475,6 @@ protected:
/// Is this value valid? (only used to signal parse errors)
bool valid;
- bool SanityCheck() const;
-
public:
/**
* Construct an invalid subnet (empty, `Match()` always returns false).
@@ -536,9 +533,7 @@ public:
friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
friend bool operator<(const CService& a, const CService& b);
std::vector<unsigned char> GetKey() const;
- std::string ToString() const;
- std::string ToStringPort() const;
- std::string ToStringIPPort() const;
+ std::string ToStringAddrPort() const;
CService(const struct in6_addr& ipv6Addr, uint16_t port);
explicit CService(const struct sockaddr_in6& addr);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index fac4b3b5d5..797f1e17f2 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -488,7 +488,7 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToString());
+ LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToStringAddrPort());
return nullptr;
}
@@ -549,11 +549,11 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (sock.Get() == INVALID_SOCKET) {
- LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToString());
+ LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToStringAddrPort());
return false;
}
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
+ LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToStringAddrPort());
return false;
}
@@ -570,11 +570,11 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
Sock::Event occurred;
if (!sock.Wait(std::chrono::milliseconds{nTimeout}, requested, &occurred)) {
LogPrintf("wait for connect to %s failed: %s\n",
- addrConnect.ToString(),
+ addrConnect.ToStringAddrPort(),
NetworkErrorString(WSAGetLastError()));
return false;
} else if (occurred == 0) {
- LogPrint(BCLog::NET, "connection attempt to %s timed out\n", addrConnect.ToString());
+ LogPrint(BCLog::NET, "connection attempt to %s timed out\n", addrConnect.ToStringAddrPort());
return false;
}
@@ -586,13 +586,13 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
socklen_t sockerr_len = sizeof(sockerr);
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) ==
SOCKET_ERROR) {
- LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
return false;
}
if (sockerr != 0) {
LogConnectFailure(manual_connection,
"connect() to %s failed after wait: %s",
- addrConnect.ToString(),
+ addrConnect.ToStringAddrPort(),
NetworkErrorString(sockerr));
return false;
}
@@ -603,7 +603,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
else
#endif
{
- LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
+ LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
return false;
}
}
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index ba1024d22e..41c0ff2118 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -64,7 +64,12 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
pblocktree.reset();
- pblocktree.reset(new CBlockTreeDB(cache_sizes.block_tree_db, options.block_tree_db_in_memory, options.reindex));
+ pblocktree = std::make_unique<CBlockTreeDB>(DBParams{
+ .path = chainman.m_options.datadir / "blocks" / "index",
+ .cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
+ .memory_only = options.block_tree_db_in_memory,
+ .wipe_data = options.reindex,
+ .options = chainman.m_options.block_tree_db});
if (options.reindex) {
pblocktree->WriteReindexing(true);
@@ -187,12 +192,23 @@ ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const C
"Only rebuild the block database if you are sure that your computer's date and time are correct")};
}
- if (!CVerifyDB().VerifyDB(
- *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
- options.check_level,
- options.check_blocks)) {
+ VerifyDBResult result = CVerifyDB().VerifyDB(
+ *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
+ options.check_level,
+ options.check_blocks);
+ switch (result) {
+ case VerifyDBResult::SUCCESS:
+ case VerifyDBResult::INTERRUPTED:
+ case VerifyDBResult::SKIPPED_MISSING_BLOCKS:
+ break;
+ case VerifyDBResult::CORRUPTED_BLOCK_DB:
return {ChainstateLoadStatus::FAILURE, _("Corrupted block database detected")};
- }
+ case VerifyDBResult::SKIPPED_L3_CHECKS:
+ if (options.require_full_verification) {
+ return {ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE, _("Insufficient dbcache for block verification")};
+ }
+ break;
+ } // no default case, so the compiler can warn about missing cases
}
}
diff --git a/src/node/chainstate.h b/src/node/chainstate.h
index d3c7656bf2..7838a62d0c 100644
--- a/src/node/chainstate.h
+++ b/src/node/chainstate.h
@@ -25,6 +25,7 @@ struct ChainstateLoadOptions {
bool reindex{false};
bool reindex_chainstate{false};
bool prune{false};
+ bool require_full_verification{true};
int64_t check_blocks{DEFAULT_CHECKBLOCKS};
int64_t check_level{DEFAULT_CHECKLEVEL};
std::function<bool()> check_interrupt;
@@ -35,7 +36,13 @@ struct ChainstateLoadOptions {
//! case, and treat other cases as errors. More complex applications may want to
//! try reindexing in the generic failure case, and pass an interrupt callback
//! and exit cleanly in the interrupted case.
-enum class ChainstateLoadStatus { SUCCESS, FAILURE, FAILURE_INCOMPATIBLE_DB, INTERRUPTED };
+enum class ChainstateLoadStatus {
+ SUCCESS,
+ FAILURE,
+ FAILURE_INCOMPATIBLE_DB,
+ FAILURE_INSUFFICIENT_DBCACHE,
+ INTERRUPTED,
+};
//! Chainstate load status code and optional error string.
using ChainstateLoadResult = std::tuple<ChainstateLoadStatus, bilingual_str>;
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
index b0d929626b..9801e6e959 100644
--- a/src/node/chainstatemanager_args.cpp
+++ b/src/node/chainstatemanager_args.cpp
@@ -5,6 +5,9 @@
#include <node/chainstatemanager_args.h>
#include <arith_uint256.h>
+#include <kernel/chainstatemanager_opts.h>
+#include <node/coins_view_args.h>
+#include <node/database_args.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/strencodings.h>
@@ -34,6 +37,10 @@ std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& args, Chains
if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value};
+ ReadDatabaseArgs(args, opts.block_tree_db);
+ ReadDatabaseArgs(args, opts.coins_db);
+ ReadCoinsViewArgs(args, opts.coins_view);
+
return std::nullopt;
}
} // namespace node
diff --git a/src/node/coins_view_args.cpp b/src/node/coins_view_args.cpp
new file mode 100644
index 0000000000..67c9b8dbac
--- /dev/null
+++ b/src/node/coins_view_args.cpp
@@ -0,0 +1,16 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/coins_view_args.h>
+
+#include <txdb.h>
+#include <util/system.h>
+
+namespace node {
+void ReadCoinsViewArgs(const ArgsManager& args, CoinsViewOptions& options)
+{
+ if (auto value = args.GetIntArg("-dbbatchsize")) options.batch_write_bytes = *value;
+ if (auto value = args.GetIntArg("-dbcrashratio")) options.simulate_crash_ratio = *value;
+}
+} // namespace node
diff --git a/src/node/coins_view_args.h b/src/node/coins_view_args.h
new file mode 100644
index 0000000000..71a7a671fd
--- /dev/null
+++ b/src/node/coins_view_args.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NODE_COINS_VIEW_ARGS_H
+#define BITCOIN_NODE_COINS_VIEW_ARGS_H
+
+class ArgsManager;
+struct CoinsViewOptions;
+
+namespace node {
+void ReadCoinsViewArgs(const ArgsManager& args, CoinsViewOptions& options);
+} // namespace node
+
+#endif // BITCOIN_NODE_COINS_VIEW_ARGS_H
diff --git a/src/node/database_args.cpp b/src/node/database_args.cpp
new file mode 100644
index 0000000000..2c53b4b47e
--- /dev/null
+++ b/src/node/database_args.cpp
@@ -0,0 +1,18 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/database_args.h>
+
+#include <dbwrapper.h>
+#include <util/system.h>
+
+namespace node {
+void ReadDatabaseArgs(const ArgsManager& args, DBOptions& options)
+{
+ // Settings here apply to all databases (chainstate, blocks, and index
+ // databases), but it'd be easy to parse database-specific options by adding
+ // a database_type string or enum parameter to this function.
+ if (auto value = args.GetBoolArg("-forcecompactdb")) options.force_compact = *value;
+}
+} // namespace node
diff --git a/src/node/database_args.h b/src/node/database_args.h
new file mode 100644
index 0000000000..001976f219
--- /dev/null
+++ b/src/node/database_args.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NODE_DATABASE_ARGS_H
+#define BITCOIN_NODE_DATABASE_ARGS_H
+
+class ArgsManager;
+struct DBOptions;
+
+namespace node {
+void ReadDatabaseArgs(const ArgsManager& args, DBOptions& options);
+} // namespace node
+
+#endif // BITCOIN_NODE_DATABASE_ARGS_H
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index c2b6fd1dc3..c7bc9a9a3d 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -56,34 +56,27 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
block.hashMerkleRoot = BlockMerkleRoot(block);
}
-BlockAssembler::Options::Options()
+static BlockAssembler::Options ClampOptions(BlockAssembler::Options options)
{
- blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
- nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
- test_block_validity = true;
+ // Limit weight to between 4K and DEFAULT_BLOCK_MAX_WEIGHT for sanity:
+ options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, 4000, DEFAULT_BLOCK_MAX_WEIGHT);
+ return options;
}
BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options)
- : test_block_validity{options.test_block_validity},
- chainparams{chainstate.m_chainman.GetParams()},
- m_mempool(mempool),
- m_chainstate(chainstate)
+ : chainparams{chainstate.m_chainman.GetParams()},
+ m_mempool{mempool},
+ m_chainstate{chainstate},
+ m_options{ClampOptions(options)}
{
- blockMinFeeRate = options.blockMinFeeRate;
- // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity:
- nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight));
}
-void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& options)
+void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& options)
{
// Block resource limits
- // If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
- options.nBlockMaxWeight = gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
- if (gArgs.IsArgSet("-blockmintxfee")) {
- std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", ""));
- options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)};
- } else {
- options.blockMinFeeRate = CFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
+ options.nBlockMaxWeight = args.GetIntArg("-blockmaxweight", options.nBlockMaxWeight);
+ if (const auto blockmintxfee{args.GetArg("-blockmintxfee")}) {
+ if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed};
}
}
static BlockAssembler::Options ConfiguredOptions()
@@ -176,7 +169,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- if (test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,
+ if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,
GetAdjustedTime, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
@@ -205,7 +198,7 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet)
bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const
{
// TODO: switch to weight-based accounting for packages instead of vsize-based accounting.
- if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= nBlockMaxWeight) {
+ if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= m_options.nBlockMaxWeight) {
return false;
}
if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST) {
@@ -377,7 +370,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
packageSigOpsCost = modit->nSigOpCostWithAncestors;
}
- if (packageFees < blockMinFeeRate.GetFee(packageSize)) {
+ if (packageFees < m_options.blockMinFeeRate.GetFee(packageSize)) {
// Everything else we might consider has a lower fee rate
return;
}
@@ -394,7 +387,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
- nBlockMaxWeight - 4000) {
+ m_options.nBlockMaxWeight - 4000) {
// Give up if we're close to full and haven't succeeded in a while
break;
}
diff --git a/src/node/miner.h b/src/node/miner.h
index ea9e470a64..f1ccffff55 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_NODE_MINER_H
#define BITCOIN_NODE_MINER_H
+#include <policy/policy.h>
#include <primitives/block.h>
#include <txmempool.h>
@@ -132,13 +133,6 @@ private:
// The constructed block template
std::unique_ptr<CBlockTemplate> pblocktemplate;
- // Configuration parameters for the block size
- unsigned int nBlockMaxWeight;
- CFeeRate blockMinFeeRate;
-
- // Whether to call TestBlockValidity() at the end of CreateNewBlock().
- const bool test_block_validity;
-
// Information on the current status of the block
uint64_t nBlockWeight;
uint64_t nBlockTx;
@@ -156,10 +150,11 @@ private:
public:
struct Options {
- Options();
- size_t nBlockMaxWeight;
- CFeeRate blockMinFeeRate;
- bool test_block_validity;
+ // Configuration parameters for the block size
+ size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT};
+ CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
+ // Whether to call TestBlockValidity() at the end of CreateNewBlock().
+ bool test_block_validity{true};
};
explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool);
@@ -172,6 +167,8 @@ public:
inline static std::optional<int64_t> m_last_block_weight{};
private:
+ const Options m_options;
+
// utility functions
/** Clear the block's state and prepare for assembling a new block */
void resetBlock();
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index d15aba5cdd..0a96be038b 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -89,11 +89,10 @@ void AskPassphraseDialog::accept()
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE);
- // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make this input mlock()'d to begin with.
- oldpass.assign(ui->passEdit1->text().toStdString().c_str());
- newpass1.assign(ui->passEdit2->text().toStdString().c_str());
- newpass2.assign(ui->passEdit3->text().toStdString().c_str());
+
+ oldpass.assign(std::string_view{ui->passEdit1->text().toStdString()});
+ newpass1.assign(std::string_view{ui->passEdit2->text().toStdString()});
+ newpass2.assign(std::string_view{ui->passEdit3->text().toStdString()});
secureClearPassFields();
@@ -154,8 +153,19 @@ void AskPassphraseDialog::accept()
case Unlock:
try {
if (!model->setWalletLocked(false, oldpass)) {
- QMessageBox::critical(this, tr("Wallet unlock failed"),
- tr("The passphrase entered for the wallet decryption was incorrect."));
+ // Check if the passphrase has a null character (see #27067 for details)
+ if (oldpass.find('\0') == std::string::npos) {
+ QMessageBox::critical(this, tr("Wallet unlock failed"),
+ tr("The passphrase entered for the wallet decryption was incorrect."));
+ } else {
+ QMessageBox::critical(this, tr("Wallet unlock failed"),
+ tr("The passphrase entered for the wallet decryption is incorrect. "
+ "It contains a null character (ie - a zero byte). "
+ "If the passphrase was set with a version of this software prior to 25.0, "
+ "please try again with only the characters up to — but not including — "
+ "the first null character. If this is successful, please set a new "
+ "passphrase to avoid this issue in the future."));
+ }
} else {
QDialog::accept(); // Success
}
@@ -174,8 +184,18 @@ void AskPassphraseDialog::accept()
}
else
{
- QMessageBox::critical(this, tr("Wallet encryption failed"),
- tr("The passphrase entered for the wallet decryption was incorrect."));
+ // Check if the old passphrase had a null character (see #27067 for details)
+ if (oldpass.find('\0') == std::string::npos) {
+ QMessageBox::critical(this, tr("Passphrase change failed"),
+ tr("The passphrase entered for the wallet decryption was incorrect."));
+ } else {
+ QMessageBox::critical(this, tr("Passphrase change failed"),
+ tr("The old passphrase entered for the wallet decryption is incorrect. "
+ "It contains a null character (ie - a zero byte). "
+ "If the passphrase was set with a version of this software prior to 25.0, "
+ "please try again with only the characters up to — but not including — "
+ "the first null character."));
+ }
}
}
else
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 59f433749d..c383c8bd58 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -167,6 +167,7 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans
static bool InitSettings()
{
+ gArgs.EnsureDataDir();
if (!gArgs.GetSettingsPath()) {
return true; // Do nothing if settings file disabled.
}
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 69ed5d3a90..8411ec4696 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -280,7 +280,7 @@ bool ClientModel::getProxyInfo(std::string& ip_port) const
{
Proxy ipv4, ipv6;
if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
- ip_port = ipv4.proxy.ToStringIPPort();
+ ip_port = ipv4.proxy.ToStringAddrPort();
return true;
}
return false;
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 53b0c3832b..6dec4b2e42 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -406,24 +406,21 @@ void OptionsDialog::updateProxyValidationState()
void OptionsDialog::updateDefaultProxyNets()
{
+ CNetAddr ui_proxy_netaddr;
+ LookupHost(ui->proxyIp->text().toStdString(), ui_proxy_netaddr, /*fAllowLookup=*/false);
+ const CService ui_proxy{ui_proxy_netaddr, ui->proxyPort->text().toUShort()};
+
Proxy proxy;
- std::string strProxy;
- QString strDefaultProxyGUI;
-
- model->node().getProxy(NET_IPV4, proxy);
- strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
- strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
- (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false);
-
- model->node().getProxy(NET_IPV6, proxy);
- strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
- strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
- (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false);
-
- model->node().getProxy(NET_ONION, proxy);
- strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
- strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
- (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
+ bool has_proxy;
+
+ has_proxy = model->node().getProxy(NET_IPV4, proxy);
+ ui->proxyReachIPv4->setChecked(has_proxy && proxy.proxy == ui_proxy);
+
+ has_proxy = model->node().getProxy(NET_IPV6, proxy);
+ ui->proxyReachIPv6->setChecked(has_proxy && proxy.proxy == ui_proxy);
+
+ has_proxy = model->node().getProxy(NET_ONION, proxy);
+ ui->proxyReachTor->setChecked(has_proxy && proxy.proxy == ui_proxy);
}
ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 7f95d527f0..bee8fafddc 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -18,6 +18,7 @@
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
#include <util/string.h>
+#include <util/system.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
@@ -668,6 +669,11 @@ bool OptionsModel::isRestartRequired() const
return settings.value("fRestartRequired", false).toBool();
}
+bool OptionsModel::hasSigner()
+{
+ return gArgs.GetArg("-signer", "") != "";
+}
+
void OptionsModel::checkAndMigrate()
{
// Migration of default values
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index a5da4dfaeb..f28a1087ba 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -99,6 +99,9 @@ public:
bool getEnablePSBTControls() const { return m_enable_psbt_controls; }
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
+ /** Whether -signer was set or not */
+ bool hasSigner();
+
/* Explicit setters */
void SetPruneTargetGB(int prune_target_gb);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 33145cc48d..89dd0ada62 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -203,7 +203,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
if (model->wallet().hasExternalSigner()) {
//: "device" usually means a hardware wallet.
ui->sendButton->setText(tr("Sign on device"));
- if (gArgs.GetArg("-signer", "") != "") {
+ if (model->getOptionsModel()->hasSigner()) {
ui->sendButton->setEnabled(true);
ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
} else {
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 049326070e..d005e08d14 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -75,7 +75,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_loader = wallet_loader.get();
node.setContext(&test.m_node);
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
{
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 15fe37c164..62f2019438 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -159,7 +159,7 @@ void TestGUI(interfaces::Node& node)
auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_loader = wallet_loader.get();
node.setContext(&test.m_node);
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
{
diff --git a/src/random.cpp b/src/random.cpp
index 5f50c001cd..432592589a 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -8,23 +8,22 @@
#include <compat/cpuid.h>
#include <crypto/sha256.h>
#include <crypto/sha512.h>
-#include <support/cleanse.h>
-#ifdef WIN32
-#include <compat/compat.h>
-#include <wincrypt.h>
-#endif
#include <logging.h>
#include <randomenv.h>
-#include <support/allocators/secure.h>
#include <span.h>
-#include <sync.h> // for Mutex
-#include <util/time.h> // for GetTimeMicros()
+#include <support/allocators/secure.h>
+#include <support/cleanse.h>
+#include <sync.h>
+#include <util/time.h>
#include <cmath>
#include <cstdlib>
#include <thread>
-#ifndef WIN32
+#ifdef WIN32
+#include <windows.h>
+#include <wincrypt.h>
+#else
#include <fcntl.h>
#include <sys/time.h>
#endif
@@ -634,7 +633,7 @@ bool Random_SanityCheck()
* GetOSRand() overwrites all 32 bytes of the output given a maximum
* number of tries.
*/
- static const ssize_t MAX_TRIES = 1024;
+ static constexpr int MAX_TRIES{1024};
uint8_t data[NUM_OS_RANDOM_BYTES];
bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */
int num_overwritten;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 8bee066ab8..28a619fe54 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1110,7 +1110,7 @@ static RPCHelpMan verifychain()
{"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
},
RPCResult{
- RPCResult::Type::BOOL, "", "Verified or not"},
+ RPCResult::Type::BOOL, "", "Verification finished successfully. If false, check debug.log for reason."},
RPCExamples{
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
@@ -1125,7 +1125,7 @@ static RPCHelpMan verifychain()
Chainstate& active_chainstate = chainman.ActiveChainstate();
return CVerifyDB().VerifyDB(
- active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
+ active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth) == VerifyDBResult::SUCCESS;
},
};
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 5fe914f0a1..eb91a151b5 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -114,6 +114,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransactionwithkey", 2, "prevtxs" },
{ "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "maxfeerate" },
+ { "sendrawtransaction", 2, "maxburnamount" },
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "maxfeerate" },
{ "submitpackage", 0, "package" },
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 44f7435a26..3a69e2d8a2 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -18,6 +18,7 @@
#include <rpc/server.h>
#include <rpc/server_util.h>
#include <rpc/util.h>
+#include <script/standard.h>
#include <txmempool.h>
#include <univalue.h>
#include <util/moneystr.h>
@@ -44,7 +45,11 @@ static RPCHelpMan sendrawtransaction()
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
- "/kvB.\nSet to 0 to accept any fee rate.\n"},
+ "/kvB.\nSet to 0 to accept any fee rate."},
+ {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)},
+ "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
+ "If burning funds through unspendable outputs is desired, increase this value.\n"
+ "This check is based on heuristics and does not guarantee spendability of outputs.\n"},
},
RPCResult{
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
@@ -61,10 +66,19 @@ static RPCHelpMan sendrawtransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
+
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
}
+
+ for (const auto& out : mtx.vout) {
+ if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
+ throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
+ }
+ }
+
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
@@ -839,15 +853,16 @@ static RPCHelpMan submitpackage()
NONFATAL_UNREACHABLE();
}
}
+ size_t num_broadcast{0};
for (const auto& tx : txns) {
- size_t num_submitted{0};
std::string err_string;
- const auto err = BroadcastTransaction(node, tx, err_string, 0, true, true);
+ const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err,
strprintf("transaction broadcast failed: %s (all transactions were submitted, %d transactions were broadcast successfully)",
- err_string, num_submitted));
+ err_string, num_broadcast));
}
+ num_broadcast++;
}
UniValue rpc_result{UniValue::VOBJ};
UniValue tx_result_map{UniValue::VOBJ};
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 618a5d0dd4..7ffa777ef4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -196,7 +196,7 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("id", stats.nodeid);
obj.pushKV("addr", stats.m_addr_name);
if (stats.addrBind.IsValid()) {
- obj.pushKV("addrbind", stats.addrBind.ToString());
+ obj.pushKV("addrbind", stats.addrBind.ToStringAddrPort());
}
if (!(stats.addrLocal.empty())) {
obj.pushKV("addrlocal", stats.addrLocal);
@@ -496,7 +496,7 @@ static RPCHelpMan getaddednodeinfo()
UniValue addresses(UniValue::VARR);
if (info.fConnected) {
UniValue address(UniValue::VOBJ);
- address.pushKV("address", info.resolvedAddress.ToString());
+ address.pushKV("address", info.resolvedAddress.ToStringAddrPort());
address.pushKV("connected", info.fInbound ? "inbound" : "outbound");
addresses.push_back(address);
}
@@ -571,7 +571,7 @@ static UniValue GetNetworksInfo()
obj.pushKV("name", GetNetworkName(network));
obj.pushKV("limited", !IsReachable(network));
obj.pushKV("reachable", IsReachable(network));
- obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string());
+ obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
networks.push_back(obj);
}
@@ -664,7 +664,7 @@ static RPCHelpMan getnetworkinfo()
for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
{
UniValue rec(UniValue::VOBJ);
- rec.pushKV("address", item.first.ToString());
+ rec.pushKV("address", item.first.ToStringAddr());
rec.pushKV("port", item.second.nPort);
rec.pushKV("score", item.second.nScore);
localAddresses.push_back(rec);
@@ -901,7 +901,7 @@ static RPCHelpMan getnodeaddresses()
UniValue obj(UniValue::VOBJ);
obj.pushKV("time", int64_t{TicksSinceEpoch<std::chrono::seconds>(addr.nTime)});
obj.pushKV("services", (uint64_t)addr.nServices);
- obj.pushKV("address", addr.ToStringIP());
+ obj.pushKV("address", addr.ToStringAddr());
obj.pushKV("port", addr.GetPort());
obj.pushKV("network", GetNetworkName(addr.GetNetClass()));
ret.push_back(obj);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 503099d8a9..5ed8aee9ea 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -522,14 +522,14 @@ static RPCHelpMan decodescript()
if (can_wrap_P2WSH) {
UniValue sr(UniValue::VOBJ);
CScript segwitScr;
- FillableSigningProvider provider;
+ FlatSigningProvider provider;
if (which_type == TxoutType::PUBKEY) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0])));
} else if (which_type == TxoutType::PUBKEYHASH) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]}));
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
- provider.AddCScript(script);
+ provider.scripts[CScriptID(script)] = script;
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
ScriptToUniv(segwitScr, /*out=*/sr, /*include_hex=*/true, /*include_address=*/true, /*provider=*/&provider);
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 15b8e1dcd0..3ba930f84f 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -21,12 +21,8 @@
#include <util/strencodings.h>
#include <util/translation.h>
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf)
+void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optional<bool> rbf)
{
- if (outputs_in.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
- }
-
UniValue inputs;
if (inputs_in.isNull()) {
inputs = UniValue::VARR;
@@ -34,18 +30,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
inputs = inputs_in.get_array();
}
- const bool outputs_is_obj = outputs_in.isObject();
- UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
-
- CMutableTransaction rawTx;
-
- if (!locktime.isNull()) {
- int64_t nLockTime = locktime.getInt<int64_t>();
- if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
- rawTx.nLockTime = nLockTime;
- }
-
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
@@ -84,6 +68,16 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vin.push_back(in);
}
+}
+
+void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in)
+{
+ if (outputs_in.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
+ }
+
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
if (!outputs_is_obj) {
// Translate array of key-value pairs into dict
@@ -132,6 +126,21 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vout.push_back(out);
}
}
+}
+
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf)
+{
+ CMutableTransaction rawTx;
+
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.getInt<int64_t>();
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ AddInputs(rawTx, inputs_in, rbf);
+ AddOutputs(rawTx, outputs_in);
if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 0c3823bc1e..a863432b7a 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -38,6 +38,13 @@ void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const
*/
void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
+
+/** Normalize univalue-represented inputs and add them to the transaction */
+void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, bool rbf);
+
+/** Normalize univalue-represented outputs and add them to the transaction */
+void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in);
+
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf);
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 03b157a847..5f4a1aceb2 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1439,7 +1439,7 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
hashOutputs = SHA256Uint256(m_outputs_single_hash);
m_bip143_segwit_ready = true;
}
- if (uses_bip341_taproot) {
+ if (uses_bip341_taproot && m_spent_outputs_ready) {
m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs);
m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs);
m_bip341_taproot_ready = true;
diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h
index c6bd685189..a0918bf463 100644
--- a/src/support/allocators/secure.h
+++ b/src/support/allocators/secure.h
@@ -56,6 +56,7 @@ struct secure_allocator : public std::allocator<T> {
};
// This is exactly like std::string, but with a custom allocator.
+// TODO: Consider finding a way to make incoming RPC request.params[i] mlock()ed as well
typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;
#endif // BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 24ae4bdd1e..f92d1d8fb7 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -42,12 +42,12 @@ static inline size_t align_up(size_t x, size_t align)
// Implementation: Arena
Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
- base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
+ base(base_in), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
{
// Start with one free chunk that covers the entire arena
auto it = size_to_free_chunk.emplace(size_in, base);
chunks_free.emplace(base, it);
- chunks_free_end.emplace(base + size_in, it);
+ chunks_free_end.emplace(static_cast<char*>(base) + size_in, it);
}
Arena::~Arena()
@@ -73,8 +73,9 @@ void* Arena::alloc(size_t size)
// Create the used-chunk, taking its space from the end of the free-chunk
const size_t size_remaining = size_ptr_it->first - size;
- auto allocated = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first;
- chunks_free_end.erase(size_ptr_it->second + size_ptr_it->first);
+ char* const free_chunk = static_cast<char*>(size_ptr_it->second);
+ auto allocated = chunks_used.emplace(free_chunk + size_remaining, size).first;
+ chunks_free_end.erase(free_chunk + size_ptr_it->first);
if (size_ptr_it->first == size) {
// whole chunk is used up
chunks_free.erase(size_ptr_it->second);
@@ -82,11 +83,11 @@ void* Arena::alloc(size_t size)
// still some memory left in the chunk
auto it_remaining = size_to_free_chunk.emplace(size_remaining, size_ptr_it->second);
chunks_free[size_ptr_it->second] = it_remaining;
- chunks_free_end.emplace(size_ptr_it->second + size_remaining, it_remaining);
+ chunks_free_end.emplace(free_chunk + size_remaining, it_remaining);
}
size_to_free_chunk.erase(size_ptr_it);
- return reinterpret_cast<void*>(allocated->first);
+ return allocated->first;
}
void Arena::free(void *ptr)
@@ -97,11 +98,11 @@ void Arena::free(void *ptr)
}
// Remove chunk from used map
- auto i = chunks_used.find(static_cast<char*>(ptr));
+ auto i = chunks_used.find(ptr);
if (i == chunks_used.end()) {
throw std::runtime_error("Arena: invalid or double free");
}
- std::pair<char*, size_t> freed = *i;
+ auto freed = std::make_pair(static_cast<char*>(i->first), i->second);
chunks_used.erase(i);
// coalesce freed with previous chunk
diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h
index 1bba459377..81e0df513a 100644
--- a/src/support/lockedpool.h
+++ b/src/support/lockedpool.h
@@ -89,23 +89,23 @@ public:
*/
bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; }
private:
- typedef std::multimap<size_t, char*> SizeToChunkSortedMap;
+ typedef std::multimap<size_t, void*> SizeToChunkSortedMap;
/** Map to enable O(log(n)) best-fit allocation, as it's sorted by size */
SizeToChunkSortedMap size_to_free_chunk;
- typedef std::unordered_map<char*, SizeToChunkSortedMap::const_iterator> ChunkToSizeMap;
+ typedef std::unordered_map<void*, SizeToChunkSortedMap::const_iterator> ChunkToSizeMap;
/** Map from begin of free chunk to its node in size_to_free_chunk */
ChunkToSizeMap chunks_free;
/** Map from end of free chunk to its node in size_to_free_chunk */
ChunkToSizeMap chunks_free_end;
/** Map from begin of used chunk to its size */
- std::unordered_map<char*, size_t> chunks_used;
+ std::unordered_map<void*, size_t> chunks_used;
/** Base address of arena */
- char* base;
+ void* base;
/** End address of arena */
- char* end;
+ void* end;
/** Minimum chunk alignment */
size_t alignment;
};
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 586cec4081..758691cfde 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -69,14 +69,14 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
// Test: Does Addrman respond correctly when empty.
BOOST_CHECK_EQUAL(addrman->Size(), 0U);
auto addr_null = addrman->Select().first;
- BOOST_CHECK_EQUAL(addr_null.ToString(), "[::]:0");
+ BOOST_CHECK_EQUAL(addr_null.ToStringAddrPort(), "[::]:0");
// Test: Does Addrman::Add work as expected.
CService addr1 = ResolveService("250.1.1.1", 8333);
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
auto addr_ret1 = addrman->Select().first;
- BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
// Test: Does IP address deduplication work correctly.
// Expected dup IP should not be added.
@@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK(addrman->Add({CAddress(addr1_port, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman->Size(), 2U);
auto addr_ret2 = addrman->Select().first;
- BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
+ BOOST_CHECK(addr_ret2.ToStringAddrPort() == "250.1.1.1:8333" || addr_ret2.ToStringAddrPort() == "250.1.1.1:8334");
// Test: Add same IP but diff port to tried table; this converts the entry with
// the specified port to tried, but not the other.
@@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK_EQUAL(addrman->Size(), 2U);
bool newOnly = true;
auto addr_ret3 = addrman->Select(newOnly).first;
- BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
}
@@ -146,16 +146,16 @@ BOOST_AUTO_TEST_CASE(addrman_select)
bool newOnly = true;
auto addr_ret1 = addrman->Select(newOnly).first;
- BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret1.ToStringAddrPort(), "250.1.1.1:8333");
// Test: move addr to tried, select from new expected nothing returned.
BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
auto addr_ret2 = addrman->Select(newOnly).first;
- BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0");
+ BOOST_CHECK_EQUAL(addr_ret2.ToStringAddrPort(), "[::]:0");
auto addr_ret3 = addrman->Select().first;
- BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK_EQUAL(addr_ret3.ToStringAddrPort(), "250.1.1.1:8333");
BOOST_CHECK_EQUAL(addrman->Size(), 1U);
@@ -714,7 +714,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
BOOST_CHECK(addrman->Size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
// Add twenty two addresses.
CNetAddr source = ResolveIP("252.2.2.2");
@@ -724,7 +724,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
// No collisions in tried.
BOOST_CHECK(addrman->Good(addr));
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
}
// Ensure Good handles duplicates well.
@@ -736,7 +736,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
BOOST_CHECK(!addrman->Good(addr));
// Verify duplicate address not marked as a collision.
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
}
}
@@ -758,13 +758,13 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
CService addr36 = ResolveService("250.1.1.36");
BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
BOOST_CHECK(!addrman->Good(addr36));
- BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.19:0");
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.19:0");
// 36 should be discarded and 19 not evicted.
// This means we keep 19 in the tried table and
// 36 stays in the new table.
addrman->ResolveCollisions();
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
// Lets create two collisions.
for (unsigned int i = 37; i < 59; i++) {
@@ -778,18 +778,18 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
BOOST_CHECK(!addrman->Good(addr59));
- BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.10:0");
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.10:0");
// Cause a second collision in the new table.
BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
// 36 still cannot be moved from new to tried due to colliding with 19
BOOST_CHECK(!addrman->Good(addr36));
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() != "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() != "[::]:0");
// Resolve all collisions.
addrman->ResolveCollisions();
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
}
BOOST_AUTO_TEST_CASE(addrman_evictionworks)
@@ -799,7 +799,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman->Size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
// Add 35 addresses
CNetAddr source = ResolveIP("252.2.2.2");
@@ -817,7 +817,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(!addrman->Good(addr));
auto info = addrman->SelectTriedCollision().first;
- BOOST_CHECK_EQUAL(info.ToString(), "250.1.1.19:0");
+ BOOST_CHECK_EQUAL(info.ToStringAddrPort(), "250.1.1.19:0");
// Ensure test of address fails, so that it is evicted.
// Update entry in tried by setting last good connection in the deep past.
@@ -826,7 +826,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
// Should swap 36 for 19.
addrman->ResolveCollisions();
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
AddressPosition addr_pos{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
BOOST_CHECK(addr_pos.tried);
@@ -835,18 +835,18 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
// We check this by verifying Good() returns false and also verifying that
// we have no collisions.
BOOST_CHECK(!addrman->Good(addr));
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
// 19 should fail as a collision (not a duplicate) if we now attempt to move
// it to the tried table.
CService addr19 = ResolveService("250.1.1.19");
BOOST_CHECK(!addrman->Good(addr19));
- BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.36:0");
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToStringAddrPort(), "250.1.1.36:0");
// Eviction is also successful if too much time has passed since last try
SetMockTime(GetTime() + 4 * 60 *60);
addrman->ResolveCollisions();
- BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToStringAddrPort() == "[::]:0");
//Now 19 is in tried again, and 36 back to new
AddressPosition addr_pos19{addrman->FindAddressEntry(CAddress(addr19, NODE_NONE)).value()};
BOOST_CHECK(addr_pos19.tried);
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index 601caf8102..7f3ca6bf93 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -6,6 +6,7 @@
#include <base58.h>
#include <test/util/json.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/vector.h>
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index e23b7228e7..4348a20886 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -7,6 +7,7 @@
#include <consensus/merkle.h>
#include <pow.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/txmempool.h>
#include <test/util/setup_common.h>
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 4888041204..5d4c5eea0e 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -12,6 +12,7 @@
#include <random.h>
#include <serialize.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/strencodings.h>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 53fbc26e15..135f107159 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -4,6 +4,7 @@
#include <checkqueue.h>
#include <sync.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/system.h>
#include <util/time.h>
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 55ecd41af1..e082800fc3 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -6,6 +6,7 @@
#include <coins.h>
#include <script/standard.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <txdb.h>
#include <uint256.h>
@@ -172,7 +173,7 @@ void SimulationTest(CCoinsView* base, bool fake_best_block)
if (InsecureRandRange(5) == 0 || coin.IsSpent()) {
Coin newcoin;
- newcoin.out.nValue = InsecureRand32();
+ newcoin.out.nValue = InsecureRandMoneyAmount();
newcoin.nHeight = 1;
// Infrequently test adding unspendable coins.
@@ -278,7 +279,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
CCoinsViewTest base;
SimulationTest(&base, false);
- CCoinsViewDB db_base{"test", /*nCacheSize=*/1 << 23, /*fMemory=*/true, /*fWipe=*/false};
+ CCoinsViewDB db_base{{.path = "test", .cache_bytes = 1 << 23, .memory_only = true}, {}};
SimulationTest(&db_base, true);
}
@@ -1064,7 +1065,7 @@ void TestFlushBehavior(
BOOST_AUTO_TEST_CASE(ccoins_flush_behavior)
{
// Create two in-memory caches atop a leveldb view.
- CCoinsViewDB base{"test", /*nCacheSize=*/ 1 << 23, /*fMemory=*/ true, /*fWipe=*/ false};
+ CCoinsViewDB base{{.path = "test", .cache_bytes = 1 << 23, .memory_only = true}, {}};
std::vector<std::unique_ptr<CCoinsViewCacheTest>> caches;
caches.push_back(std::make_unique<CCoinsViewCacheTest>(&base));
caches.push_back(std::make_unique<CCoinsViewCacheTest>(caches.back().get()));
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index ed851b5266..e4e8596a5d 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -17,6 +17,7 @@
#include <crypto/muhash.h>
#include <random.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index c7c34cc8c9..eafbcf5681 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -1,9 +1,11 @@
// Copyright (c) 2012-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
#include <cuckoocache.h>
#include <random.h>
#include <script/sigcache.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 7ad123754b..723a1ceee3 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <dbwrapper.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/string.h>
@@ -28,7 +29,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
- CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = true, .wipe_data = false, .obfuscate = obfuscate});
uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -47,7 +48,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_basic_data)
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false");
- CDBWrapper dbw(ph, (1 << 20), false, true, obfuscate);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = false, .wipe_data = true, .obfuscate = obfuscate});
uint256 res;
uint32_t res_uint_32;
@@ -128,7 +129,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
- CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = true, .wipe_data = false, .obfuscate = obfuscate});
uint8_t key{'i'};
uint256 in = InsecureRand256();
@@ -164,7 +165,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
- CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = true, .wipe_data = false, .obfuscate = obfuscate});
// The two keys are intentionally chosen for ordering
uint8_t key{'j'};
@@ -207,7 +208,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
fs::create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(DBParams{.path = ph, .cache_bytes = 1 << 10, .memory_only = false, .wipe_data = false, .obfuscate = false});
uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -220,7 +221,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
dbw.reset();
// Now, set up another wrapper that wants to obfuscate the same directory
- CDBWrapper odbw(ph, (1 << 10), false, false, true);
+ CDBWrapper odbw({.path = ph, .cache_bytes = 1 << 10, .memory_only = false, .wipe_data = false, .obfuscate = true});
// Check that the key/val we wrote with unobfuscated wrapper exists and
// is readable.
@@ -248,7 +249,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
fs::create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(DBParams{.path = ph, .cache_bytes = 1 << 10, .memory_only = false, .wipe_data = false, .obfuscate = false});
uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -261,7 +262,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
dbw.reset();
// Simulate a -reindex by wiping the existing data store
- CDBWrapper odbw(ph, (1 << 10), false, true, true);
+ CDBWrapper odbw({.path = ph, .cache_bytes = 1 << 10, .memory_only = false, .wipe_data = true, .obfuscate = true});
// Check that the key/val we wrote with unobfuscated wrapper doesn't exist
uint256 res2;
@@ -280,7 +281,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
fs::path ph = m_args.GetDataDirBase() / "iterator_ordering";
- CDBWrapper dbw(ph, (1 << 20), true, false, false);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = true, .wipe_data = false, .obfuscate = false});
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
uint32_t value = x*x;
@@ -348,7 +349,7 @@ struct StringContentsSerializer {
BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
fs::path ph = m_args.GetDataDirBase() / "iterator_string_ordering";
- CDBWrapper dbw(ph, (1 << 20), true, false, false);
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20, .memory_only = true, .wipe_data = false, .obfuscate = false});
for (int x = 0; x < 10; ++x) {
for (int y = 0; y < 10; ++y) {
std::string key{ToString(x)};
@@ -390,7 +391,7 @@ BOOST_AUTO_TEST_CASE(unicodepath)
// the ANSI CreateDirectoryA call and the code page isn't UTF8.
// It will succeed if created with CreateDirectoryW.
fs::path ph = m_args.GetDataDirBase() / "test_runner_₿_🏃_20191128_104644";
- CDBWrapper dbw(ph, (1 << 20));
+ CDBWrapper dbw({.path = ph, .cache_bytes = 1 << 20});
fs::path lockPath = ph / "LOCK";
BOOST_CHECK(fs::exists(lockPath));
diff --git a/src/test/fuzz/http_request.cpp b/src/test/fuzz/http_request.cpp
index 66a1ff945f..9928c4a1ab 100644
--- a/src/test/fuzz/http_request.cpp
+++ b/src/test/fuzz/http_request.cpp
@@ -59,7 +59,7 @@ FUZZ_TARGET(http_request)
const std::string body = http_request.ReadBody();
assert(body.empty());
const CService service = http_request.GetPeer();
- assert(service.ToString() == "[::]:0");
+ assert(service.ToStringAddrPort() == "[::]:0");
evbuffer_free(evbuf);
evhttp_request_free(evreq);
diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp
index 1b791fc19c..73096cd5ca 100644
--- a/src/test/fuzz/miniscript.cpp
+++ b/src/test/fuzz/miniscript.cpp
@@ -253,7 +253,9 @@ using Type = miniscript::Type;
using miniscript::operator"" _mst;
//! Construct a miniscript node as a shared_ptr.
-template<typename... Args> NodeRef MakeNodeRef(Args&&... args) { return miniscript::MakeNodeRef<CPubKey>(KEY_COMP, std::forward<Args>(args)...); }
+template<typename... Args> NodeRef MakeNodeRef(Args&&... args) {
+ return miniscript::MakeNodeRef<CPubKey>(miniscript::internal::NoDupCheck{}, std::forward<Args>(args)...);
+}
/** Information about a yet to be constructed Miniscript node. */
struct NodeInfo {
@@ -762,6 +764,7 @@ NodeRef GenNode(F ConsumeNode, Type root_type = ""_mst, bool strict_valid = fals
}
}
assert(stack.size() == 1);
+ stack[0]->DuplicateKeyCheck(KEY_COMP);
return std::move(stack[0]);
}
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index d61aef6d81..049ae02f4d 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -70,8 +70,7 @@ FUZZ_TARGET(netaddress)
assert(net_addr.GetNetwork() == Network::NET_ONION);
}
(void)net_addr.IsValid();
- (void)net_addr.ToString();
- (void)net_addr.ToStringIP();
+ (void)net_addr.ToStringAddr();
const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
(void)sub_net.IsValid();
@@ -80,9 +79,7 @@ FUZZ_TARGET(netaddress)
const CService service{net_addr, fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
(void)service.GetKey();
(void)service.GetPort();
- (void)service.ToString();
- (void)service.ToStringIPPort();
- (void)service.ToStringPort();
+ (void)service.ToStringAddrPort();
(void)CServiceHash()(service);
(void)CServiceHash(0, 0)(service);
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 5b5158884a..f1f435591b 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -5,6 +5,7 @@
#include <clientversion.h>
#include <crypto/siphash.h>
#include <hash.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index edf28cfbfc..ea5b94f3a5 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -6,6 +6,7 @@
#include <key_io.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/strencodings.h>
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 74e01fc2a5..66f7be3c4e 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/merkle.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index e766a55673..9e484f919e 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -9,6 +9,7 @@
#include <node/miner.h>
#include <policy/policy.h>
#include <script/standard.h>
+#include <test/util/random.h>
#include <test/util/txmempool.h>
#include <timedata.h>
#include <txmempool.h>
diff --git a/src/test/minisketch_tests.cpp b/src/test/minisketch_tests.cpp
index 59c0aab053..10506da783 100644
--- a/src/test/minisketch_tests.cpp
+++ b/src/test/minisketch_tests.cpp
@@ -5,6 +5,7 @@
#include <minisketch.h>
#include <node/minisketchwrapper.h>
#include <random.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 5a97e9429a..4fbd9b3a6e 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "0.0.0.0");
// IPv4, INADDR_NONE
BOOST_REQUIRE(LookupHost("255.255.255.255", addr, false));
@@ -150,7 +150,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "255.255.255.255");
// IPv4, casual
BOOST_REQUIRE(LookupHost("12.34.56.78", addr, false));
@@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "12.34.56.78");
// IPv6, in6addr_any
BOOST_REQUIRE(LookupHost("::", addr, false));
@@ -168,7 +168,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "::");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "::");
// IPv6, casual
BOOST_REQUIRE(LookupHost("1122:3344:5566:7788:9900:aabb:ccdd:eeff", addr, false));
@@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
// IPv6, scoped/link-local. See https://tools.ietf.org/html/rfc4007
// We support non-negative decimal integers (uint32_t) as zone id indices.
@@ -190,14 +190,14 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsIPv6());
BOOST_CHECK(!addr.IsBindAny());
- BOOST_CHECK_EQUAL(addr.ToString(), scoped_addr);
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), scoped_addr);
// Test that the delimiter "%" and default zone id of 0 can be omitted for the default scope.
BOOST_REQUIRE(LookupHost(link_local + "%0", addr, false));
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsIPv6());
BOOST_CHECK(!addr.IsBindAny());
- BOOST_CHECK_EQUAL(addr.ToString(), link_local);
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), link_local);
// TORv2, no longer supported
BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), torv3_addr);
// TORv3, broken, with wrong checksum
BOOST_CHECK(!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.onion"));
@@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsTor());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), ToLower(i2p_addr));
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), ToLower(i2p_addr));
// I2P, correct length, but decodes to less than the expected number of bytes.
BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jn=.b32.i2p"));
@@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "esffpvrt3wpeaygy.internal");
// Totally bogus
BOOST_CHECK(!addr.SetSpecial("totally bogus"));
@@ -321,7 +321,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_tostring_canonical_ipv6)
CNetAddr net_addr;
BOOST_REQUIRE(LookupHost(input_address, net_addr, false));
BOOST_REQUIRE(net_addr.IsIPv6());
- BOOST_CHECK_EQUAL(net_addr.ToString(), expected_canonical_representation_output);
+ BOOST_CHECK_EQUAL(net_addr.ToStringAddr(), expected_canonical_representation_output);
}
}
@@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv4());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "1.2.3.4");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "1.2.3.4");
BOOST_REQUIRE(s.empty());
// Invalid IPv4, valid length but address itself is shorter.
@@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv6());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "102:304:506:708:90a:b0c:d0e:f10");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "102:304:506:708:90a:b0c:d0e:f10");
BOOST_REQUIRE(s.empty());
// Valid IPv6, contains embedded "internal".
@@ -459,7 +459,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
s >> addr;
BOOST_CHECK(addr.IsInternal());
BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "zklycewkdo64v6wc.internal");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "zklycewkdo64v6wc.internal");
BOOST_REQUIRE(s.empty());
// Invalid IPv6, with bogus length.
@@ -505,7 +505,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsTor());
BOOST_CHECK(!addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(),
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(),
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion");
BOOST_REQUIRE(s.empty());
@@ -528,7 +528,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsI2P());
BOOST_CHECK(!addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(),
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(),
"ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p");
BOOST_REQUIRE(s.empty());
@@ -551,7 +551,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsCJDNS());
BOOST_CHECK(!addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7");
+ BOOST_CHECK_EQUAL(addr.ToStringAddr(), "fc00:1:2:3:4:5:6:7");
BOOST_REQUIRE(s.empty());
// Invalid CJDNS, wrong prefix.
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index cef42b7dd8..7e91819ddc 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(netbase_splithost)
bool static TestParse(std::string src, std::string canon)
{
CService addr(LookupNumeric(src, 65535));
- return canon == addr.ToString();
+ return canon == addr.ToStringAddrPort();
}
BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
@@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(embedded_test)
CNetAddr addr1(ResolveIP("1.2.3.4"));
CNetAddr addr2(ResolveIP("::FFFF:0102:0304"));
BOOST_CHECK(addr2.IsIPv4());
- BOOST_CHECK_EQUAL(addr1.ToString(), addr2.ToString());
+ BOOST_CHECK_EQUAL(addr1.ToStringAddr(), addr2.ToStringAddr());
}
BOOST_AUTO_TEST_CASE(subnet_test)
@@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(subnet_test)
subnet = CSubNet(tor_addr);
BOOST_CHECK(subnet.IsValid());
- BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToString());
+ BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToStringAddr());
BOOST_CHECK(subnet.Match(tor_addr));
BOOST_CHECK(
!subnet.Match(ResolveIP("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion")));
diff --git a/src/test/orphanage_tests.cpp b/src/test/orphanage_tests.cpp
index d95b9711d0..a2c4774338 100644
--- a/src/test/orphanage_tests.cpp
+++ b/src/test/orphanage_tests.cpp
@@ -7,6 +7,7 @@
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <txorphanage.h>
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index 21e0dd2fc5..a1e672d174 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -6,6 +6,7 @@
#include <merkleblock.h>
#include <serialize.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <version.h>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 7cd12ede0a..addc925bab 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <chainparams.h>
#include <pow.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 5f4d307048..1559011fcd 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -9,6 +9,7 @@
#include <serialize.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index b16f63d685..45d9f2cf29 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -16,6 +16,7 @@
#include <script/signingprovider.h>
#include <streams.h>
#include <test/util/json.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/transaction_utils.h>
#include <util/strencodings.h>
diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp
index f6af32cf6c..b36bdc02ca 100644
--- a/src/test/serfloat_tests.cpp
+++ b/src/test/serfloat_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <hash.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/serfloat.h>
#include <serialize.h>
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 368f9e6047..e2d11afa6a 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -11,6 +11,7 @@
#include <streams.h>
#include <test/data/sighash.json.h>
#include <test/util/json.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -109,7 +110,7 @@ void static RandomTransaction(CMutableTransaction& tx, bool fSingle)
for (int out = 0; out < outs; out++) {
tx.vout.push_back(CTxOut());
CTxOut &txout = tx.vout.back();
- txout.nValue = InsecureRandRange(100000000);
+ txout.nValue = InsecureRandMoneyAmount();
RandomScript(txout.scriptPubKey);
}
}
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index ae9021df58..050033e43a 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <vector>
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index b7c1ce5066..a9b5251ad3 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -4,6 +4,7 @@
#include <fs.h>
#include <streams.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 507284a566..11efb6a5c3 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -22,6 +22,7 @@
#include <script/standard.h>
#include <streams.h>
#include <test/util/json.h>
+#include <test/util/random.h>
#include <test/util/script.h>
#include <test/util/transaction_utils.h>
#include <util/strencodings.h>
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index e438867d15..024526497c 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -9,6 +9,7 @@
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/standard.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <validation.h>
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
index a4ed1e8b3a..17a55d5ab5 100644
--- a/src/test/txrequest_tests.cpp
+++ b/src/test/txrequest_tests.cpp
@@ -6,6 +6,7 @@
#include <txrequest.h>
#include <uint256.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <algorithm>
diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp
index 3ae22921b9..ec703c6a7b 100644
--- a/src/test/util/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -28,4 +28,3 @@ bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index,
filter = BlockFilter(filter_type, block, block_undo);
return true;
}
-
diff --git a/src/test/util/coins.cpp b/src/test/util/coins.cpp
new file mode 100644
index 0000000000..9b6c5535c5
--- /dev/null
+++ b/src/test/util/coins.cpp
@@ -0,0 +1,27 @@
+// Copyright (c) 2023 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/util/coins.h>
+
+#include <coins.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <test/util/random.h>
+#include <uint256.h>
+
+#include <stdint.h>
+#include <utility>
+
+COutPoint AddTestCoin(CCoinsViewCache& coins_view)
+{
+ Coin new_coin;
+ const uint256 txid{InsecureRand256()};
+ COutPoint outpoint{txid, /*nIn=*/0};
+ new_coin.nHeight = 1;
+ new_coin.out.nValue = InsecureRandMoneyAmount();
+ new_coin.out.scriptPubKey.assign(uint32_t{56}, 1);
+ coins_view.AddCoin(outpoint, std::move(new_coin), /*possible_overwrite=*/false);
+
+ return outpoint;
+};
diff --git a/src/test/util/coins.h b/src/test/util/coins.h
new file mode 100644
index 0000000000..5e6f4293ae
--- /dev/null
+++ b/src/test/util/coins.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2023 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_COINS_H
+#define BITCOIN_TEST_UTIL_COINS_H
+
+#include <primitives/transaction.h>
+
+class CCoinsViewCache;
+
+/**
+ * Create a Coin with DynamicMemoryUsage of 80 bytes and add it to the given view.
+ * @param[in,out] coins_view The coins view cache to add the new coin to.
+ * @returns the COutPoint of the created coin.
+ */
+COutPoint AddTestCoin(CCoinsViewCache& coins_view);
+
+#endif // BITCOIN_TEST_UTIL_COINS_H
diff --git a/src/test/util/random.h b/src/test/util/random.h
new file mode 100644
index 0000000000..7997e8a346
--- /dev/null
+++ b/src/test/util/random.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2023 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_RANDOM_H
+#define BITCOIN_TEST_UTIL_RANDOM_H
+
+#include <consensus/amount.h>
+#include <random.h>
+#include <test/util/setup_common.h>
+#include <uint256.h>
+
+#include <cstdint>
+
+static inline uint32_t InsecureRand32()
+{
+ return g_insecure_rand_ctx.rand32();
+}
+
+static inline uint256 InsecureRand256()
+{
+ return g_insecure_rand_ctx.rand256();
+}
+
+static inline uint64_t InsecureRandBits(int bits)
+{
+ return g_insecure_rand_ctx.randbits(bits);
+}
+
+static inline uint64_t InsecureRandRange(uint64_t range)
+{
+ return g_insecure_rand_ctx.randrange(range);
+}
+
+static inline bool InsecureRandBool()
+{
+ return g_insecure_rand_ctx.randbool();
+}
+
+static inline CAmount InsecureRandMoneyAmount()
+{
+ return static_cast<CAmount>(InsecureRandRange(MAX_MONEY + 1));
+}
+
+#endif // BITCOIN_TEST_UTIL_RANDOM_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 6e72f69968..4e0000cb3d 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -180,11 +180,15 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
+ .datadir = m_args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
.check_block_index = true,
};
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
- m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(m_cache_sizes.block_tree_db, true);
+ m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{
+ .path = m_args.GetDataDirNet() / "blocks" / "index",
+ .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
+ .memory_only = true});
constexpr int script_check_threads = 2;
StartScriptCheckWorkerThreads(script_check_threads);
@@ -218,6 +222,7 @@ void TestingSetup::LoadVerifyActivateChainstate()
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
+ options.require_full_verification = m_args.IsArgSet("-checkblocks") || m_args.IsArgSet("-checklevel");
auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options);
assert(status == node::ChainstateLoadStatus::SUCCESS);
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 5f653d83ae..8874db7e75 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -71,12 +71,6 @@ static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED)
}
}
-static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); }
-static inline uint256 InsecureRand256() { return g_insecure_rand_ctx.rand256(); }
-static inline uint64_t InsecureRandBits(int bits) { return g_insecure_rand_ctx.randbits(bits); }
-static inline uint64_t InsecureRandRange(uint64_t range) { return g_insecure_rand_ctx.randrange(range); }
-static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); }
-
static constexpr CAmount CENT{1000000};
/** Basic testing setup.
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 6b6bb18523..f0dcee7a9b 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -9,6 +9,7 @@
#include <hash.h> // For Hash()
#include <key.h> // For CKey
#include <sync.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/getuniquepath.h>
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 823c9877ac..4c8687ce69 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -11,6 +11,7 @@
#include <pow.h>
#include <random.h>
#include <script/standard.h>
+#include <test/util/random.h>
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <util/time.h>
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index c40481a95c..2078fcd8f8 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -8,6 +8,8 @@
#include <rpc/blockchain.h>
#include <sync.h>
#include <test/util/chainstate.h>
+#include <test/util/coins.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <validation.h>
@@ -24,20 +26,6 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
ChainstateManager& manager = *Assert(m_node.chainman);
CTxMemPool& mempool = *Assert(m_node.mempool);
-
- //! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
- auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
- Coin newcoin;
- uint256 txid = InsecureRand256();
- COutPoint outp{txid, 0};
- newcoin.nHeight = 1;
- newcoin.out.nValue = InsecureRand32();
- newcoin.out.scriptPubKey.assign(uint32_t{56}, 1);
- coins_view.AddCoin(outp, std::move(newcoin), false);
-
- return outp;
- };
-
Chainstate& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
@@ -47,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
// Add a coin to the in-memory cache, upsize once, then downsize.
{
LOCK(::cs_main);
- auto outpoint = add_coin(c1.CoinsTip());
+ const auto outpoint = AddTestCoin(c1.CoinsTip());
// Set a meaningless bestblock value in the coinsview cache - otherwise we won't
// flush during ResizecoinsCaches() and will subsequently hit an assertion.
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 56867a584b..78301c7c14 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -9,6 +9,7 @@
#include <rpc/blockchain.h>
#include <sync.h>
#include <test/util/chainstate.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <timedata.h>
#include <uint256.h>
@@ -374,6 +375,7 @@ struct SnapshotTestSetup : TestChain100Setup {
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
const ChainstateManager::Options chainman_opts{
.chainparams = ::Params(),
+ .datadir = m_args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
};
// For robustness, ensure the old manager is destroyed before creating a
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index f2ff570ca6..26c48eb0e0 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -3,6 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <sync.h>
+#include <test/util/coins.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <validation.h>
@@ -24,19 +26,6 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
LOCK(::cs_main);
auto& view = chainstate.CoinsTip();
- //! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
- auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
- Coin newcoin;
- uint256 txid = InsecureRand256();
- COutPoint outp{txid, 0};
- newcoin.nHeight = 1;
- newcoin.out.nValue = InsecureRand32();
- newcoin.out.scriptPubKey.assign(uint32_t{56}, 1);
- coins_view.AddCoin(outp, std::move(newcoin), false);
-
- return outp;
- };
-
// The number of bytes consumed by coin's heap data, i.e. CScript
// (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
//
@@ -61,7 +50,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Add a bunch of coins to see that we at least flip over to CRITICAL.
for (int i{0}; i < 1000; ++i) {
- COutPoint res = add_coin(view);
+ const COutPoint res = AddTestCoin(view);
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
}
@@ -83,7 +72,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
constexpr int COINS_UNTIL_CRITICAL{3};
for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
- COutPoint res = add_coin(view);
+ const COutPoint res = AddTestCoin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
BOOST_CHECK_EQUAL(
@@ -93,7 +82,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Adding some additional coins will push us over the edge to CRITICAL.
for (int i{0}; i < 4; ++i) {
- add_coin(view);
+ AddTestCoin(view);
print_view_mem_usage(view);
if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0) ==
CoinsCacheSizeState::CRITICAL) {
@@ -111,7 +100,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
CoinsCacheSizeState::OK);
for (int i{0}; i < 3; ++i) {
- add_coin(view);
+ AddTestCoin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/1 << 10),
@@ -120,7 +109,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Adding another coin with the additional mempool room will put us >90%
// but not yet critical.
- add_coin(view);
+ AddTestCoin(view);
print_view_mem_usage(view);
// Only perform these checks on 64 bit hosts; I haven't done the math for 32.
@@ -136,7 +125,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Using the default max_* values permits way more coins to be added.
for (int i{0}; i < 1000; ++i) {
- add_coin(view);
+ AddTestCoin(view);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(),
CoinsCacheSizeState::OK);
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 91383ee4a5..80c00036e7 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <chainparams.h>
#include <consensus/params.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <versionbits.h>
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index ece77f9023..d4daeacd3e 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -380,7 +380,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
Assume(resolved.IsValid());
- LogPrint(BCLog::TOR, "Configuring onion proxy for %s\n", resolved.ToStringIPPort());
+ LogPrint(BCLog::TOR, "Configuring onion proxy for %s\n", resolved.ToStringAddrPort());
Proxy addrOnion = Proxy(resolved, true);
SetProxy(NET_ONION, addrOnion);
@@ -421,7 +421,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
return;
}
service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort());
- LogPrintfCategory(BCLog::TOR, "Got service ID %s, advertising service %s\n", service_id, service.ToString());
+ LogPrintfCategory(BCLog::TOR, "Got service ID %s, advertising service %s\n", service_id, service.ToStringAddrPort());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
LogPrint(BCLog::TOR, "Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
} else {
@@ -453,7 +453,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
}
// Request onion service, redirect port.
// Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
- _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, Params().GetDefaultPort(), m_target.ToStringIPPort()),
+ _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, Params().GetDefaultPort(), m_target.ToStringAddrPort()),
std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Authentication failed\n");
diff --git a/src/txdb.cpp b/src/txdb.cpp
index c12b540b9b..7257fb4959 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -70,21 +70,22 @@ struct CoinEntry {
} // namespace
-CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) :
- m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)),
- m_ldb_path(ldb_path),
- m_is_memory(fMemory) { }
+CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
+ m_db_params{std::move(db_params)},
+ m_options{std::move(options)},
+ m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
void CCoinsViewDB::ResizeCache(size_t new_cache_size)
{
// We can't do this operation with an in-memory DB since we'll lose all the coins upon
// reset.
- if (!m_is_memory) {
+ if (!m_db_params.memory_only) {
// Have to do a reset first to get the original `m_db` state to release its
// filesystem lock.
m_db.reset();
- m_db = std::make_unique<CDBWrapper>(
- m_ldb_path, new_cache_size, m_is_memory, /*fWipe=*/false, /*obfuscate=*/true);
+ m_db_params.cache_bytes = new_cache_size;
+ m_db_params.wipe_data = false;
+ m_db = std::make_unique<CDBWrapper>(m_db_params);
}
}
@@ -115,8 +116,6 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
CDBBatch batch(*m_db);
size_t count = 0;
size_t changed = 0;
- size_t batch_size = (size_t)gArgs.GetIntArg("-dbbatchsize", nDefaultDbBatchSize);
- int crash_simulate = gArgs.GetIntArg("-dbcrashratio", 0);
assert(!hashBlock.IsNull());
uint256 old_tip = GetBestBlock();
@@ -147,13 +146,13 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
}
count++;
it = erase ? mapCoins.erase(it) : std::next(it);
- if (batch.SizeEstimate() > batch_size) {
+ if (batch.SizeEstimate() > m_options.batch_write_bytes) {
LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
m_db->WriteBatch(batch);
batch.Clear();
- if (crash_simulate) {
+ if (m_options.simulate_crash_ratio) {
static FastRandomContext rng;
- if (rng.randrange(crash_simulate) == 0) {
+ if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
LogPrintf("Simulating a crash. Goodbye.\n");
_Exit(0);
}
@@ -176,9 +175,6 @@ size_t CCoinsViewDB::EstimateSize() const
return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
}
-CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(gArgs.GetDataDirNet() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
-}
-
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
}
diff --git a/src/txdb.h b/src/txdb.h
index e3422846c0..8a876349fb 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -45,18 +45,24 @@ static const int64_t max_filter_index_cache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
+//! User-controlled performance and debug options.
+struct CoinsViewOptions {
+ //! Maximum database write batch size in bytes.
+ size_t batch_write_bytes = nDefaultDbBatchSize;
+ //! If non-zero, randomly exit when the database is flushed with (1/ratio)
+ //! probability.
+ int simulate_crash_ratio = 0;
+};
+
/** CCoinsView backed by the coin database (chainstate/) */
class CCoinsViewDB final : public CCoinsView
{
protected:
+ DBParams m_db_params;
+ CoinsViewOptions m_options;
std::unique_ptr<CDBWrapper> m_db;
- fs::path m_ldb_path;
- bool m_is_memory;
public:
- /**
- * @param[in] ldb_path Location in the filesystem where leveldb data will be stored.
- */
- explicit CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe);
+ explicit CCoinsViewDB(DBParams db_params, CoinsViewOptions options);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
@@ -80,8 +86,7 @@ public:
class CBlockTreeDB : public CDBWrapper
{
public:
- explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
-
+ using CDBWrapper::CDBWrapper;
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
bool ReadLastBlockFile(int &nFile);
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 193265c842..309877d067 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -33,6 +33,8 @@ bilingual_str TransactionErrorString(const TransactionError err)
return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
+ case TransactionError::MAX_BURN_EXCEEDED:
+ return Untranslated("Unspendable output exceeds maximum configured by user (maxburnamount)");
case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
return Untranslated("External signer not found");
case TransactionError::EXTERNAL_SIGNER_FAILED:
diff --git a/src/util/error.h b/src/util/error.h
index 649200c98e..a52a8f47de 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -30,6 +30,7 @@ enum class TransactionError {
PSBT_MISMATCH,
SIGHASH_MISMATCH,
MAX_FEE_EXCEEDED,
+ MAX_BURN_EXCEEDED,
EXTERNAL_SIGNER_NOT_FOUND,
EXTERNAL_SIGNER_FAILED,
INVALID_PACKAGE,
diff --git a/src/util/system.cpp b/src/util/system.cpp
index e72c970157..77b659df7e 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -417,8 +417,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
LOCK(cs_args);
fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
- // Cache the path to avoid calling fs::create_directories on every call of
- // this function
+ // Used cached path if available
if (!path.empty()) return path;
const fs::path datadir{GetPathArg("-datadir")};
@@ -432,20 +431,34 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
path = GetDefaultDataDir();
}
- if (!fs::exists(path)) {
- fs::create_directories(path / "wallets");
- }
-
if (net_specific && !BaseParams().DataDir().empty()) {
path /= fs::PathFromString(BaseParams().DataDir());
- if (!fs::exists(path)) {
- fs::create_directories(path / "wallets");
- }
}
return path;
}
+void ArgsManager::EnsureDataDir() const
+{
+ /**
+ * "/wallets" subdirectories are created in all **new**
+ * datadirs, because wallet code will create new wallets in the "wallets"
+ * subdirectory only if exists already, otherwise it will create them in
+ * the top-level datadir where they could interfere with other files.
+ * Wallet init code currently avoids creating "wallets" directories itself
+ * for backwards compatibility, but this be changed in the future and
+ * wallet code here could go away (#16220).
+ */
+ auto path{GetDataDir(false)};
+ if (!fs::exists(path)) {
+ fs::create_directories(path / "wallets");
+ }
+ path = GetDataDir(true);
+ if (!fs::exists(path)) {
+ fs::create_directories(path / "wallets");
+ }
+}
+
void ArgsManager::ClearPathCache()
{
LOCK(cs_args);
@@ -491,6 +504,7 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
bool ArgsManager::InitSettings(std::string& error)
{
+ EnsureDataDir();
if (!GetSettingsPath()) {
return true; // Do nothing if settings file disabled.
}
@@ -965,6 +979,11 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return true;
}
+fs::path ArgsManager::GetConfigFilePath() const
+{
+ return GetConfigFile(GetPathArg("-conf", BITCOIN_CONF_FILENAME));
+}
+
bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
{
@@ -973,8 +992,8 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
m_config_sections.clear();
}
- const fs::path conf_path = GetPathArg("-conf", BITCOIN_CONF_FILENAME);
- std::ifstream stream{GetConfigFile(conf_path)};
+ const auto conf_path{GetConfigFilePath()};
+ std::ifstream stream{conf_path};
// not ok to have a config file specified that cannot be opened
if (IsArgSet("-conf") && !stream.good()) {
diff --git a/src/util/system.h b/src/util/system.h
index c053adf8c3..14f093501a 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -242,6 +242,11 @@ protected:
void SelectConfigNetwork(const std::string& network);
[[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
+
+ /**
+ * Return config file path (read-only)
+ */
+ fs::path GetConfigFilePath() const;
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/**
@@ -475,13 +480,18 @@ protected:
*/
void LogArgs() const;
+ /**
+ * If datadir does not exist, create it along with wallets/
+ * subdirectory(s).
+ */
+ void EnsureDataDir() const;
+
private:
/**
* Get data directory path
*
* @param net_specific Append network identifier to the returned path
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
- * @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDir(bool net_specific) const;
diff --git a/src/validation.cpp b/src/validation.cpp
index f0ffb748dd..1357de3c01 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1511,13 +1511,9 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
-CoinsViews::CoinsViews(
- fs::path ldb_name,
- size_t cache_size_bytes,
- bool in_memory,
- bool should_wipe) : m_dbview(
- gArgs.GetDataDirNet() / ldb_name, cache_size_bytes, in_memory, should_wipe),
- m_catcherview(&m_dbview) {}
+CoinsViews::CoinsViews(DBParams db_params, CoinsViewOptions options)
+ : m_dbview{std::move(db_params), std::move(options)},
+ m_catcherview(&m_dbview) {}
void CoinsViews::InitCache()
{
@@ -1546,7 +1542,14 @@ void Chainstate::InitCoinsDB(
}
m_coins_views = std::make_unique<CoinsViews>(
- leveldb_name, cache_size_bytes, in_memory, should_wipe);
+ DBParams{
+ .path = m_chainman.m_options.datadir / leveldb_name,
+ .cache_bytes = cache_size_bytes,
+ .memory_only = in_memory,
+ .wipe_data = should_wipe,
+ .obfuscate = true,
+ .options = m_chainman.m_options.coins_db},
+ m_chainman.m_options.coins_view);
}
void Chainstate::InitCoinsCache(size_t cache_size_bytes)
@@ -4057,7 +4060,7 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100, false);
}
-bool CVerifyDB::VerifyDB(
+VerifyDBResult CVerifyDB::VerifyDB(
Chainstate& chainstate,
const Consensus::Params& consensus_params,
CCoinsView& coinsview,
@@ -4066,7 +4069,7 @@ bool CVerifyDB::VerifyDB(
AssertLockHeld(cs_main);
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr) {
- return true;
+ return VerifyDBResult::SUCCESS;
}
// Verify blocks in the best chain
@@ -4081,6 +4084,7 @@ bool CVerifyDB::VerifyDB(
int nGoodTransactions = 0;
BlockValidationState state;
int reportDone = 0;
+ bool skipped_no_block_data{false};
bool skipped_l3_checks{false};
LogPrintf("Verification progress: 0%%\n");
@@ -4100,25 +4104,29 @@ bool CVerifyDB::VerifyDB(
if ((chainstate.m_blockman.IsPruneMode() || is_snapshot_cs) && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning or running under an assumeutxo snapshot, only go
// back as far as we have data.
- LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight);
+ LogPrintf("VerifyDB(): block verification stopping at height %d (no data). This could be due to pruning or use of an assumeutxo snapshot.\n", pindex->nHeight);
+ skipped_no_block_data = true;
break;
}
CBlock block;
// check level 0: read from disk
if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
- return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state, consensus_params)) {
- return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__,
- pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ LogPrintf("Verification error: found bad block at %d, hash=%s (%s)\n",
+ pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
if (!pindex->GetUndoPos().IsNull()) {
if (!UndoReadFromDisk(undo, pindex)) {
- return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ LogPrintf("Verification error: found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
}
}
@@ -4130,7 +4138,8 @@ bool CVerifyDB::VerifyDB(
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
- return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ LogPrintf("Verification error: irrecoverable inconsistency in block data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
if (res == DISCONNECT_UNCLEAN) {
nGoodTransactions = 0;
@@ -4142,14 +4151,16 @@ bool CVerifyDB::VerifyDB(
skipped_l3_checks = true;
}
}
- if (ShutdownRequested()) return true;
+ if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
}
if (pindexFailure) {
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
if (skipped_l3_checks) {
LogPrintf("Skipped verification of level >=3 (insufficient database cache size). Consider increasing -dbcache.\n");
}
+
// store block count as we move pindex at check level >= 4
int block_count = chainstate.m_chain.Height() - pindex->nHeight;
@@ -4165,18 +4176,27 @@ bool CVerifyDB::VerifyDB(
uiInterface.ShowProgress(_("Verifying blocks…").translated, percentageDone, false);
pindex = chainstate.m_chain.Next(pindex);
CBlock block;
- if (!ReadBlockFromDisk(block, pindex, consensus_params))
- return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
+ LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
+ }
if (!chainstate.ConnectBlock(block, state, pindex, coins)) {
- return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ return VerifyDBResult::CORRUPTED_BLOCK_DB;
}
- if (ShutdownRequested()) return true;
+ if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED;
}
}
LogPrintf("Verification: No coin database inconsistencies in last %i blocks (%i transactions)\n", block_count, nGoodTransactions);
- return true;
+ if (skipped_l3_checks) {
+ return VerifyDBResult::SKIPPED_L3_CHECKS;
+ }
+ if (skipped_no_block_data) {
+ return VerifyDBResult::SKIPPED_MISSING_BLOCKS;
+ }
+ return VerifyDBResult::SUCCESS;
}
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
diff --git a/src/validation.h b/src/validation.h
index 7170467b00..36c6becf4f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -349,12 +349,20 @@ bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consens
/** Return the sum of the work on a given set of headers */
arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers);
+enum class VerifyDBResult {
+ SUCCESS,
+ CORRUPTED_BLOCK_DB,
+ INTERRUPTED,
+ SKIPPED_L3_CHECKS,
+ SKIPPED_MISSING_BLOCKS,
+};
+
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
- bool VerifyDB(
+ [[nodiscard]] VerifyDBResult VerifyDB(
Chainstate& chainstate,
const Consensus::Params& consensus_params,
CCoinsView& coinsview,
@@ -408,7 +416,7 @@ public:
//! state to disk, which should not be done until the health of the database is verified.
//!
//! All arguments forwarded onto CCoinsViewDB.
- CoinsViews(fs::path ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe);
+ CoinsViews(DBParams db_params, CoinsViewOptions options);
//! Initialize the CCoinsViewCache member.
void InitCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 403ec711ff..69208c19dc 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -5,6 +5,7 @@
#include <wallet/dump.h>
#include <fs.h>
+#include <util/system.h>
#include <util/translation.h>
#include <wallet/wallet.h>
@@ -202,7 +203,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::
// dummy chain interface
bool ret = true;
- std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, gArgs, std::move(database)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
{
LOCK(wallet->cs_wallet);
DBErrors load_wallet_ret = wallet->LoadWallet();
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index 9918979a81..01dc80b1ca 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -13,11 +13,11 @@ namespace wallet {
class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
{
public:
- ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
- : DescriptorScriptPubKeyMan(storage, descriptor)
+ ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size)
+ : DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)
{}
- ExternalSignerScriptPubKeyMan(WalletStorage& storage)
- : DescriptorScriptPubKeyMan(storage)
+ ExternalSignerScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size)
+ : DescriptorScriptPubKeyMan(storage, keypool_size)
{}
/** Provide a descriptor at setup time
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index bd158b5985..37a704bfa4 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -155,7 +155,7 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
}
Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine)
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs)
{
// We are going to modify coin control later, copy to re-use
CCoinControl new_coin_control(coin_control);
@@ -222,11 +222,19 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
return result;
}
- // Fill in recipients(and preserve a single change key if there is one)
- // While we're here, calculate the output amount
- std::vector<CRecipient> recipients;
+ // Calculate the old output amount.
CAmount output_value = 0;
- for (const auto& output : wtx.tx->vout) {
+ for (const auto& old_output : wtx.tx->vout) {
+ output_value += old_output.nValue;
+ }
+
+ old_fee = input_value - output_value;
+
+ // Fill in recipients (and preserve a single change key if there
+ // is one). If outputs vector is non-empty, replace original
+ // outputs with its contents, otherwise use original outputs.
+ std::vector<CRecipient> recipients;
+ for (const auto& output : outputs.empty() ? wtx.tx->vout : outputs) {
if (!OutputIsChange(wallet, output)) {
CRecipient recipient = {output.scriptPubKey, output.nValue, false};
recipients.push_back(recipient);
@@ -235,11 +243,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
ExtractDestination(output.scriptPubKey, change_dest);
new_coin_control.destChange = change_dest;
}
- output_value += output.nValue;
}
- old_fee = input_value - output_value;
-
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
// We calculate this here to avoid compiler warning on the cs_wallet lock
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index a96871b26f..53cf16e0f1 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -51,7 +51,8 @@ Result CreateRateBumpTransaction(CWallet& wallet,
CAmount& old_fee,
CAmount& new_fee,
CMutableTransaction& mtx,
- bool require_mine);
+ bool require_mine,
+ const std::vector<CTxOut>& outputs);
//! Sign the new transaction,
//! @return false if the tx couldn't be found or if it was
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 68dd3da9b5..1a76e46c54 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -291,7 +291,8 @@ public:
CAmount& new_fee,
CMutableTransaction& mtx) override
{
- return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx, /* require_mine= */ true) == feebumper::Result::OK;
+ std::vector<CTxOut> outputs; // just an empty list of new recipients for now
+ return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx, /* require_mine= */ true, outputs) == feebumper::Result::OK;
}
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 93a6bbde20..744537cfbd 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -17,7 +17,6 @@
#include <sync.h>
#include <uint256.h>
#include <util/bip32.h>
-#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <wallet/rpc/util.h>
@@ -1478,7 +1477,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
} else {
warnings.push_back("Range not given, using default keypool range");
range_start = 0;
- range_end = gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE);
+ range_end = wallet.m_keypool_size;
}
next_index = range_start;
@@ -1651,10 +1650,14 @@ RPCHelpMan importdescriptors()
}
WalletRescanReserver reserver(*pwallet);
- if (!reserver.reserve()) {
+ if (!reserver.reserve(/*with_passphrase=*/true)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}
+ // Ensure that the wallet is not locked for the remainder of this RPC, as
+ // the passphrase is used to top up the keypool.
+ LOCK(pwallet->m_relock_mutex);
+
const UniValue& requests = main_request.params[0];
const int64_t minimum_timestamp = 1;
int64_t now = 0;
diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp
index fcf25e01d6..0226d15698 100644
--- a/src/wallet/rpc/encrypt.cpp
+++ b/src/wallet/rpc/encrypt.cpp
@@ -49,9 +49,7 @@ RPCHelpMan walletpassphrase()
// Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
SecureString strWalletPass;
strWalletPass.reserve(100);
- // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make request.params[0] mlock()'d to begin with.
- strWalletPass = request.params[0].get_str().c_str();
+ strWalletPass = std::string_view{request.params[0].get_str()};
// Get the timeout
nSleepTime = request.params[1].getInt<int64_t>();
@@ -70,7 +68,17 @@ RPCHelpMan walletpassphrase()
}
if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ // Check if the passphrase has a null character (see #27067 for details)
+ if (strWalletPass.find('\0') == std::string::npos) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ } else {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered is incorrect. "
+ "It contains a null character (ie - a zero byte). "
+ "If the passphrase was set with a version of this software prior to 25.0, "
+ "please try again with only the characters up to — but not including — "
+ "the first null character. If this is successful, please set a new "
+ "passphrase to avoid this issue in the future.");
+ }
}
pwallet->TopUpKeyPool();
@@ -90,7 +98,7 @@ RPCHelpMan walletpassphrase()
std::weak_ptr<CWallet> weak_wallet = wallet;
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
if (auto shared_wallet = weak_wallet.lock()) {
- LOCK(shared_wallet->cs_wallet);
+ LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet);
// Skip if this is not the most recent rpcRunLater callback.
if (shared_wallet->nRelockTime != relock_time) return;
shared_wallet->Lock();
@@ -122,28 +130,39 @@ RPCHelpMan walletpassphrasechange()
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return UniValue::VNULL;
- LOCK(pwallet->cs_wallet);
-
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
}
- // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make request.params[0] mlock()'d to begin with.
+ if (pwallet->IsScanningWithPassphrase()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before changing the passphrase.");
+ }
+
+ LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
+
SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
- strOldWalletPass = request.params[0].get_str().c_str();
+ strOldWalletPass = std::string_view{request.params[0].get_str()};
SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
- strNewWalletPass = request.params[1].get_str().c_str();
+ strNewWalletPass = std::string_view{request.params[1].get_str()};
if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
}
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ // Check if the old passphrase had a null character (see #27067 for details)
+ if (strOldWalletPass.find('\0') == std::string::npos) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ } else {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The old wallet passphrase entered is incorrect. "
+ "It contains a null character (ie - a zero byte). "
+ "If the old passphrase was set with a version of this software prior to 25.0, "
+ "please try again with only the characters up to — but not including — "
+ "the first null character.");
+ }
}
return UniValue::VNULL;
@@ -175,12 +194,16 @@ RPCHelpMan walletlock()
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return UniValue::VNULL;
- LOCK(pwallet->cs_wallet);
-
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
}
+ if (pwallet->IsScanningWithPassphrase()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before locking the wallet.");
+ }
+
+ LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
+
pwallet->Lock();
pwallet->nRelockTime = 0;
@@ -219,8 +242,6 @@ RPCHelpMan encryptwallet()
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return UniValue::VNULL;
- LOCK(pwallet->cs_wallet);
-
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
}
@@ -229,11 +250,15 @@ RPCHelpMan encryptwallet()
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
}
- // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make request.params[0] mlock()'d to begin with.
+ if (pwallet->IsScanningWithPassphrase()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before encrypting the wallet.");
+ }
+
+ LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
+
SecureString strWalletPass;
strWalletPass.reserve(100);
- strWalletPass = request.params[0].get_str().c_str();
+ strWalletPass = std::string_view{request.params[0].get_str()};
if (strWalletPass.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index cab797bbce..88ee6e96b0 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -956,6 +956,26 @@ RPCHelpMan signrawtransactionwithwallet()
};
}
+// Definition of allowed formats of specifying transaction outputs in
+// `bumpfee`, `psbtbumpfee`, `send` and `walletcreatefundedpsbt` RPCs.
+static std::vector<RPCArg> OutputsDoc()
+{
+ return
+ {
+ {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
+ {
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address,\n"
+ "the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
+ },
+ },
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ };
+}
+
static RPCHelpMan bumpfee_helper(std::string method_name)
{
const bool want_psbt = method_name == "psbtbumpfee";
@@ -992,7 +1012,12 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
"still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
"are replaceable).\n"},
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
- "\"" + FeeModes("\"\n\"") + "\""},
+ "\"" + FeeModes("\"\n\"") + "\""},
+ {"outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "New outputs (key-value pairs) which will replace\n"
+ "the original ones, if provided. Each address can only appear once and there can\n"
+ "only be one \"data\" object.\n",
+ OutputsDoc(),
+ RPCArgOptions{.skip_type_check = true}},
},
RPCArgOptions{.oneline_description="options"}},
},
@@ -1029,6 +1054,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
coin_control.fAllowWatchOnly = pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
// optional parameters
coin_control.m_signal_bip125_rbf = true;
+ std::vector<CTxOut> outputs;
if (!request.params[1].isNull()) {
UniValue options = request.params[1];
@@ -1039,6 +1065,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
{"replaceable", UniValueType(UniValue::VBOOL)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
+ {"outputs", UniValueType()}, // will be checked by AddOutputs()
},
true, true);
@@ -1052,6 +1079,16 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
SetFeeEstimateMode(*pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /*override_min_fee=*/false);
+
+ // Prepare new outputs by creating a temporary tx and calling AddOutputs().
+ if (!options["outputs"].isNull()) {
+ if (options["outputs"].isArray() && options["outputs"].empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument cannot be an empty array");
+ }
+ CMutableTransaction tempTx;
+ AddOutputs(tempTx, options["outputs"]);
+ outputs = tempTx.vout;
+ }
}
// Make sure the results are valid at least up to the most recent block
@@ -1069,7 +1106,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
CMutableTransaction mtx;
feebumper::Result res;
// Targeting feerate bump.
- res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt);
+ res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt, outputs);
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
@@ -1144,18 +1181,7 @@ RPCHelpMan send()
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
- {
- {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
- {
- {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
- },
- },
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
- {
- {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
- },
- },
- },
+ OutputsDoc(),
RPCArgOptions{.skip_type_check = true}},
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
@@ -1606,19 +1632,8 @@ RPCHelpMan walletcreatefundedpsbt()
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
"accepted as second parameter.",
- {
- {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
- {
- {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
- },
- },
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
- {
- {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
- },
- },
- },
- RPCArgOptions{.skip_type_check = true}},
+ OutputsDoc(),
+ RPCArgOptions{.skip_type_check = true}},
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
Cat<std::vector<RPCArg>>(
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index e590aa1f08..3bfe296d90 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -872,15 +872,18 @@ RPCHelpMan rescanblockchain()
wallet.BlockUntilSyncedToCurrentChain();
WalletRescanReserver reserver(*pwallet);
- if (!reserver.reserve()) {
+ if (!reserver.reserve(/*with_passphrase=*/true)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}
int start_height = 0;
std::optional<int> stop_height;
uint256 start_block;
+
+ LOCK(pwallet->m_relock_mutex);
{
LOCK(pwallet->cs_wallet);
+ EnsureWalletIsUnlocked(*pwallet);
int tip_height = pwallet->GetLastBlockHeight();
if (!request.params[0].isNull()) {
diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp
index 31435a69ba..4d82e0a41f 100644
--- a/src/wallet/rpc/util.cpp
+++ b/src/wallet/rpc/util.cpp
@@ -6,6 +6,7 @@
#include <common/url.h>
#include <rpc/util.h>
+#include <util/system.h>
#include <util/translation.h>
#include <wallet/context.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index 23a88cd51b..16595267b4 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -359,7 +359,7 @@ static RPCHelpMan createwallet()
passphrase.reserve(100);
std::vector<bilingual_str> warnings;
if (!request.params[3].isNull()) {
- passphrase = request.params[3].get_str().c_str();
+ passphrase = std::string_view{request.params[3].get_str()};
if (passphrase.empty()) {
// Empty string means unencrypted
warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
@@ -720,9 +720,12 @@ static RPCHelpMan migratewallet()
"A new wallet backup will need to be made.\n"
"\nThe migration process will create a backup of the wallet before migrating. This backup\n"
"file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
- "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet." +
- HELP_REQUIRING_PASSPHRASE,
- {},
+ "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
+ "\nEncrypted wallets must have the passphrase provided as an argument to this call.",
+ {
+ {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
+ },
RPCResult{
RPCResult::Type::OBJ, "", "",
{
@@ -738,16 +741,26 @@ static RPCHelpMan migratewallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
+ std::string wallet_name;
+ if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
+ if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
+ }
+ } else {
+ if (request.params[0].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Either RPC endpoint wallet or wallet_name parameter must be provided");
+ }
+ wallet_name = request.params[0].get_str();
+ }
- if (wallet->IsCrypted()) {
- throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: migratewallet on encrypted wallets is currently unsupported.");
+ SecureString wallet_pass;
+ wallet_pass.reserve(100);
+ if (!request.params[1].isNull()) {
+ wallet_pass = std::string_view{request.params[1].get_str()};
}
WalletContext& context = EnsureWalletContext(request.context);
-
- util::Result<MigrationResult> res = MigrateLegacyToDescriptor(std::move(wallet), context);
+ util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context);
if (!res) {
throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
}
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index 84f33e50b3..e2b4dbf4c2 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -135,7 +135,7 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil
}
DbTxn* ptxn = env->TxnBegin();
- CWallet dummyWallet(nullptr, "", gArgs, CreateDummyWalletDatabase());
+ CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData)
{
/* Filter for only private key type KV pairs to be added to the salvaged wallet */
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 59cf87355b..c109533d7a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -11,7 +11,6 @@
#include <util/bip32.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <wallet/scriptpubkeyman.h>
@@ -1294,7 +1293,7 @@ bool LegacyScriptPubKeyMan::TopUpChain(CHDChain& chain, unsigned int kpSize)
if (kpSize > 0) {
nTargetSize = kpSize;
} else {
- nTargetSize = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{0});
+ nTargetSize = m_keypool_size;
}
int64_t target = std::max((int64_t) nTargetSize, int64_t{1});
@@ -1784,7 +1783,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc));
+ auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
desc_spk_man->TopUp();
auto desc_spks = desc_spk_man->GetScriptPubKeys();
@@ -1829,7 +1828,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc));
+ auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
desc_spk_man->AddDescriptorKey(master_key.key, master_key.key.GetPubKey());
desc_spk_man->TopUp();
auto desc_spks = desc_spk_man->GetScriptPubKeys();
@@ -1891,7 +1890,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
} else {
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc));
+ auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
for (const auto& keyid : privkeyids) {
CKey key;
if (!GetKey(keyid, key)) {
@@ -2122,7 +2121,7 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
if (size > 0) {
target_size = size;
} else {
- target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1});
+ target_size = m_keypool_size;
}
// Calculate the new range_end
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 4399ac2087..4d14325241 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -286,6 +286,9 @@ private:
int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = 0;
+ //! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
+ int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
+
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
@@ -363,7 +366,7 @@ private:
bool TopUpChain(CHDChain& chain, unsigned int size);
public:
- using ScriptPubKeyMan::ScriptPubKeyMan;
+ LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : ScriptPubKeyMan(storage), m_keypool_size(keypool_size) {}
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
isminetype IsMine(const CScript& script) const override;
@@ -555,6 +558,9 @@ private:
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
+ //! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
+ int64_t m_keypool_size GUARDED_BY(cs_desc_man){DEFAULT_KEYPOOL_SIZE};
+
bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
@@ -572,12 +578,14 @@ protected:
WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man);
public:
- DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
+ DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size)
: ScriptPubKeyMan(storage),
+ m_keypool_size(keypool_size),
m_wallet_descriptor(descriptor)
{}
- DescriptorScriptPubKeyMan(WalletStorage& storage)
- : ScriptPubKeyMan(storage)
+ DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size)
+ : ScriptPubKeyMan(storage),
+ m_keypool_size(keypool_size)
{}
mutable RecursiveMutex cs_desc_man;
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 0ef56ab8ed..1a79b59d12 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -1090,6 +1090,13 @@ util::Result<CreatedTransactionResult> CreateTransaction(
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
CCoinControl tmp_cc = coin_control;
tmp_cc.m_avoid_partial_spends = true;
+
+ // Re-use the change destination from the first creation attempt to avoid skipping BIP44 indexes
+ const int ungrouped_change_pos = txr_ungrouped.change_pos;
+ if (ungrouped_change_pos != -1) {
+ ExtractDestination(txr_ungrouped.tx->vout[ungrouped_change_pos].scriptPubKey, tmp_cc.destChange);
+ }
+
auto txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign);
// if fee of this alternative one is within the range of the max fee, we use this one
const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 5281d0589a..1c731b95e5 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_selection_params_bnb.m_subtract_fee_outputs = true;
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -339,7 +339,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
BOOST_CHECK(result10);
}
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
FastRandomContext rand{};
const auto temp1{[&rand](std::vector<OutputGroup>& g, const CAmount& v, CAmount c) { return KnapsackSolver(g, v, c, rand); }};
const auto KnapsackSolver{temp1};
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -714,7 +714,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
{
FastRandomContext rand{};
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -736,7 +736,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -921,9 +921,9 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
}
-static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, interfaces::Chain* chain, const ArgsManager& args)
+static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, interfaces::Chain* chain)
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(chain, "", args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(chain, "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -984,7 +984,7 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
add_coin(available_coins, wallet, CAmount(50 * COIN), CFeeRate(0), 144, false, 0, true);
return available_coins;
},
- chain, m_args);
+ chain);
BOOST_CHECK(result);
BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(50 * COIN)));
@@ -1009,7 +1009,7 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
}
return available_coins;
},
- chain, m_args);
+ chain);
BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(0.0625 * COIN)));
BOOST_CHECK(has_coin(result->GetInputSet(), CAmount(0.025 * COIN)));
@@ -1030,7 +1030,7 @@ BOOST_AUTO_TEST_CASE(check_max_weight)
}
return available_coins;
},
- chain, m_args);
+ chain);
// No results
// 1515 inputs * 68 bytes = 103,020 bytes
@@ -1045,7 +1045,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
// This test creates a coin whose value is higher than the target but whose effective value is lower than the target.
// The coin is selected using coin control, with m_allow_other_inputs = false. SelectCoins should fail due to insufficient funds.
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -1053,7 +1053,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
CoinsResult available_coins;
{
- std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", CreateMockWalletDatabase());
dummyWallet->LoadWallet();
LOCK(dummyWallet->cs_wallet);
dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -1094,7 +1094,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_coinsresult_test, BasicTestingSetup)
// Test case to verify CoinsResult object sanity.
CoinsResult available_coins;
{
- std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", m_args, CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", CreateMockWalletDatabase());
BOOST_CHECK_EQUAL(dummyWallet->LoadWallet(), DBErrors::LOAD_OK);
LOCK(dummyWallet->cs_wallet);
dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index 151b09d2a6..90f369b22a 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "pk(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "pk(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "pkh(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "pkh(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "sh(pkh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid) - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "sh(sh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@@ -247,7 +247,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid) - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wsh(sh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid) - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wsh(wpkh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@@ -301,7 +301,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wsh(wsh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wpkh(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -357,7 +357,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -378,7 +378,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wpkh(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@@ -387,7 +387,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -422,7 +422,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@@ -434,7 +434,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -457,7 +457,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "sh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))";
@@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -500,7 +500,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + "))";
@@ -514,7 +514,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -543,7 +543,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key (invalid) - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "wsh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))";
@@ -553,7 +553,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH - Legacy
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -583,7 +583,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "sh(wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + ")))";
@@ -598,7 +598,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Combo - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "combo(" + EncodeSecret(keys[0]) + ")";
@@ -642,7 +642,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Taproot - Descriptor
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
std::string desc_str = "tr(" + EncodeSecret(keys[0]) + ")";
@@ -660,7 +660,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -675,7 +675,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -705,7 +705,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
- CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index a524b85ccb..90042f5252 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -18,7 +18,7 @@ BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(CanProvide)
{
// Set up wallet and keyman variables.
- CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index 364cc5c20b..6e87d1cb49 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -18,7 +18,7 @@ BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)
BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), m_args, coinbaseKey);
+ auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);
// Check that a subtract-from-recipient transaction slightly less than the
// coinbase input amount does not create a change output (because it would
@@ -118,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup)
// Add 4 spendable UTXO, 50 BTC each, to the wallet (total balance 200 BTC)
for (int i = 0; i < 4; i++) CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), m_args, coinbaseKey);
+ auto wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);
LOCK(wallet->cs_wallet);
auto available_coins = AvailableCoins(*wallet);
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index 225871fd91..b7bf312edf 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -14,9 +14,9 @@
#include <memory>
namespace wallet {
-std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key)
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
{
- auto wallet = std::make_unique<CWallet>(&chain, "", args, CreateMockWalletDatabase());
+ auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
index 635a5152ec..d726517e21 100644
--- a/src/wallet/test/util.h
+++ b/src/wallet/test/util.h
@@ -21,7 +21,7 @@ class CWallet;
struct DatabaseOptions;
class WalletDatabase;
-std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key);
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
// Creates a copy of the provided database
std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database, DatabaseOptions& options);
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index 6b8542f378..d5e75bb892 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <wallet/crypter.h>
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index c47e56c093..2dd8f9ad33 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -10,7 +10,7 @@ namespace wallet {
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet_loader{interfaces::MakeWalletLoader(*m_node.chain, *Assert(m_node.args))},
- m_wallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase())
+ m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase())
{
m_wallet.LoadWallet();
m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index f056c54734..2e95a14807 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -98,7 +98,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
- CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
@@ -119,7 +119,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
@@ -164,7 +164,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
@@ -192,7 +192,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
@@ -232,7 +232,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context;
@@ -298,7 +298,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
WalletContext context;
context.args = &m_args;
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -321,7 +321,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
@@ -355,7 +355,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
- CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
@@ -527,7 +527,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), m_args, coinbaseKey);
+ wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);
}
~ListCoinsTestingSetup()
@@ -665,7 +665,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTest, ListCoinsTest)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
{
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
@@ -673,7 +673,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
}
{
- const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet->SetMinVersion(FEATURE_LATEST);
@@ -961,7 +961,7 @@ public:
*/
BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
{
- CWallet wallet(m_node.chain.get(), "", m_args, std::make_unique<FailDatabase>());
+ CWallet wallet(m_node.chain.get(), "", std::make_unique<FailDatabase>());
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
diff --git a/src/wallet/test/walletload_tests.cpp b/src/wallet/test/walletload_tests.cpp
index f1feb28e7d..9f5a4b14d3 100644
--- a/src/wallet/test/walletload_tests.cpp
+++ b/src/wallet/test/walletload_tests.cpp
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup)
{
// Now try to load the wallet and verify the error.
- const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, std::move(database)));
+ const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::UNKNOWN_DESCRIPTOR);
}
}
@@ -84,7 +84,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
{ // Context setup.
// Create and encrypt legacy wallet
- std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase()));
+ std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockWalletDatabase()));
LOCK(wallet->cs_wallet);
auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan();
BOOST_CHECK(legacy_spkm->SetupGeneration(true));
@@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
// the records every time that 'CWallet::Unlock' gets called, which is not good.
// Load the wallet and check that is encrypted
- std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, get_db(dbs)));
+ std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", get_db(dbs)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
BOOST_CHECK(wallet->IsCrypted());
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
}
// Load the wallet and check that is encrypted
- std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, std::move(db)));
+ std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
BOOST_CHECK(wallet->IsCrypted());
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
@@ -166,7 +166,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
BOOST_CHECK(batch->Write(key, value, /*fOverwrite=*/true));
}
- std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, std::move(db)));
+ std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
}
@@ -182,7 +182,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
BOOST_CHECK(db->MakeBatch(false)->Write(key, value, /*fOverwrite=*/true));
}
- std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, std::move(db)));
+ std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
}
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b709bd9650..daddd6446d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -35,6 +35,7 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/string.h>
+#include <util/system.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
@@ -551,7 +552,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
bool fWasLocked = IsLocked();
{
- LOCK(cs_wallet);
+ LOCK2(m_relock_mutex, cs_wallet);
Lock();
CCrypter crypter;
@@ -786,7 +787,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
return false;
{
- LOCK(cs_wallet);
+ LOCK2(m_relock_mutex, cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
WalletBatch* encrypted_batch = new WalletBatch(GetDatabase());
if (!encrypted_batch->TxnBegin()) {
@@ -1109,7 +1110,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
#if HAVE_SYSTEM
// notify an external script when a wallet transaction comes in or is updated
- std::string strCmd = m_args.GetArg("-walletnotify", "");
+ std::string strCmd = m_notify_tx_changed_script;
if (!strCmd.empty())
{
@@ -2914,7 +2915,11 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
const auto start{SteadyClock::now()};
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, args, std::move(database)), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
+ walletInstance->m_keypool_size = std::max(args.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1});
+ walletInstance->m_notify_tx_changed_script = args.GetArg("-walletnotify", "");
+
+ // Load wallet
bool rescan_required = false;
DBErrors nLoadWalletRet = walletInstance->LoadWallet();
if (nLoadWalletRet != DBErrors::LOAD_OK) {
@@ -3407,7 +3412,7 @@ bool CWallet::Lock()
return false;
{
- LOCK(cs_wallet);
+ LOCK2(m_relock_mutex, cs_wallet);
if (!vMasterKey.empty()) {
memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type));
vMasterKey.clear();
@@ -3538,7 +3543,7 @@ void CWallet::SetupLegacyScriptPubKeyMan()
return;
}
- auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this));
+ auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this, m_keypool_size));
for (const auto& type : LEGACY_OUTPUT_TYPES) {
m_internal_spk_managers[type] = spk_manager.get();
m_external_spk_managers[type] = spk_manager.get();
@@ -3567,10 +3572,10 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
{
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
- auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
+ auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc, m_keypool_size));
m_spk_managers[id] = std::move(spk_manager);
} else {
- auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
+ auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size));
m_spk_managers[id] = std::move(spk_manager);
}
}
@@ -3581,7 +3586,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans(const CExtKey& master_key)
for (bool internal : {false, true}) {
for (OutputType t : OUTPUT_TYPES) {
- auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this));
+ auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, m_keypool_size));
if (IsCrypted()) {
if (IsLocked()) {
throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
@@ -3637,7 +3642,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
continue;
}
OutputType t = *desc->GetOutputType();
- auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this));
+ auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, m_keypool_size));
spk_manager->SetupDescriptor(std::move(desc));
uint256 id = spk_manager->GetID();
m_spk_managers[id] = std::move(spk_manager);
@@ -3753,7 +3758,7 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
spk_man->UpdateWalletDescriptor(desc);
} else {
- auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
+ auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size));
spk_man = new_spk_man.get();
// Save the descriptor to memory
@@ -3879,7 +3884,7 @@ std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& err
std::optional<MigrationData> res = legacy_spkm->MigrateToDescriptor();
if (res == std::nullopt) {
- error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure the wallet is unlocked first");
+ error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted.");
return std::nullopt;
}
return res;
@@ -4169,32 +4174,19 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error,
return true;
}
-util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>&& wallet, WalletContext& context)
+util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context)
{
- // Before anything else, check if there is something to migrate.
- if (!wallet->GetLegacyScriptPubKeyMan()) {
- return util::Error{_("Error: This wallet is already a descriptor wallet")};
- }
-
MigrationResult res;
bilingual_str error;
std::vector<bilingual_str> warnings;
- // Make a backup of the DB
- std::string wallet_name = wallet->GetName();
- fs::path this_wallet_dir = fs::absolute(fs::PathFromString(wallet->GetDatabase().Filename())).parent_path();
- fs::path backup_filename = fs::PathFromString(strprintf("%s-%d.legacy.bak", wallet_name, GetTime()));
- fs::path backup_path = this_wallet_dir / backup_filename;
- if (!wallet->BackupWallet(fs::PathToString(backup_path))) {
- return util::Error{_("Error: Unable to make a backup of your wallet")};
- }
- res.backup_path = backup_path;
-
- // Unload the wallet so that nothing else tries to use it while we're changing it
- if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
- return util::Error{_("Unable to unload the wallet before migrating")};
+ // If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
+ if (auto wallet = GetWallet(context, wallet_name)) {
+ if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
+ return util::Error{_("Unable to unload the wallet before migrating")};
+ }
+ UnloadWallet(std::move(wallet));
}
- UnloadWallet(std::move(wallet));
// Load the wallet but only in the context of this function.
// No signals should be connected nor should anything else be aware of this wallet
@@ -4208,15 +4200,43 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
return util::Error{Untranslated("Wallet file verification failed.") + Untranslated(" ") + error};
}
+ // Make the local wallet
std::shared_ptr<CWallet> local_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings);
if (!local_wallet) {
return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error};
}
+ // Before anything else, check if there is something to migrate.
+ if (!local_wallet->GetLegacyScriptPubKeyMan()) {
+ return util::Error{_("Error: This wallet is already a descriptor wallet")};
+ }
+
+ // Make a backup of the DB
+ fs::path this_wallet_dir = fs::absolute(fs::PathFromString(local_wallet->GetDatabase().Filename())).parent_path();
+ fs::path backup_filename = fs::PathFromString(strprintf("%s-%d.legacy.bak", wallet_name, GetTime()));
+ fs::path backup_path = this_wallet_dir / backup_filename;
+ if (!local_wallet->BackupWallet(fs::PathToString(backup_path))) {
+ return util::Error{_("Error: Unable to make a backup of your wallet")};
+ }
+ res.backup_path = backup_path;
+
bool success = false;
{
LOCK(local_wallet->cs_wallet);
+ // Unlock the wallet if needed
+ if (local_wallet->IsLocked() && !local_wallet->Unlock(passphrase)) {
+ if (passphrase.find('\0') == std::string::npos) {
+ return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect.")};
+ } else {
+ return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase entered was incorrect. "
+ "The passphrase contains a null character (ie - a zero byte). "
+ "If this passphrase was set with a version of this software prior to 25.0, "
+ "please try again with only the characters up to — but not including — "
+ "the first null character.")};
+ }
+ }
+
// First change to using SQLite
if (!local_wallet->MigrateToSQLite(error)) return util::Error{error};
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index f104a15f98..32cb3e3f59 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -10,6 +10,7 @@
#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
+#include <logging.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <psbt.h>
@@ -19,7 +20,6 @@
#include <util/result.h>
#include <util/strencodings.h>
#include <util/string.h>
-#include <util/system.h>
#include <util/time.h>
#include <util/ui_change_type.h>
#include <validationinterface.h>
@@ -243,6 +243,7 @@ private:
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<bool> m_attaching_chain{false};
+ std::atomic<bool> m_scanning_with_passphrase{false};
std::atomic<int64_t> m_scanning_start{0};
std::atomic<double> m_scanning_progress{0};
friend class WalletRescanReserver;
@@ -307,9 +308,6 @@ private:
//! Unset the blank wallet flag and saves it to disk
void UnsetBlankWalletFlag(WalletBatch& batch) override;
- /** Provider of aplication-wide arguments. */
- const ArgsManager& m_args;
-
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
@@ -373,9 +371,8 @@ public:
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain* chain, const std::string& name, const ArgsManager& args, std::unique_ptr<WalletDatabase> database)
- : m_args(args),
- m_chain(chain),
+ CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
+ : m_chain(chain),
m_name(name),
m_database(std::move(database))
{
@@ -467,6 +464,7 @@ public:
void AbortRescan() { fAbortRescan = true; }
bool IsAbortingRescan() const { return fAbortRescan; }
bool IsScanning() const { return fScanningWallet; }
+ bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; }
int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
@@ -486,6 +484,9 @@ public:
// Used to prevent concurrent calls to walletpassphrase RPC.
Mutex m_unlock_mutex;
+ // Used to prevent deleting the passphrase from memory when it is still in use.
+ RecursiveMutex m_relock_mutex;
+
bool Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys = false);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
@@ -642,6 +643,12 @@ public:
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
+ /** Number of pre-generated keys/scripts by each spkm (part of the look-ahead process, used to detect payments) */
+ int64_t m_keypool_size{DEFAULT_KEYPOOL_SIZE};
+
+ /** Notify external script when a wallet transaction comes in or is updated (handled by -walletnotify) */
+ std::string m_notify_tx_changed_script;
+
size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
@@ -960,12 +967,13 @@ private:
public:
explicit WalletRescanReserver(CWallet& w) : m_wallet(w) {}
- bool reserve()
+ bool reserve(bool with_passphrase = false)
{
assert(!m_could_reserve);
if (m_wallet.fScanningWallet.exchange(true)) {
return false;
}
+ m_wallet.m_scanning_with_passphrase.exchange(with_passphrase);
m_wallet.m_scanning_start = GetTimeMillis();
m_wallet.m_scanning_progress = 0;
m_could_reserve = true;
@@ -985,6 +993,7 @@ public:
{
if (m_could_reserve) {
m_wallet.fScanningWallet = false;
+ m_wallet.m_scanning_with_passphrase = false;
}
}
};
@@ -1007,7 +1016,7 @@ struct MigrationResult {
};
//! Do all steps to migrate a legacy wallet to a descriptor wallet
-util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>&& wallet, WalletContext& context);
+util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context);
} // namespace wallet
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 96537b9873..f389676204 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -47,7 +47,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
wallet_instance->TopUpKeyPool();
}
-static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options)
+static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
{
DatabaseStatus status;
bilingual_str error;
@@ -58,7 +58,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, name, args, std::move(database)), WalletToolReleaseWallet};
+ std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
load_wallet_ret = wallet_instance->LoadWallet();
@@ -159,7 +159,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
options.require_format = DatabaseFormat::SQLITE;
}
- const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -168,7 +168,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
DatabaseOptions options;
ReadDatabaseArgs(args, options);
options.require_existing = true;
- const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -194,7 +194,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
DatabaseOptions options;
ReadDatabaseArgs(args, options);
options.require_existing = true;
- const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (!wallet_instance) return false;
bilingual_str error;
bool ret = DumpWallet(args, *wallet_instance, error);
diff --git a/test/README.md b/test/README.md
index fdbb91832a..0eddb72e1f 100644
--- a/test/README.md
+++ b/test/README.md
@@ -109,34 +109,57 @@ how many jobs to run, append `--jobs=n`
The individual tests and the test_runner harness have many command-line
options. Run `test/functional/test_runner.py -h` to see them all.
-#### Speed up test runs with a ramdisk
+#### Speed up test runs with a RAM disk
-If you have available RAM on your system you can create a ramdisk to use as the `cache` and `tmp` directories for the functional tests in order to speed them up.
-Speed-up amount varies on each system (and according to your ram speed and other variables), but a 2-3x speed-up is not uncommon.
+If you have available RAM on your system you can create a RAM disk to use as the `cache` and `tmp` directories for the functional tests in order to speed them up.
+Speed-up amount varies on each system (and according to your RAM speed and other variables), but a 2-3x speed-up is not uncommon.
-To create a 4GB ramdisk on Linux at `/mnt/tmp/`:
+**Linux**
+
+To create a 4 GiB RAM disk at `/mnt/tmp/`:
```bash
sudo mkdir -p /mnt/tmp
sudo mount -t tmpfs -o size=4g tmpfs /mnt/tmp/
```
-Configure the size of the ramdisk using the `size=` option.
-The size of the ramdisk needed is relative to the number of concurrent jobs the test suite runs.
-For example running the test suite with `--jobs=100` might need a 4GB ramdisk, but running with `--jobs=32` will only need a 2.5GB ramdisk.
+Configure the size of the RAM disk using the `size=` option.
+The size of the RAM disk needed is relative to the number of concurrent jobs the test suite runs.
+For example running the test suite with `--jobs=100` might need a 4 GiB RAM disk, but running with `--jobs=32` will only need a 2.5 GiB RAM disk.
-To use, run the test suite specifying the ramdisk as the `cachedir` and `tmpdir`:
+To use, run the test suite specifying the RAM disk as the `cachedir` and `tmpdir`:
```bash
test/functional/test_runner.py --cachedir=/mnt/tmp/cache --tmpdir=/mnt/tmp
```
-Once finished with the tests and the disk, and to free the ram, simply unmount the disk:
+Once finished with the tests and the disk, and to free the RAM, simply unmount the disk:
```bash
sudo umount /mnt/tmp
```
+**macOS**
+
+To create a 4 GiB RAM disk named "ramdisk" at `/Volumes/ramdisk/`:
+
+```bash
+diskutil erasevolume HFS+ ramdisk $(hdiutil attach -nomount ram://8388608)
+```
+
+Configure the RAM disk size, expressed as the number of blocks, at the end of the command
+(`4096 MiB * 2048 blocks/MiB = 8388608 blocks` for 4 GiB). To run the tests using the RAM disk:
+
+```bash
+test/functional/test_runner.py --cachedir=/Volumes/ramdisk/cache --tmpdir=/Volumes/ramdisk/tmp
+```
+
+To unmount:
+
+```bash
+umount /Volumes/ramdisk
+```
+
#### Troubleshooting and debugging test failures
##### Resource contention
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
index eff4d9b149..4f8541a5d7 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -156,9 +156,10 @@ class CoinStatsIndexTest(BitcoinTestFramework):
# Generate and send another tx with an OP_RETURN output (which is unspendable)
tx2 = self.wallet.create_self_transfer(utxo_to_spend=tx1_out_21)['tx']
- tx2.vout = [CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx2_val = '20.99'
+ tx2.vout = [CTxOut(int(Decimal(tx2_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
tx2_hex = tx2.serialize().hex()
- self.nodes[0].sendrawtransaction(tx2_hex)
+ self.nodes[0].sendrawtransaction(tx2_hex, 0, tx2_val)
# Include both txs in a block
self.generate(self.nodes[0], 1)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index a37d614535..f9730b48c5 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -251,11 +251,43 @@ class ConfArgsTest(BitcoinTestFramework):
]):
self.nodes[0].setmocktime(start + 65)
+ def test_connect_with_seednode(self):
+ self.log.info('Test -connect with -seednode')
+ seednode_ignored = ['-seednode is ignored when -connect is used\n']
+ dnsseed_ignored = ['-dnsseed is ignored when -connect is used and -proxy is specified\n']
+ addcon_thread_started = ['addcon thread start\n']
+ self.stop_node(0)
+
+ # When -connect is supplied, expanding addrman via getaddr calls to ADDR_FETCH(-seednode)
+ # nodes is irrelevant and -seednode is ignored.
+ with self.nodes[0].assert_debug_log(expected_msgs=seednode_ignored):
+ self.start_node(0, extra_args=['-connect=fakeaddress1', '-seednode=fakeaddress2'])
+
+ # With -proxy, an ADDR_FETCH connection is made to a peer that the dns seed resolves to.
+ # ADDR_FETCH connections are not used when -connect is used.
+ with self.nodes[0].assert_debug_log(expected_msgs=dnsseed_ignored):
+ self.restart_node(0, extra_args=['-connect=fakeaddress1', '-dnsseed=1', '-proxy=1.2.3.4'])
+
+ # If the user did not disable -dnsseed, but it was soft-disabled because they provided -connect,
+ # they shouldn't see a warning about -dnsseed being ignored.
+ with self.nodes[0].assert_debug_log(expected_msgs=addcon_thread_started,
+ unexpected_msgs=dnsseed_ignored):
+ self.restart_node(0, extra_args=['-connect=fakeaddress1', '-proxy=1.2.3.4'])
+
+ # We have to supply expected_msgs as it's a required argument
+ # The expected_msg must be something we are confident will be logged after the unexpected_msg
+ # These cases test for -connect being supplied but only to disable it
+ for connect_arg in ['-connect=0', '-noconnect']:
+ with self.nodes[0].assert_debug_log(expected_msgs=addcon_thread_started,
+ unexpected_msgs=seednode_ignored):
+ self.restart_node(0, extra_args=[connect_arg, '-seednode=fakeaddress2'])
+
def run_test(self):
self.test_log_buffer()
self.test_args_log()
self.test_seed_peers()
self.test_networkactive()
+ self.test_connect_with_seednode()
self.test_config_file_parser()
self.test_invalid_command_line_options()
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index e2bc566f53..1f2e0936ed 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -85,7 +85,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.nodes[node_index].waitforblock(expected_tip)
utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2']
return utxo_hash
- except:
+ except Exception:
# An exception here should mean the node is about to crash.
# If bitcoind exits, then try again. wait_for_node_exit()
# should raise an exception if bitcoind doesn't exit.
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 664ed779db..519877ac5b 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -223,8 +223,8 @@ class PruneTest(BitcoinTestFramework):
def reorg_back(self):
# Verify that a block on the old main chain fork has been pruned away
assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash)
- with self.nodes[2].assert_debug_log(expected_msgs=['block verification stopping at height', '(pruning, no data)']):
- self.nodes[2].verifychain(checklevel=4, nblocks=0)
+ with self.nodes[2].assert_debug_log(expected_msgs=['block verification stopping at height', '(no data)']):
+ assert not self.nodes[2].verifychain(checklevel=4, nblocks=0)
self.log.info(f"Will need to redownload block {self.forkheight}")
# Verify that we have enough history to reorg back to the fork point
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 144e01c367..8ac06f570d 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -750,7 +750,7 @@ def spenders_taproot_active():
# Reusing the scripts above, test that various features affect the sighash.
add_spender(spenders, "sighash/annex", tap=tap, leaf="pk_codesep", key=secs[1], hashtype=hashtype, standard=False, **SINGLE_SIG, annex=bytes([ANNEX_TAG]), failure={"sighash": override(default_sighash, annex=None)}, **ERR_SIG_SCHNORR)
add_spender(spenders, "sighash/script", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, script_taproot=tap.leaves["codesep_pk"].script)}, **ERR_SIG_SCHNORR)
- add_spender(spenders, "sighash/leafver", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, leafversion=random.choice([x & 0xFE for x in range(0x100) if x & 0xFE != 0xC0]))}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/leafver", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, leafversion=random.choice([x & 0xFE for x in range(0x100) if x & 0xFE != LEAF_VERSION_TAPSCRIPT]))}, **ERR_SIG_SCHNORR)
add_spender(spenders, "sighash/scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, leaf=None)}, **ERR_SIG_SCHNORR)
add_spender(spenders, "sighash/keypath", tap=tap, key=secs[0], **common, failure={"sighash": override(default_sighash, leaf="pk_codesep")}, **ERR_SIG_SCHNORR)
@@ -1555,12 +1555,16 @@ class TaprootTest(BitcoinTestFramework):
script_lists = [
None,
- [("0", CScript([pubs[50], OP_CHECKSIG]), 0xc0)],
- [("0", CScript([pubs[51], OP_CHECKSIG]), 0xc0)],
- [("0", CScript([pubs[52], OP_CHECKSIG]), 0xc0), ("1", CScript([b"BIP341"]), VALID_LEAF_VERS[pubs[99][0] % 41])],
- [("0", CScript([pubs[53], OP_CHECKSIG]), 0xc0), ("1", CScript([b"Taproot"]), VALID_LEAF_VERS[pubs[99][1] % 41])],
- [("0", CScript([pubs[54], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[55], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[56], OP_CHECKSIG]), 0xc0)]],
- [("0", CScript([pubs[57], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[58], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[59], OP_CHECKSIG]), 0xc0)]],
+ [("0", CScript([pubs[50], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT)],
+ [("0", CScript([pubs[51], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT)],
+ [("0", CScript([pubs[52], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT), ("1", CScript([b"BIP341"]), VALID_LEAF_VERS[pubs[99][0] % 41])],
+ [("0", CScript([pubs[53], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT), ("1", CScript([b"Taproot"]), VALID_LEAF_VERS[pubs[99][1] % 41])],
+ [("0", CScript([pubs[54], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT),
+ [("1", CScript([pubs[55], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT), ("2", CScript([pubs[56], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT)]
+ ],
+ [("0", CScript([pubs[57], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT),
+ [("1", CScript([pubs[58], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT), ("2", CScript([pubs[59], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT)]
+ ],
]
taps = [taproot_construct(inner_keys[i], script_lists[i]) for i in range(len(inner_keys))]
diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py
index b2f0659eda..394009f30f 100755
--- a/test/functional/p2p_disconnect_ban.py
+++ b/test/functional/p2p_disconnect_ban.py
@@ -116,7 +116,7 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: successfully disconnect node by address")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
self.nodes[0].disconnectnode(address=address1)
- self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 1, timeout=10)
assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
self.log.info("disconnectnode: successfully reconnect node")
@@ -127,7 +127,7 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: successfully disconnect node by node id")
id1 = self.nodes[0].getpeerinfo()[0]['id']
self.nodes[0].disconnectnode(nodeid=id1)
- self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 1, timeout=10)
assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1]
if __name__ == '__main__':
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 5a0003d3ef..a56afbcf7b 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -85,7 +85,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.connect_nodes(0, 2)
try:
self.sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
- except:
+ except Exception:
pass
# node2 must remain at height 0
assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0)
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 8df3b68785..673836bd04 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -272,6 +272,9 @@ class DecodeScriptTest(BitcoinTestFramework):
# Miniscript-incompatible offered HTLC
res = self.nodes[0].decodescript("82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2")
assert res["segwit"]["desc"] == "wsh(raw(82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2))#ra6w2xa7"
+ # Miniscript-compatible multisig bigger than 520 byte P2SH limit.
+ res = self.nodes[0].decodescript("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68")
+ assert_equal(res["segwit"]["desc"], "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4")
def run_test(self):
self.log.info("Test decoding of standard input scripts [scriptSig]")
diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py
index 91298937fd..3062a86565 100755
--- a/test/functional/rpc_preciousblock.py
+++ b/test/functional/rpc_preciousblock.py
@@ -16,7 +16,7 @@ def unidirectional_node_sync_via_rpc(node_src, node_dest):
try:
assert len(node_dest.getblock(blockhash, False)) > 0
break
- except:
+ except Exception:
blocks_to_copy.append(blockhash)
blockhash = node_src.getblockheader(blockhash, True)['previousblockhash']
blocks_to_copy.reverse()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index cdec4b2a85..2395935620 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -18,9 +18,17 @@ from itertools import product
from test_framework.messages import (
MAX_BIP125_RBF_SEQUENCE,
+ COIN,
CTransaction,
+ CTxOut,
tx_from_hex,
)
+from test_framework.script import (
+ CScript,
+ OP_FALSE,
+ OP_INVALIDOPCODE,
+ OP_RETURN,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -331,6 +339,57 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx)
+ self.log.info("Test sendrawtransaction exceeding, falling short of, and equaling maxburnamount")
+ max_burn_exceeded = "Unspendable output exceeds maximum configured by user (maxburnamount)"
+
+
+ # Test that spendable transaction with default maxburnamount (0) gets sent
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_hex = tx.serialize().hex()
+ self.nodes[2].sendrawtransaction(hexstring=tx_hex)
+
+ # Test that datacarrier transaction with default maxburnamount (0) does not get sent
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.001
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx_hex = tx.serialize().hex()
+ assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
+
+ # Test that oversized script gets rejected by sendrawtransaction
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.001
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_FALSE] * 10001))]
+ tx_hex = tx.serialize().hex()
+ assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
+
+ # Test that script containing invalid opcode gets rejected by sendrawtransaction
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.01
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_INVALIDOPCODE]))]
+ tx_hex = tx.serialize().hex()
+ assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
+
+ # Test a transaction where our burn exceeds maxburnamount
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.001
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx_hex = tx.serialize().hex()
+ assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex, 0, 0.0009)
+
+ # Test a transaction where our burn falls short of maxburnamount
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.001
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx_hex = tx.serialize().hex()
+ self.nodes[2].sendrawtransaction(hexstring=tx_hex, maxfeerate='0', maxburnamount='0.0011')
+
+ # Test a transaction where our burn equals maxburnamount
+ tx = self.wallet.create_self_transfer()['tx']
+ tx_val = 0.001
+ tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx_hex = tx.serialize().hex()
+ self.nodes[2].sendrawtransaction(hexstring=tx_hex, maxfeerate='0', maxburnamount='0.001')
+
def sendrawtransaction_testmempoolaccept_tests(self):
self.log.info("Test sendrawtransaction/testmempoolaccept with maxfeerate")
fee_exceeds_max = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index dd20b28550..61f92aeac3 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -78,7 +78,10 @@ class AuthServiceProxy():
passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
authpair = user + b':' + passwd
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
- self.timeout = timeout
+ # clamp the socket timeout, since larger values can cause an
+ # "Invalid argument" exception in Python's HTTP(S) client
+ # library on some operating systems (e.g. OpenBSD, FreeBSD)
+ self.timeout = min(timeout, 2147483)
self._set_conn(connection)
def __getattr__(self, name):
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index 59157f4755..c5768177bd 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -385,7 +385,7 @@ class P2PInterface(P2PConnection):
self.message_count[msgtype] += 1
self.last_message[msgtype] = message
getattr(self, 'on_' + msgtype)(message)
- except:
+ except Exception:
print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]))
raise
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 513b795478..9620951a16 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -279,10 +279,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if seed is None:
seed = random.randrange(sys.maxsize)
else:
- self.log.debug("User supplied random seed {}".format(seed))
+ self.log.info("User supplied random seed {}".format(seed))
random.seed(seed)
- self.log.debug("PRNG seed is: {}".format(seed))
+ self.log.info("PRNG seed is: {}".format(seed))
self.log.debug('Setting up network thread')
self.network_thread = NetworkThread()
@@ -557,7 +557,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
node.start(extra_args[i], *args, **kwargs)
for node in self.nodes:
node.wait_for_rpc_connection()
- except:
+ except Exception:
# If one node failed to start, stop the others
self.stop_nodes()
raise
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index b515538a1a..882f82e0f2 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -829,7 +829,7 @@ class RPCOverloadWrapper():
int(address ,16)
is_hex = True
desc = descsum_create('raw(' + address + ')')
- except:
+ except Exception:
desc = descsum_create('addr(' + address + ')')
reqs = [{
'desc': desc,
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0fe28b3855..26ebce039b 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -216,6 +216,8 @@ BASE_SCRIPTS = [
'rpc_blockchain.py',
'rpc_deprecated.py',
'wallet_disable.py',
+ 'wallet_change_address.py --legacy-wallet',
+ 'wallet_change_address.py --descriptors',
'p2p_addr_relay.py',
'p2p_getaddr_caching.py',
'p2p_getdata.py',
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index a2ae997ecb..ad79e0288c 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -81,7 +81,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.log.info("Running tests")
dest_address = peer_node.getnewaddress()
- for mode in ["default", "fee_rate"]:
+ for mode in ["default", "fee_rate", "new_outputs"]:
test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address)
self.test_invalid_parameters(rbf_node, peer_node, dest_address)
test_segwit_bumpfee_succeeds(self, rbf_node, dest_address)
@@ -157,6 +157,14 @@ class BumpFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"',
rbf_node.bumpfee, rbfid, {"estimate_mode": mode})
+ self.log.info("Test invalid outputs values")
+ assert_raises_rpc_error(-8, "Invalid parameter, output argument cannot be an empty array",
+ rbf_node.bumpfee, rbfid, {"outputs": []})
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: " + dest_address,
+ rbf_node.bumpfee, rbfid, {"outputs": [{dest_address: 0.1}, {dest_address: 0.2}]})
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data",
+ rbf_node.bumpfee, rbfid, {"outputs": [{"data": "deadbeef"}, {"data": "deadbeef"}]})
+
self.clear_mempool()
@@ -169,6 +177,10 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
if mode == "fee_rate":
bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": str(NORMAL)})
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
+ elif mode == "new_outputs":
+ new_address = peer_node.getnewaddress()
+ bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"outputs": {new_address: 0.0003}})
+ bumped_tx = rbf_node.bumpfee(rbfid, {"outputs": {new_address: 0.0003}})
else:
bumped_psbt = rbf_node.psbtbumpfee(rbfid)
bumped_tx = rbf_node.bumpfee(rbfid)
@@ -192,6 +204,10 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
bumpedwtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(oldwtx["replaced_by_txid"], bumped_tx["txid"])
assert_equal(bumpedwtx["replaces_txid"], rbfid)
+ # if this is a new_outputs test, check that outputs were indeed replaced
+ if mode == "new_outputs":
+ assert len(bumpedwtx["details"]) == 1
+ assert bumpedwtx["details"][0]["address"] == new_address
self.clear_mempool()
@@ -628,12 +644,14 @@ def test_change_script_match(self, rbf_node, dest_address):
self.clear_mempool()
-def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
+def spend_one_input(node, dest_address, change_size=Decimal("0.00049000"), data=None):
tx_input = dict(
sequence=MAX_BIP125_RBF_SEQUENCE, **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
destinations = {dest_address: Decimal("0.00050000")}
if change_size > 0:
destinations[node.getrawchangeaddress()] = change_size
+ if data:
+ destinations['data'] = data
rawtx = node.createrawtransaction([tx_input], destinations)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx["hex"])
diff --git a/test/functional/wallet_change_address.py b/test/functional/wallet_change_address.py
new file mode 100755
index 0000000000..f8bfe9eebf
--- /dev/null
+++ b/test/functional/wallet_change_address.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# Copyright (c) 2023 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test wallet change address selection"""
+
+import re
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+
+class WalletChangeAddressTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ self.add_wallet_options(parser)
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+ # discardfee is used to make change outputs less likely in the change_pos test
+ self.extra_args = [
+ [],
+ ["-discardfee=1"],
+ ["-avoidpartialspends", "-discardfee=1"]
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def assert_change_index(self, node, tx, index):
+ change_index = None
+ for vout in tx["vout"]:
+ info = node.getaddressinfo(vout["scriptPubKey"]["address"])
+ if (info["ismine"] and info["ischange"]):
+ change_index = int(re.findall(r'\d+', info["hdkeypath"])[-1])
+ break
+ assert_equal(change_index, index)
+
+ def assert_change_pos(self, wallet, tx, pos):
+ change_pos = None
+ for index, output in enumerate(tx["vout"]):
+ info = wallet.getaddressinfo(output["scriptPubKey"]["address"])
+ if (info["ismine"] and info["ischange"]):
+ change_pos = index
+ break
+ assert_equal(change_pos, pos)
+
+ def run_test(self):
+ self.log.info("Setting up")
+ # Mine some coins
+ self.generate(self.nodes[0], COINBASE_MATURITY + 1)
+
+ # Get some addresses from the two nodes
+ addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
+ addr2 = [self.nodes[2].getnewaddress() for _ in range(3)]
+ addrs = addr1 + addr2
+
+ # Send 1 + 0.5 coin to each address
+ [self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs]
+ [self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs]
+ self.generate(self.nodes[0], 1)
+
+ for i in range(20):
+ for n in [1, 2]:
+ self.log.debug(f"Send transaction from node {n}: expected change index {i}")
+ txid = self.nodes[n].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
+ tx = self.nodes[n].getrawtransaction(txid, True)
+ # find the change output and ensure that expected change index was used
+ self.assert_change_index(self.nodes[n], tx, i)
+
+ # Start next test with fresh wallets and new coins
+ self.nodes[1].createwallet("w1")
+ self.nodes[2].createwallet("w2")
+ w1 = self.nodes[1].get_wallet_rpc("w1")
+ w2 = self.nodes[2].get_wallet_rpc("w2")
+ addr1 = w1.getnewaddress()
+ addr2 = w2.getnewaddress()
+ self.nodes[0].sendtoaddress(addr1, 3.0)
+ self.nodes[0].sendtoaddress(addr1, 0.1)
+ self.nodes[0].sendtoaddress(addr2, 3.0)
+ self.nodes[0].sendtoaddress(addr2, 0.1)
+ self.generate(self.nodes[0], 1)
+
+ sendTo1 = self.nodes[0].getnewaddress()
+ sendTo2 = self.nodes[0].getnewaddress()
+ sendTo3 = self.nodes[0].getnewaddress()
+
+ # The avoid partial spends wallet will always create a change output
+ node = self.nodes[2]
+ res = w2.send({sendTo1: "1.0", sendTo2: "1.0", sendTo3: "0.9999"}, options={"change_position": 0})
+ tx = node.getrawtransaction(res["txid"], True)
+ self.assert_change_pos(w2, tx, 0)
+
+ # The default wallet will internally create a tx without change first,
+ # then create a second candidate using APS that requires a change output.
+ # Ensure that the user-configured change position is kept
+ node = self.nodes[1]
+ res = w1.send({sendTo1: "1.0", sendTo2: "1.0", sendTo3: "0.9999"}, options={"change_position": 0})
+ tx = node.getrawtransaction(res["txid"], True)
+ # If the wallet ignores the user's change_position there is still a 25%
+ # that the random change position passes the test
+ self.assert_change_pos(w1, tx, 0)
+
+if __name__ == '__main__':
+ WalletChangeAddressTest().main()
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index 885c52cf2e..88b9ebbddd 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -90,6 +90,17 @@ class WalletEncryptionTest(BitcoinTestFramework):
self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE + 1000)
actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
assert_equal(actual_time, expected_time)
+ self.nodes[0].walletlock()
+
+ # Test passphrase with null characters
+ passphrase_with_nulls = "Phrase\0With\0Nulls"
+ self.nodes[0].walletpassphrasechange(passphrase2, passphrase_with_nulls)
+ # walletpassphrasechange should not stop at null characters
+ assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase_with_nulls.partition("\0")[0], 10)
+ self.nodes[0].walletpassphrase(passphrase_with_nulls, 10)
+ sig = self.nodes[0].signmessage(address, msg)
+ assert self.nodes[0].verifymessage(address, sig, msg)
+ self.nodes[0].walletlock()
if __name__ == '__main__':
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index ca0209b61d..e66eb2c289 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -448,14 +448,14 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wallet=wmulti_priv)
assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1001) # Range end (1000) is inclusive, so 1001 addresses generated
- addr = wmulti_priv.getnewaddress('', 'bech32')
+ addr = wmulti_priv.getnewaddress('', 'bech32') # uses receive 0
assert_equal(addr, 'bcrt1qdt0qy5p7dzhxzmegnn4ulzhard33s2809arjqgjndx87rv5vd0fq2czhy8') # Derived at m/84'/0'/0'/0
- change_addr = wmulti_priv.getrawchangeaddress('bech32')
- assert_equal(change_addr, 'bcrt1qt9uhe3a9hnq7vajl7a094z4s3crm9ttf8zw3f5v9gr2nyd7e3lnsy44n8e')
+ change_addr = wmulti_priv.getrawchangeaddress('bech32') # uses change 0
+ assert_equal(change_addr, 'bcrt1qt9uhe3a9hnq7vajl7a094z4s3crm9ttf8zw3f5v9gr2nyd7e3lnsy44n8e') # Derived at m/84'/1'/0'/0
assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1000)
txid = w0.sendtoaddress(addr, 10)
self.generate(self.nodes[0], 6)
- send_txid = wmulti_priv.sendtoaddress(w0.getnewaddress(), 8)
+ send_txid = wmulti_priv.sendtoaddress(w0.getnewaddress(), 8) # uses change 1
decoded = wmulti_priv.gettransaction(txid=send_txid, verbose=True)['decoded']
assert_equal(len(decoded['vin'][0]['txinwitness']), 4)
self.sync_all()
@@ -481,10 +481,10 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wallet=wmulti_pub)
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 1000) # The first one was already consumed by previous import and is detected as used
- addr = wmulti_pub.getnewaddress('', 'bech32')
+ addr = wmulti_pub.getnewaddress('', 'bech32') # uses receive 1
assert_equal(addr, 'bcrt1qp8s25ckjl7gr6x2q3dx3tn2pytwp05upkjztk6ey857tt50r5aeqn6mvr9') # Derived at m/84'/0'/0'/1
- change_addr = wmulti_pub.getrawchangeaddress('bech32')
- assert_equal(change_addr, 'bcrt1qzxl0qz2t88kljdnkzg4n4gapr6kte26390gttrg79x66nt4p04fssj53nl')
+ change_addr = wmulti_pub.getrawchangeaddress('bech32') # uses change 2
+ assert_equal(change_addr, 'bcrt1qp6j3jw8yetefte7kw6v5pc89rkgakzy98p6gf7ayslaveaxqyjusnw580c') # Derived at m/84'/1'/0'/2
assert send_txid in self.nodes[0].getrawmempool(True)
assert send_txid in (x['txid'] for x in wmulti_pub.listunspent(0))
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
@@ -667,5 +667,33 @@ class ImportDescriptorsTest(BitcoinTestFramework):
success=True,
warnings=["Unknown output type, cannot set descriptor to active."])
+ self.log.info("Test importing a descriptor to an encrypted wallet")
+
+ descriptor = {"desc": descsum_create("pkh(" + xpriv + "/1h/*h)"),
+ "timestamp": "now",
+ "active": True,
+ "range": [0,4000],
+ "next_index": 4000}
+
+ self.nodes[0].createwallet("temp_wallet", blank=True, descriptors=True)
+ temp_wallet = self.nodes[0].get_wallet_rpc("temp_wallet")
+ temp_wallet.importdescriptors([descriptor])
+ self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
+ self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
+
+ self.nodes[0].createwallet("encrypted_wallet", blank=True, descriptors=True, passphrase="passphrase")
+ encrypted_wallet = self.nodes[0].get_wallet_rpc("encrypted_wallet")
+
+ descriptor["timestamp"] = 0
+ descriptor["next_index"] = 0
+
+ batch = []
+ batch.append(encrypted_wallet.walletpassphrase.get_request("passphrase", 3))
+ batch.append(encrypted_wallet.importdescriptors.get_request([descriptor]))
+
+ encrypted_wallet.batch(batch)
+
+ assert_equal(temp_wallet.getbalance(), encrypted_wallet.getbalance())
+
if __name__ == '__main__':
ImportDescriptorsTest().main()
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index 72c5fe7b84..7c2959bb89 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -400,11 +400,75 @@ class WalletMigrationTest(BitcoinTestFramework):
def test_encrypted(self):
self.log.info("Test migration of an encrypted wallet")
wallet = self.create_legacy_wallet("encrypted")
+ default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
wallet.encryptwallet("pass")
+ addr = wallet.getnewaddress()
+ txid = default.sendtoaddress(addr, 1)
+ self.generate(self.nodes[0], 1)
+ bals = wallet.getbalances()
+
+ assert_raises_rpc_error(-4, "Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect", wallet.migratewallet)
+ assert_raises_rpc_error(-4, "Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect", wallet.migratewallet, None, "badpass")
+ assert_raises_rpc_error(-4, "The passphrase contains a null character", wallet.migratewallet, None, "pass\0with\0null")
+
+ wallet.migratewallet(passphrase="pass")
+
+ info = wallet.getwalletinfo()
+ assert_equal(info["descriptors"], True)
+ assert_equal(info["format"], "sqlite")
+ assert_equal(info["unlocked_until"], 0)
+ wallet.gettransaction(txid)
+
+ assert_equal(bals, wallet.getbalances())
+
+ def test_unloaded(self):
+ self.log.info("Test migration of a wallet that isn't loaded")
+ wallet = self.create_legacy_wallet("notloaded")
+ default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ addr = wallet.getnewaddress()
+ txid = default.sendtoaddress(addr, 1)
+ self.generate(self.nodes[0], 1)
+ bals = wallet.getbalances()
+
+ wallet.unloadwallet()
+
+ assert_raises_rpc_error(-8, "RPC endpoint wallet and wallet_name parameter specify different wallets", wallet.migratewallet, "someotherwallet")
+ assert_raises_rpc_error(-8, "Either RPC endpoint wallet or wallet_name parameter must be provided", self.nodes[0].migratewallet)
+ self.nodes[0].migratewallet("notloaded")
- assert_raises_rpc_error(-15, "Error: migratewallet on encrypted wallets is currently unsupported.", wallet.migratewallet)
- # TODO: Fix migratewallet so that we can actually migrate encrypted wallets
+ info = wallet.getwalletinfo()
+ assert_equal(info["descriptors"], True)
+ assert_equal(info["format"], "sqlite")
+ wallet.gettransaction(txid)
+
+ assert_equal(bals, wallet.getbalances())
+
+ def test_unloaded_by_path(self):
+ self.log.info("Test migration of a wallet that isn't loaded, specified by path")
+ wallet = self.create_legacy_wallet("notloaded2")
+ default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ addr = wallet.getnewaddress()
+ txid = default.sendtoaddress(addr, 1)
+ self.generate(self.nodes[0], 1)
+ bals = wallet.getbalances()
+
+ wallet.unloadwallet()
+
+ wallet_file_path = os.path.join(self.nodes[0].datadir, "regtest", "wallets", "notloaded2")
+ self.nodes[0].migratewallet(wallet_file_path)
+
+ # Because we gave the name by full path, the loaded wallet's name is that path too.
+ wallet = self.nodes[0].get_wallet_rpc(wallet_file_path)
+
+ info = wallet.getwalletinfo()
+ assert_equal(info["descriptors"], True)
+ assert_equal(info["format"], "sqlite")
+ wallet.gettransaction(txid)
+
+ assert_equal(bals, wallet.getbalances())
def run_test(self):
self.generate(self.nodes[0], 101)
@@ -416,6 +480,8 @@ class WalletMigrationTest(BitcoinTestFramework):
self.test_no_privkeys()
self.test_pk_coinbases()
self.test_encrypted()
+ self.test_unloaded()
+ self.test_unloaded_by_path()
if __name__ == '__main__':
WalletMigrationTest().main()
diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py
index de9616b4a1..904013cdef 100755
--- a/test/functional/wallet_transactiontime_rescan.py
+++ b/test/functional/wallet_transactiontime_rescan.py
@@ -14,6 +14,9 @@ from test_framework.util import (
assert_raises_rpc_error,
set_node_times,
)
+from test_framework.wallet_util import (
+ get_generate_key,
+)
class TransactionTimeRescanTest(BitcoinTestFramework):
@@ -23,6 +26,10 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = False
self.num_nodes = 3
+ self.extra_args = [["-keypool=400"],
+ ["-keypool=400"],
+ []
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -167,6 +174,38 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Invalid stop_height", restorewo_wallet.rescanblockchain, 1, -1)
assert_raises_rpc_error(-8, "stop_height must be greater than start_height", restorewo_wallet.rescanblockchain, 20, 10)
+ self.log.info("Test `rescanblockchain` fails when wallet is encrypted and locked")
+ usernode.createwallet(wallet_name="enc_wallet", passphrase="passphrase")
+ enc_wallet = usernode.get_wallet_rpc("enc_wallet")
+ assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", enc_wallet.rescanblockchain)
+
+ if not self.options.descriptors:
+ self.log.info("Test rescanning an encrypted wallet")
+ hd_seed = get_generate_key().privkey
+
+ usernode.createwallet(wallet_name="temp_wallet", blank=True, descriptors=False)
+ temp_wallet = usernode.get_wallet_rpc("temp_wallet")
+ temp_wallet.sethdseed(seed=hd_seed)
+
+ for i in range(399):
+ temp_wallet.getnewaddress()
+
+ self.generatetoaddress(usernode, COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
+ self.generatetoaddress(usernode, COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
+
+ minernode.createwallet("encrypted_wallet", blank=True, passphrase="passphrase", descriptors=False)
+ encrypted_wallet = minernode.get_wallet_rpc("encrypted_wallet")
+
+ encrypted_wallet.walletpassphrase("passphrase", 1)
+ encrypted_wallet.sethdseed(seed=hd_seed)
+
+ batch = []
+ batch.append(encrypted_wallet.walletpassphrase.get_request("passphrase", 3))
+ batch.append(encrypted_wallet.rescanblockchain.get_request())
+
+ encrypted_wallet.batch(batch)
+
+ assert_equal(encrypted_wallet.getbalance(), temp_wallet.getbalance())
if __name__ == '__main__':
TransactionTimeRescanTest().main()
diff --git a/test/lint/lint-locale-dependence.py b/test/lint/lint-locale-dependence.py
index b37ba05a93..faea643882 100755
--- a/test/lint/lint-locale-dependence.py
+++ b/test/lint/lint-locale-dependence.py
@@ -34,8 +34,6 @@
#
# See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings and
# https://stackoverflow.com/a/34878283 for more details.
-#
-# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf.
import re
import sys
diff --git a/test/lint/lint-python.py b/test/lint/lint-python.py
index 4d16facfea..4ec7608708 100755
--- a/test/lint/lint-python.py
+++ b/test/lint/lint-python.py
@@ -47,6 +47,7 @@ ENABLED = (
'E711,' # comparison to None should be 'if cond is None:'
'E714,' # test for object identity should be "is not"
'E721,' # do not compare types, use "isinstance()"
+ 'E722,' # do not use bare 'except'
'E742,' # do not define classes named "l", "O", or "I"
'E743,' # do not define functions named "l", "O", or "I"
'E901,' # SyntaxError: invalid syntax
diff --git a/test/util/test_runner.py b/test/util/test_runner.py
index ea3626fa65..e5cdd0bc3a 100755
--- a/test/util/test_runner.py
+++ b/test/util/test_runner.py
@@ -54,7 +54,7 @@ def bctester(testDir, input_basename, buildenv):
try:
bctest(testDir, testObj, buildenv)
logging.info("PASSED: " + testObj["description"])
- except:
+ except Exception:
logging.info("FAILED: " + testObj["description"])
failed_testcases.append(testObj["description"])
@@ -96,7 +96,7 @@ def bctest(testDir, testObj, buildenv):
try:
with open(os.path.join(testDir, outputFn), encoding="utf8") as f:
outputData = f.read()
- except:
+ except Exception:
logging.error("Output file " + outputFn + " cannot be opened")
raise
if not outputData: