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/README.md4
-rw-r--r--contrib/verify-commits/allow-incorrect-sha512-commits2
-rw-r--r--contrib/verify-commits/allow-revsig-commits820
-rw-r--r--contrib/verify-commits/allow-unclean-merge-commits4
-rwxr-xr-xcontrib/verify-commits/gpg.sh42
-rw-r--r--contrib/verify-commits/trusted-git-root2
-rw-r--r--contrib/verify-commits/trusted-keys1
-rwxr-xr-xcontrib/verify-commits/verify-commits.py49
-rw-r--r--doc/build-freebsd.md35
-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
188 files changed, 1714 insertions, 1963 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/README.md b/contrib/verify-commits/README.md
index b8b15280ba..020890c366 100644
--- a/contrib/verify-commits/README.md
+++ b/contrib/verify-commits/README.md
@@ -27,6 +27,10 @@ Note that the above isn't a good UI/UX yet, and needs significant improvements
to make it more convenient and reduce the chance of errors; pull-reqs
improving this process would be much appreciated.
+Unless `--clean-merge 0` is specified, `verify-commits.py` will attempt to verify that
+each merge commit applies cleanly (with some exceptions). This requires using at least
+git v2.38.0.
+
Configuration files
-------------------
diff --git a/contrib/verify-commits/allow-incorrect-sha512-commits b/contrib/verify-commits/allow-incorrect-sha512-commits
index c572806f26..e69de29bb2 100644
--- a/contrib/verify-commits/allow-incorrect-sha512-commits
+++ b/contrib/verify-commits/allow-incorrect-sha512-commits
@@ -1,2 +0,0 @@
-f8feaa4636260b599294c7285bcf1c8b7737f74e
-8040ae6fc576e9504186f2ae3ff2c8125de1095c
diff --git a/contrib/verify-commits/allow-revsig-commits b/contrib/verify-commits/allow-revsig-commits
index 0c43d9cce5..e69de29bb2 100644
--- a/contrib/verify-commits/allow-revsig-commits
+++ b/contrib/verify-commits/allow-revsig-commits
@@ -1,820 +0,0 @@
-a06ede9a138d0fb86b0de17c42b936d9fe6e2158
-923dc447eaa8e017985b2afbbb12dd1283fbea0e
-71148b8947fe8b4d756822420a7f31c380159425
-6696b4635ceb9b47aaa63244bff9032fa7b08354
-812714fd80e96e28cd288c553c83838cecbfc2d9
-8a445c5651edb9a1f51497055b7ddf4402be9188
-e126d0c12ca66278d9e7b12187c5ff4fc02a7e6c
-3908fc4728059719bed0e1c7b1c8b388c2d4a8da
-8b66bf74e2a349e71eaa183af81fa63eaee76ad2
-05950427d310654774031764a7141a1a4fd9c6e4
-07fd147b9f12e9205afd66a624edce357977d615
-12e31127948fa4bb01c3bddc1b8c85b432f7465b
-8c87f175d335e9d9e93f987d871ae9f05f6a10a7
-46b249e578e8a3dfbe85bc7253a12e82ef4b658b
-a55716abe5662ec74c2f8af93023f1e7cca901fc
-f646275b90b1de93bc62b4c4d045d75ac0b96eee
-c252685aa5867631e9a5ef07ccae7c7c25cae8ff
-a7d55c93385359952d85decd5037843ac70ba3d4
-7dac1e5e9e887f5f6ff146e812a05bd3bf281eae
-2a524b8e8fe69ef487fd8ea1b4f7a03f473ed201
-ce5c1f4acae43477989cdf9a82ed33703919cda2
-2db4cbcc437f51f5dac82cc4de46f383b92e6f11
-7aa700424cbda387536373d8dfec88aee43f950e
-b99a093afed880f23fb279c443cc6ae5e379cc43
-b83264d9c7a8ddb79f64bd9540caddc8632ef31f
-57e337d40e94ba33d8cd265c134d6ef857b32b59
-a1dcf2e1087beaf3981739fd2bb74f35ecad630a
-d38b0d7a6b6056cba26999b702815775e2437d87
-815640ec6af9a38d6a2da4a4400056e2f4105080
-09c4fd157c5b88df2d97fad4826c79b094db90c9
-2efcfa5acfacb958973d9e8125e1d81f102e2dfd
-dc6dee41f7cf2ba93fcd0fea7c157e4b2775d439
-ad826b3df9f763b49f1e3e3d50c4efdd438c7547
-c1a52276848d8caa9a9789dff176408c1aa6b1ed
-3bf06e9bac57b5b5a746677b75e297a7b154bdbd
-72ae6f8cf0224370e8121d6769b21e612ca15d6f
-a143b88dbd4971ecfdd1d39a494489c8f2db0344
-76fec09d878d6dbf214bdb6228d480bd9195db4c
-93566e0c37c5ae104095474fea89f00dcb40f551
-407d9232ef5cb1ebf6cff21f3d13e07ea4158eeb
-9346f8429957e356d21c665bab59fe45bcf1f74e
-6eeac6e30d65f9a972067c1ea8c49978c8e631ac
-dc6b9406bdfab2af8c86cb080cb3e6cf8f2385d8
-9f554e03ebe5701c1b75ff03b3d6152095c0cad3
-05009935f9ac070197113954d680bc2c9150b9b3
-508404de98a8a5435f52916cef8f328e82651961
-ed0cc50afed146c27f6d8129c683c225fb940093
-6429cfa8a70308241c576aeb92ffe3db5203b2ef
-6898213409811b140843c3d89af43328c3b22fad
-5b2ea29cf4fd298346437bb16a54407f8c1f9dca
-e2a1a1ee895149c544d4ae295466611f0cec3094
-e82fb872ff5cc8fd22d43327c1ee3e755f61c562
-19b0f33de0efd9da788e8e4f3fdc2a9e159abdb1
-89de1538ce1f8c00f80e8d11f43e1b77e24d7dea
-de07fdcf77e97b8613091285e4d0a734f5de7492
-01680195f8aa586c55c44767397380def3a23b54
-05e1c85fb687c82ae477c72d4a7e2d6b0c692167
-c072b8fd95cd4fa84f08189a0cd8b173ea2dbb8e
-9a0ed08b40b15ae2b791aa8549b53e69934b4ea7
-53f8f226bd1d627c4a6dec5862a1d4ea5a933e45
-9d0f43b7ca7241d8a018fd35dd3bc01555235ec6
-f12d2b5a8ac397e4bcaefcc19898f8ff5705dea5
-8250de13587ed05ca45df3e12c5dc9bcb1500e2c
-d727f77e390426e9e463336bda08d50c451c7086
-484312bda2d43e3ea60047be076332299463adf8
-c7e05b35ab0a791c7a8e2d863e716fdec6f3f671
-b9c1cd81848da9de1baf9c2f29c19c50e549de13
-8ea7d31e384975019733b5778feabbd9955c79d8
-f798b891bcecea9548eedacae70eeb9906c1ddbf
-ebefe7a00b46579cdd1e033a8c7fd8ce9aa578e4
-ad087638ee4864d6244ec9381ff764bfa6ee5086
-66db2d62d59817320c9182fc18e75a93b76828ea
-7ce9ac5c83b1844a518ef2e12e87aae3cacdfe58
-4286f43025149cf44207c3ad98e4a1f068520ada
-cd0c5135ab2291aaa5410ac919bad3fc87249a4a
-66ed450d771a8fc01c159a8402648ebd1c35eb4c
-a82f03393a32842d49236e8666ee57805ca701f8
-f972b04d63eb8af79ff3cec1dc561ed13dfa6053
-ec45cc5e27668171b55271b0c735194c70e7da41
-715e9fd7454f7a48d7adba7d42f662c20a3e3367
-2e0a99037dcc35bc63ba0d54371bc678af737c8e
-7fa8d758598407f3bf0beb0118dc122ea5340736
-6a22373771edbc3c7513cacb9355f880c73c2cbf
-b89ef131147f71a96152a7b5c4374266cdf539b2
-01d8359983e2f77b5118fede3ffa947072c666c8
-58f0c929a3d70a4bff79cc200f1c186f71ef1675
-950be19727a581970591d8f8138dfe4725750382
-425278d17bd0edf8a3a7cc81e55016f7fd8e7726
-c028c7b7557da2baff7af8840108e8be4db8e0c6
-47a7cfb0aa2498f6801026d258a59f9de48f60b0
-f6b7df3155ddb4cedfbcf5d3eb3383d4614b3a85
-d72098038f3b55a714ed8adb34fab547b15eb0d5
-c49c825bd9f4764536b45df5a684d97173673fc7
-33799afe83eec4200ff140e9bf5eae83701a4d7f
-5c3f8ddcaa1164079105c452429fccf8127b01b6
-1f01443567b03ac75a91c810f1733f5c21b5699d
-b3e42b6d02e8d19658a9135e427ebceab5367779
-69b3a6dd9d9a0adf5506c8b9fde42187356bd4a8
-bafd075c5e6a1088ef0f1aa0b0b224e026a3d3e0
-7daa3adb242d9c8728fdb15c6af6596aaad5502f
-514993554c370f4cf30a109ac28d5d64893dbf0a
-c8d2473e6cb042e7275a10c49d3f6a4a91bf0166
-386f4385ab04b0b2c3d47bddc0dc0f2de7354964
-9f33dba05c01ecc5c56eb1284ab7d64d42f55171
-7466a26cab5d66665991433947964a638f5b957e
-b43aba89e356ff95b706e80d4802f60fc46a569a
-02b7e8319aef2a870264ad4fa2e3bb18664dcc36
-f686002a8eba820a40ac2f34a6e8f57b2b5cc54c
-2b1c50b9352ab1dc40b0f877db23c1fa4048fae3
-2405ce1df043f778b8efb9205009500cbc17313a
-4ad3b3c72c73d61e0a0cab541dca20acf651320d
-4ba3d4f4393d81148422d24d222fe7ed00130194
-8ee5c7b747171e335793c74cd9d2f7491da58164
-872c921c0a208b04bd0713758e52fcab5b7c1684
-00d1680498c5550e7db1f359202d3433a092fafd
-585db41e9ab7a6fb262c8bad7f427cdbdc497188
-18462960c0f13bd07d8f52b61e7d7bc17e991eea
-0630974647dacaf25e7fcb7f9cbb785bb078ede6
-0f58d7f3d62f012f2584f5e781fc73de4763dd9e
-3d16f581538b0974853e820508e8b3093269d2fd
-66e91420ab233cf1dac64504e0dc129019bf8c0d
-d8d9162f5bad39b2720dd2b2da237c6159e4755f
-29fad97c320c892ab6a480c81e2078ec22ab354b
-791c3ea61b4e49fd46a1a71b84ca99ddf69d2ff7
-a312e201ba56742499a5480b5f2115f01505c217
-ce56fdd2e8cdf94fd0ab76d71adbfa755e23ce7d
-480f42630cbd598c04fa59ee0e406f56904ecffb
-6012f1caf744ac9b53383d7d10a8f1b70ca2c0e1
-ded6a2afa549f693dcabb430ce0862f8631360c8
-07090c5339436f856e79a8036d1c85deeb453803
-0e265916d1c6a63e4a3821dab9db597b5ec64b46
-e4ffcacc2187d3419c8ea12b82fb06d82d8751d2
-e117cfe45eee9169409e74a44ef4a866be25bc35
-dcfe218626b05204e9fbc95ba5d95ca0eb72ec9b
-23481fa50301201ef5a60675ef899aa6ce94ca03
-27c59dc502f29cf1d76290556c21e366145e3b2e
-4a62ddd01873d18dbca96c81d756be1020249b45
-a233fb4f1d037e68ff70eef3a9f5b7bf1d631918
-b2089c51cc4af2f7e1c0ec75be9449ee222b1d69
-c997f8808256521397f1c003bb1e9896fee6eaa0
-5dc00f68c49c46a380a98d06233f90528b8e2557
-fe53d5f3636aed064823bc220d828c7ff08d1d52
-935eb8de039dec65669a96a1c3b86f4b03a1b86c
-0277173b1defb63216d40a8d8805ae6d5d563c26
-2a30e67d20f76bbcd9a7d445f616f005316e0a1a
-d32528e733f2711b34dbc41fbb2bb0f153bf7e9a
-4cad91663df381d0dff8526f3b4aa74569dfb626
-1b06ed136f17b526360617a70026aed5ded5746c
-895fbd768f0c89cea3f78acac58b233d4e3a145e
-f0295becbf3ef1fb78095306408789253fe0c114
-8d573198638e52e2dbd9abc609861430f9d2bcc3
-9d9c4185fadaf243bb97c226e2fef16b65299699
-eebe4580bc8d6484d79ecb24dd87412221cf2ea7
-9cf6393a4f82b9c81d3b4b468a17a89db10531a2
-598a9c4e4dcd03c6d80fba005de729a6a3aeba7e
-6970b30c6f1d2be7947295fe18f2390649b17a4b
-f359afcc410432ed5d30001acda0c66741ee8935
-126000ba9e7ff16271be2f4eef3df99ade8d624f
-b5e4b9b5100ec15217d43edb5f4149439f4b20a5
-b987ca4ee495a7fff82f0ac14ef0753bfb7586e2
-b03013396cb2f4bf25746388b3982a2c3616e16b
-9a97f39afaa890caa7987c6bc001b9a66e3e74e8
-cad504bf4c302f7a72e0a0e191f3fdbafda7340f
-45cf8a03cb57b8639a8d47323bde46ba22d9eeaf
-b7450cdbd89a1c862f4d4d8bf093f8a0b5448f9c
-0910cbe4ef31eb95fd76c7c2f820419fe64a3150
-92a810d04b906722c9efe60e3997243c71ff3d4c
-45173fa6fca9537abb0a0554f731d14b9f89c456
-fd4ca17360e6fc0c9bb76bf6b5b07c9102c12728
-ddff3447f29b62d79a33f728791f42fa9436216e
-36a5a4404836da323c755523fbd27563a8e84f94
-c991b304dee368f506cfee27ddaa333f1f82c518
-d38d1a3e75aa97ffa8755ddd431754a6d0942964
-a332a7d5a15214015f9553fdb2bcf80a1a4b8dc0
-604e08c83cf58ca7e7cda2ab284c1ace7bb12977
-18a1bbad98bd4321f15e7921d9aec91661499d90
-8049241e226c16bd07b029c0cb4b62ac40f0c923
-797441ee995aac59f55d59a93ecb55e8ecbe7dbc
-62fdf9b07087b80d2142799bdd2324f61483359d
-f60b4ad57912b78a96af08046a503f7905610a8c
-13e31dd6548d64a5992f439e74bb424bf88aca04
-fbce66a982679b5409a295be5c99a2eef429cabf
-9f2c2dba21855b8cb9b193b1819be73fa4a23a99
-a89221873a3ee2451c73b41bbe2d99d36f439d31
-3d6ad407770e13958e157bf026cae0bfb9254899
-901ba3e3819405306414628306746552b0aa1d28
-7a43fbb959c38e025e558e472ad57de357539894
-0d89fa0877930c6c8a539a656c1009ad8ab6755b
-54aedc013744c86b11157423fa3cffc9a51eef02
-f0c1f8abb0182da557d07372b938f3a0a4bb906f
-4ed818060ecf4a38a02c8cb48f6cbc78d2ee7708
-3bdf242fc68a8d767932c6214455d4d413effbc9
-5e468994fbb349e8eefc996954a31a67a34aaa15
-41aa9c4a801a01eca1fad22a7095372d23dace60
-2adbddb03840ad71e843c6c4a207a13e871cd1d4
-13e352dc53dec0127c5f94a60055d0ca829420dc
-95e14dc81dd30ee0d396ad08dca9a6980d16eee1
-61fb80660f73e5aa5b69302ecc7ac33da206ba5a
-05a761932edd05cf94ffe938908baf058f38632a
-ee92243e66f2df03b3a759a8ffb75dc06f0cea0d
-22cdf93c062eeaa0f8f9d6220f01b67240073dfb
-76b33491596736ca804e3a29bd8398d7a1516ab7
-6e4e98ee8ce2da3cca2e2fd210e9e8dbc9b1c936
-c838283ecdfb9490425bb071b7c22e542de46c7c
-5e3f5e4f25b65b583d3bfefac9e1148035781089
-f7388e93d3dd91a90239aedac4ec58404f103a2e
-0a2f46b0158b6fc7244a585913b0925c0acf707f
-dd561667cb7ccbbfed3134b05a565971ef6f5873
-6f01dcf63873a5e42798635ab4026c9a5f9fa213
-70fec9e36bcd1a3d93df019be084aaf89cecd7d7
-f9b74ef3fc74fd7d2aa94560820341f03cda8e12
-998c3046fab2b52bc9f141cfb588a18c05506a86
-89cc4f905e30b913ca20e4192d538cc5cbe2c38d
-87d90efd69b64f769116956a5db89e536e9e3714
-5aeaa9ccd1568a77e075dbe2bd2435bd60c87c91
-bfb270acfa30713dc8c968bb9ee40cf5a2360359
-1b8c88451b0554502435d3883c528ad0aad1b09b
-57ee73990f1ce29916adfd99f93eae1ccea1a43b
-808c84f89d0edcef9ddaab0b849a382719f6ec9e
-14b860bf64020451ced823b859da8cb912278ab9
-c63364610f4a041df1c1bd81d01b1f6856160749
-92eadc395071876d77f3babddc056b4325bdbabc
-e93fff1463ae906fc986bf98c3b118c82f171546
-9ccafb1d7bdd172a9b963444072a844da379c4f7
-b4a509a3f817121c3df98ddfd96b2769e18a3e5a
-dbc4ae03963014ab4b7957d62ba59dbd8f938c33
-8ddf60db7ad636b6a31b590251c671ded635fa1d
-f199b8a33d9443a258a1f49a1a29674cd9ee9a20
-e542728cde676f218c552d841d0af29b92f9800b
-763231051596b8e3455b839911ad6a3a1f1c3c74
-ff4cd6075b12fb32b9a906deea3ed033e3f9560a
-9c3c9cdae3e20b5bdea91a0631edac5116bbc89f
-93d20a734d2ee873832bed8ca5c05cf8e539c53c
-ef8340d25f7c5dd5682bdecea97ce84cfce1493c
-69c7ecef405d168f658a9cc7996da84c17f61e66
-4ce2f3d0d33346e9f0e96851689ee6550b2a72e3
-44e1fd926cfb0df0fbd8c41de8cd65ed8d5d6e18
-d6d2c8503c4039b682196d83a67dc28359c10c5c
-ae233c4ec3d14a97c6195059f52873cdba2b4755
-0f399a9ff227896265cafab9b2e9fab6cdb9b5b9
-f4ed44ab4a8f9a87ba678d5fd1449fbf636103dc
-7fcd61b2613c211bb042a82a889655178be6a212
-42973f834445d7735738bdba8847812ba3c34d95
-8df48b36ed3201d938b9974ecbee455d7dc2fb84
-96ac26e56627f0c24213fcd3a1cce9fc95f1f661
-cce94c518a46b7b0006f984bbe4d69e8749182d2
-801dd40666d1e6009920ad3ff755c7bb993b2a62
-ce829855cfca103dde55661fa1524e66b139d063
-b148803b181e30213e8a7f3bd89c8239e9dcb866
-c377feaad87f8109f85da6caf62602b30c20effc
-b37cab65c63e051ebc5b491da9bd687581df94df
-16e41844e7d6c5876d2caaeef6010656950c6ec5
-ee50c9e48786dea0d9df2e45805c25565c100fe3
-11dacc6154c42bc6fe3ba94c1823f8a46e4fe81a
-791a0e6ddade27d1b69f4861a6640de60b9553cf
-638e6c59da4fad987c437592174b188510193b2e
-52f8877525d5238f3440e73710507be889d14127
-2a56baf395bf11835d784c4f8634f4525deed6a1
-bc561b4b7d6a3f71649d37d5eb9047c29efa2b13
-31809d6f8514c4a8d5677e947e3f1ebb0db210b9
-a31e9ad4f027955d43c04a05517244647e250161
-777519bd96f68c18150a0f5942f8f97a91937f5e
-4eb1f39d421024d9666cec61deaf96715ffae4c6
-50fae68d416b4b8ec4ca192923dfd5ae9ea42773
-ce665863b137ac4a7470cf006a92aa7694faca71
-81f8c0378b2ab5ea0d7b65635cb529bd3c69127c
-108222b9c323a05cc9339368f10ddd0859f62b43
-28f788e47e58f2b462351d6989348a4e1a241b2b
-d81dccf191a48a6b59c3747d7b4ccbe3535dde40
-a90e6d2bffc422ddcdb771c53aac0bceb970a2c4
-91e49c51f1aecc9e1d75457f4920d52a4b0a133c
-60dd9cc470584960431de425e2a9ffbed0e8034a
-ede386c2193fc31351e193b3a8cf30030d6be62c
-a084767b40c0d3ba8fa8f8d60f1e8d99a9dc3457
-3f726c99f819f97f2ab21b94d34c6b3129cd883a
-77fc469fc78cdd87c29f398d46ac58dbb9ef62c0
-4ae6d0fbef60ccbecf8f23bb482e201b3678f7a3
-8858b6ddd3bce9daa08da6e05de3ca863a399c15
-22e301a3d56dc9e6878380ee92c7d19ca43119d2
-c484ec6c9b85ca4e331e395c564ae232fd0681dd
-a46a671e253528e450bd57645c400bf761da07ab
-655970d9c60ae6850daf452457e14e21047c0e1b
-b6a48914c50631914192aa11b19205436a9c664d
-7db65c363a0cc6ca7cdb04de9a973ab70013baad
-6366941275344dac7e2130b0c972e90117d37ed0
-4fb2586661471a1572c2df2a5a091011d45eb7c4
-d7be7b39fa1021ec4518186afe145ee948e12a94
-85aec87b11ec41295558175c63f1f5a849460fdf
-aeb31756276034dd506fdf97c8aaade0e7e584f5
-ac016e17d20253129a0287cee7e1d06b7ef15966
-bf74d377fb8e20140da6eac1407414928384bcea
-2c811e08db651a4aed6ea0f7c1972d60de6de8ab
-e5d26e47c7a482c072a7fe47bb84c56854734184
-96a63a3e0cefe920819bd42add0041837b1214a1
-e526ca6284b9e13be1b912b80dd73a34e739b539
-ecd21357f16106e541e9c2854ead2a906659b938
-4b5a7ce0c301ad971f383eb60f61bf9b4026efda
-929fd7276c0f0c30b9416f61a6f5f35d763d81e4
-fa8a0639f7b0ce04030b72b4d5be4f0aa36fc5cb
-f1f1605c22a6283bbfd757055fcf2b584a857709
-0c173a15ca1bf20999f74987988985508c9de463
-df0793f324e33066cc746c0cb1d053d35733d626
-2b0179d8a9b75397937126b36114df0dddeab40c
-bf0a08be281dc42241e7f264c2a20515eb4781bb
-3895e25a77363ae8b49358fb793f50fa8b271e2d
-1fc783fc08bc078239537535f174ab8a489772c0
-1d4805ce04645f3203b0cfd3d66ea710e7433eb4
-d3b58704d1d325875fc605580c1c02b825c1bbcc
-ed88e3194c4bc43aeafef929da7b419d03dea1ad
-dd07f47b79628668e29cc0143b21e790100ee445
-65cc7aacfbfc7b747926375280a1d839e88d576b
-080ec5209172ac9605f1434559dbb3c1e012b10a
-416af3edf5b5ab265acf95568f2bc9eabd3d96de
-e0a7801223fd573863939e76cb633f1dcc2d22c4
-4bc853b50fd9127687eb9e4f3b679dd261a4fa96
-c68a9a69278aa194fed96bd9733d32af3690a11e
-c38f540298f0e188df5ed68fd56c623b9ac8331b
-643fa0b22d70e459d7f7ec3d728ae4811dc5158f
-e053e05c130549f43953f1d70e724dc9ce3e1b85
-75e898c094eea533d1dfaf141c6afccc3072c49f
-2805d606bc46bf5589093a1b92d3542c13ce50c2
-32751807c9c06011eb689cba56b401a6302699c0
-30853e16d332816752dafcfca92147c7ffef5b54
-bea5b00cfe95cd37832305c0f93c339a22a7d79d
-c871f323b418fac27bf834843ca26985010df53f
-329fc1dce7a1c372c8b10c2f2f8732b2c60daff0
-1aefc94dd78d6e0c9209cb09fc16f53dedf42108
-8e5725666b519b61fcdc3141da5c6a57c1959909
-a4ca0b042365061020627a8c045cddacea3312ec
-8bd16ee12fc8ef6723e0572c29b979c15b92b4f4
-87abe20fc118721cc5efdbd94a8462468cd1da2b
-4b766fcdd4ca16399075d1e081a321b3b05ce516
-f6241b3e420e19f3f0507cbbc872fe9218916a02
-7ee523604851af62c0a47c07ee023a8710ef32f7
-776ba233e939fe41a74c6b2632b93a0679a32c71
-6a796b2b53fe542e0f340f250f4f20d69efed8d0
-23d78c4dd01bc74ba35db3e3df95280f6f1b2e22
-f4b15e2de97c4f8cdbb40bef4c9d0ab2807974d9
-fff72de5bf8ac7b70208e655f237b80e70e18851
-170bc2c381f86a523de2fc8b71d62ade66303c0d
-314ebdfcb38d4b4c977579f787d5e1a20d068c94
-e9274839bf316b1972d80d28e45759f898edbf86
-75171f099e82e3527d7c3469b15891bd92227ec2
-3c5e6c94caf40395e031fbde44a0cca46fdd76ec
-dc8fc0c73bebbc1c48ac5540026030c9cc00ec23
-492d22f92919d8d9d59568318c26c1e2ac4890cc
-80c3a734298e824f9321c4efdd446086a3baad89
-47535d7c3ec79c5978cdcc03a5351ddbbb22538d
-1b25b6df0f08f7474228c5b6ed13b58682e1e440
-c530c15180631cea95e9c292cf7fabde9dca9db3
-2723bcdce3248417e98e6c43207bef74d34076c1
-ed22eb4a62bd8d5369aaec87d4cbdc03c9f16368
-9111df9673beb6d6616d491a5478f09b5f14d040
-d86bb075bf6d1e78c1e4f3dd38b0ea828ef5ecfe
-50a1cc0f0aef1514b917a5a3f4476967170b429d
-6ce733747e160ca699711f2c47e686284ca9aa07
-b44adf92342ad4f9c343ba29c081a91687932936
-88799ea1b1c08f4bc1a487c9e3c2effd5e1650ae
-080d7c700fc3291560d79fc590e05b8e2bad984f
-12af74b289f8cdc6caf850dc6c802f9936b1e8b3
-8e4f7e72410df3ba430082c7cf385f26fd75b033
-8ac80412867118172dc4172494304e19969e9489
-f2734c2828f69d9cfd535e5eab0592a7674b2b61
-0b9fb682890b8fe10cec54072b809a5efe57d33d
-5b029aaedb5fcf7cadd249607dd28eb3f233ab8c
-79af9fbd8c3c0e54702a9c92b171f134bd4466c8
-c412fd805ddf3282dc2e1f28e30f51ffcb1f1da2
-111849345bb5140f86b48e730ceab4bff45fa2e9
-a0b1e57b20a17177ed5a9a54e4a8aab597a546b4
-ca209230c8e73745cf8cfc79f500c9c46e103306
-a230b0588788dbe1ac84622aea169c577b381241
-dfef6b6af08097f0676a2323085558fbbd3c48c6
-3192e5278abca7c1f3b4a2a7f77a0ce941c73985
-7c7ddd9ead99a8b5033a1a5d4698032c9e2b3a92
-10b930dde8f14e9cb661810e97a33bbf144fc55c
-9225de2cf652fe2bf6e50636824cdb641546f57d
-598ef9c44b3ea2cc142c175f077b493f39f5ba22
-c49355c7170a64bdd7864cc3ba9a64916b67fe7c
-857d1e171e051b254a617f27b39f6a551054cee2
-21833f9456f6ad5bc06321ad6d9590f42ce0195c
-8910b4717e5bb946ee6988f7fe9fd461f53a5935
-5703dff0939f05c7457cebd6fc61d88ab13afe41
-8bfa13b15b84cb372950fb7b25a1080173060b6a
-ac23a7c1f19b3d8c326ffe75c8e13edf285f90fe
-19be26afe3d04783a92d032b55bf3fb1e2ae63cc
-f7ec7cfd38b543ba81ac7bed5b77f9a19739460b
-36afd4db4442c45d4078b1a7ad16a1872b5bee0d
-88c2ae3ed2bb5d367dd408c9255cd8f1e7a36c7d
-a13a417cdcfdfd1f1b3bf997bb6ffe6e69b096b9
-d6064a89ac97dc0d2ce9da3982e1a4e25afaeda8
-7146d96de3e15a80cafbab2af48ff6f65d8e41bb
-5628c70f2a44567695e5331fe2293c5b7f35b629
-7ff4a538a8682cdf02a4bcd6f15499c841001b73
-aa5fa642b0e7ce2ea55e2298886f212f11a8894e
-8efd1c820b9a782d8608d54d924658536178295c
-50a226563cd8d7c0a5e8448e87fede0eb72a8354
-b860915f8b0dae98e57a254d11575ea41f5c5a79
-d304fef3746039183f51b3ac8f4774dcf3a64f59
-53ab12d9318d5d195ccc77028b0e3ae66dc6e1fd
-668de70be039a4f1ffcf20aeae2a22ee71fc55a8
-0fea960ca917b73aff853fe88476174c8a313863
-f89502306dcf6393a2c7b0efbb0fa728fc582137
-ff58b1c3bdff5e5f687f10f9e40ce495ca49674e
-0b96abc35f1a9d46a27eeddd7df418d107c29c57
-b0b57a17306a7e963a4fe463f84e2b150a00a859
-4105cb6fd964ad13099ca83b1fdf3d35f3961f74
-23281a4dc3afc42a001346caec4dbb8193f0bb53
-8daf103fa138f9a184448ebf1c2e03b9dbd96f21
-02e5308c1b9f3771bbe49bc5036215fa2bd66aa9
-a65ced1a66575c652baf5084644b8647f531be8c
-2456a835f0bc7796d9ff71f64837fa6790e2b7cc
-9ec1330b455c1ab2eb6b89f8a2ab885677d4ae8a
-0b738075bd43fbd4410e30a51e0498cbfd2b7513
-98c80e374b84e5a9c2d5c36889a0b1ebed5b814b
-25720fc394e27a951bcad26095fb5a711bfacb8f
-4cfd57d2e38207d78722ce8c9274ba8dd700d1cc
-0fc1c31a878e93d938c67db3f958e82e3c39659f
-df1ab5b4d67b46b5e9e840b1fbe0ff02520831f9
-5bc3b6cede8dabdf3f4f27ddb03723cbb7cde51a
-c2ea1e6561caba3abffce361abc800822b9e0efe
-caa2f106d704ec3ade63498031dd58d34510bc76
-dce853ef76ef90c46d84294225088d595467d08c
-dbc8a8c86ae50059fddb2d6834fa5f0c9bbf9b71
-0f921e6a0492c4e9f037a9ed91f474885032d68c
-041331e1da23e4136fd046ed870cdcc177464176
-e6ba5068f107ac234576e77cedbd748b665369c2
-76fcd9d5034143a5b041766552670d19f926097d
-72bf1b3d0962304850a3ef5fe375db4bff1d0a39
-919db037f1f5cc73cdcaef92dd9cb0e7f5c8dec3
-c36229b0b2e9d4554053f5c9fc451ac29a493b1f
-9e4bb312e6958d2baa309ba670e5eed1523c6f47
-d7ba4a233bd5a6f8fadee681c68a995e23fe36d7
-98514988a3d3e8b7dbf0463884a5c38f5ed5562d
-5412c08c3cf13577566064edd04da021c37b7cbe
-31bcc667863f368157efa1143a78623a5db8f0d1
-7bd1aa566fb4a4fe194f209085649f2c722b0cff
-c4522e71c7e1d8ecfd70112e9375b9d00d6733a8
-e22f409f18881b63a8e747036584a71217f40e6e
-97ec6e5c9098a1240655cfcab05b6cd5eedb6cd1
-bc121b0eb19713ec72002b5be03ba5ac35903a17
-c98f6b3d93a2cc1b49a6db425ea2b661089d0f9e
-0de7fd36de57a68e543b4c1f184fba192c398c73
-e662d281b837c25b2b70525aa8fe8af894339823
-44adf683ad232db8ce0cb89b3e236a1f5944cfb0
-cb2ed300a89ebf9f0654da869ced665ed8b2abe7
-0a6d48d9ed60b0b02177059ab116f8f46d2cbed3
-b42291334651fff46dbfe5947a726f65cb9d7dfe
-e5364991daecb73aca3bb5ac37f2619d7a89211b
-4a2b170c075ce703cbdc82519a48016a9ee3f99c
-924de0bd75a7f75df65d7d15f9d1587a2e794abf
-1253f8692fc3a11be9430685cd405236a68df6c3
-2b799ae9e1e0a540f9a5971ddf27d83254668279
-c9bdf9a75f9fde8cd011e4aa94be4ed4347078a3
-3d69ecb4edeb80003a1a41442e320898a30dbd9c
-f08222e882b18c1f279308636e03beceece2dbf1
-23e03f8d26d7bd03273a5dcbdcfe3905dfb49ffb
-03dd707dc027fbf6f24120213f8eb66571600374
-d0754799698de2c032abcb8198ee5d5401063213
-072116fceb2294b97d1c40f79305f2e3ff71812b
-e66cc1d58e16bf1650dd6479fed64ecaca8c6098
-f137753a2dcd8229f89d1d1ac28039364e5850b4
-61d191fbf953700ba8aeadc9c8cf4c195efbd10c
-76f3c02fb01a6df98fbd8c16ac21d159d4649d37
-6013c73b3312e11b447ed387426749014716f820
-6faffb8a83db3f209a303a4464dbdd597faad5a4
-cc9e8aca5f950c78dcfeff63c441ba993c1fe12f
-8ca69a2a88a77eb06149fa049ab1a7e6de38b321
-2f71490d21796594ca6f55e375558944de9db5a0
-08cc5fd666456cb476467473ed1880c90c92dedb
-e31a43c725ebe641d7c219c3886eee18eebf0bb8
-52b5a8785de760a204b2b0aab19dfaf79c2c3ff0
-483e8e4f4875a1a621ec9e9df2880d3037d95ed7
-1e5799c52535a3fc20e885916f1e7ed33ecc7f46
-a82e5d8220bbc8b5d786bed99b0876f530b9b7cc
-7fe6c5c993706e8395cdaf7977bee793c06f48f3
-2a0836f6d5e7c1d7e97bedb0e0ea33dcaf981f77
-ddc308068d69c6c9aa629ee3c4ce75e1d1cf08b5
-ec139a5621a9c9f03e1988391a3c7c6c5d849776
-c01a6c48b982d625fd9f4f69005878781d3d56fa
-95a983d56dbda457e3bf8766d59bac74c7aa5699
-760741a00833876976389ed7a6b73f36ee5b4c13
-6e5e5abba6f8bbbe61c22795df440dfafcfdc378
-cf2cecb18779ce83de9adebf382dff1c19b12840
-af9b7a9f2f73b1a2f9728106774dd13e8d1cdd8d
-115735d547fdeade822f547eb3e8c8f9961a9b07
-c2c69edf37b5c02aafa01d0407dadbf5ef8751b5
-a072d1a83787e786d074a4b5871b0b961781f7c6
-ed2cd59e258f756b2eaed7909a60956ade6ef7ee
-ae5575ba41c8a782805afb1c08730343cfc22397
-6ff2c8d29f6b5a5c2ce63f0a16f3bb0dbd049451
-a80de15113166354cdf208e3d8b6e25f4511a591
-06bd4f637f15e769f088d9051a5af94bbb0217a3
-6700cc993cc07fb0f5b8b577ff8c4afcf0b18274
-37f9a1f627c0995d89b62923e75cd092600894f9
-8844ef15ded02d5ed86fb95aaf251235fcef2396
-1b87e5b5b184a0a6c683eda23b36393822b57f03
-e2bf830bb6c1bfa038c943dd6f5d92a406bd723f
-423ca302a3ee87000530da3c105f269b8fabece7
-4e14afe42fdd468d5de11df8cc13defdcb8e83f8
-3e90fe6534206412ea22beaa445cf20d28fbe718
-88b77c7da0a672c89e24df37ea6e9085b4e2a05c
-0ad104190465d8d65c2344bbe10dcf3df025d86c
-5c7df7022bcd360e6af00b9458b1a3fd54e1cc9a
-59ad56851a342d2c62f6b38bf15002b23ab439e1
-d8cd7b137fb075616f31d2b43b85fa2e27ea7477
-655937ebcbf681ededf86b1f0f60aac45c73393d
-abdfd2d0e3ebec7dbead89317ee9192189a35809
-e439aeb30c0439001a781c5979aec41e1fc2aa50
-b9b26d9c3615d15669ae0a049c1dede39a9e59a9
-fdf146f3293c487afdc4d6d9f6b64099aa8bd28a
-16e3b175781caacee403a2dc40cd6c70448e12ef
-b30c62d4b954df05bf404cfbeb5b728282201496
-b3f377daaa86cd7755a552fa3adfeb195835f58e
-0a8f519a0626d7cd385114ce610d23215c051b3f
-544f3234384b2f6c290e987ad18576e1b50d7db9
-91482e5bf22d283d32c9f83c8057f10971848107
-e754c6e33194e9ed69ba5350c5139b0423b645fe
-dc1e54206d76e5fa378d28a18ae1fb2bcf714485
-b2863c0685a5c12f829095cbabaf26ccc49e46ec
-b14db5abab405a708f0166293f1ea12222a6bf03
-8010ded6da56842c09b14665343cd189d7e08401
-d387507aeca652a5569825af65243536f2ce26ea
-27bf14f6f3e0fb1f348f13c1b54fc6b67b3bef6e
-f8d470e24606297dab95e30b1d39ff664fbda31d
-b25a4c2284babdf1e8cf0ec3b1402200dd25f33f
-1329ef1f00e4fad83937ddd8721d0292ccfe7808
-9a1ad2c5cbdfa3114d05df57103c34f72e087f26
-1e90862f5d0b5f7dcc18fb018b2bbaa323dcca1d
-ad552a54c56a420be84b47154882c3e4c76f9bdd
-90b1c7e5c50551f39d4983008d1d5ab3b085803e
-d6b2235ca45e072961e25a35e6a159e97c9e556b
-2643fa50869f22672cbc72ac497d9c30234075b8
-01f909828d126d5443bc28758f51781eccdf5848
-f54f3738c8ce839c413d7b6b719be2ff341536ca
-418ae49ee1eac2c9d6cd4ba83c036a41f1afe922
-5a666428b0f11d62af2002bd54a45ff2f79f30cd
-a07e8caa5d5000286604458e6887f57fec7fdcbb
-8b262eb2d80bfa27ae8501078ce47bc1407e9c55
-5df84de583c900e00fef63bedaef32786f205a33
-4ba6da55743a55189164e29e45ac9e73a074d808
-88430cbab4dca36b6a867364cab319cde6a9ebca
-e0f7515f5500968c86e5a9f4912d83d4abc5b2b9
-9b8b1079ddab64ac955766536c38d23dc57bc499
-af20f9b1d485582b8c8aa8294bac4f2c540246d2
-7be9a9a570c1140048f8781ced1111e1d930e517
-2bac3e484114c30548e286972525dd799dbd0a5b
-df529dcc65e8037c5a3a3ad74545be294a770f07
-6acd8700bc0ee1d10207a362c1e07372ba274041
-ffc6e48b2983189dc0ce7de0a038e5329bc07b1b
-252ae7111cbff09a4cbc5caee9e02b6ed3580476
-b3ecb7bab6074377d87c700bf0c5d351e5d3174f
-d9fdac130a5ed1d96fcac6bb87c10bec9d596b17
-a07e8caa5d5000286604458e6887f57fec7fdcbb
-8b262eb2d80bfa27ae8501078ce47bc1407e9c55
-5df84de583c900e00fef63bedaef32786f205a33
-4ba6da55743a55189164e29e45ac9e73a074d808
-5bea05bc1d17aa43cbdf3a3413241f8132790d93
-c17f11f7b43ad3bd9e242c67db1f3679558a0581
-5ea932a51083837cdd27715e10a3a0d5d553af24
-033c78671b91b12d589ebff6c5ede8d94d7500f8
-ef8a634358848847e006c43ce621bc17a612fd1f
-ba216b5fa63e7e6cae847d1e3621f5c54840f898
-26fee4f6bd9aec62c6caa60683ad66574cf16aa6
-6ab0e4cf49549640b903bf5fce0e6035b8116397
-326a5652e0d25fdb60c337ef4f1c98a63e0748f0
-424be03305143cbe5da5d5adb54d73d3dc3747b6
-38c201f47c0bc388a05cdb35d6137150fa90193e
-12ed800ab870e0fc527a84d6e4584b10c8d239f5
-aeed345c9bade5d52a3fbf0a943203f6c82e6344
-c6223b3daab0328ca742b1cc3c15e89e698630bb
-877678710800a4d78afc12519424f232f1a583d3
-6c4fecfaf7beefad0d1c3f8520bf50bb515a0716
-98212745c8acb5cc4e688bbb3979bfd46b25f98a
-b9bceaf1c081a84d9fcc680372614e797b168a9e
-1afc22a7667a7a5c66b4b5d7f50832356dd5ec12
-3255d6347b1f9eccbec3d6d93d4a424087a3b35b
-ec20f01ba0945a3113797ac98a6b3500e24603d4
-75b5643c47c3b382ed97a9f5e2bdc883a0f98709
-fee0d803fb55c8d85b5cd1ff69d799c5ad522e18
-565494619d809655fa94d274bb2202d25553e485
-ad6fce67b9bb6eee864c8431ad3291aebaa2e5d2
-99c7db8731cc77f143b52f544b3fdd93033ed20d
-b4d03be3cac04da8b5d5fa17e29c5220b75d970b
-ef37f2033c4ae104585cd980141262f95d33166e
-5cfdda2503c995cdd563b1a2a29162ac298d173d
-c5904e871479514b2e2e18b4fdbbe468c4e5ec8e
-10b22e3141a603ec891d2cfc7100c29c7409aabe
-afd2fca911c4a5e3a4d1f0993a226d40f250aff4
-505955052e60e0681865f3064e005ca0d3aa90bf
-8fdd23a224ba236874ef662c4ca311b002dbcab3
-1c011ff430106b5f727f2eaa0f7f4883cd2122a3
-ec8a50b8d786a8cd1192e692ab19b46979add582
-f90603ac6d24f5263649675d51233f1fce8b2ecd
-b7d6623c76e1468f2a93db5a3120580e2784d74a
-66270a416edb1610f276124483feceef9cba93ff
-e4fcbf797ed3b472d352ac3794ec82f581209c50
-479afa0f8486146a35f1fb96be1826061ecbcf23
-2a09a3891fde052a585dc019eea9fba26d42445d
-90a002ea647dcea57a2ed4294eab77897168ba1d
-30c21306c17165c3925fea4ac9d1a4763c6d2a99
-b3eb0d6485510f2bdf36d256ab60ce29b8213744
-efbcf2b1d5ff4ee7132eae9c9e203d2b875c545b
-b33ca14f594e2cf2a16ef27778169deb7cc9f4dc
-d636f3943d39ec893dab2d2546f77f3f2607769d
-cafe24f039e117d53288387c2720f44f27deecd0
-de8db47b7ff351f3287c5efb85102ba8836058d6
-d76e84a21416ef77e78138e326d4d249454e79dc
-7a74f88a26cf251ba36b26f604f1ac9940fd9c92
-1ad3d4e1261f4a444d982a1470c257c78233bda3
-8d9f45ea6a5e4220e44d34139438eea75a07530b
-c98ebf1bfb29a8203b5090412afeb333384213cd
-f18bb49547095020a30e81b648075bc7e707515c
-76f268b9bd1b69eb7784c5324abbb67f3e395b97
-e801084decf4542d57cf5ddb95820643766a172a
-be3e042c20e2f3449b7b55d1cab0a80b0c6f00af
-400fdd08cc95f1e85afafd07ddd9c0bed11483ea
-098b01dc58ff555c473ae58c92c34b03a77eda5f
-7cc2c670e3d7cf26454ac8547a94ec2c8ca90b34
-1088b02f0ccd7358d2b7076bb9e122d59d502d02
-f94b7d5bfa911ea7125920589723ee63a3eec9f0
-b4b057a3e0712dd16b50cbcfe7d613e4413ffa1c
-b40ceed98a112f4f0d07351ce07270d9ff2bf796
-4cb8757aae1ae31e5519d81e854f44ed062d9836
-f2f7e97e8cc24cf7a2b7954cb74ecfd0f91a95ad
-ae786098bc58b1ca92f596a698b23aeade9cd2cd
-c33652576ce21694b33a94832378f737dd6959fb
-e317c0d19201ff75fa7afedf93a9d1cd2c560af2
-bee35299716cc72cb7d5bd4daa9fddca05c58378
-318ea50a1c2f612e750a93e36620dd0c4531e9cf
-b6ee855b411ee9bc39f935d0da3298a773a2ed37
-daf3e7def7b9e5db7a32f5a20b5c4e09e3f0dd18
-bc64b5aa0fc543fe8fd3dbaec275f89df44dc409
-3f57c55dba6ef1fda2bdf6fd9abd8ca7eb6828e4
-431a548faaf51c7a5fc89b6e479187a1c0e29805
-e4bbd3d230f22401ba0a0a72c8ed41ee1bd098a0
-c45da32047cac54afc99cf9b8a539389c577ded1
-ab1f1d32469180b3d011e9625d67c86a22b55903
-a550f6e415fd8aec8c45d4704712a408c37ecd18
-c73af5416b66f09cec0eb106f5a10f9bb6ef9cb1
-a077a90da88f12d9f10c8b85840bdb847a98b0a0
-c5e9e428a9198c8c4076f239b5eaa8dc95e7985b
-b7365f0545b1a6862e3277b2b2139ee0d5aee1cf
-4bd0e9b90a39c5c6a016b83882ae44cb4d28f1f8
-7438ceac716fdfe6621728c05e718eaa89dd89aa
-4e3efd47e0d50c6cd1dc81ccc9669a5b2658f495
-5ab6a942764bf6577ae311f2551153dde3d4830c
-b04f42efe31e23e15cc945efe0de906ed2eadb2b
-ceae0eb7e31f9d3495a13a23df7372e5e870b572
-5bf65ec66e5986c9188e3f6234f1c5c0f8dc7f90
-55c9e2d790fa2e137ccd0d91e6cf3e2d0bff4813
-ba29911e21c88f49780c6c87f94ff8ed6e764a9d
-fffff0abb9c71f0af83a7925db3c293b3bb12158
-aaeb315ff0f7956449a92736160795f0140369e3
-0dd34773334c7f4db7b05df30ee61b011795b46d
-2598720d6c1ef15288045599de7e65f8707d09bc
-bc83710fdcc09d8e427e77457df107acc9db1be5
-ddd7a39aa960ee3639ef1e59b2e53852e0862c52
-0808c88d7bd992d5c9ded0009c9563f6177b4035
-a085a554913ae8f4ed83afac830ce6dc39c9cc65
-b1a824dd06aa58618947783edee2dd891b5204dc
-a4e066af8573dcefb11dff120e1c09e8cf7f40c2
-58b9d6cf9e9b801be9c677a3ae121e5d2950ce66
-7377ed778c6d832ecd291e65b2789af7bac2ae2b
-c3a41ad980cc5149de3f9ec8414962c183b1fed9
-5884a47c367f6ff1aff3ae1ef6894881c5a5e0b7
-1d39c9ca0672e7ad4c1f0959f9d58d2fcc7dc46b
-e16f6441044fc2123e0cbdcbd8a5842ec3aae7a0
-6c6cc7989cac79450bf83b932ca82d390a37e17b
-bc28ca3afb7f6656a0bf50038a5e383ee7f9b219
-57a491bee17af88f75c2cea8c109d93b1cdbc9a8
-f8586b25f6a4f1e30a54e58f45dd28aaf580bbc6
-e5df0ba0d97e5f8cfd748f09d6ed77b7bfc45471
-1b0469199bdaedfd452eea718268be7fd50db3c0
-015717e2b873b7a2ce433bd3be2328a782aa5d91
-3b3c66f85959f3393a3a9e87a29004b526f91b93
-874529665c1c326fc86fc0d0d6c3512fab087ab8
-7f2c983e1cfdb58b6f84eabe5ff6a16f143f39aa
-0ea92cad5274f3939f09d6890da31a21b8481282
-489b5876698f9bb2d93b1b1d62d514148b31effd
-faf25b09d9e78f2ff129e25b90f67930d2fc1c4f
-df933596e7e9aa17f7e5cd6e1c850520f5b56f1b
-9e4fbebcc8e497016563e46de4c64fa094edab2d
-1557014378cc5a6234a9244fa60132955206fd27
-c5fbcf5f8d7b36bee54ac80d1027d0dccea2aa75
-cccbc5fe3ea5ae52426203f4485b11071fbe4b6e
-5174a139c92c1238f9700d06e362dc628d81a0a9
-9dae9f5f1e2bf29f58d3f49b0c612063d883b8b3
-e282764e049523439bc8adaadc002a1420122830
-d8ae5044488248d5eb134aa7c0a15c813a2f8715
-06ea2783a2c11e7b171e2809c3211bb3091d894d
-00ce8543f16f4357926eb6dc701ac6229142be80
-1f63b460a8506675ccacb4647941f07d391735e3
-a100c42a136da5ddfd09aa442543ec2190f24faf
-636991d0c0f969968c790d490c82c1d2fa4e8047
-dd52f79a73eca18301db1569d517197160018dbb
-e157b98640c7cfb94cae7e0faca3bcffc2dde990
-ad9e5eaf77bf7e19a926a43407c88386a8a1e58f
-c5e67be03bb06a5d7885c55db1f016fbf2333fe3
-48eec32347494da781f26478fa488b28336afbd2
-c324b07a541a04698954ece94e5879ae7131c1c7
-4901631dac6a883c6ddd0d4e5e3edd08b10d7609
-cacbdbaa95317b45cf2100702bca92410fb43b9a
-b4f686952a60bbadc7ed2250651d0d6af0959f4d
-90e49c1ececd6296c6ec6109cea525a208c0626e
-700808754884919916a5518e7ecfdabadef956d8
-0cd1a2eff9e0020ec1052a931f3863794d1a95d9
-51527ec1ec4264f7e24ef548bb049db07a89fc7f
-ed4eeafbb6e2e73ff9fb9c03bd66bbb049b8aacd
-d4475ea7ae70ad1a1f9374b88c68f01590a88d54
-5e1aacab576b8d8918da129097a9ac0816b6ead2
-fe6a299fc0020cd62156d4b7dd9c8dac358c69c5
-0047d9b89b9fa6be660c363961cf0af72fa62ecf
-037c5e511fe2185d244049cae25a98f99b878787
-8730bd3fc87c8a7d44347267e1ff3c7a8674201b
-47b8256da872722953693c4037d1b9e07caadcb1
-85aea18ae660b5edf7b6c1415f033cfcb15307f9
-132d5f8c2f2397a4600a42203f413dafdb6bcc37
-23ebd7a8027f12e722834d214113892fe8561fe1
-a19f641a80cb2841ca9f593e9be589dbc4b4ae7c
-1e7db37e76c510d373c4404eea2b97508b367aca
-16fa967d3cca66eef0f17b41fd8aaee6a1420fbc
-9eedbe98c86ff2a9214c24c37f6524ce67fd129b
-0342ae1d395ca82614f6d3b8fabb6a44403baf2a
-777b89b3008e53374eb13fdee70db315cd61a703
-8b686776ef5cbd6ef9d5281c3136eded25ea35a4
-c90b42bcdb594638c5759ef5ef0773314d0a1379
-7134327be5c1bdcef7919ed735049a6bbfc457ec
-e88a52e9a2fda971d34425bb80e42ad2d6623d68
-173c79626867e9f89d49be7dcbb0c2042c480553
-2513499348fa955d0e4b0970b08ba9e715e6316e
-43bb10661360d9f35d921d493a1f94ac95df00e2
-6f55ab57cbfa414d57a8e9fb9a47f9bcd8c836d7
-6300b9556ec927a61371053fafe1a4045f5afb00
-f8b2e9bcfc76fede05f5e12f7b15f0d9c9d0add5
-b297b945f7610772434817181ad12067b2832565
-57a73d71a36ce212977607d3e94de6ef55521bfc
-5fdf37e14bb3b66264a7e6868250c2084ac39054
-3059d4dd72af73b654077d9f72019c47edd47674
-333a41882c5ccd5f0c7f884f97d25449bdeec07b
-7da4f65a00a8d96da2119de613ed7fbee2a28a0d
-e14f0fa6a346afecbb1d5470aef5226a8cc33e57
-cf0a8b9c4870cc88254a757286140d9632e7b70c
-b69fd5eaa99f84b62a49d7c7f48d8cee1227592a
-1e3ed01faa77215a7c36308237280aaa58895532
-6c9bc14a3f2cfa50144607c820ebab5288f9571c
-8e3c266a4f02093d57d563f32ba73d3ab4b5f208
-decde9bba6f9d3671bdf0af4fe6ff4bf28992d1d
-9b7eb584ade2ce73dbfcda080935172c3857b758
-3bbc46ddafb61f68785c7e581817db952f99d93a
-bbb83f0b2b2671980c06453fd243b1f2801a1cc4
-6c9460edaeb6c89692b71f51be7b7ee386f4f5c4
-b3072799248fae8fc16f910b642edb9c5acf8bac
-696d39410fc3372d120a6e89695c1543ac2fc052
-c5c4fb31828107a5ded88627632e19e05b2c7e83
-9ce1c506a3a5d20b1bf254235bfae48af592d86c
-fe66dad8a779ed928b1c2fc0c3accf594b042877
-f421de5be611f874a027392d5fee7e113dce4f54
-d492dc1cdaabdc52b0766bf4cba4bd73178325d0
-6348bc61b533705a229f2c2ddcff2bdd98849d07
-83b26cb97cb46516aa4fdee3bcbfa751d28c1233
-afac75f140a3e7d89877f03420e1bc64a8d8c6cd
-171f6f2699dc27e77843318be2fefdfcd9e589fb
-50c806f001d66e20f314777b9fa7fefa01dc6893
-bdbabc50ba6c87ded97ea2bbacd3605c59cd12d0
-9e32adbb5c543885b2c01a984bf1e4b80e8cec16
-7c08d81e119570792648fe95bbacddbb1d5f9ae2
-65e9ca22785f4a799cbcff6d95cbe1ce4b4a6bd2
-2948d6dea098bf722828b969838668f833c2cb00
-deb847b75710d600e5b0d3d5c77fa5166d80808a
-05e5af5a6c884d2ade3d7acc766ad5380cb85b64
-cba41db327a241f992f9329b214d9070888255b8
-f6d335e82822ed8f2da56c1bcaddf7f99acd4997
-30308cc380a8176a5ec0e0bd2beed8b9c482ccf7
-8b6cd42c6226dea28c182a48a214d1c091b9b5bb
-267917f5632a99bb51fc3fe516d8308e79d31ed1
-ba11eb354b9f3420ebb8608227062fb639a07496
-848b11615b67a3c49f76ebbcaa241a322d8014d8
-25290071c434638e2719a99784572deef44542ad
-159f89c118645c0f9e23e453f01cede84abac795
-37637bea3a9a48c0d52d68d3f78f154f8249a009
-0a76bf848c72211f986a6cc5b1c13de820b861dd
-358fe779cbb2681666ae5ab23a19662db21a2c46
-c44e734dca64a15fae92255a5d848c04adaad2fa
-8add59d77dd621be57059229f378822e4b707318
-922c49a1389531d9fba30168257c466bd413f625
-df0825046acc7cb496c47666e36af18118beb030
-c23bf06492dddacfc0eece3d4dd12cce81496dd0
-3eec29ed3aa1c8eb293a7a7a6be356fc014f8813
-a7e80449c0811b361cdaea39b6bab78ca5fbf668
-5e8e0b3d7f6055e326bda61e60712b530e8920f0
-a5edd191be93aff8f9c0f60f04e711e2e78ecc77
-515200298b555845696a07ae2bc0a84a5dd02ae4
-e8a3882e20f0ffeeb9b3964c2c09d8aa5eb53fd4
-c545a7aeb1d559377933c7b2e6edc2d4a37b33fb
-df669230cf2001dd869e897bb4f2d9c46f9accd9
-56a0fbf8365343d73cdff2b0a0e16542294d7577
-196b4599201dbce3e0317e9b98753fa6a244b82d
-cf5bb048e80d4cde8828787b266b7f5f2e3b6d7b
-b94d0c7af11bd91dad4f180ce2a2ffa09e4b5668
-792d0d8d512cf8ddca200317b74ce550c1a1a428
-767ee2e3a1082468b4e2248bac3ef8bd54bb2ddb
-31db3dd874dfbba88616c96a5767e2c9861d9a7a
-018fd9620293582f0ce43d344ac3110e19c4dedc
-801aaac2b39564aa14009785146ba26d2506fb53
-121d47afe3e67ff7f94d26e08a39573dccf652aa
-af7fba3af788e91a460582351d40f8f5e2118760
-8f1c28a609b203e0d0a844d9cc5ada9eb9160a5e
-8319c4e906e6df5f2048e7c048942fde285a93a2
-66be456d93a66526322b7f36fd734a8dbd5e5524
-c006ab29ceec9274dc85a0de7f7d0502021a4b87
-1220af5e6d1072ea306f6ecaaa7effe3d386c379
-14ba286556faad794f288ef38493c540382897cb
-784a21d35466736a7a372364498ed94482a12a2a
-4ad59042b359f473d5888ecee0c9288dcf98f1c9
-fee16b15fa3425871670239c25d4e61ae961e0c5
-216f4ca9e7ccb1f0fcb9bab0f9940992a87ae55f
-2d0bdb2089644f5904629413423cdc897911b081
-50c502f54abd9eb15c8ddca013f0fdfae3d132a9
-c840ab0231bc29057172179f005001c9ab299554
-aab5e48d422d396aec09bd6389de68613b19def5
diff --git a/contrib/verify-commits/allow-unclean-merge-commits b/contrib/verify-commits/allow-unclean-merge-commits
index 7aab274b9a..e69de29bb2 100644
--- a/contrib/verify-commits/allow-unclean-merge-commits
+++ b/contrib/verify-commits/allow-unclean-merge-commits
@@ -1,4 +0,0 @@
-6052d509105790a26b3ad5df43dd61e7f1b24a12
-3798e5de334c3deb5f71302b782f6b8fbd5087f1
-326ffed09bfcc209a2efd6a2ebc69edf6bd200b5
-97d83739db0631be5d4ba86af3616014652c00ec
diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh
index db5bfce208..cfd68e45b8 100755
--- a/contrib/verify-commits/gpg.sh
+++ b/contrib/verify-commits/gpg.sh
@@ -5,12 +5,9 @@
export LC_ALL=C
INPUT=$(cat /dev/stdin)
-VALID=false
-REVSIG=false
-IFS='
-'
if [ "$BITCOIN_VERIFY_COMMITS_ALLOW_SHA1" = 1 ]; then
- GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
+ printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
+ exit $?
else
# Note how we've disabled SHA1 with the --weak-digest option, disabling
# signatures - including selfsigs - that use SHA1. While you might think that
@@ -20,12 +17,12 @@ else
# an attacker could construct a pull-req that results in a commit object that
# they've created a collision for. Not the most likely attack, but preventing
# it is pretty easy so we do so as a "belt-and-suspenders" measure.
- GPG_RES=""
for LINE in $(gpg --version); do
case "$LINE" in
"gpg (GnuPG) 1.4.1"*|"gpg (GnuPG) 2.0."*)
echo "Please upgrade to at least gpg 2.1.10 to check for weak signatures" > /dev/stderr
- GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
+ printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
+ exit $?
;;
# We assume if you're running 2.1+, you're probably running 2.1.10+
# gpg will fail otherwise
@@ -33,33 +30,6 @@ else
# gpg will fail otherwise
esac
done
- [ "$GPG_RES" = "" ] && GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
-fi
-for LINE in $GPG_RES; do
- case "$LINE" in
- "[GNUPG:] VALIDSIG "*)
- while read KEY; do
- [ "${LINE#?GNUPG:? VALIDSIG * * * * * * * * * }" = "$KEY" ] && VALID=true
- done < ./contrib/verify-commits/trusted-keys
- ;;
- "[GNUPG:] REVKEYSIG "*)
- [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
- REVSIG=true
- GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
- ;;
- "[GNUPG:] EXPKEYSIG "*)
- [ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
- REVSIG=true
- GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
- ;;
- esac
-done
-if ! $VALID; then
- exit 1
-fi
-if $VALID && $REVSIG; then
- printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "^\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)"
- echo "$GOODREVSIG"
-else
- printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null
+ printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null
+ exit $?
fi
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/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py
index 3825caf5de..f301964280 100755
--- a/contrib/verify-commits/verify-commits.py
+++ b/contrib/verify-commits/verify-commits.py
@@ -92,8 +92,10 @@ def main():
unclean_merge_allowed = f.read().splitlines()
with open(dirname + "/allow-incorrect-sha512-commits", "r", encoding="utf8") as f:
incorrect_sha512_allowed = f.read().splitlines()
+ with open(dirname + "/trusted-keys", "r", encoding="utf8") as f:
+ trusted_keys = f.read().splitlines()
- # Set commit and branch and set variables
+ # Set commit and variables
current_commit = args.commit
if ' ' in current_commit:
print("Commit must not contain spaces", file=sys.stderr)
@@ -102,7 +104,6 @@ def main():
no_sha1 = True
prev_commit = ""
initial_commit = current_commit
- branch = subprocess.check_output([GIT, 'show', '-s', '--format=%H', initial_commit]).decode('utf8').splitlines()[0]
# Iterate through commits
while True:
@@ -113,17 +114,41 @@ def main():
if current_commit == verified_root:
print('There is a valid path from "{}" to {} where all commits are signed!'.format(initial_commit, verified_root))
sys.exit(0)
- if current_commit == verified_sha512_root:
- if verify_tree:
+ else:
+ # Make sure this commit isn't older than trusted roots
+ check_root_older_res = subprocess.run([GIT, "merge-base", "--is-ancestor", verified_root, current_commit])
+ if check_root_older_res.returncode != 0:
+ print(f"\"{current_commit}\" predates the trusted root, stopping!")
+ sys.exit(0)
+
+ if verify_tree:
+ if current_commit == verified_sha512_root:
print("All Tree-SHA512s matched up to {}".format(verified_sha512_root), file=sys.stderr)
- verify_tree = False
- no_sha1 = False
+ verify_tree = False
+ no_sha1 = False
+ else:
+ # Skip the tree check if we are older than the trusted root
+ check_root_older_res = subprocess.run([GIT, "merge-base", "--is-ancestor", verified_sha512_root, current_commit])
+ if check_root_older_res.returncode != 0:
+ print(f"\"{current_commit}\" predates the trusted SHA512 root, disabling tree verification.")
+ verify_tree = False
+ no_sha1 = False
+
os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_SHA1'] = "0" if no_sha1 else "1"
- os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG'] = "1" if current_commit in revsig_allowed else "0"
+ allow_revsig = current_commit in revsig_allowed
# Check that the commit (and parents) was signed with a trusted key
- if subprocess.call([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', current_commit], stdout=subprocess.DEVNULL):
+ valid_sig = False
+ verify_res = subprocess.run([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', "--raw", current_commit], capture_output=True)
+ for line in verify_res.stderr.decode().splitlines():
+ if line.startswith("[GNUPG:] VALIDSIG "):
+ key = line.split(" ")[-1]
+ valid_sig = key in trusted_keys
+ elif (line.startswith("[GNUPG:] REVKEYSIG ") or line.startswith("[GNUPG:] EXPKEYSIG ")) and not allow_revsig:
+ valid_sig = False
+ break
+ if not valid_sig:
if prev_commit != "":
print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr)
print("Parents are:", file=sys.stderr)
@@ -153,15 +178,11 @@ def main():
allow_unclean = current_commit in unclean_merge_allowed
if len(parents) == 2 and check_merge and not allow_unclean:
current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit]).decode('utf8').splitlines()[0]
- subprocess.call([GIT, 'checkout', '--force', '--quiet', parents[0]])
- subprocess.call([GIT, 'merge', '--no-ff', '--quiet', '--no-gpg-sign', parents[1]], stdout=subprocess.DEVNULL)
- recreated_tree = subprocess.check_output([GIT, 'show', '--format=format:%T', 'HEAD']).decode('utf8').splitlines()[0]
+ recreated_tree = subprocess.check_output([GIT, "merge-tree", parents[0], parents[1]]).decode('utf8').splitlines()[0]
if current_tree != recreated_tree:
print("Merge commit {} is not clean".format(current_commit), file=sys.stderr)
- subprocess.call([GIT, 'diff', current_commit])
- subprocess.call([GIT, 'checkout', '--force', '--quiet', branch])
+ subprocess.call([GIT, 'diff', recreated_tree, current_tree])
sys.exit(1)
- subprocess.call([GIT, 'checkout', '--force', '--quiet', branch])
prev_commit = current_commit
current_commit = parents[0]
diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md
index d45e9c4d0d..aa10e4a891 100644
--- a/doc/build-freebsd.md
+++ b/doc/build-freebsd.md
@@ -36,13 +36,30 @@ pkg install sqlite3
```
###### Legacy Wallet Support
-`db5` is only required to support legacy wallets.
-Skip if you don't intend to use legacy wallets.
+BerkeleyDB is only required if legacy wallet support is required.
+
+It is required to use Berkeley DB 4.8. You **cannot** use the BerkeleyDB library
+from ports. However, you can build DB 4.8 yourself [using depends](/depends).
-```bash
-pkg install db5
```
----
+gmake -C depends NO_BOOST=1 NO_LIBEVENT=1 NO_QT=1 NO_SQLITE=1 NO_NATPMP=1 NO_UPNP=1 NO_ZMQ=1 NO_USDT=1
+```
+
+When the build is complete, the Berkeley DB installation location will be displayed:
+
+```
+to: /path/to/bitcoin/depends/x86_64-unknown-freebsd[release-number]
+```
+
+Finally, set `BDB_PREFIX` to this path according to your shell:
+
+```
+csh: setenv BDB_PREFIX [path displayed above]
+```
+
+```
+sh/bash: export BDB_PREFIX=[path displayed above]
+```
#### GUI Dependencies
###### Qt5
@@ -91,12 +108,12 @@ This explicitly enables the GUI and disables legacy wallet support, assuming `sq
##### Descriptor & Legacy Wallet. No GUI:
This enables support for both wallet types and disables the GUI, assuming
-`sqlite3` and `db5` are both installed.
+`sqlite3` and `db4` are both installed.
```bash
./autogen.sh
-./configure --with-gui=no --with-incompatible-bdb \
- BDB_LIBS="-ldb_cxx-5" \
- BDB_CFLAGS="-I/usr/local/include/db5" \
+./configure --with-gui=no \
+ BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" \
+ BDB_CFLAGS="-I${BDB_PREFIX}/include" \
MAKE=gmake
```
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 01e3cc8aed..a13552653e 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: