aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--CONTRIBUTING.md10
-rw-r--r--build_msvc/common.init.vcxproj.in2
-rwxr-xr-xci/lint/06_script.sh2
-rwxr-xr-xci/test/00_setup_env_android.sh6
-rwxr-xr-xci/test/00_setup_env_native_tsan.sh2
-rwxr-xr-xci/test/00_setup_env_win64.sh2
-rwxr-xr-xci/test/05_before_script.sh6
-rwxr-xr-xci/test/06_script_a.sh12
-rwxr-xr-xci/test/06_script_b.sh3
-rw-r--r--configure.ac10
-rw-r--r--contrib/verify-commits/trusted-git-root2
-rw-r--r--contrib/verify-commits/trusted-keys2
-rw-r--r--contrib/windeploy/win-codesign.cert189
-rw-r--r--doc/build-openbsd.md17
-rw-r--r--doc/developer-notes.md33
-rw-r--r--doc/release-notes.md10
-rw-r--r--doc/release-notes/release-notes-24408.md5
-rw-r--r--doc/release-process.md39
-rw-r--r--doc/zmq.md2
-rw-r--r--src/Makefile.am37
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/addrman.cpp26
-rw-r--r--src/banman.cpp47
-rw-r--r--src/banman.h3
-rw-r--r--src/base58.h1
-rw-r--r--src/bench/bench.cpp2
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/mempool_stress.cpp13
-rw-r--r--src/bitcoin-chainstate.cpp24
-rw-r--r--src/bitcoin-cli.cpp8
-rw-r--r--src/bitcoin-util.cpp1
-rw-r--r--src/bitcoind.cpp5
-rw-r--r--src/compat/glibcxx_sanity.cpp62
-rw-r--r--src/compat/sanity.h10
-rw-r--r--src/consensus/params.h6
-rw-r--r--src/core_io.h1
-rw-r--r--src/dbwrapper.cpp6
-rw-r--r--src/hash.h1
-rw-r--r--src/httpserver.cpp20
-rw-r--r--src/i2p.cpp2
-rw-r--r--src/index/coinstatsindex.cpp44
-rw-r--r--src/index/coinstatsindex.h4
-rw-r--r--src/init.cpp45
-rw-r--r--src/init.h7
-rw-r--r--src/init/common.cpp43
-rw-r--r--src/init/common.h7
-rw-r--r--src/interfaces/node.h29
-rw-r--r--src/kernel/chainstatemanager_opts.h23
-rw-r--r--src/kernel/checks.cpp30
-rw-r--r--src/kernel/checks.h27
-rw-r--r--src/kernel/coinstats.cpp (renamed from src/node/coinstats.cpp)70
-rw-r--r--src/kernel/coinstats.h (renamed from src/node/coinstats.h)26
-rw-r--r--src/kernel/context.cpp33
-rw-r--r--src/kernel/context.h31
-rw-r--r--src/logging.cpp112
-rw-r--r--src/logging.h44
-rw-r--r--src/net.cpp54
-rw-r--r--src/net_processing.cpp56
-rw-r--r--src/net_processing.h2
-rw-r--r--src/netaddress.h1
-rw-r--r--src/node/blockstorage.cpp6
-rw-r--r--src/node/blockstorage.h2
-rw-r--r--src/node/chainstate.cpp11
-rw-r--r--src/node/chainstate.h5
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h4
-rw-r--r--src/node/interfaces.cpp60
-rw-r--r--src/node/miner.cpp14
-rw-r--r--src/node/miner.h4
-rw-r--r--src/node/transaction.h1
-rw-r--r--src/outputtype.h1
-rw-r--r--src/policy/feerate.cpp2
-rw-r--r--src/policy/feerate.h3
-rw-r--r--src/policy/fees.cpp18
-rw-r--r--src/policy/fees.h6
-rw-r--r--src/policy/packages.cpp6
-rw-r--r--src/policy/packages.h2
-rw-r--r--src/policy/policy.cpp14
-rw-r--r--src/policy/policy.h9
-rw-r--r--src/policy/rbf.cpp10
-rw-r--r--src/policy/rbf.h9
-rw-r--r--src/policy/settings.cpp1
-rw-r--r--src/policy/settings.h7
-rw-r--r--src/protocol.h8
-rw-r--r--src/psbt.h1
-rw-r--r--src/qt/addressbookpage.cpp18
-rw-r--r--src/qt/android/res/values/libs.xml3
-rw-r--r--src/qt/bitcoin.cpp18
-rw-r--r--src/qt/optionsmodel.cpp470
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/rpcconsole.cpp8
-rw-r--r--src/qt/sendcoinsdialog.cpp4
-rw-r--r--src/qt/test/addressbooktests.cpp54
-rw-r--r--src/qt/test/apptests.cpp2
-rw-r--r--src/qt/test/optiontests.cpp7
-rw-r--r--src/qt/test/optiontests.h2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/transactiondesc.cpp32
-rw-r--r--src/qt/walletmodel.h3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp91
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/fees.cpp2
-rw-r--r--src/rpc/mempool.cpp109
-rw-r--r--src/rpc/mining.cpp22
-rw-r--r--src/rpc/output_script.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/txoutproof.cpp4
-rw-r--r--src/rpc/util.cpp2
-rw-r--r--src/rpc/util.h1
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/script/miniscript.cpp95
-rw-r--r--src/script/miniscript.h415
-rw-r--r--src/script/standard.h1
-rw-r--r--src/test/blockfilter_index_tests.cpp5
-rw-r--r--src/test/coins_tests.cpp1
-rw-r--r--src/test/coinstatsindex_tests.cpp14
-rw-r--r--src/test/denialofservice_tests.cpp17
-rw-r--r--src/test/fuzz/coins_view.cpp15
-rw-r--r--src/test/fuzz/miniscript.cpp167
-rw-r--r--src/test/fuzz/miniscript_decode.cpp72
-rw-r--r--src/test/fuzz/rpc.cpp1
-rw-r--r--src/test/fuzz/tx_out.cpp1
-rw-r--r--src/test/fuzz/tx_pool.cpp2
-rw-r--r--src/test/fuzz/util.h1
-rw-r--r--src/test/logging_tests.cpp120
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/test/miniscript_tests.cpp36
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/settings_tests.cpp8
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/test/util/mining.cpp2
-rw-r--r--src/test/util/setup_common.cpp78
-rw-r--r--src/test/util/setup_common.h19
-rw-r--r--src/test/validation_block_tests.cpp4
-rw-r--r--src/test/validation_chainstate_tests.cpp15
-rw-r--r--src/test/versionbits_tests.cpp3
-rw-r--r--src/timedata.cpp2
-rw-r--r--src/torcontrol.cpp36
-rw-r--r--src/univalue/TODO10
-rw-r--r--src/univalue/include/univalue.h58
-rw-r--r--src/univalue/test/object.cpp23
-rw-r--r--src/util/bip32.h1
-rw-r--r--src/util/designator.h21
-rw-r--r--src/util/moneystr.h1
-rw-r--r--src/util/settings.cpp4
-rw-r--r--src/util/settings.h6
-rw-r--r--src/util/strencodings.h1
-rw-r--r--src/util/system.cpp35
-rw-r--r--src/util/system.h18
-rw-r--r--src/util/time.h4
-rw-r--r--src/validation.cpp33
-rw-r--r--src/validation.h16
-rw-r--r--src/validationinterface.cpp1
-rw-r--r--src/wallet/coinselection.cpp18
-rw-r--r--src/wallet/coinselection.h47
-rw-r--r--src/wallet/rpc/addresses.cpp4
-rw-r--r--src/wallet/rpc/backup.cpp2
-rw-r--r--src/wallet/rpc/coins.cpp31
-rw-r--r--src/wallet/rpc/spend.cpp2
-rw-r--r--src/wallet/spend.cpp27
-rw-r--r--src/wallet/spend.h8
-rw-r--r--src/wallet/test/coinselector_tests.cpp116
-rw-r--r--src/wallet/test/fuzz/coinselection.cpp6
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/wallet.cpp13
-rw-r--r--src/zmq/zmqnotificationinterface.cpp12
-rw-r--r--src/zmq/zmqpublishnotifier.cpp24
-rw-r--r--src/zmq/zmqutil.cpp2
-rw-r--r--test/README.md2
-rwxr-xr-xtest/functional/feature_dbcrash.py55
-rwxr-xr-xtest/functional/feature_index_prune.py6
-rwxr-xr-xtest/functional/feature_init.py46
-rwxr-xr-xtest/functional/feature_minchainwork.py29
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/feature_rbf.py96
-rwxr-xr-xtest/functional/mempool_accept.py4
-rwxr-xr-xtest/functional/mempool_packages.py6
-rwxr-xr-xtest/functional/p2p_block_sync.py37
-rwxr-xr-xtest/functional/p2p_blockfilters.py5
-rwxr-xr-xtest/functional/p2p_getaddr_caching.py64
-rwxr-xr-xtest/functional/rpc_createmultisig.py11
-rwxr-xr-xtest/functional/rpc_getblockfrompeer.py13
-rwxr-xr-xtest/functional/rpc_mempool_entry_fee_fields_deprecation.py67
-rwxr-xr-xtest/functional/rpc_mempool_info.py102
-rwxr-xr-xtest/functional/rpc_rawtransaction.py165
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py51
-rwxr-xr-xtest/functional/test_framework/test_framework.py4
-rwxr-xr-xtest/functional/test_framework/test_node.py7
-rw-r--r--test/functional/test_framework/wallet.py25
-rwxr-xr-xtest/functional/test_runner.py4
-rwxr-xr-xtest/functional/wallet_listreceivedby.py3
-rw-r--r--test/lint/README.md2
-rwxr-xr-xtest/lint/all-lint.py (renamed from test/lint/lint-all.py)11
-rwxr-xr-xtest/lint/lint-circular-dependencies.py3
-rwxr-xr-xtest/lint/lint-files.py77
-rwxr-xr-xtest/lint/lint-format-strings.py1
-rwxr-xr-xtest/lint/lint-logs.py4
-rw-r--r--test/lint/spelling.ignore-words.txt2
200 files changed, 3171 insertions, 1827 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index c4aae3a50a..ccf7077546 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -324,7 +324,7 @@ task:
<< : *BASE_TEMPLATE
android_sdk_cache:
folder: "depends/SDKs/android"
- fingerprint_key: "ANDROID_API_LEVEL=28 ANDROID_BUILD_TOOLS_VERSION=28.0.3 ANDROID_NDK_VERSION=23.1.7779620"
+ fingerprint_key: "ANDROID_API_LEVEL=28 ANDROID_BUILD_TOOLS_VERSION=28.0.3 ANDROID_NDK_VERSION=23.2.8568313"
depends_sources_cache:
folder: "depends/sources"
fingerprint_script: git rev-list -1 HEAD ./depends
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aec6995d3b..0ae4ff1a92 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -216,9 +216,9 @@ Please update the resulting commit message, if needed. It should read as a
coherent message. In most cases, this means not just listing the interim
commits.
-If you have problems with squashing or other git workflows, you can enable
-"Allow edits from maintainers" in the right-hand sidebar of the GitHub web
-interface and ask for help in the pull request.
+If your change contains a merge commit, the above workflow may not work and you
+will need to remove the merge commit first. See the next section for details on
+how to rebase.
Please refrain from creating several pull requests for the same change.
Use the pull request that is already open (or was created earlier) to amend
@@ -231,7 +231,9 @@ pull request to pull request.
### Rebasing Changes
When a pull request conflicts with the target branch, you may be asked to rebase it on top of the current target branch.
-The `git rebase` command will take care of rebuilding your commits on top of the new base.
+
+ git fetch https://github.com/bitcoin/bitcoin # Fetch the latest upstream commit
+ git rebase FETCH_HEAD # Rebuild commits on top of the new base
This project aims to have a clean git history, where code changes are only made in non-merge commits. This simplifies
auditability because merge commits can be assumed to not contain arbitrary code changes. Merge commits should be signed,
diff --git a/build_msvc/common.init.vcxproj.in b/build_msvc/common.init.vcxproj.in
index 182efff233..4c435ea09d 100644
--- a/build_msvc/common.init.vcxproj.in
+++ b/build_msvc/common.init.vcxproj.in
@@ -90,7 +90,7 @@
<AdditionalOptions>/utf-8 /Zc:__cplusplus /std:c++17 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
- <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>DISABLE_DESIGNATED_INITIALIZER_ERRORS;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\src\minisketch\include;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh
index f174b4d074..84b3404e78 100755
--- a/ci/lint/06_script.sh
+++ b/ci/lint/06_script.sh
@@ -22,7 +22,7 @@ test/lint/git-subtree-check.sh src/univalue
test/lint/git-subtree-check.sh src/leveldb
test/lint/git-subtree-check.sh src/crc32c
test/lint/check-doc.py
-test/lint/lint-all.py
+test/lint/all-lint.py
if [ "$CIRRUS_REPO_FULL_NAME" = "bitcoin/bitcoin" ] && [ "$CIRRUS_PR" = "" ] ; then
# Sanity check only the last few commits to get notified of missing sigs,
diff --git a/ci/test/00_setup_env_android.sh b/ci/test/00_setup_env_android.sh
index 522a5497fa..6732db36ad 100755
--- a/ci/test/00_setup_env_android.sh
+++ b/ci/test/00_setup_env_android.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export HOST=aarch64-linux-android
-export PACKAGES="clang llvm unzip openjdk-8-jdk gradle"
+export PACKAGES="unzip openjdk-8-jdk gradle"
export CONTAINER_NAME=ci_android
export DOCKER_NAME_TAG="ubuntu:focal"
@@ -16,8 +16,8 @@ export RUN_FUNCTIONAL_TESTS=false
export ANDROID_API_LEVEL=28
export ANDROID_BUILD_TOOLS_VERSION=28.0.3
-export ANDROID_NDK_VERSION=23.1.7779620
-export ANDROID_TOOLS_URL=https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip
+export ANDROID_NDK_VERSION=23.2.8568313
+export ANDROID_TOOLS_URL=https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip
export ANDROID_HOME="${DEPENDS_DIR}/SDKs/android"
export ANDROID_NDK_HOME="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}"
export DEP_OPTS="ANDROID_SDK=${ANDROID_HOME} ANDROID_NDK=${ANDROID_NDK_HOME} ANDROID_API_LEVEL=${ANDROID_API_LEVEL} ANDROID_TOOLCHAIN_BIN=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/"
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index ae942d892b..6bf8391209 100755
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -11,4 +11,4 @@ export DOCKER_NAME_TAG=ubuntu:22.04
export PACKAGES="clang-13 llvm-13 libc++abi-13-dev libc++-13-dev python3-zmq"
export DEP_OPTS="CC=clang-13 CXX='clang++-13 -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' CXXFLAGS='-g' --with-sanitizers=thread CC=clang-13 CXX='clang++-13 -stdlib=libc++'"
+export BITCOIN_CONFIG="--enable-zmq CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER -DDEBUG_LOCKCONTENTION' CXXFLAGS='-g' --with-sanitizers=thread CC=clang-13 CXX='clang++-13 -stdlib=libc++'"
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
index 3f43bf227b..3600113551 100755
--- a/ci/test/00_setup_env_win64.sh
+++ b/ci/test/00_setup_env_win64.sh
@@ -10,7 +10,7 @@ export CONTAINER_NAME=ci_win64
export DOCKER_NAME_TAG=ubuntu:22.04 # Check that Jammy can cross-compile to win64
export HOST=x86_64-w64-mingw32
export DPKG_ADD_ARCH="i386"
-export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 wine32 file"
+export PACKAGES="python3 nsis g++-mingw-w64-x86-64-posix wine-binfmt wine64 wine32 file"
export RUN_FUNCTIONAL_TESTS=false
export GOAL="deploy"
export BITCOIN_CONFIG="--enable-reduce-exports --disable-external-signer --disable-gui-tests"
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index fc2f76797c..f3da6b4f31 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -31,9 +31,9 @@ if [ -n "$ANDROID_HOME" ] && [ ! -d "$ANDROID_HOME" ]; then
if [ ! -f "$ANDROID_TOOLS_PATH" ]; then
CI_EXEC curl --location --fail "${ANDROID_TOOLS_URL}" -o "$ANDROID_TOOLS_PATH"
fi
- CI_EXEC mkdir -p "${ANDROID_HOME}/cmdline-tools"
- CI_EXEC unzip -o "$ANDROID_TOOLS_PATH" -d "${ANDROID_HOME}/cmdline-tools"
- CI_EXEC "yes | ${ANDROID_HOME}/cmdline-tools/tools/bin/sdkmanager --install \"build-tools;${ANDROID_BUILD_TOOLS_VERSION}\" \"platform-tools\" \"platforms;android-${ANDROID_API_LEVEL}\" \"ndk;${ANDROID_NDK_VERSION}\""
+ CI_EXEC mkdir -p "$ANDROID_HOME"
+ CI_EXEC unzip -o "$ANDROID_TOOLS_PATH" -d "$ANDROID_HOME"
+ CI_EXEC "yes | ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=\"${ANDROID_HOME}\" --install \"build-tools;${ANDROID_BUILD_TOOLS_VERSION}\" \"platform-tools\" \"platforms;android-${ANDROID_API_LEVEL}\" \"ndk;${ANDROID_NDK_VERSION}\""
fi
if [ -z "$NO_DEPENDS" ]; then
diff --git a/ci/test/06_script_a.sh b/ci/test/06_script_a.sh
index 6a6bde05a1..218f5eeb63 100755
--- a/ci/test/06_script_a.sh
+++ b/ci/test/06_script_a.sh
@@ -6,18 +6,20 @@
export LC_ALL=C.UTF-8
+BITCOIN_CONFIG_ALL="--enable-suppress-external-warnings --disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST"
+if [ -z "$NO_WERROR" ]; then
+ BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} --enable-werror"
+fi
+
if [ -n "$ANDROID_TOOLS_URL" ]; then
CI_EXEC make distclean || true
CI_EXEC ./autogen.sh
- CI_EXEC ./configure "$BITCOIN_CONFIG" --prefix="${DEPENDS_DIR}/aarch64-linux-android" || ( (CI_EXEC cat config.log) && false)
+ CI_EXEC ./configure "$BITCOIN_CONFIG_ALL" "$BITCOIN_CONFIG" || ( (CI_EXEC cat config.log) && false)
CI_EXEC "make $MAKEJOBS && cd src/qt && ANDROID_HOME=${ANDROID_HOME} ANDROID_NDK_HOME=${ANDROID_NDK_HOME} make apk"
exit 0
fi
-BITCOIN_CONFIG_ALL="--enable-external-signer --enable-suppress-external-warnings --disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
-if [ -z "$NO_WERROR" ]; then
- BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} --enable-werror"
-fi
+BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} --enable-external-signer --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
CI_EXEC "ccache --zero-stats --max-size=$CCACHE_SIZE"
if [ -n "$CONFIG_SHELL" ]; then
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index e64af2ad5d..bdb68e0f6f 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -41,6 +41,9 @@ if [ "${RUN_TIDY}" = "true" ]; then
CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/iwyu_tool.py"\
" src/compat"\
" src/init"\
+ " src/policy/feerate.cpp"\
+ " src/policy/packages.cpp"\
+ " src/policy/settings.cpp"\
" src/rpc/fees.cpp"\
" src/rpc/signmessage.cpp"\
" -p . ${MAKEJOBS} -- -Xiwyu --cxx17ns -Xiwyu --mapping_file=${BASE_BUILD_DIR}/bitcoin-$HOST/contrib/devtools/iwyu/bitcoin.core.imp"
diff --git a/configure.ac b/configure.ac
index 96fe38fe0d..f6d00d0283 100644
--- a/configure.ac
+++ b/configure.ac
@@ -378,6 +378,7 @@ if test "$enable_debug" = "yes"; then
AX_CHECK_PREPROC_FLAG([-DDEBUG], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG"], [], [$CXXFLAG_WERROR])
AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKORDER], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKORDER"], [], [$CXXFLAG_WERROR])
+ AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKCONTENTION], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKCONTENTION"], [], [$CXXFLAG_WERROR])
AX_CHECK_PREPROC_FLAG([-DRPC_DOC_CHECK], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DRPC_DOC_CHECK"], [], [$CXXFLAG_WERROR])
AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"], [], [$CXXFLAG_WERROR])
AX_CHECK_COMPILE_FLAG([-ftrapv], [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -ftrapv"], [], [$CXXFLAG_WERROR])
@@ -934,10 +935,7 @@ if test "$use_hardening" != "no"; then
AX_CHECK_COMPILE_FLAG([-Wstack-protector], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
AX_CHECK_COMPILE_FLAG([-fstack-protector-all], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"])
- dnl -fcf-protection used with Clang 7 causes ld to emit warnings:
- dnl ld: error: ... <corrupt x86 feature size: 0x8>
- dnl Use CHECK_LINK_FLAG & --fatal-warnings to ensure we won't use the flag in this case.
- AX_CHECK_LINK_FLAG([-fcf-protection=full], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"], [], [$LDFLAG_WERROR])
+ AX_CHECK_COMPILE_FLAG([-fcf-protection=full], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"])
case $host in
*mingw*)
@@ -1456,6 +1454,10 @@ if test "$use_boost" = "yes"; then
dnl we don't use multi_index serialization
BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION"
+ if test "$enable_debug" = "yes" || test "$enable_fuzz" = "yes"; then
+ BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE"
+ fi
+
if test "$suppress_external_warnings" != "no"; then
BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS)
fi
diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root
index c60f8ab695..1c42195961 100644
--- a/contrib/verify-commits/trusted-git-root
+++ b/contrib/verify-commits/trusted-git-root
@@ -1 +1 @@
-82bcf405f6db1d55b684a1f63a4aabad376cdad7
+577bd51a4b8de066466a445192c1c653872657e2
diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys
index e83bfd7345..2f8a21009a 100644
--- a/contrib/verify-commits/trusted-keys
+++ b/contrib/verify-commits/trusted-keys
@@ -1,8 +1,6 @@
71A3B16735405025D447E8F274810B012346C9A6
133EAC179436F14A5CF1B794860FEB804E669320
-32EE5C4C3FA15CCADB46ABE529D4BCB6416F53EC
B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B
-CA03882CB1FC067B5D3ACFE4D300116E1C875A3D
E777299FC265DD04793070EB944D35F9AC3DB76A
D1DBF2C4B96F2DEBF4C16654410108112E7EA81F
152812300785C96444D3334D17565732E08E5E41
diff --git a/contrib/windeploy/win-codesign.cert b/contrib/windeploy/win-codesign.cert
index e763df5847..22f17296b6 100644
--- a/contrib/windeploy/win-codesign.cert
+++ b/contrib/windeploy/win-codesign.cert
@@ -1,89 +1,112 @@
+-----BEGIN CERTIFICATE-----
+MIIHfDCCBWSgAwIBAgIQCmVvdQal72U2QxbUTT3SRTANBgkqhkiG9w0BAQsFADBp
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT
+OERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0
+IDIwMjEgQ0ExMB4XDTIyMDUyNDAwMDAwMFoXDTI0MDUyOTIzNTk1OVowgYAxCzAJ
+BgNVBAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMFTGV3ZXMxJjAk
+BgNVBAoTHUJpdGNvaW4gQ29yZSBDb2RlIFNpZ25pbmcgTExDMSYwJAYDVQQDEx1C
+aXRjb2luIENvcmUgQ29kZSBTaWduaW5nIExMQzCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBALewxfjztuRTDNAGf7zkqqWNEt28CZmVJHoYltVRxtE1BP45
+BfmptH5eM1JC/XosTPytHRFeOkO4YVAtiELxK9S/82OZlKA7Mx7PW6vv1184u8+m
+P3WpTN/KAZTaW9fB0ELTSCuqsvXq2crM2T7NudJnSyWh2VBjLfPPCAcYwzyGKQbl
+jQWjFEJDJWFK83t9mK/v0WQgA3jGJeaz+V6CYXMS7UgpdG8dUhg9o63gYJZAW5pY
+RIsNRcJCM5LHhwEMW5329UsTmYCfP7/53emepbQ0n8ijVZjgaJ+LZ8NspBLSeCiF
+9UPCKX82uWiQAUTbYHCfSi3I0f3wQidXL9ZY+PXmalM7BMuQ+c2xEcl97CnhrDzx
+EBwZvvOC9wGoG+8+epV4TjUZWf+7QN1ZYeg1rai7c7c8u9ILogE8su2xVoz333TH
+CDvScIgnQXmk+cbKMBtg9kM0F+aLWsN2xVf0uAj3U7sdXLrfJeW0DZIktWtTBQzX
+O/OE4Ka+1WFnDg0HJIih0cTjl9YYvfe53L4pCGy+qGt/XGBRqCMfXp3g+H9FGR5r
+pensVVcsrv3GbTfYdlpdmp9OHH5G57GTAZueobCZg7r7RKK0zPU9EiTLJxzyXuai
+v/Ksd8eIhHRjewMaQuAtQM1tO+oKAbLF0v2M7v7/aVT76X32JllYAizm3zjvAgMB
+AAGjggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNV
+HQ4EFgQUvCpU58PIuofv0kHJ3Ty0YDKEy3cwDgYDVR0PAQH/BAQDAgeAMBMGA1Ud
+JQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwz
+LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5
+NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5j
+b20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIx
+Q0ExLmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRw
+Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsG
+AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0
+dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVT
+aWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJ
+KoZIhvcNAQELBQADggIBABhpTZufRws1vrtI0xB1/UWrSEJxdPHivfpXE708dzum
+Jh3TFzpsEUCQX5BJJet1l7x92sKNeAL7votA+8O8YvMD64Kim7VKA2BB8AOHKQbp
+r1c2iZBwwofInviRYvsrvQta6KBy2KOe1L/l0KnpUazL9Tv4VKvuWAw/Qc0/eTQr
+NZRsmADORxnZ1qW+SpF+/WbazIYjod/Oqb1U3on+PzyiGD3SjzNhsdFRptqzrIaY
+UVV+2XHG4fN6A8wkyQL5NIVXGiK7rqS5VrRAv58Lf1ZZTghdAL+5SySE0OsR9t0K
+W73ZB9pxbuZZ6Zfxjotjw+IilCEm3ADbc7Eb2ijI4x8mix0XWMUrhL34s7/jRyDi
+P+30aSgjWp611tp/EYRW5kpIaFR8AesDdM0DSSCCRXOMwQG2Tq2+CnqItB5oLNPp
+2XySwlIWvmjbzsREfIpE3yh3bxmHY+vFIc2R0nNkbWNIT6AGtaEQ7oWkgpK8YMkA
+QCf4EUC4Qa7qHiH6YSmYJhjApBLC7UDwevgwxuDrwimWAj+tDkzdnENMcBp4SAy6
+LwUuDi2IU6HRSXWdh2YEkDbc3FdwknnnEWaB4dlRL85YjHyLXN0KiE7SKTj1LfR4
+dGeDqVUlDj9D5+X4a7F89wLP/um40/52HUQv5t5WcNr/47r9aVkx9DHs1b8oUnLg
+-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIGQzCCBSugAwIBAgIQBSN7Cm16Z0UT9p7lA2jiKDANBgkqhkiG9w0BAQsFADBy
+MIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQg
-SUQgQ29kZSBTaWduaW5nIENBMB4XDTIxMDUyMTAwMDAwMFoXDTIyMDUyNjIzNTk1
-OVowgYAxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMF
-TGV3ZXMxJjAkBgNVBAoTHUJpdGNvaW4gQ29yZSBDb2RlIFNpZ25pbmcgTExDMSYw
-JAYDVQQDEx1CaXRjb2luIENvcmUgQ29kZSBTaWduaW5nIExMQzCCAiIwDQYJKoZI
-hvcNAQEBBQADggIPADCCAgoCggIBAKe6xtFgKAQ68MvxwCjNtpgPobfDQCLKvCAN
-uBKGYuub6ufQB5dhCLN9fjMgfg33AyauvU3PcEUDUWD3/k925bPqgxHC3E7YqoB+
-11b/2Y7a86okqUgcGgvKhaKoHmXxElpM9EjQHjJ0yL4QAR1Lp+9CMMW3wIulBYKt
-wLIArFvbuQhMO/6rxL8frpK049v//WfQzB16GXuFnzN/6fDK7oOt5IrKTg4H6EY2
-fj4+QaUj0lNX7aHnZ6Ki45h2RUPDgN1ipRIuhM67npyZ/tdzPPjI3PUgfXCccN6D
-+qWWnbbbvPuOht4ziPciVnPd57PqJmAOnLI86gisDfd7VKlcpOSEaagdUGvMbU6f
-uAps818GwnJzwCGllxlKASCgXDAckLLvMuit4RfYAhhdhw5R0AsaWK0HW88oHOqi
-U7eWlMCbSGk34x9hBrxYl7tvcNcLPWIPYrrhFWNFpkV8bVVIoV5rUNRgWvBcdOq1
-CCPTfsJp3nEH2WCoBghZquDZLSW12wMw2UsQyEojBeGhrR1inn8uK93wSnVCC8F4
-21yWNRMNe/LQVhmZDgFOen9r/WijBsBdQw1bL8N4zGdYv8+soqkrWzW417FfSx81
-pj4j5FEXYXXV5k/4/eBpIARXVRR8xya0nGkhNJmBk0jjDGD8fPW2gFQbqnUwAQ34
-vOr8NUqHAgMBAAGjggHEMIIBwDAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32
-ZXUOWDAdBgNVHQ4EFgQUVSLtZnifEHvd8z3E7AyLYNuDiaMwDgYDVR0PAQH/BAQD
-AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6
-Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Ax
-hi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNy
-bDBLBgNVHSAERDBCMDYGCWCGSAGG/WwDATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
-d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYw
-JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcw
-AoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3Vy
-ZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
-BQADggEBAOaJneI91NJgqghUxgc0AWQ01SAJTgN4z7xMQ3W0ZAtwGbA0byT7YRlj
-j7h+j+hMX/JYkRJETTh8Nalq2tPWJBiMMEPOGFVttFER1pwouHkK9pSKyp4xRvNU
-L0LPh7fE4EYMJoynys6ZTpMCHLku+X3jFat1+1moh9TJRvK5+ETZYGl0seFNU3mJ
-dZzusObm4scffIGgi40kmmISKd5ZRuooRTu9FFR/3vpfbA+7Vg4RSH3CcQPo9bfk
-+h/qRQhSfQInTBn7obRpIlvEcK782qivqseJGdtnTmcdVRShD5ckTVza1yv25uQz
-l/yTqmG2LXlYjl5iMSdF0C1xYq6IsOA=
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
+RzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRy
+dXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1
+M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZ
+wZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI
+8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGi
+TUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLm
+ysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3S
+vUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tv
+k2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+
+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3s
+MJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FK
+PkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1H
+s/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+HQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LS
+cV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
+BQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
+Z2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
+Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYy
+aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j
+cmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQAD
+ggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L
+/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHV
+UHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rd
+KOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK
+6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43N
+b3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4Z
+XDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvm
+oLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8
+y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMM
+B0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+F
+SCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhO
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBl
+MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
-b3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQG
-EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
-cnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBT
-aWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8O
-Ea9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq
-8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRp
-wsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/p
-fMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3
-jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczye
-n6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV
-HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBr
-MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUH
-MAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
-RFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
-ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6
-Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmww
-TwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
-d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsq
-CqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgP
-MA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHX
-fgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddf
-RHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8Al
-EeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+
-L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8
-B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHv
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
+RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
+Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
+ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
+xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
+ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
+DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
+jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
+CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
+EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
+fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
+uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
+chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
+9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
+ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
+SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
+fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
+sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
+cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
+0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
+4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
+r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
+/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
+gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
-b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
-EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
-cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
-JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
-mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
-wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
-VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
-AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
-AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
-BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
-pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
-dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
-fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
-NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
-H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
-+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
------END CERTIFICATE-----
-
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index 99daf73c86..afbb5c8e75 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -1,6 +1,6 @@
# OpenBSD Build Guide
-**Updated for OpenBSD [7.0](https://www.openbsd.org/70.html)**
+**Updated for OpenBSD [7.1](https://www.openbsd.org/71.html)**
This guide describes how to build bitcoind, command-line utilities, and GUI on OpenBSD.
@@ -34,7 +34,7 @@ It is not necessary to build wallet functionality to run either `bitcoind` or `b
`sqlite3` is required to support [descriptor wallets](descriptors.md).
``` bash
-pkg_add install sqlite3
+pkg_add sqlite3
```
###### Legacy Wallet Support
@@ -78,12 +78,9 @@ export AUTOMAKE_VERSION=1.16
### 1. Configuration
-Note that building with external signer support currently fails on OpenBSD,
-hence you have to explicitly disable it by passing the parameter
-`--disable-external-signer` to the configure script. The feature requires the
-header-only library boost::process, which is available on OpenBSD, but contains
-certain system calls and preprocessor defines like `waitid()` and `WEXITED` that
-are not available.
+Note that external signer support is currently not available on OpenBSD, since
+the used header-only library Boost.Process fails to compile (certain system
+calls and preprocessor defines like `waitid()` and `WEXITED` are missing).
There are many ways to configure Bitcoin Core, here are a few common examples:
@@ -91,14 +88,14 @@ There are many ways to configure Bitcoin Core, here are a few common examples:
This enables the GUI and descriptor wallet support, assuming `sqlite` and `qt5` are installed.
```bash
-./configure --disable-external-signer MAKE=gmake
+./configure MAKE=gmake
```
##### Descriptor & Legacy Wallet. No GUI:
This enables support for both wallet types and disables the GUI:
```bash
-./configure --disable-external-signer --with-gui=no \
+./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/developer-notes.md b/doc/developer-notes.md
index ea08c4f1a8..d1b660efff 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -110,6 +110,28 @@ code.
- `nullptr` is preferred over `NULL` or `(void*)0`.
- `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking.
+For function calls a namespace should be specified explicitly, unless such functions have been declared within it.
+Otherwise, [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl), also known as ADL, could be
+triggered that makes code harder to maintain and reason about:
+```c++
+#include <filesystem>
+
+namespace fs {
+class path : public std::filesystem::path
+{
+};
+// The intention is to disallow this function.
+bool exists(const fs::path& p) = delete;
+} // namespace fs
+
+int main()
+{
+ //fs::path p; // error
+ std::filesystem::path p; // compiled
+ exists(p); // ADL being used for unqualified name lookup
+}
+```
+
Block style example:
```c++
int g_count = 0;
@@ -394,8 +416,10 @@ Defining `DEBUG_LOCKCONTENTION` adds a "lock" logging category to the logging
RPC that, when enabled, logs the location and duration of each lock contention
to the `debug.log` file.
-To enable it, run configure with `-DDEBUG_LOCKCONTENTION` added to your
-CPPFLAGS, e.g. `CPPFLAGS="-DDEBUG_LOCKCONTENTION"`, then build and run bitcoind.
+The `--enable-debug` configure option adds `-DDEBUG_LOCKCONTENTION` to the
+compiler flags. You may also enable it manually for a non-debug build by running
+configure with `-DDEBUG_LOCKCONTENTION` added to your CPPFLAGS,
+i.e. `CPPFLAGS="-DDEBUG_LOCKCONTENTION"`, then build and run bitcoind.
You can then use the `-debug=lock` configuration option at bitcoind startup or
`bitcoin-cli logging '["lock"]'` at runtime to turn on lock contention logging.
@@ -807,11 +831,6 @@ int GetInt(Tabs tab)
Strings and formatting
------------------------
-- Be careful of `LogPrint` versus `LogPrintf`. `LogPrint` takes a `category` argument, `LogPrintf` does not.
-
- - *Rationale*: Confusion of these can result in runtime exceptions due to
- formatting mismatch, and it is easy to get wrong because of subtly similar naming.
-
- Use `std::string`, avoid C string manipulation functions.
- *Rationale*: C++ string handling is marginally safer, less scope for
diff --git a/doc/release-notes.md b/doc/release-notes.md
index ebccd4d6fa..35f0713879 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -62,6 +62,13 @@ Updated RPCs
change that behaviour. Excluding coinbases was previously deprecated in 23.0.
(#25171)
+- The `deprecatedrpc=fees` configuration option has been removed. The top-level
+ fee fields `fee`, `modifiedfee`, `ancestorfees` and `descendantfees` are no
+ longer returned by RPCs `getmempoolentry`, `getrawmempool(verbose=true)`,
+ `getmempoolancestors(verbose=true)` and `getmempooldescendants(verbose=true)`.
+ The same fee fields can be accessed through the `fees` object in the result.
+ The top-level fee fields were previously deprecated in 23.0. (#25204)
+
Changes to wallet related RPCs can be found in the Wallet section below.
New RPCs
@@ -85,6 +92,9 @@ Tools and Utilities
Wallet
------
+- RPC `getreceivedbylabel` now returns an error, "Label not found
+ in wallet" (-4), if the label is not in the address book. (#25122)
+
GUI changes
-----------
diff --git a/doc/release-notes/release-notes-24408.md b/doc/release-notes/release-notes-24408.md
new file mode 100644
index 0000000000..1072ec786a
--- /dev/null
+++ b/doc/release-notes/release-notes-24408.md
@@ -0,0 +1,5 @@
+New RPCs
+--------
+
+- A new `gettxspendingprevout` RPC has been added, which scans the mempool to find
+ transactions spending any of the given outpoints. (#24408) \ No newline at end of file
diff --git a/doc/release-process.md b/doc/release-process.md
index 03cea6f194..17a03f7dcd 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -29,14 +29,20 @@ Release Process
#### Before branch-off
* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example.
-* Update [`src/chainparams.cpp`](/src/chainparams.cpp) m_assumed_blockchain_size and m_assumed_chain_state_size with the current size plus some overhead (see [this](#how-to-calculate-assumed-blockchain-and-chain-state-size) for information on how to calculate them).
-* Update [`src/chainparams.cpp`](/src/chainparams.cpp) chainTxData with statistics about the transaction count and rate. Use the output of the `getchaintxstats` RPC, see
- [this pull request](https://github.com/bitcoin/bitcoin/pull/20263) for an example. Reviewers can verify the results by running `getchaintxstats <window_block_count> <window_final_block_hash>` with the `window_block_count` and `window_final_block_hash` from your output.
-* Update `src/chainparams.cpp` nMinimumChainWork and defaultAssumeValid (and the block height comment) with information from the `getblockheader` (and `getblockhash`) RPCs.
- - The selected value must not be orphaned so it may be useful to set the value two blocks back from the tip.
- - Testnet should be set some tens of thousands back from the tip due to reorgs there.
- - This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect
- that causes rejection of blocks in the past history.
+* Update the following variables in [`src/chainparams.cpp`](/src/chainparams.cpp) for mainnet, testnet, and signet:
+ - `m_assumed_blockchain_size` and `m_assumed_chain_state_size` with the current size plus some overhead (see
+ [this](#how-to-calculate-assumed-blockchain-and-chain-state-size) for information on how to calculate them).
+ - The following updates should be reviewed with `reindex-chainstate` and `assumevalid=0` to catch any defect
+ that causes rejection of blocks in the past history.
+ - `chainTxData` with statistics about the transaction count and rate. Use the output of the `getchaintxstats` RPC with an
+ `nBlocks` of 4096 (28 days) and a `bestblockhash` of RPC `getbestblockhash`; see
+ [this pull request](https://github.com/bitcoin/bitcoin/pull/20263) for an example. Reviewers can verify the results by running
+ `getchaintxstats <window_block_count> <window_final_block_hash>` with the `window_block_count` and `window_final_block_hash` from your output.
+ - `defaultAssumeValid` with the output of RPC `getblockhash` using the `height` of `window_final_block_height` above
+ (and update the block height comment with that height), taking into account the following:
+ - On mainnet, the selected value must not be orphaned, so it may be useful to set the height two blocks back from the tip.
+ - Testnet should be set with a height some tens of thousands back from the tip, due to reorgs there.
+ - `nMinimumChainWork` with the "chainwork" value of RPC `getblockheader` using the same height as that selected for the previous step.
- Clear the release notes and move them to the wiki (see "Write the release notes" below).
- Translations on Transifex:
- Pull translations from Transifex into the master branch.
@@ -303,15 +309,16 @@ cat "$VERSION"/*/all.SHA256SUMS.asc > SHA256SUMS.asc
Both variables are used as a guideline for how much space the user needs on their drive in total, not just strictly for the blockchain.
Note that all values should be taken from a **fully synced** node and have an overhead of 5-10% added on top of its base value.
-To calculate `m_assumed_blockchain_size`:
-- For `mainnet` -> Take the size of the data directory, excluding `/regtest` and `/testnet3` directories.
-- For `testnet` -> Take the size of the `/testnet3` directory.
+To calculate `m_assumed_blockchain_size`, take the size in GiB of these directories:
+- For `mainnet` -> the data directory, excluding the `/testnet3`, `/signet`, and `/regtest` directories and any overly large files, e.g. a huge `debug.log`
+- For `testnet` -> `/testnet3`
+- For `signet` -> `/signet`
-
-To calculate `m_assumed_chain_state_size`:
-- For `mainnet` -> Take the size of the `/chainstate` directory.
-- For `testnet` -> Take the size of the `/testnet3/chainstate` directory.
+To calculate `m_assumed_chain_state_size`, take the size in GiB of these directories:
+- For `mainnet` -> `/chainstate`
+- For `testnet` -> `/testnet3/chainstate`
+- For `signet` -> `/signet/chainstate`
Notes:
- When taking the size for `m_assumed_blockchain_size`, there's no need to exclude the `/chainstate` directory since it's a guideline value and an overhead will be added anyway.
-- The expected overhead for growth may change over time, so it may not be the same value as last release; pay attention to that when changing the variables.
+- The expected overhead for growth may change over time. Consider whether the percentage needs to be changed in response; if so, update it here in this section.
diff --git a/doc/zmq.md b/doc/zmq.md
index b832e71734..4055505d74 100644
--- a/doc/zmq.md
+++ b/doc/zmq.md
@@ -76,7 +76,7 @@ The option to set the PUB socket's outbound message high water mark
-zmqpubhashblockhwm=n
-zmqpubrawblockhwm=n
-zmqpubrawtxhwm=n
- -zmqpubsequencehwm=address
+ -zmqpubsequencehwm=n
The high water mark value must be an integer greater than or equal to 0.
diff --git a/src/Makefile.am b/src/Makefile.am
index 357e562c69..89329f5f69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,7 @@ noinst_PROGRAMS =
TESTS =
BENCHMARKS =
-BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
LIBBITCOIN_NODE=libbitcoin_node.a
LIBBITCOIN_COMMON=libbitcoin_common.a
@@ -139,7 +139,6 @@ BITCOIN_CORE_H = \
compat/byteswap.h \
compat/cpuid.h \
compat/endian.h \
- compat/sanity.h \
compressor.h \
consensus/consensus.h \
consensus/tx_check.h \
@@ -171,6 +170,10 @@ BITCOIN_CORE_H = \
interfaces/ipc.h \
interfaces/node.h \
interfaces/wallet.h \
+ kernel/chainstatemanager_opts.h \
+ kernel/checks.h \
+ kernel/coinstats.h \
+ kernel/context.h \
key.h \
key_io.h \
logging.h \
@@ -190,7 +193,6 @@ BITCOIN_CORE_H = \
node/caches.h \
node/chainstate.h \
node/coin.h \
- node/coinstats.h \
node/context.h \
node/miner.h \
node/minisketchwrapper.h \
@@ -254,6 +256,7 @@ BITCOIN_CORE_H = \
util/bip32.h \
util/bytevectorhash.h \
util/check.h \
+ util/designator.h \
util/epochguard.h \
util/error.h \
util/fastrange.h \
@@ -329,7 +332,6 @@ obj/build.h: FORCE
"$(abs_top_srcdir)"
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
-ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
# server: shared between bitcoind and bitcoin-qt
# Contains code accessing mempool and chain state that is meant to be separated
@@ -356,6 +358,9 @@ libbitcoin_node_a_SOURCES = \
index/coinstatsindex.cpp \
index/txindex.cpp \
init.cpp \
+ kernel/checks.cpp \
+ kernel/coinstats.cpp \
+ kernel/context.cpp \
mapport.cpp \
net.cpp \
netgroup.cpp \
@@ -364,7 +369,6 @@ libbitcoin_node_a_SOURCES = \
node/caches.cpp \
node/chainstate.cpp \
node/coin.cpp \
- node/coinstats.cpp \
node/context.cpp \
node/interfaces.cpp \
node/miner.cpp \
@@ -407,6 +411,7 @@ libbitcoin_node_a_SOURCES = \
if ENABLE_WALLET
libbitcoin_node_a_SOURCES += wallet/init.cpp
+libbitcoin_node_a_CPPFLAGS += $(BDB_CPPFLAGS)
endif
if !ENABLE_WALLET
libbitcoin_node_a_SOURCES += dummywallet.cpp
@@ -426,7 +431,7 @@ endif
# wallet: shared between bitcoind and bitcoin-qt, but only linked
# when wallet enabled
-libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(SQLITE_CFLAGS)
+libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BDB_CPPFLAGS) $(SQLITE_CFLAGS)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
wallet/coincontrol.cpp \
@@ -629,15 +634,12 @@ libbitcoin_common_a_SOURCES = \
$(BITCOIN_CORE_H)
# util: shared between all executables.
-# This library *must* be included to make sure that the glibc
-# sanity checks are linked.
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
- compat/glibcxx_sanity.cpp \
fs.cpp \
interfaces/echo.cpp \
interfaces/handler.cpp \
@@ -852,13 +854,11 @@ endif
libbitcoinkernel_la_SOURCES = \
kernel/bitcoinkernel.cpp \
arith_uint256.cpp \
- blockfilter.cpp \
chain.cpp \
chainparamsbase.cpp \
chainparams.cpp \
clientversion.cpp \
coins.cpp \
- compat/glibcxx_sanity.cpp \
compressor.cpp \
consensus/merkle.cpp \
consensus/tx_check.cpp \
@@ -870,16 +870,13 @@ libbitcoinkernel_la_SOURCES = \
flatfile.cpp \
fs.cpp \
hash.cpp \
- index/base.cpp \
- index/blockfilterindex.cpp \
- index/coinstatsindex.cpp \
- init/common.cpp \
+ kernel/checks.cpp \
+ kernel/coinstats.cpp \
+ kernel/context.cpp \
key.cpp \
logging.cpp \
- netaddress.cpp \
node/blockstorage.cpp \
node/chainstate.cpp \
- node/coinstats.cpp \
node/ui_interface.cpp \
policy/feerate.cpp \
policy/fees.cpp \
@@ -905,11 +902,9 @@ libbitcoinkernel_la_SOURCES = \
support/lockedpool.cpp \
sync.cpp \
threadinterrupt.cpp \
- timedata.cpp \
txdb.cpp \
txmempool.cpp \
uint256.cpp \
- util/asmap.cpp \
util/bytevectorhash.cpp \
util/check.cpp \
util/getuniquepath.cpp \
@@ -1018,6 +1013,10 @@ libbitcoin_ipc_mpgen_input = \
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
%.capnp:
+# Explicitly list dependencies on generated headers as described in
+# https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html#Recording-Dependencies-manually
+ipc/capnp/libbitcoin_ipc_a-protocol.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
+
if BUILD_MULTIPROCESS
LIBBITCOIN_IPC=libbitcoin_ipc.a
libbitcoin_ipc_a_SOURCES = \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 77ff683974..ebd9e860cf 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -201,6 +201,7 @@ test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EV
test_test_bitcoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
+test_test_bitcoin_CPPFLAGS += $(BDB_CPPFLAGS)
endif
test_test_bitcoin_LDADD += $(LIBBITCOIN_NODE) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
@@ -269,7 +270,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/locale.cpp \
test/fuzz/merkleblock.cpp \
test/fuzz/message.cpp \
- test/fuzz/miniscript_decode.cpp \
+ test/fuzz/miniscript.cpp \
test/fuzz/minisketch.cpp \
test/fuzz/muhash.cpp \
test/fuzz/multiplication_overflow.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index f74729d47b..204bb544c5 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -66,14 +66,16 @@ int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) con
bool AddrInfo::IsTerrible(int64_t nNow) const
{
- if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
+ if (nNow - nLastTry <= 60) { // never remove things tried in the last minute
return false;
+ }
if (nTime > nNow + 10 * 60) // came in a flying DeLorean
return true;
- if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) // not seen in recent history
+ if (nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) { // not seen in recent history
return true;
+ }
if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success
return true;
@@ -557,15 +559,17 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_
// periodically update nTime
bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
- if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
+ if (pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty) {
pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
+ }
// add services
pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
// do not update if no new information is present
- if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
+ if (addr.nTime <= pinfo->nTime) {
return false;
+ }
// do not update if the entry was already in the "tried" table
if (pinfo->fInTried)
@@ -866,25 +870,27 @@ void AddrManImpl::ResolveCollisions_()
int id_old = vvTried[tried_bucket][tried_bucket_pos];
AddrInfo& info_old = mapInfo[id_old];
+ const auto current_time{GetAdjustedTime()};
+
// Has successfully connected in last X hours
- if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
+ if (current_time - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
erase_collision = true;
- } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
+ } else if (current_time - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
// Give address at least 60 seconds to successfully connect
- if (GetAdjustedTime() - info_old.nLastTry > 60) {
+ if (current_time - info_old.nLastTry > 60) {
LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
// Replaces an existing address already in the tried table with the new address
- Good_(info_new, false, GetAdjustedTime());
+ Good_(info_new, false, current_time);
erase_collision = true;
}
- } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
+ } else if (current_time - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
// 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());
- Good_(info_new, false, GetAdjustedTime());
+ Good_(info_new, false, current_time);
erase_collision = true;
}
} else { // Collision is not actually a collision anymore
diff --git a/src/banman.cpp b/src/banman.cpp
index b28e3f7f7c..2a6e0e010f 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -16,6 +16,19 @@
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
+ LoadBanlist();
+ DumpBanlist();
+}
+
+BanMan::~BanMan()
+{
+ DumpBanlist();
+}
+
+void BanMan::LoadBanlist()
+{
+ LOCK(m_cs_banned);
+
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
@@ -29,13 +42,6 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
m_banned = {};
m_is_dirty = true;
}
-
- DumpBanlist();
-}
-
-BanMan::~BanMan()
-{
- DumpBanlist();
}
void BanMan::DumpBanlist()
@@ -173,23 +179,24 @@ void BanMan::GetBanned(banmap_t& banmap)
void BanMan::SweepBanned()
{
+ AssertLockHeld(m_cs_banned);
+
int64_t now = GetTime();
bool notify_ui = false;
- {
- LOCK(m_cs_banned);
- banmap_t::iterator it = m_banned.begin();
- while (it != m_banned.end()) {
- CSubNet sub_net = (*it).first;
- CBanEntry ban_entry = (*it).second;
- if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
- m_banned.erase(it++);
- m_is_dirty = true;
- notify_ui = true;
- LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
- } else
- ++it;
+ banmap_t::iterator it = m_banned.begin();
+ while (it != m_banned.end()) {
+ CSubNet sub_net = (*it).first;
+ CBanEntry ban_entry = (*it).second;
+ if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
+ m_banned.erase(it++);
+ m_is_dirty = true;
+ notify_ui = true;
+ LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
+ } else {
+ ++it;
}
}
+
// update UI
if (notify_ui && m_client_interface) {
m_client_interface->BannedListChanged();
diff --git a/src/banman.h b/src/banman.h
index f268fffa5a..77b043f081 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -80,11 +80,12 @@ public:
void DumpBanlist();
private:
+ void LoadBanlist() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_banned);
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty = true);
//!clean unused entries (if bantime has expired)
- void SweepBanned();
+ void SweepBanned() EXCLUSIVE_LOCKS_REQUIRED(m_cs_banned);
RecursiveMutex m_cs_banned;
banmap_t m_banned GUARDED_BY(m_cs_banned);
diff --git a/src/base58.h b/src/base58.h
index 9ba5af73e0..d2a8d5e3bc 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,7 +14,6 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
-#include <attributes.h>
#include <span.h>
#include <string>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index d26b52410c..26975bb59d 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -58,7 +58,7 @@ void benchmark::BenchRunner::RunAll(const Args& args)
std::smatch baseMatch;
if (args.sanity_check) {
- std::cout << "Running with --sanity check option, benchmark results will be useless." << std::endl;
+ std::cout << "Running with --sanity-check option, benchmark results will be useless." << std::endl;
}
std::vector<ankerl::nanobench::Result> benchmarkResults;
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index de8ab9807c..b2958bcc9f 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -58,7 +58,7 @@ static void CoinSelection(benchmark::Bench& bench)
// Create coins
std::vector<COutput> coins;
for (const auto& wtx : wtxs) {
- coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true);
+ coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -88,7 +88,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0);
set.emplace_back();
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
}
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index a58658c4f1..725a6f8f5b 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -88,7 +88,7 @@ static void ComplexMemPool(benchmark::Bench& bench)
}
std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /*min_ancestors=*/1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
- CTxMemPool pool;
+ CTxMemPool& pool = *testing_setup.get()->m_node.mempool;
LOCK2(cs_main, pool.cs);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
for (auto& tx : ordered_coins) {
@@ -102,16 +102,15 @@ static void ComplexMemPool(benchmark::Bench& bench)
static void MempoolCheck(benchmark::Bench& bench)
{
FastRandomContext det_rand{true};
- const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
- const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /*min_ancestors=*/5);
- const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
- CTxMemPool pool;
+ auto testing_setup = MakeNoLogFileContext<TestChain100Setup>(CBaseChainParams::REGTEST, {"-checkmempool=1"});
+ CTxMemPool& pool = *testing_setup.get()->m_node.mempool;
LOCK2(cs_main, pool.cs);
+ testing_setup->PopulateMempool(det_rand, 400, true);
const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
- for (auto& tx : ordered_coins) AddTx(tx, pool);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
- pool.check(coins_tip, /*spendheight=*/2);
+ // Bump up the spendheight so we don't hit premature coinbase spend errors.
+ pool.check(coins_tip, /*spendheight=*/300);
});
}
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index ab805ac1ec..1817aa1a53 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -11,10 +11,12 @@
//
// It is part of the libbitcoinkernel project.
+#include <kernel/checks.h>
+#include <kernel/context.h>
+
#include <chainparams.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <init/common.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <scheduler.h>
@@ -24,6 +26,7 @@
#include <validation.h>
#include <validationinterface.h>
+#include <cassert>
#include <filesystem>
#include <functional>
#include <iosfwd>
@@ -49,7 +52,11 @@ int main(int argc, char* argv[])
SelectParams(CBaseChainParams::MAIN);
const CChainParams& chainparams = Params();
- init::SetGlobals(); // ECC_Start, etc.
+ kernel::Context kernel_context{};
+ // We can't use a goto here, but we can use an assert since none of the
+ // things instantiated so far requires running the epilogue to be torn down
+ // properly
+ assert(!kernel::SanityChecks(kernel_context).has_value());
// Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
// which will try the script cache first and fall back to actually
@@ -70,13 +77,16 @@ int main(int argc, char* argv[])
// SETUP: Chainstate
- ChainstateManager chainman{chainparams};
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ static_cast<int64_t(*)()>(GetTime),
+ };
+ ChainstateManager chainman{chainman_opts};
auto rv = node::LoadChainstate(false,
std::ref(chainman),
nullptr,
false,
- chainparams.GetConsensus(),
false,
2 << 20,
2 << 22,
@@ -91,10 +101,8 @@ int main(int argc, char* argv[])
auto maybe_verify_error = node::VerifyLoadedChainstate(std::ref(chainman),
false,
false,
- chainparams.GetConsensus(),
DEFAULT_CHECKBLOCKS,
- DEFAULT_CHECKLEVEL,
- /*get_unix_time_seconds=*/static_cast<int64_t (*)()>(GetTime));
+ DEFAULT_CHECKLEVEL);
if (maybe_verify_error.has_value()) {
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
goto epilogue;
@@ -253,6 +261,4 @@ epilogue:
}
}
GetMainSignals().UnregisterBackgroundSignalScheduler();
-
- init::UnsetGlobals();
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 88cbab1d11..b9e5a81f8d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -412,8 +412,8 @@ private:
bool is_addr_relay_enabled;
bool is_bip152_hb_from;
bool is_bip152_hb_to;
- bool is_block_relay;
bool is_outbound;
+ bool is_tx_relay;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
};
std::vector<Peer> m_peers;
@@ -477,7 +477,7 @@ public:
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
const bool is_outbound{!peer["inbound"].get_bool()};
- const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
@@ -505,7 +505,7 @@ public:
const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
- m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
@@ -538,7 +538,7 @@ public:
PingTimeToString(peer.ping),
peer.last_send ? ToString(time_now - peer.last_send) : "",
peer.last_recv ? ToString(time_now - peer.last_recv) : "",
- peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "",
+ peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_addr_processed_length, // variable spacing
diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp
index a2f3fca26c..1aeac3cef0 100644
--- a/src/bitcoin-util.cpp
+++ b/src/bitcoin-util.cpp
@@ -15,6 +15,7 @@
#include <streams.h>
#include <util/system.h>
#include <util/translation.h>
+#include <version.h>
#include <atomic>
#include <cstdio>
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index bc063faed1..92e73d7c2a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -188,11 +188,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (!AppInitSanityChecks())
+
+ node.kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*node.kernel))
{
// InitError will have been called with detailed error, which ends up on console
return false;
}
+
if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
#if HAVE_DECL_FORK
tfm::format(std::cout, PACKAGE_NAME " starting\n");
diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp
deleted file mode 100644
index f2ceeeeb9c..0000000000
--- a/src/compat/glibcxx_sanity.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2009-2018 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 <list>
-#include <locale>
-#include <stdexcept>
-#include <string>
-
-namespace
-{
-// trigger: use ctype<char>::widen to trigger ctype<char>::_M_widen_init().
-// test: convert a char from narrow to wide and back. Verify that the result
-// matches the original.
-bool sanity_test_widen(char testchar)
-{
- const std::ctype<char>& test(std::use_facet<std::ctype<char> >(std::locale()));
- return test.narrow(test.widen(testchar), 'b') == testchar;
-}
-
-// trigger: use list::push_back and list::pop_back to trigger _M_hook and
-// _M_unhook.
-// test: Push a sequence of integers into a list. Pop them off and verify that
-// they match the original sequence.
-bool sanity_test_list(unsigned int size)
-{
- std::list<unsigned int> test;
- for (unsigned int i = 0; i != size; ++i)
- test.push_back(i + 1);
-
- if (test.size() != size)
- return false;
-
- while (!test.empty()) {
- if (test.back() != test.size())
- return false;
- test.pop_back();
- }
- return true;
-}
-
-} // namespace
-
-// trigger: string::at(x) on an empty string to trigger __throw_out_of_range_fmt.
-// test: force std::string to throw an out_of_range exception. Verify that
-// it's caught correctly.
-bool sanity_test_range_fmt()
-{
- std::string test;
- try {
- test.at(1);
- } catch (const std::out_of_range&) {
- return true;
- } catch (...) {
- }
- return false;
-}
-
-bool glibcxx_sanity_test()
-{
- return sanity_test_widen('a') && sanity_test_list(100) && sanity_test_range_fmt();
-}
diff --git a/src/compat/sanity.h b/src/compat/sanity.h
deleted file mode 100644
index 8e5811f1fd..0000000000
--- a/src/compat/sanity.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2009-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.
-
-#ifndef BITCOIN_COMPAT_SANITY_H
-#define BITCOIN_COMPAT_SANITY_H
-
-bool glibcxx_sanity_test();
-
-#endif // BITCOIN_COMPAT_SANITY_H
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 0881f2e388..794e0f5383 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -40,11 +40,11 @@ constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BIT
*/
struct BIP9Deployment {
/** Bit position to select the particular bit in nVersion. */
- int bit;
+ int bit{28};
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
- int64_t nStartTime;
+ int64_t nStartTime{NEVER_ACTIVE};
/** Timeout/expiry MedianTime for the deployment attempt. */
- int64_t nTimeout;
+ int64_t nTimeout{NEVER_ACTIVE};
/** If lock in occurs, delay activation until at least this block
* height. Note that activation will only occur on a retarget
* boundary.
diff --git a/src/core_io.h b/src/core_io.h
index aa1381c374..c91c8199d8 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -6,7 +6,6 @@
#define BITCOIN_CORE_IO_H
#include <consensus/amount.h>
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 50a601c684..a2f1f32780 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -19,7 +19,7 @@ public:
// This code is adapted from posix_logger.h, which is why it is using vsprintf.
// Please do not do this in normal code
void Logv(const char * format, va_list ap) override {
- if (!LogAcceptCategory(BCLog::LEVELDB)) {
+ if (!LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug)) {
return;
}
char buffer[500];
@@ -63,7 +63,7 @@ public:
assert(p <= limit);
base[std::min(bufsize - 1, (int)(p - base))] = '\0';
- LogPrintf("leveldb: %s", base); /* Continued */
+ LogPrintLevel(BCLog::LEVELDB, BCLog::Level::Debug, "%s", base); /* Continued */
if (base != buffer) {
delete[] base;
}
@@ -186,7 +186,7 @@ CDBWrapper::~CDBWrapper()
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
{
- const bool log_memory = LogAcceptCategory(BCLog::LEVELDB);
+ const bool log_memory = LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug);
double mem_before = 0;
if (log_memory) {
mem_before = DynamicMemoryUsage() / 1024.0 / 1024;
diff --git a/src/hash.h b/src/hash.h
index 9f582842c1..0ccef2105f 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
-#include <attributes.h>
#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index b9a1fc672a..44937dc523 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -344,10 +344,22 @@ static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
- if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
- LogPrintf("libevent: %s\n", msg);
- else
- LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg);
+ BCLog::Level level;
+ switch (severity) {
+ case EVENT_LOG_DEBUG:
+ level = BCLog::Level::Debug;
+ break;
+ case EVENT_LOG_MSG:
+ level = BCLog::Level::Info;
+ break;
+ case EVENT_LOG_WARN:
+ level = BCLog::Level::Warning;
+ break;
+ default: // EVENT_LOG_ERR and others are mapped to error
+ level = BCLog::Level::Error;
+ break;
+ }
+ LogPrintLevel(BCLog::LIBEVENT, level, "%s\n", msg);
}
bool InitHTTPServer()
diff --git a/src/i2p.cpp b/src/i2p.cpp
index e08b5461fe..9b8f83caef 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -242,7 +242,7 @@ std::string Session::Reply::Get(const std::string& key) const
template <typename... Args>
void Session::Log(const std::string& fmt, const Args&... args) const
{
- LogPrint(BCLog::I2P, "I2P: %s\n", tfm::format(fmt, args...));
+ LogPrint(BCLog::I2P, "%s\n", tfm::format(fmt, args...));
}
Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 69078708f9..687e330fe0 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -12,10 +12,11 @@
#include <undo.h>
#include <validation.h>
-using node::CCoinsStats;
-using node::GetBogoSize;
+using kernel::CCoinsStats;
+using kernel::GetBogoSize;
+using kernel::TxOutSer;
+
using node::ReadBlockFromDisk;
-using node::TxOutSer;
using node::UndoReadFromDisk;
static constexpr uint8_t DB_BLOCK_HASH{'s'};
@@ -316,28 +317,31 @@ static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVa
return db.Read(DBHashKey(block_index->GetBlockHash()), result);
}
-bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const
+std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const
{
+ CCoinsStats stats{Assert(block_index)->nHeight, block_index->GetBlockHash()};
+ stats.index_used = true;
+
DBVal entry;
if (!LookUpOne(*m_db, block_index, entry)) {
- return false;
+ return std::nullopt;
}
- coins_stats.hashSerialized = entry.muhash;
- coins_stats.nTransactionOutputs = entry.transaction_output_count;
- coins_stats.nBogoSize = entry.bogo_size;
- coins_stats.total_amount = entry.total_amount;
- coins_stats.total_subsidy = entry.total_subsidy;
- coins_stats.total_unspendable_amount = entry.total_unspendable_amount;
- coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
- coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
- coins_stats.total_coinbase_amount = entry.total_coinbase_amount;
- coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
- coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
- coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts;
- coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
-
- return true;
+ stats.hashSerialized = entry.muhash;
+ stats.nTransactionOutputs = entry.transaction_output_count;
+ stats.nBogoSize = entry.bogo_size;
+ stats.total_amount = entry.total_amount;
+ stats.total_subsidy = entry.total_subsidy;
+ stats.total_unspendable_amount = entry.total_unspendable_amount;
+ stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
+ stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
+ stats.total_coinbase_amount = entry.total_coinbase_amount;
+ stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
+ stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
+ stats.total_unspendables_scripts = entry.total_unspendables_scripts;
+ stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
+
+ return stats;
}
bool CoinStatsIndex::Init()
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index 6f53bb74fb..cae052d913 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -9,7 +9,7 @@
#include <crypto/muhash.h>
#include <flatfile.h>
#include <index/base.h>
-#include <node/coinstats.h>
+#include <kernel/coinstats.h>
/**
* CoinStatsIndex maintains statistics on the UTXO set.
@@ -56,7 +56,7 @@ public:
explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
// Look up stats for a specific block using CBlockIndex
- bool LookUpStats(const CBlockIndex* block_index, node::CCoinsStats& coins_stats) const;
+ std::optional<kernel::CCoinsStats> LookUpStats(const CBlockIndex* block_index) const;
};
/// The global UTXO set hash object.
diff --git a/src/init.cpp b/src/init.cpp
index e4b13cdf5b..f6e6f7cf43 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -9,12 +9,13 @@
#include <init.h>
+#include <kernel/checks.h>
+
#include <addrman.h>
#include <banman.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <compat/sanity.h>
#include <consensus/amount.h>
#include <deploymentstatus.h>
#include <fs.h>
@@ -86,7 +87,6 @@
#include <vector>
#ifndef WIN32
-#include <attributes.h>
#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
@@ -306,7 +306,7 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- init::UnsetGlobals();
+ node.kernel.reset();
node.mempool.reset();
node.fee_estimator.reset();
node.chainman.reset();
@@ -320,7 +320,6 @@ void Shutdown(NodeContext& node)
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
- node.args = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -1092,13 +1091,24 @@ static bool LockDataDirectory(bool probeOnly)
return true;
}
-bool AppInitSanityChecks()
+bool AppInitSanityChecks(const kernel::Context& kernel)
{
// ********************************************************* Step 4: sanity checks
+ auto maybe_error = kernel::SanityChecks(kernel);
+
+ if (maybe_error.has_value()) {
+ switch (maybe_error.value()) {
+ case kernel::SanityCheckError::ERROR_ECC:
+ InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_RANDOM:
+ InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_CHRONO:
+ InitError(Untranslated("Clock epoch mismatch. Aborting."));
+ break;
+ } // no default case, so the compiler can warn about missing cases
- init::SetGlobals();
-
- if (!init::SanityChecks()) {
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
}
@@ -1421,7 +1431,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), mempool_check_ratio);
- node.chainman = std::make_unique<ChainstateManager>(chainparams);
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ GetAdjustedTime,
+ };
+ node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
ChainstateManager& chainman = *node.chainman;
const bool fReset = fReindex;
@@ -1435,7 +1449,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
chainman,
Assert(node.mempool.get()),
fPruneMode,
- chainparams.GetConsensus(),
fReindexChainState,
cache_sizes.block_tree_db,
cache_sizes.coins_db,
@@ -1482,7 +1495,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
break;
case ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED:
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
- chainparams.GetConsensus().SegwitHeight);
+ chainman.GetConsensus().SegwitHeight);
break;
case ChainstateLoadingError::SHUTDOWN_PROBED:
break;
@@ -1499,10 +1512,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
maybe_verify_error = VerifyLoadedChainstate(chainman,
fReset,
fReindexChainState,
- chainparams.GetConsensus(),
check_blocks,
- args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL));
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
maybe_verify_error = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
@@ -1558,7 +1569,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ChainstateManager& chainman = *Assert(node.chainman);
assert(!node.peerman);
- node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
+ node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(),
chainman, *node.mempool, ignores_incoming_txs);
RegisterValidationInterface(node.peerman.get());
@@ -1680,8 +1691,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
chain_active_height = chainman.ActiveChain().Height();
if (tip_info) {
tip_info->block_height = chain_active_height;
- tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : Params().GenesisBlock().GetBlockTime();
- tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip());
+ tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : chainman.GetParams().GenesisBlock().GetBlockTime();
+ tip_info->verification_progress = GuessVerificationProgress(chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && chainman.m_best_header) {
tip_info->header_height = chainman.m_best_header->nHeight;
diff --git a/src/init.h b/src/init.h
index 2250ae20a0..e8e6a55eba 100644
--- a/src/init.h
+++ b/src/init.h
@@ -19,6 +19,9 @@ class ArgsManager;
namespace interfaces {
struct BlockAndHeaderTipInfo;
}
+namespace kernel {
+struct Context;
+}
namespace node {
struct NodeContext;
} // namespace node
@@ -43,11 +46,11 @@ bool AppInitBasicSetup(const ArgsManager& args);
*/
bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandbox = true);
/**
- * Initialization sanity checks: ecc init, sanity checks, dir lock.
+ * Initialization sanity checks.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
*/
-bool AppInitSanityChecks();
+bool AppInitSanityChecks(const kernel::Context& kernel);
/**
* Lock bitcoin core data directory.
* @note This should only be done after daemonization. Do not call Shutdown() if this function fails.
diff --git a/src/init/common.cpp b/src/init/common.cpp
index eac6732968..d4e45454d2 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -7,62 +7,19 @@
#endif
#include <clientversion.h>
-#include <compat/sanity.h>
-#include <crypto/sha256.h>
#include <fs.h>
-#include <key.h>
#include <logging.h>
#include <node/ui_interface.h>
-#include <pubkey.h>
-#include <random.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <algorithm>
-#include <memory>
#include <string>
#include <vector>
-static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
-
namespace init {
-void SetGlobals()
-{
- std::string sha256_algo = SHA256AutoDetect();
- LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
- RandomInit();
- ECC_Start();
- globalVerifyHandle.reset(new ECCVerifyHandle());
-}
-
-void UnsetGlobals()
-{
- globalVerifyHandle.reset();
- ECC_Stop();
-}
-
-bool SanityChecks()
-{
- if (!ECC_InitSanityCheck()) {
- return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
- }
-
- if (!glibcxx_sanity_test())
- return false;
-
- if (!Random_SanityCheck()) {
- return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
- }
-
- if (!ChronoSanityCheck()) {
- return InitError(Untranslated("Clock epoch mismatch. Aborting."));
- }
-
- return true;
-}
-
void AddLoggingArgs(ArgsManager& argsman)
{
argsman.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
diff --git a/src/init/common.h b/src/init/common.h
index fc4bc1b280..2c7f485908 100644
--- a/src/init/common.h
+++ b/src/init/common.h
@@ -11,13 +11,6 @@
class ArgsManager;
namespace init {
-void SetGlobals();
-void UnsetGlobals();
-/**
- * Ensure a usable environment with all
- * necessary library support.
- */
-bool SanityChecks();
void AddLoggingArgs(ArgsManager& args);
void SetLoggingOptions(const ArgsManager& args);
void SetLoggingCategories(const ArgsManager& args);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index c4dc303dd5..2c31e12ada 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -5,12 +5,13 @@
#ifndef BITCOIN_INTERFACES_NODE_H
#define BITCOIN_INTERFACES_NODE_H
-#include <consensus/amount.h>
-#include <net.h> // For NodeId
-#include <net_types.h> // For banmap_t
-#include <netaddress.h> // For Network
-#include <netbase.h> // For ConnectionDirection
+#include <consensus/amount.h> // For CAmount
+#include <net.h> // For NodeId
+#include <net_types.h> // For banmap_t
+#include <netaddress.h> // For Network
+#include <netbase.h> // For ConnectionDirection
#include <support/allocators/secure.h> // For SecureString
+#include <util/settings.h> // For util::SettingsValue
#include <util/translation.h>
#include <functional>
@@ -97,6 +98,24 @@ public:
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
+ //! Return whether a particular setting in <datadir>/settings.json is or
+ //! would be ignored because it is also specified in the command line.
+ virtual bool isSettingIgnored(const std::string& name) = 0;
+
+ //! Return setting value from <datadir>/settings.json or bitcoin.conf.
+ virtual util::SettingsValue getPersistentSetting(const std::string& name) = 0;
+
+ //! Update a setting in <datadir>/settings.json.
+ virtual void updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0;
+
+ //! Force a setting value to be applied, overriding any other configuration
+ //! source, but not being persisted.
+ virtual void forceSetting(const std::string& name, const util::SettingsValue& value) = 0;
+
+ //! Clear all settings in <datadir>/settings.json and store a backup of
+ //! previous settings in <datadir>/settings.json.bak.
+ virtual void resetSettings() = 0;
+
//! Map port.
virtual void mapPort(bool use_upnp, bool use_natpmp) = 0;
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
new file mode 100644
index 0000000000..575d94e2e9
--- /dev/null
+++ b/src/kernel/chainstatemanager_opts.h
@@ -0,0 +1,23 @@
+// 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_KERNEL_CHAINSTATEMANAGER_OPTS_H
+#define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
+
+#include <cstdint>
+#include <functional>
+
+class CChainParams;
+
+/**
+ * An options struct for `ChainstateManager`, more ergonomically referred to as
+ * `ChainstateManager::Options` due to the using-declaration in
+ * `ChainstateManager`.
+ */
+struct ChainstateManagerOpts {
+ const CChainParams& chainparams;
+ const std::function<int64_t()> adjusted_time_callback{nullptr};
+};
+
+#endif // BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp
new file mode 100644
index 0000000000..2a1dd3bfa2
--- /dev/null
+++ b/src/kernel/checks.cpp
@@ -0,0 +1,30 @@
+// 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 <kernel/checks.h>
+
+#include <key.h>
+#include <random.h>
+#include <util/time.h>
+
+namespace kernel {
+
+std::optional<SanityCheckError> SanityChecks(const Context&)
+{
+ if (!ECC_InitSanityCheck()) {
+ return SanityCheckError::ERROR_ECC;
+ }
+
+ if (!Random_SanityCheck()) {
+ return SanityCheckError::ERROR_RANDOM;
+ }
+
+ if (!ChronoSanityCheck()) {
+ return SanityCheckError::ERROR_CHRONO;
+ }
+
+ return std::nullopt;
+}
+
+}
diff --git a/src/kernel/checks.h b/src/kernel/checks.h
new file mode 100644
index 0000000000..80b207f607
--- /dev/null
+++ b/src/kernel/checks.h
@@ -0,0 +1,27 @@
+// 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_KERNEL_CHECKS_H
+#define BITCOIN_KERNEL_CHECKS_H
+
+#include <optional>
+
+namespace kernel {
+
+struct Context;
+
+enum class SanityCheckError {
+ ERROR_ECC,
+ ERROR_RANDOM,
+ ERROR_CHRONO,
+};
+
+/**
+ * Ensure a usable environment with all necessary library support.
+ */
+std::optional<SanityCheckError> SanityChecks(const Context&);
+
+}
+
+#endif // BITCOIN_KERNEL_CHECKS_H
diff --git a/src/node/coinstats.cpp b/src/kernel/coinstats.cpp
index eed43a1bc7..f380871627 100644
--- a/src/node/coinstats.cpp
+++ b/src/kernel/coinstats.cpp
@@ -1,14 +1,12 @@
-// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2021 The Bitcoin Core developers
+// 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/coinstats.h>
+#include <kernel/coinstats.h>
#include <coins.h>
#include <crypto/muhash.h>
#include <hash.h>
-#include <index/coinstatsindex.h>
#include <serialize.h>
#include <uint256.h>
#include <util/overflow.h>
@@ -17,7 +15,12 @@
#include <map>
-namespace node {
+namespace kernel {
+
+CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
+ : nHeight(block_height),
+ hashBlock(block_hash) {}
+
// Database-independent metric indicating the UTXO set size
uint64_t GetBogoSize(const CScript& script_pub_key)
{
@@ -93,24 +96,11 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<u
//! Calculate statistics about the unspent transaction output set
template <typename T>
-static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point, const CBlockIndex* pindex)
+static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
- if (!pindex) {
- LOCK(cs_main);
- pindex = blockman.LookupBlockIndex(view->GetBestBlock());
- }
- stats.nHeight = Assert(pindex)->nHeight;
- stats.hashBlock = pindex->GetBlockHash();
-
- // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
- if ((stats.m_hash_type == CoinStatsHashType::MUHASH || stats.m_hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && stats.index_requested) {
- stats.index_used = true;
- return g_coin_stats_index->LookUpStats(pindex, stats);
- }
-
PrepareHash(hash_obj, stats);
uint256 prevkey;
@@ -141,25 +131,36 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
FinalizeHash(hash_obj, stats);
stats.nDiskSize = view->EstimateSize();
+
return true;
}
-bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point, const CBlockIndex* pindex)
+std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
{
- switch (stats.m_hash_type) {
- case(CoinStatsHashType::HASH_SERIALIZED): {
- CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex);
- }
- case(CoinStatsHashType::MUHASH): {
- MuHash3072 muhash;
- return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex);
- }
- case(CoinStatsHashType::NONE): {
- return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex);
+ CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
+ CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
+
+ bool success = [&]() -> bool {
+ switch (hash_type) {
+ case(CoinStatsHashType::HASH_SERIALIZED): {
+ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
+ return ComputeUTXOStats(view, stats, ss, interruption_point);
+ }
+ case(CoinStatsHashType::MUHASH): {
+ MuHash3072 muhash;
+ return ComputeUTXOStats(view, stats, muhash, interruption_point);
+ }
+ case(CoinStatsHashType::NONE): {
+ return ComputeUTXOStats(view, stats, nullptr, interruption_point);
+ }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ }();
+
+ if (!success) {
+ return std::nullopt;
}
- } // no default case, so the compiler can warn about missing cases
- assert(false);
+ return stats;
}
// The legacy hash serializes the hashBlock
@@ -182,4 +183,5 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
stats.hashSerialized = out;
}
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
-} // namespace node
+
+} // namespace kernel
diff --git a/src/node/coinstats.h b/src/kernel/coinstats.h
index aa771b18b0..a15957233f 100644
--- a/src/node/coinstats.h
+++ b/src/kernel/coinstats.h
@@ -1,10 +1,9 @@
-// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2021 The Bitcoin Core developers
+// 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_COINSTATS_H
-#define BITCOIN_NODE_COINSTATS_H
+#ifndef BITCOIN_KERNEL_COINSTATS_H
+#define BITCOIN_KERNEL_COINSTATS_H
#include <chain.h>
#include <coins.h>
@@ -20,7 +19,7 @@ namespace node {
class BlockManager;
} // namespace node
-namespace node {
+namespace kernel {
enum class CoinStatsHashType {
HASH_SERIALIZED,
MUHASH,
@@ -28,9 +27,6 @@ enum class CoinStatsHashType {
};
struct CCoinsStats {
- //! Which hash type to use
- const CoinStatsHashType m_hash_type;
-
int nHeight{0};
uint256 hashBlock{};
uint64_t nTransactions{0};
@@ -44,8 +40,6 @@ struct CCoinsStats {
//! The number of coins contained.
uint64_t coins_count{0};
- //! Signals if the coinstatsindex should be used (when available).
- bool index_requested{true};
//! Signals if the coinstatsindex was used to retrieve the statistics.
bool index_used{false};
@@ -70,15 +64,15 @@ struct CCoinsStats {
//! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block
CAmount total_unspendables_unclaimed_rewards{0};
- CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {}
+ CCoinsStats() = default;
+ CCoinsStats(int block_height, const uint256& block_hash);
};
-//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {}, const CBlockIndex* pindex = nullptr);
-
uint64_t GetBogoSize(const CScript& script_pub_key);
CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
-} // namespace node
-#endif // BITCOIN_NODE_COINSTATS_H
+std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point = {});
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_COINSTATS_H
diff --git a/src/kernel/context.cpp b/src/kernel/context.cpp
new file mode 100644
index 0000000000..15413c1840
--- /dev/null
+++ b/src/kernel/context.cpp
@@ -0,0 +1,33 @@
+// 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 <kernel/context.h>
+
+#include <crypto/sha256.h>
+#include <key.h>
+#include <logging.h>
+#include <pubkey.h>
+#include <random.h>
+
+#include <string>
+
+
+namespace kernel {
+
+Context::Context()
+{
+ std::string sha256_algo = SHA256AutoDetect();
+ LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
+ RandomInit();
+ ECC_Start();
+ ecc_verify_handle.reset(new ECCVerifyHandle());
+}
+
+Context::~Context()
+{
+ ecc_verify_handle.reset();
+ ECC_Stop();
+}
+
+} // namespace kernel
diff --git a/src/kernel/context.h b/src/kernel/context.h
new file mode 100644
index 0000000000..9746ef994b
--- /dev/null
+++ b/src/kernel/context.h
@@ -0,0 +1,31 @@
+// 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_KERNEL_CONTEXT_H
+#define BITCOIN_KERNEL_CONTEXT_H
+
+#include <memory>
+
+class ECCVerifyHandle;
+
+namespace kernel {
+//! Context struct holding the kernel library's logically global state, and
+//! passed to external libbitcoin_kernel functions which need access to this
+//! state. The kernel library API is a work in progress, so state organization
+//! and member list will evolve over time.
+//!
+//! State stored directly in this struct should be simple. More complex state
+//! should be stored to std::unique_ptr members pointing to opaque types.
+struct Context {
+ std::unique_ptr<ECCVerifyHandle> ecc_verify_handle;
+
+ //! Declare default constructor and destructor that are not inline, so code
+ //! instantiating the kernel::Context struct doesn't need to #include class
+ //! definitions for all the unique_ptr members.
+ Context();
+ ~Context();
+};
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_CONTEXT_H
diff --git a/src/logging.cpp b/src/logging.cpp
index a5e5fdb4cd..1e2c1d5a77 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -171,7 +171,7 @@ const CLogCategoryDesc LogCategories[] =
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
{
- if (str == "") {
+ if (str.empty()) {
flag = BCLog::ALL;
return true;
}
@@ -184,6 +184,91 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
return false;
}
+std::string LogLevelToStr(BCLog::Level level)
+{
+ switch (level) {
+ case BCLog::Level::None:
+ return "none";
+ case BCLog::Level::Debug:
+ return "debug";
+ case BCLog::Level::Info:
+ return "info";
+ case BCLog::Level::Warning:
+ return "warning";
+ case BCLog::Level::Error:
+ return "error";
+ }
+ assert(false);
+}
+
+std::string LogCategoryToStr(BCLog::LogFlags category)
+{
+ // Each log category string representation should sync with LogCategories
+ switch (category) {
+ case BCLog::LogFlags::NONE:
+ return "none";
+ case BCLog::LogFlags::NET:
+ return "net";
+ case BCLog::LogFlags::TOR:
+ return "tor";
+ case BCLog::LogFlags::MEMPOOL:
+ return "mempool";
+ case BCLog::LogFlags::HTTP:
+ return "http";
+ case BCLog::LogFlags::BENCH:
+ return "bench";
+ case BCLog::LogFlags::ZMQ:
+ return "zmq";
+ case BCLog::LogFlags::WALLETDB:
+ return "walletdb";
+ case BCLog::LogFlags::RPC:
+ return "rpc";
+ case BCLog::LogFlags::ESTIMATEFEE:
+ return "estimatefee";
+ case BCLog::LogFlags::ADDRMAN:
+ return "addrman";
+ case BCLog::LogFlags::SELECTCOINS:
+ return "selectcoins";
+ case BCLog::LogFlags::REINDEX:
+ return "reindex";
+ case BCLog::LogFlags::CMPCTBLOCK:
+ return "cmpctblock";
+ case BCLog::LogFlags::RAND:
+ return "rand";
+ case BCLog::LogFlags::PRUNE:
+ return "prune";
+ case BCLog::LogFlags::PROXY:
+ return "proxy";
+ case BCLog::LogFlags::MEMPOOLREJ:
+ return "mempoolrej";
+ case BCLog::LogFlags::LIBEVENT:
+ return "libevent";
+ case BCLog::LogFlags::COINDB:
+ return "coindb";
+ case BCLog::LogFlags::QT:
+ return "qt";
+ case BCLog::LogFlags::LEVELDB:
+ return "leveldb";
+ case BCLog::LogFlags::VALIDATION:
+ return "validation";
+ case BCLog::LogFlags::I2P:
+ return "i2p";
+ case BCLog::LogFlags::IPC:
+ return "ipc";
+#ifdef DEBUG_LOCKCONTENTION
+ case BCLog::LogFlags::LOCK:
+ return "lock";
+#endif
+ case BCLog::LogFlags::UTIL:
+ return "util";
+ case BCLog::LogFlags::BLOCKSTORE:
+ return "blockstorage";
+ case BCLog::LogFlags::ALL:
+ return "all";
+ }
+ assert(false);
+}
+
std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
{
// Sort log categories by alphabetical order.
@@ -249,17 +334,38 @@ namespace BCLog {
}
} // namespace BCLog
-void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line)
+void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level)
{
StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str);
+ if ((category != LogFlags::NONE || level != Level::None) && m_started_new_line) {
+ std::string s{"["};
+
+ if (category != LogFlags::NONE) {
+ s += LogCategoryToStr(category);
+ }
+
+ if (category != LogFlags::NONE && level != Level::None) {
+ // Only add separator if both flag and level are not NONE
+ s += ":";
+ }
+
+ if (level != Level::None) {
+ s += LogLevelToStr(level);
+ }
+
+ s += "] ";
+ str_prefixed.insert(0, s);
+ }
+
if (m_log_sourcelocations && m_started_new_line) {
str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
}
if (m_log_threadnames && m_started_new_line) {
- str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
+ const auto threadname = util::ThreadGetInternalName();
+ str_prefixed.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
}
str_prefixed = LogTimestampStr(str_prefixed);
diff --git a/src/logging.h b/src/logging.h
index ae8cad906c..8a896b6b33 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -67,6 +67,13 @@ namespace BCLog {
BLOCKSTORE = (1 << 26),
ALL = ~(uint32_t)0,
};
+ enum class Level {
+ Debug = 0,
+ None = 1,
+ Info = 2,
+ Warning = 3,
+ Error = 4,
+ };
class Logger
{
@@ -105,7 +112,7 @@ namespace BCLog {
std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */
- void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line);
+ void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level);
/** Returns whether logs will be written to any output */
bool Enabled() const
@@ -159,9 +166,14 @@ namespace BCLog {
BCLog::Logger& LogInstance();
-/** Return true if log accepts specified category */
-static inline bool LogAcceptCategory(BCLog::LogFlags category)
+/** Return true if log accepts specified category, at the specified level. */
+static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
{
+ // Log messages at Warning and Error level unconditionally, so that
+ // important troubleshooting information doesn't get lost.
+ if (level >= BCLog::Level::Warning) {
+ return true;
+ }
return LogInstance().WillLogCategory(category);
}
@@ -173,7 +185,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
// peer can fill up a user's disk with debug.log entries.
template <typename... Args>
-static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args)
+static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags flag, const BCLog::Level level, const char* fmt, const Args&... args)
{
if (LogInstance().Enabled()) {
std::string log_msg;
@@ -183,19 +195,29 @@ static inline void LogPrintf_(const std::string& logging_function, const std::st
/* Original format string will have newline so don't add one here */
log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
}
- LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line);
+ LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line, flag, level);
}
}
-#define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__)
+
+#define LogPrintLevel_(category, level, ...) LogPrintf_(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
+
+#define LogPrintf(...) LogPrintLevel_(BCLog::LogFlags::NONE, BCLog::Level::None, __VA_ARGS__)
// Use a macro instead of a function for conditional logging to prevent
// evaluating arguments when logging for the category is not enabled.
-#define LogPrint(category, ...) \
- do { \
- if (LogAcceptCategory((category))) { \
- LogPrintf(__VA_ARGS__); \
- } \
+#define LogPrint(category, ...) \
+ do { \
+ if (LogAcceptCategory((category), BCLog::Level::Debug)) { \
+ LogPrintLevel_(category, BCLog::Level::None, __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define LogPrintLevel(category, level, ...) \
+ do { \
+ if (LogAcceptCategory((category), (level))) { \
+ LogPrintLevel_(category, level, __VA_ARGS__); \
+ } \
} while (0)
#endif // BITCOIN_LOGGING_H
diff --git a/src/net.cpp b/src/net.cpp
index 8d1cff5620..0cdf98c40f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -25,6 +25,7 @@
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
+#include <util/designator.h>
#include <util/sock.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
@@ -430,7 +431,7 @@ static CAddress GetBindAddress(SOCKET sock)
if (!getsockname(sock, (struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) {
addr_bind.SetSockAddr((const struct sockaddr*)&sockaddr_bind);
} else {
- LogPrint(BCLog::NET, "Warning: getsockname failed\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n");
}
}
return addr_bind;
@@ -454,9 +455,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
}
/// debug print
- LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n",
- pszDest ? pszDest : addrConnect.ToString(),
- pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying connection %s lastseen=%.1fhrs\n",
+ pszDest ? pszDest : addrConnect.ToString(),
+ pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0);
// Resolve
const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
@@ -1101,12 +1102,20 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
- NodeEvictionCandidate candidate = {node->GetId(), node->m_connected, node->m_min_ping_time,
- node->m_last_block_time, node->m_last_tx_time,
- HasAllDesirableServiceFlags(node->nServices),
- node->m_relays_txs.load(), node->m_bloom_filter_loaded.load(),
- node->nKeyedNetGroup, node->m_prefer_evict, node->addr.IsLocal(),
- node->ConnectedThroughNetwork()};
+ NodeEvictionCandidate candidate{
+ Desig(id) node->GetId(),
+ Desig(m_connected) node->m_connected,
+ Desig(m_min_ping_time) node->m_min_ping_time,
+ Desig(m_last_block_time) node->m_last_block_time,
+ Desig(m_last_tx_time) node->m_last_tx_time,
+ Desig(fRelevantServices) HasAllDesirableServiceFlags(node->nServices),
+ Desig(m_relay_txs) node->m_relays_txs.load(),
+ Desig(fBloomFilter) node->m_bloom_filter_loaded.load(),
+ Desig(nKeyedNetGroup) node->nKeyedNetGroup,
+ Desig(prefer_evict) node->m_prefer_evict,
+ Desig(m_is_local) node->addr.IsLocal(),
+ Desig(m_network) node->ConnectedThroughNetwork(),
+ };
vEvictionCandidates.push_back(candidate);
}
}
@@ -1140,7 +1149,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "Unknown socket family\n");
} else {
addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}
@@ -1864,12 +1873,12 @@ bool CConnman::GetTryNewOutboundPeer() const
void CConnman::SetTryNewOutboundPeer(bool flag)
{
m_try_another_outbound_peer = flag;
- LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
+ LogPrint(BCLog::NET, "setting try another outbound peer=%s\n", flag ? "true" : "false");
}
void CConnman::StartExtraBlockRelayPeers()
{
- LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n");
+ LogPrint(BCLog::NET, "enabling extra block-relay-only peers\n");
m_start_extra_block_relay_peers = true;
}
@@ -2397,15 +2406,15 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
- strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString());
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToString());
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
std::unique_ptr<Sock> sock = CreateSock(addrBind);
if (!sock) {
- strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2441,7 +2450,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
- LogPrintf("%s\n", strError.original);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
@@ -2449,8 +2458,8 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Listen for incoming connections
if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR)
{
- strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(_("Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2805,8 +2814,11 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
{
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
- .Write(requestor.addr.GetNetwork())
+ .Write(requestor.ConnectedThroughNetwork())
.Write(local_socket_bytes.data(), local_socket_bytes.size())
+ // For outbound connections, the port of the bound address is randomly
+ // assigned by the OS and would therefore not be useful for seeding.
+ .Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
.Finalize();
const auto current_time = GetTime<std::chrono::microseconds>();
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b81119bdd6..1d69975f55 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -21,6 +21,7 @@
#include <node/blockstorage.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
@@ -459,7 +460,7 @@ struct CNodeState {
class PeerManagerImpl final : public PeerManager
{
public:
- PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+ PeerManagerImpl(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
@@ -1587,17 +1588,17 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl
return std::nullopt;
}
-std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+std::unique_ptr<PeerManager> PeerManager::make(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
{
- return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, ignore_incoming_txs);
+ return std::make_unique<PeerManagerImpl>(connman, addrman, banman, chainman, pool, ignore_incoming_txs);
}
-PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
- : m_chainparams(chainparams),
+ : m_chainparams(chainman.GetParams()),
m_connman(connman),
m_addrman(addrman),
m_banman(banman),
@@ -3300,9 +3301,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d while importing/reindexing\n", pfrom.GetId());
+ return;
+ }
+
LOCK(cs_main);
- if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(NetPermissionFlags::Download)) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
+
+ // Note that if we were to be on a chain that forks from the checkpointed
+ // chain, then serving those headers to a peer that has seen the
+ // checkpointed chain would cause that peer to disconnect us. Requiring
+ // that our chainwork exceed nMinimumChainWork is a protection against
+ // being fed a bogus chain when we started up for the first time and
+ // getting partitioned off the honest network for serving that chain to
+ // others.
+ if (m_chainman.ActiveTip() == nullptr ||
+ (m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && !pfrom.HasPermission(NetPermissionFlags::Download))) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work\n", pfrom.GetId());
return;
}
@@ -4703,10 +4718,31 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (m_chainman.m_best_header == nullptr) {
m_chainman.m_best_header = m_chainman.ActiveChain().Tip();
}
- bool fFetch = state.fPreferredDownload || (m_num_preferred_download_peers == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
+
+ // Determine whether we might try initial headers sync or parallel
+ // block download from this peer -- this mostly affects behavior while
+ // in IBD (once out of IBD, we sync from all peers).
+ bool sync_blocks_and_headers_from_peer = false;
+ if (state.fPreferredDownload) {
+ sync_blocks_and_headers_from_peer = true;
+ } else if (!pto->fClient && !pto->IsAddrFetchConn()) {
+ // Typically this is an inbound peer. If we don't have any outbound
+ // peers, or if we aren't downloading any blocks from such peers,
+ // then allow block downloads from this peer, too.
+ // We prefer downloading blocks from outbound peers to avoid
+ // putting undue load on (say) some home user who is just making
+ // outbound connections to the network, but if our only source of
+ // the latest blocks is from an inbound peer, we have to be sure to
+ // eventually download it (and not just wait indefinitely for an
+ // outbound peer to have it).
+ if (m_num_preferred_download_peers == 0 || mapBlocksInFlight.empty()) {
+ sync_blocks_and_headers_from_peer = true;
+ }
+ }
+
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
(
@@ -5079,7 +5115,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((sync_blocks_and_headers_from_peer && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
diff --git a/src/net_processing.h b/src/net_processing.h
index c982b919a6..d5c73e6c79 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -39,7 +39,7 @@ struct CNodeStateStats {
class PeerManager : public CValidationInterface, public NetEventsInterface
{
public:
- static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+ static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
virtual ~PeerManager() { }
diff --git a/src/netaddress.h b/src/netaddress.h
index 77e6171054..47ba045334 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,7 +9,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <prevector.h>
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 17ab226a30..cadafcaa8d 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -226,7 +226,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
}
}
- LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
+ LogPrint(BCLog::PRUNE, "target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
nLastBlockWeCanPrune, count);
@@ -318,9 +318,9 @@ bool BlockManager::WriteBlockIndexDB()
return true;
}
-bool BlockManager::LoadBlockIndexDB()
+bool BlockManager::LoadBlockIndexDB(const Consensus::Params& consensus_params)
{
- if (!LoadBlockIndex(::Params().GetConsensus())) {
+ if (!LoadBlockIndex(consensus_params)) {
return false;
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 488713dbd8..2e52716649 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -153,7 +153,7 @@ public:
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
- bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ bool LoadBlockIndexDB(const Consensus::Params& consensus_params) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, CBlockIndex*& best_header) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index 99615dea69..54ba5b7966 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -13,7 +13,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
ChainstateManager& chainman,
CTxMemPool* mempool,
bool fPruneMode,
- const Consensus::Params& consensus_params,
bool fReindexChainState,
int64_t nBlockTreeDBCache,
int64_t nCoinDBCache,
@@ -57,7 +56,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
}
if (!chainman.BlockIndex().empty() &&
- !chainman.m_blockman.LookupBlockIndex(consensus_params.hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) {
return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
}
@@ -126,10 +125,8 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman,
bool fReset,
bool fReindexChainState,
- const Consensus::Params& consensus_params,
int check_blocks,
- int check_level,
- std::function<int64_t()> get_unix_time_seconds)
+ int check_level)
{
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
@@ -140,12 +137,12 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
const CBlockIndex* tip = chainstate->m_chain.Tip();
- if (tip && tip->nTime > get_unix_time_seconds() + MAX_FUTURE_BLOCK_TIME) {
+ if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE;
}
if (!CVerifyDB().VerifyDB(
- *chainstate, consensus_params, chainstate->CoinsDB(),
+ *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
check_level,
check_blocks)) {
return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB;
diff --git a/src/node/chainstate.h b/src/node/chainstate.h
index 8ba04f1436..ff7935e8e0 100644
--- a/src/node/chainstate.h
+++ b/src/node/chainstate.h
@@ -59,7 +59,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
ChainstateManager& chainman,
CTxMemPool* mempool,
bool fPruneMode,
- const Consensus::Params& consensus_params,
bool fReindexChainState,
int64_t nBlockTreeDBCache,
int64_t nCoinDBCache,
@@ -78,10 +77,8 @@ enum class ChainstateLoadVerifyError {
std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman,
bool fReset,
bool fReindexChainState,
- const Consensus::Params& consensus_params,
int check_blocks,
- int check_level,
- std::function<int64_t()> get_unix_time_seconds);
+ int check_level);
} // namespace node
#endif // BITCOIN_NODE_CHAINSTATE_H
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 4787efa1de..d80b8ca7a7 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -7,6 +7,7 @@
#include <addrman.h>
#include <banman.h>
#include <interfaces/chain.h>
+#include <kernel/context.h>
#include <net.h>
#include <net_processing.h>
#include <netgroup.h>
diff --git a/src/node/context.h b/src/node/context.h
index 91ba456219..31be308787 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
+#include <kernel/context.h>
+
#include <cassert>
#include <functional>
#include <memory>
@@ -39,6 +41,8 @@ namespace node {
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
+ //! libbitcoin_kernel context
+ std::unique_ptr<kernel::Context> kernel;
//! Init interface for initializing current process and connecting to other processes.
interfaces::Init* init{nullptr};
std::unique_ptr<AddrMan> addrman;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index f2debc790c..7752fb0f65 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -90,8 +90,16 @@ public:
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
- return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs, /*use_syscall_sandbox=*/false) && AppInitSanityChecks() &&
- AppInitLockDataDirectory() && AppInitInterfaces(*m_context);
+ if (!AppInitBasicSetup(gArgs)) return false;
+ if (!AppInitParameterInteraction(gArgs, /*use_syscall_sandbox=*/false)) return false;
+
+ m_context->kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*m_context->kernel)) return false;
+
+ if (!AppInitLockDataDirectory()) return false;
+ if (!AppInitInterfaces(*m_context)) return false;
+
+ return true;
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
@@ -112,6 +120,46 @@ public:
}
}
bool shutdownRequested() override { return ShutdownRequested(); }
+ bool isSettingIgnored(const std::string& name) override
+ {
+ bool ignored = false;
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (auto* options = util::FindKey(settings.command_line_options, name)) {
+ ignored = !options->empty();
+ }
+ });
+ return ignored;
+ }
+ util::SettingsValue getPersistentSetting(const std::string& name) override { return gArgs.GetPersistentSetting(name); }
+ void updateRwSetting(const std::string& name, const util::SettingsValue& value) override
+ {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (value.isNull()) {
+ settings.rw_settings.erase(name);
+ } else {
+ settings.rw_settings[name] = value;
+ }
+ });
+ gArgs.WriteSettingsFile();
+ }
+ void forceSetting(const std::string& name, const util::SettingsValue& value) override
+ {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (value.isNull()) {
+ settings.forced_settings.erase(name);
+ } else {
+ settings.forced_settings[name] = value;
+ }
+ });
+ }
+ void resetSettings() override
+ {
+ gArgs.WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true);
+ gArgs.LockSettings([&](util::Settings& settings) {
+ settings.rw_settings.clear();
+ });
+ gArgs.WriteSettingsFile();
+ }
void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(ConnectionDirection flags) override
@@ -228,7 +276,7 @@ public:
uint256 getBestBlockHash() override
{
const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
- return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
+ return tip ? tip->GetBlockHash() : chainman().GetParams().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
@@ -236,7 +284,7 @@ public:
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
- return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
+ return chainman().GetParams().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
double getVerificationProgress() override
{
@@ -245,7 +293,7 @@ public:
LOCK(::cs_main);
tip = chainman().ActiveChain().Tip();
}
- return GuessVerificationProgress(Params().TxData(), tip);
+ return GuessVerificationProgress(chainman().GetParams().TxData(), tip);
}
bool isInitialBlockDownload() override {
return chainman().ActiveChainstate().IsInitialBlockDownload();
@@ -546,7 +594,7 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
+ return GuessVerificationProgress(chainman().GetParams().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
{
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 770ccdbe1a..01db49d5cf 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -62,8 +62,8 @@ BlockAssembler::Options::Options()
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options)
- : chainparams(params),
+BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const Options& options)
+ : chainparams{chainstate.m_chainman.GetParams()},
m_mempool(mempool),
m_chainstate(chainstate)
{
@@ -87,8 +87,8 @@ static BlockAssembler::Options DefaultOptions()
return options;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params)
- : BlockAssembler(chainstate, mempool, params, DefaultOptions()) {}
+BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool)
+ : BlockAssembler(chainstate, mempool, DefaultOptions()) {}
void BlockAssembler::resetBlock()
{
@@ -167,7 +167,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
+ if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, GetAdjustedTime, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
int64_t nTime2 = GetTimeMicros();
@@ -310,10 +310,6 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
// Keep track of entries that failed inclusion, to avoid duplicate work
CTxMemPool::setEntries failedTx;
- // Start by adding all descendants of previously added txs to mapModifiedTx
- // and modifying them for their already included ancestors
- UpdatePackagesForAdded(inBlock, mapModifiedTx);
-
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = m_mempool.mapTx.get<ancestor_score>().begin();
CTxMemPool::txiter iter;
diff --git a/src/node/miner.h b/src/node/miner.h
index 678df815c0..7cf8e3fb9e 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -157,8 +157,8 @@ public:
CFeeRate blockMinFeeRate;
};
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params);
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
+ explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool);
+ explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
diff --git a/src/node/transaction.h b/src/node/transaction.h
index b7cf225636..0604754a46 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H
-#include <attributes.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <util/error.h>
diff --git a/src/outputtype.h b/src/outputtype.h
index 66fe489bb0..6b4e695760 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_OUTPUTTYPE_H
#define BITCOIN_OUTPUTTYPE_H
-#include <attributes.h>
#include <script/signingprovider.h>
#include <script/standard.h>
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index 0ea56d8db7..82b767793d 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <policy/feerate.h>
-
#include <tinyformat.h>
#include <cmath>
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 50fd6fd11b..a8d4d2fc63 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -9,7 +9,10 @@
#include <consensus/amount.h>
#include <serialize.h>
+
+#include <cstdint>
#include <string>
+#include <type_traits>
const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index d2deaf69d0..b39632364f 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -6,12 +6,30 @@
#include <policy/fees.h>
#include <clientversion.h>
+#include <consensus/amount.h>
#include <fs.h>
#include <logging.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <random.h>
+#include <serialize.h>
#include <streams.h>
+#include <sync.h>
+#include <tinyformat.h>
#include <txmempool.h>
+#include <uint256.h>
#include <util/serfloat.h>
#include <util/system.h>
+#include <util/time.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <exception>
+#include <stdexcept>
+#include <utility>
static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 6e25bb42b8..dea1e1d31b 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -7,20 +7,20 @@
#include <consensus/amount.h>
#include <policy/feerate.h>
-#include <uint256.h>
#include <random.h>
#include <sync.h>
+#include <threadsafety.h>
+#include <uint256.h>
#include <array>
#include <map>
#include <memory>
+#include <set>
#include <string>
#include <vector>
class CAutoFile;
-class CFeeRate;
class CTxMemPoolEntry;
-class CTxMemPool;
class TxConfirmStats;
/* Identifier for each of the 3 different TxConfirmStats which will track
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
index 21f5488816..67918c9dec 100644
--- a/src/policy/packages.cpp
+++ b/src/policy/packages.cpp
@@ -2,12 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <consensus/validation.h>
#include <policy/packages.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <uint256.h>
#include <util/hasher.h>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
#include <numeric>
#include <unordered_set>
diff --git a/src/policy/packages.h b/src/policy/packages.h
index 9f274f6b7d..ba6a3a9a06 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -5,10 +5,12 @@
#ifndef BITCOIN_POLICY_PACKAGES_H
#define BITCOIN_POLICY_PACKAGES_H
+#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <cstdint>
#include <vector>
/** Default maximum number of transactions in a package. */
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 6aba6a4a5b..f6452266b7 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -7,10 +7,22 @@
#include <policy/policy.h>
-#include <consensus/validation.h>
#include <coins.h>
+#include <consensus/amount.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <script/interpreter.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <serialize.h>
#include <span.h>
+#include <algorithm>
+#include <cstddef>
+#include <vector>
+
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
// "Dust" is defined in terms of dustRelayFee,
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 89f6e72618..94f9623b8a 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -6,15 +6,18 @@
#ifndef BITCOIN_POLICY_POLICY_H
#define BITCOIN_POLICY_POLICY_H
+#include <consensus/amount.h>
#include <consensus/consensus.h>
-#include <policy/feerate.h>
+#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <script/standard.h>
+#include <cstdint>
#include <string>
class CCoinsViewCache;
-class CTxOut;
+class CFeeRate;
+class CScript;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
@@ -52,6 +55,8 @@ static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
static const unsigned int DUST_RELAY_TX_FEE = 3000;
+/** Default for -minrelaytxfee, minimum relay fee for transactions */
+static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 8fe4dc35b8..e25f5c7c5b 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -4,11 +4,19 @@
#include <policy/rbf.h>
-#include <policy/settings.h>
+#include <consensus/amount.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <sync.h>
#include <tinyformat.h>
+#include <txmempool.h>
+#include <uint256.h>
#include <util/moneystr.h>
#include <util/rbf.h>
+#include <limits>
+#include <vector>
+
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
AssertLockHeld(pool.cs);
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index fcec7052ed..07f68c8fd4 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -5,13 +5,20 @@
#ifndef BITCOIN_POLICY_RBF_H
#define BITCOIN_POLICY_RBF_H
+#include <consensus/amount.h>
#include <primitives/transaction.h>
+#include <threadsafety.h>
#include <txmempool.h>
-#include <uint256.h>
+#include <cstddef>
+#include <cstdint>
#include <optional>
+#include <set>
#include <string>
+class CFeeRate;
+class uint256;
+
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
* mempool conflicts and their descendants. */
static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100};
diff --git a/src/policy/settings.cpp b/src/policy/settings.cpp
index eb2ec56850..0b67d274ce 100644
--- a/src/policy/settings.cpp
+++ b/src/policy/settings.cpp
@@ -11,4 +11,5 @@
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
+CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
diff --git a/src/policy/settings.h b/src/policy/settings.h
index 0b4fc1e770..2311d01fe8 100644
--- a/src/policy/settings.h
+++ b/src/policy/settings.h
@@ -6,14 +6,19 @@
#ifndef BITCOIN_POLICY_SETTINGS_H
#define BITCOIN_POLICY_SETTINGS_H
+#include <policy/feerate.h>
#include <policy/policy.h>
-class CFeeRate;
+#include <cstdint>
+#include <string>
+
class CTransaction;
// Policy settings which are configurable at runtime.
extern CFeeRate incrementalRelayFee;
extern CFeeRate dustRelayFee;
+/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
+extern CFeeRate minRelayTxFee;
extern unsigned int nBytesPerSigOp;
extern bool fIsBareMultisigStd;
diff --git a/src/protocol.h b/src/protocol.h
index fdeaa9a9c5..da2d24aff3 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -3,10 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef __cplusplus
-#error This header can only be compiled as C++.
-#endif
-
#ifndef BITCOIN_PROTOCOL_H
#define BITCOIN_PROTOCOL_H
@@ -15,10 +11,9 @@
#include <serialize.h>
#include <streams.h>
#include <uint256.h>
-#include <version.h>
+#include <cstdint>
#include <limits>
-#include <stdint.h>
#include <string>
/** Message header.
@@ -420,7 +415,6 @@ public:
use_v2 = s.GetVersion() & ADDRV2_FORMAT;
}
- SER_READ(obj, obj.nTime = TIME_INIT);
READWRITE(obj.nTime);
// nServices is serialized as CompactSize in V2; as uint64_t in V1.
if (use_v2) {
diff --git a/src/psbt.h b/src/psbt.h
index 8a9cbd33d2..8fda889bb4 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_PSBT_H
#define BITCOIN_PSBT_H
-#include <attributes.h>
#include <node/transaction.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index d59a4345f3..a82bd5f73e 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -19,6 +19,11 @@
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QRegularExpression>
+#else
+#include <QRegExp>
+#endif
class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
{
@@ -46,12 +51,13 @@ protected:
auto address = model->index(row, AddressTableModel::Address, parent);
- if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
- filterRegExp().indexIn(model->data(label).toString()) < 0) {
- return false;
- }
-
- return true;
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ const auto pattern = filterRegularExpression();
+#else
+ const auto pattern = filterRegExp();
+#endif
+ return (model->data(address).toString().contains(pattern) ||
+ model->data(label).toString().contains(pattern));
}
};
diff --git a/src/qt/android/res/values/libs.xml b/src/qt/android/res/values/libs.xml
index 0f20df4eb0..b4b77b1c7b 100644
--- a/src/qt/android/res/values/libs.xml
+++ b/src/qt/android/res/values/libs.xml
@@ -10,8 +10,5 @@
<item>
x86_64;libbitcoin-qt_x86_64.so
</item>
- <item>
- x86;libbitcoin-qt_x86.so
- </item>
</array>
</resources>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8cac28400f..53fbac0106 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -261,7 +261,7 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(this, resetSettings);
+ optionsModel = new OptionsModel(node(), this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -292,7 +292,6 @@ void BitcoinApplication::createNode(interfaces::Init& init)
{
assert(!m_node);
m_node = init.makeNode();
- if (optionsModel) optionsModel->setNode(*m_node);
if (m_splash) m_splash->setNode(*m_node);
}
@@ -308,7 +307,9 @@ void BitcoinApplication::startThread()
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] {
+ QCoreApplication::exit(0);
+ });
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -633,6 +634,12 @@ int GuiMain(int argc, char* argv[])
// Allow parameter interaction before we create the options model
app.parameterSetup();
GUIUtil::LogQtInfo();
+
+ if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
+ app.createSplashScreen(networkStyle.data());
+
+ app.createNode(*init);
+
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
@@ -641,11 +648,6 @@ int GuiMain(int argc, char* argv[])
app.InitPruneSetting(prune_MiB);
}
- if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
- app.createSplashScreen(networkStyle.data());
-
- app.createNode(*init);
-
int rv = EXIT_SUCCESS;
try
{
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 40b9ed5483..612d3009c1 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -30,8 +30,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
- QAbstractListModel(parent)
+OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent), m_node{node}
{
Init(resetSettings);
}
@@ -335,260 +335,272 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
if(role == Qt::EditRole)
{
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- return GUIUtil::GetStartOnSystemStartup();
- case ShowTrayIcon:
- return m_show_tray_icon;
- case MinimizeToTray:
- return fMinimizeToTray;
- case MapPortUPnP:
+ return getOption(OptionID(index.row()));
+ }
+ return QVariant();
+}
+
+// write QSettings values
+bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ bool successful = true; /* set to false on parse error */
+ if(role == Qt::EditRole)
+ {
+ successful = setOption(OptionID(index.row()), value);
+ }
+
+ Q_EMIT dataChanged(index, index);
+
+ return successful;
+}
+
+QVariant OptionsModel::getOption(OptionID option) const
+{
+ QSettings settings;
+ switch (option) {
+ case StartAtStartup:
+ return GUIUtil::GetStartOnSystemStartup();
+ case ShowTrayIcon:
+ return m_show_tray_icon;
+ case MinimizeToTray:
+ return fMinimizeToTray;
+ case MapPortUPnP:
#ifdef USE_UPNP
- return settings.value("fUseUPnP");
+ return settings.value("fUseUPnP");
#else
- return false;
+ return false;
#endif // USE_UPNP
- case MapPortNatpmp:
+ case MapPortNatpmp:
#ifdef USE_NATPMP
- return settings.value("fUseNatpmp");
+ return settings.value("fUseNatpmp");
#else
- return false;
+ return false;
#endif // USE_NATPMP
- case MinimizeOnClose:
- return fMinimizeOnClose;
-
- // default proxy
- case ProxyUse:
- return settings.value("fUseProxy", false);
- case ProxyIP:
- return GetProxySetting(settings, "addrProxy").ip;
- case ProxyPort:
- return GetProxySetting(settings, "addrProxy").port;
-
- // separate Tor proxy
- case ProxyUseTor:
- return settings.value("fUseSeparateProxyTor", false);
- case ProxyIPTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").ip;
- case ProxyPortTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").port;
+ case MinimizeOnClose:
+ return fMinimizeOnClose;
+
+ // default proxy
+ case ProxyUse:
+ return settings.value("fUseProxy", false);
+ case ProxyIP:
+ return GetProxySetting(settings, "addrProxy").ip;
+ case ProxyPort:
+ return GetProxySetting(settings, "addrProxy").port;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ return settings.value("fUseSeparateProxyTor", false);
+ case ProxyIPTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").ip;
+ case ProxyPortTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").port;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- return settings.value("bSpendZeroConfChange");
- case ExternalSignerPath:
- return settings.value("external_signer_path");
- case SubFeeFromAmount:
- return m_sub_fee_from_amount;
+ case SpendZeroConfChange:
+ return settings.value("bSpendZeroConfChange");
+ case ExternalSignerPath:
+ return settings.value("external_signer_path");
+ case SubFeeFromAmount:
+ return m_sub_fee_from_amount;
#endif
- case DisplayUnit:
- return QVariant::fromValue(m_display_bitcoin_unit);
- case ThirdPartyTxUrls:
- return strThirdPartyTxUrls;
- case Language:
- return settings.value("language");
- case UseEmbeddedMonospacedFont:
- return m_use_embedded_monospaced_font;
- case CoinControlFeatures:
- return fCoinControlFeatures;
- case EnablePSBTControls:
- return settings.value("enable_psbt_controls");
- case Prune:
- return settings.value("bPrune");
- case PruneSize:
- return settings.value("nPruneSize");
- case DatabaseCache:
- return settings.value("nDatabaseCache");
- case ThreadsScriptVerif:
- return settings.value("nThreadsScriptVerif");
- case Listen:
- return settings.value("fListen");
- case Server:
- return settings.value("server");
- default:
- return QVariant();
- }
+ case DisplayUnit:
+ return QVariant::fromValue(m_display_bitcoin_unit);
+ case ThirdPartyTxUrls:
+ return strThirdPartyTxUrls;
+ case Language:
+ return settings.value("language");
+ case UseEmbeddedMonospacedFont:
+ return m_use_embedded_monospaced_font;
+ case CoinControlFeatures:
+ return fCoinControlFeatures;
+ case EnablePSBTControls:
+ return settings.value("enable_psbt_controls");
+ case Prune:
+ return settings.value("bPrune");
+ case PruneSize:
+ return settings.value("nPruneSize");
+ case DatabaseCache:
+ return settings.value("nDatabaseCache");
+ case ThreadsScriptVerif:
+ return settings.value("nThreadsScriptVerif");
+ case Listen:
+ return settings.value("fListen");
+ case Server:
+ return settings.value("server");
+ default:
+ return QVariant();
}
- return QVariant();
}
-// write QSettings values
-bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+bool OptionsModel::setOption(OptionID option, const QVariant& value)
{
bool successful = true; /* set to false on parse error */
- if(role == Qt::EditRole)
- {
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
- break;
- case ShowTrayIcon:
- m_show_tray_icon = value.toBool();
- settings.setValue("fHideTrayIcon", !m_show_tray_icon);
- Q_EMIT showTrayIconChanged(m_show_tray_icon);
- break;
- case MinimizeToTray:
- fMinimizeToTray = value.toBool();
- settings.setValue("fMinimizeToTray", fMinimizeToTray);
- break;
- case MapPortUPnP: // core option - can be changed on-the-fly
- settings.setValue("fUseUPnP", value.toBool());
- break;
- case MapPortNatpmp: // core option - can be changed on-the-fly
- settings.setValue("fUseNatpmp", value.toBool());
- break;
- case MinimizeOnClose:
- fMinimizeOnClose = value.toBool();
- settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
- break;
-
- // default proxy
- case ProxyUse:
- if (settings.value("fUseProxy") != value) {
- settings.setValue("fUseProxy", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIP: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ QSettings settings;
+
+ switch (option) {
+ case StartAtStartup:
+ successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
break;
- case ProxyPort: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ case ShowTrayIcon:
+ m_show_tray_icon = value.toBool();
+ settings.setValue("fHideTrayIcon", !m_show_tray_icon);
+ Q_EMIT showTrayIconChanged(m_show_tray_icon);
+ break;
+ case MinimizeToTray:
+ fMinimizeToTray = value.toBool();
+ settings.setValue("fMinimizeToTray", fMinimizeToTray);
+ break;
+ case MapPortUPnP: // core option - can be changed on-the-fly
+ settings.setValue("fUseUPnP", value.toBool());
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ settings.setValue("fUseNatpmp", value.toBool());
+ break;
+ case MinimizeOnClose:
+ fMinimizeOnClose = value.toBool();
+ settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
break;
- // separate Tor proxy
- case ProxyUseTor:
- if (settings.value("fUseSeparateProxyTor") != value) {
- settings.setValue("fUseSeparateProxyTor", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIPTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ // default proxy
+ case ProxyUse:
+ if (settings.value("fUseProxy") != value) {
+ settings.setValue("fUseProxy", value.toBool());
+ setRestartRequired(true);
}
break;
- case ProxyPortTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ case ProxyIP: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPort: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ if (settings.value("fUseSeparateProxyTor") != value) {
+ settings.setValue("fUseSeparateProxyTor", value.toBool());
+ setRestartRequired(true);
}
break;
+ case ProxyIPTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPortTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- if (settings.value("bSpendZeroConfChange") != value) {
- settings.setValue("bSpendZeroConfChange", value);
- setRestartRequired(true);
- }
- break;
- case ExternalSignerPath:
- if (settings.value("external_signer_path") != value.toString()) {
- settings.setValue("external_signer_path", value.toString());
- setRestartRequired(true);
- }
- break;
- case SubFeeFromAmount:
- m_sub_fee_from_amount = value.toBool();
- settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
- break;
+ case SpendZeroConfChange:
+ if (settings.value("bSpendZeroConfChange") != value) {
+ settings.setValue("bSpendZeroConfChange", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ExternalSignerPath:
+ if (settings.value("external_signer_path") != value.toString()) {
+ settings.setValue("external_signer_path", value.toString());
+ setRestartRequired(true);
+ }
+ break;
+ case SubFeeFromAmount:
+ m_sub_fee_from_amount = value.toBool();
+ settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
+ break;
#endif
- case DisplayUnit:
- setDisplayUnit(value);
- break;
- case ThirdPartyTxUrls:
- if (strThirdPartyTxUrls != value.toString()) {
- strThirdPartyTxUrls = value.toString();
- settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
- setRestartRequired(true);
- }
- break;
- case Language:
- if (settings.value("language") != value) {
- settings.setValue("language", value);
- setRestartRequired(true);
- }
- break;
- case UseEmbeddedMonospacedFont:
- m_use_embedded_monospaced_font = value.toBool();
- settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
- Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
- break;
- case CoinControlFeatures:
- fCoinControlFeatures = value.toBool();
- settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
- Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
- break;
- case EnablePSBTControls:
- m_enable_psbt_controls = value.toBool();
- settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
- break;
- case Prune:
- if (settings.value("bPrune") != value) {
- settings.setValue("bPrune", value);
- setRestartRequired(true);
- }
- break;
- case PruneSize:
- if (settings.value("nPruneSize") != value) {
- settings.setValue("nPruneSize", value);
- setRestartRequired(true);
- }
- break;
- case DatabaseCache:
- if (settings.value("nDatabaseCache") != value) {
- settings.setValue("nDatabaseCache", value);
- setRestartRequired(true);
- }
- break;
- case ThreadsScriptVerif:
- if (settings.value("nThreadsScriptVerif") != value) {
- settings.setValue("nThreadsScriptVerif", value);
- setRestartRequired(true);
- }
- break;
- case Listen:
- if (settings.value("fListen") != value) {
- settings.setValue("fListen", value);
- setRestartRequired(true);
- }
- break;
- case Server:
- if (settings.value("server") != value) {
- settings.setValue("server", value);
- setRestartRequired(true);
- }
- break;
- default:
- break;
+ case DisplayUnit:
+ setDisplayUnit(value);
+ break;
+ case ThirdPartyTxUrls:
+ if (strThirdPartyTxUrls != value.toString()) {
+ strThirdPartyTxUrls = value.toString();
+ settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
+ setRestartRequired(true);
}
+ break;
+ case Language:
+ if (settings.value("language") != value) {
+ settings.setValue("language", value);
+ setRestartRequired(true);
+ }
+ break;
+ case UseEmbeddedMonospacedFont:
+ m_use_embedded_monospaced_font = value.toBool();
+ settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+ break;
+ case CoinControlFeatures:
+ fCoinControlFeatures = value.toBool();
+ settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
+ Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
+ break;
+ case EnablePSBTControls:
+ m_enable_psbt_controls = value.toBool();
+ settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
+ break;
+ case Prune:
+ if (settings.value("bPrune") != value) {
+ settings.setValue("bPrune", value);
+ setRestartRequired(true);
+ }
+ break;
+ case PruneSize:
+ if (settings.value("nPruneSize") != value) {
+ settings.setValue("nPruneSize", value);
+ setRestartRequired(true);
+ }
+ break;
+ case DatabaseCache:
+ if (settings.value("nDatabaseCache") != value) {
+ settings.setValue("nDatabaseCache", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ThreadsScriptVerif:
+ if (settings.value("nThreadsScriptVerif") != value) {
+ settings.setValue("nThreadsScriptVerif", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Listen:
+ if (settings.value("fListen") != value) {
+ settings.setValue("fListen", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Server:
+ if (settings.value("server") != value) {
+ settings.setValue("server", value);
+ setRestartRequired(true);
+ }
+ break;
+ default:
+ break;
}
- Q_EMIT dataChanged(index, index);
-
return successful;
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 510ebb5cfd..92f80ecf21 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -41,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -80,6 +80,8 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
+ QVariant getOption(OptionID option) const;
+ bool setOption(OptionID option, const QVariant& value);
/** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */
void setDisplayUnit(const QVariant& new_unit);
@@ -103,11 +105,10 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { assert(m_node); return *m_node; }
- void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
+ interfaces::Node& node() const { return m_node; }
private:
- interfaces::Node* m_node = nullptr;
+ interfaces::Node& m_node;
/* Qt-only settings */
bool m_show_tray_icon;
bool fMinimizeToTray;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 7d18bfb229..b791fd30c4 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -27,14 +27,6 @@
#include <univalue.h>
-#ifdef ENABLE_WALLET
-#ifdef USE_BDB
-#include <wallet/bdb.h>
-#endif
-#include <wallet/db.h>
-#include <wallet/wallet.h>
-#endif
-
#include <QAbstractButton>
#include <QAbstractItemModel>
#include <QDateTime>
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index fd8eccb86d..d8daccecbd 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -757,10 +757,6 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
case WalletModel::AbsurdFee:
msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
break;
- case WalletModel::PaymentRequestExpired:
- msgParams.first = tr("Payment request expired.");
- msgParams.second = CClientUIInterface::MSG_ERROR;
- break;
// included to prevent a compiler warning.
case WalletModel::OK:
default:
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index a60e0647e3..3b7a40438b 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <QApplication>
+#include <QLineEdit>
#include <QMessageBox>
#include <QTableView>
#include <QTimer>
@@ -102,11 +103,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
QString s_label("already here (s)");
// Define a new address (which should add to the address book successfully).
- QString new_address;
+ QString new_address_a;
+ QString new_address_b;
std::tie(r_key_dest, preexisting_r_address) = build_address();
std::tie(s_key_dest, preexisting_s_address) = build_address();
- std::tie(std::ignore, new_address) = build_address();
+ std::tie(std::ignore, new_address_a) = build_address();
+ std::tie(std::ignore, new_address_b) = build_address();
{
LOCK(wallet->cs_wallet);
@@ -124,7 +127,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
@@ -159,9 +162,52 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Submit a new address which should add successfully - we expect the
// warning message to be blank.
EditAddressAndSubmit(
- &editAddressDialog, QString("new"), new_address, QString(""));
+ &editAddressDialog, QString("io - new A"), new_address_a, QString(""));
check_addbook_size(3);
QCOMPARE(table_view->model()->rowCount(), 2);
+
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("io - new B"), new_address_b, QString(""));
+ check_addbook_size(4);
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit");
+
+ search_line->setText(r_label);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(s_label);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("io");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wildcard "?".
+ search_line->setText("io?new");
+ QCOMPARE(table_view->model()->rowCount(), 0);
+ search_line->setText("io???new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wildcard "*".
+ search_line->setText("io*new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+ search_line->setText("*");
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ search_line->setText(preexisting_r_address);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(preexisting_s_address);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_a);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_b);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("");
+ QCOMPARE(table_view->model()->rowCount(), 3);
}
} // namespace
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index b191082ab0..9648ef6188 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -119,6 +119,6 @@ AppTests::HandleCallback::~HandleCallback()
assert(it != callbacks.end());
callbacks.erase(it);
if (callbacks.empty()) {
- m_app_tests.m_app.quit();
+ m_app_tests.m_app.exit(0);
}
}
diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp
index 4a943a6343..2ef9230c59 100644
--- a/src/qt/test/optiontests.cpp
+++ b/src/qt/test/optiontests.cpp
@@ -13,8 +13,7 @@
#include <univalue.h>
-//! Entry point for BitcoinApplication tests.
-void OptionTests::optionTests()
+void OptionTests::integerGetArgBug()
{
// Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure
// that setting integer prune value doesn't cause an exception to be thrown
@@ -24,7 +23,7 @@ void OptionTests::optionTests()
settings.rw_settings["prune"] = 3814;
});
gArgs.WriteSettingsFile();
- OptionsModel{};
+ OptionsModel{m_node};
gArgs.LockSettings([&](util::Settings& settings) {
settings.rw_settings.erase("prune");
});
@@ -49,7 +48,7 @@ void OptionTests::parametersInteraction()
QSettings settings;
settings.setValue("fListen", false);
- OptionsModel{};
+ OptionsModel{m_node};
const bool expected{false};
diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h
index 39c1612c8f..257a0b65be 100644
--- a/src/qt/test/optiontests.h
+++ b/src/qt/test/optiontests.h
@@ -16,7 +16,7 @@ public:
explicit OptionTests(interfaces::Node& node) : m_node(node) {}
private Q_SLOTS:
- void optionTests();
+ void integerGetArgBug();
void parametersInteraction();
private:
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 70b7f83872..bc06f0f23b 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -184,7 +184,7 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 9e92f89543..a61d5407b3 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -36,13 +36,41 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status
{
int depth = status.depth_in_main_chain;
if (depth < 0) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that conflicts with a confirmed
+ transaction. */
return tr("conflicted with a transaction with %1 confirmations").arg(-depth);
} else if (depth == 0) {
- const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()};
- return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned;
+ QString s;
+ if (inMempool) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that is in the memory pool. */
+ s = tr("0/unconfirmed, in memory pool");
+ } else {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that is not in the memory pool. */
+ s = tr("0/unconfirmed, not in memory pool");
+ }
+ if (status.is_abandoned) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This
+ status represents an abandoned transaction. */
+ s += QLatin1String(", ") + tr("abandoned");
+ }
+ return s;
} else if (depth < 6) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This
+ status represents a transaction confirmed in at least one block,
+ but less than 6 blocks. */
return tr("%1/unconfirmed").arg(depth);
} else {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents a transaction confirmed in 6 or more blocks. */
return tr("%1 confirmations").arg(depth);
}
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 540fdaafe3..d524d48111 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -66,8 +66,7 @@ public:
AmountWithFeeExceedsBalance,
DuplicateAddress,
TransactionCreationFailed, // Error returned when wallet is still locked
- AbsurdFee,
- PaymentRequestExpired
+ AbsurdFee
};
enum EncryptionStatus
diff --git a/src/rest.cpp b/src/rest.cpp
index 22b5d2e074..1b90baaf95 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -305,7 +305,7 @@ static bool rest_block(const std::any& context,
if (chainman.m_blockman.IsBlockPruned(pblockindex))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
- if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
+ if (!ReadBlockFromDisk(block, pblockindex, chainman.GetParams().GetConsensus()))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 3292f6cc3a..bf4bfbb95f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -19,11 +19,11 @@
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
+#include <kernel/coinstats.h>
#include <logging/timer.h>
#include <net.h>
#include <net_processing.h>
#include <node/blockstorage.h>
-#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <primitives/transaction.h>
@@ -51,10 +51,10 @@
#include <memory>
#include <mutex>
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
+
using node::BlockManager;
-using node::CCoinsStats;
-using node::CoinStatsHashType;
-using node::GetUTXOStats;
using node::NodeContext;
using node::ReadBlockFromDisk;
using node::SnapshotMetadata;
@@ -777,11 +777,11 @@ static RPCHelpMan pruneblockchain()
unsigned int height = (unsigned int) heightParam;
unsigned int chainHeight = (unsigned int) active_chain.Height();
- if (chainHeight < Params().PruneAfterHeight())
+ if (chainHeight < chainman.GetParams().PruneAfterHeight()) {
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
- else if (height > chainHeight)
+ } else if (height > chainHeight) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
- else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
+ } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
@@ -790,7 +790,7 @@ static RPCHelpMan pruneblockchain()
const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)};
- return static_cast<uint64_t>(last_block->nHeight);
+ return static_cast<int64_t>(last_block->nHeight - 1);
},
};
}
@@ -808,6 +808,36 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input)
}
}
+/**
+ * Calculate statistics about the unspent transaction output set
+ *
+ * @param[in] index_requested Signals if the coinstatsindex should be used (when available).
+ */
+static std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
+ kernel::CoinStatsHashType hash_type,
+ const std::function<void()>& interruption_point = {},
+ const CBlockIndex* pindex = nullptr,
+ bool index_requested = true)
+{
+ // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
+ if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
+ if (pindex) {
+ return g_coin_stats_index->LookUpStats(pindex);
+ } else {
+ CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
+ return g_coin_stats_index->LookUpStats(block_index);
+ }
+ }
+
+ // If the coinstats index isn't requested or is otherwise not usable, the
+ // pindex should either be null or equal to the view's best block. This is
+ // because without the coinstats index we can only get coinstats about the
+ // best block.
+ CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
+
+ return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
+}
+
static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
@@ -862,8 +892,7 @@ static RPCHelpMan gettxoutsetinfo()
const CBlockIndex* pindex{nullptr};
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
- CCoinsStats stats{hash_type};
- stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
+ bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
@@ -884,14 +913,14 @@ static RPCHelpMan gettxoutsetinfo()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
}
- if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) {
+ if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
}
pindex = ParseHashOrHeight(request.params[1], chainman);
}
- if (stats.index_requested && g_coin_stats_index) {
+ if (index_requested && g_coin_stats_index) {
if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
const IndexSummary summary{g_coin_stats_index->GetSummary()};
@@ -903,7 +932,9 @@ static RPCHelpMan gettxoutsetinfo()
}
}
- if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
+ const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
+ if (maybe_stats.has_value()) {
+ const CCoinsStats& stats = maybe_stats.value();
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
@@ -922,10 +953,13 @@ static RPCHelpMan gettxoutsetinfo()
} else {
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
- CCoinsStats prev_stats{hash_type};
-
+ CCoinsStats prev_stats{};
if (pindex->nHeight > 0) {
- GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev);
+ const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
+ if (!maybe_prev_stats) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
+ }
+ prev_stats = maybe_prev_stats.value();
}
UniValue block_info(UniValue::VOBJ);
@@ -1058,7 +1092,7 @@ static RPCHelpMan verifychain()
CChainState& active_chainstate = chainman.ActiveChainstate();
return CVerifyDB().VerifyDB(
- active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
+ active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
},
};
}
@@ -1189,14 +1223,14 @@ RPCHelpMan getblockchaininfo()
const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
const int height{tip.nHeight};
UniValue obj(UniValue::VOBJ);
- obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("chain", chainman.GetParams().NetworkIDString());
obj.pushKV("blocks", height);
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
obj.pushKV("difficulty", GetDifficulty(&tip));
obj.pushKV("time", tip.GetBlockTime());
obj.pushKV("mediantime", tip.GetMedianTimePast());
- obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip));
+ obj.pushKV("verificationprogress", GuessVerificationProgress(chainman.GetParams().TxData(), &tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip.nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
@@ -1563,7 +1597,7 @@ static RPCHelpMan getchaintxstats()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
const CBlockIndex* pindex;
- int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
+ int blockcount = 30 * 24 * 60 * 60 / chainman.GetParams().GetConsensus().nPowTargetSpacing; // By default: 1 month
if (request.params[1].isNull()) {
LOCK(cs_main);
@@ -1879,7 +1913,7 @@ static RPCHelpMan getblockstats()
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
ret_all.pushKV("outs", outputs);
- ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, Params().GetConsensus()));
+ ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus()));
ret_all.pushKV("swtotal_size", swtotal_size);
ret_all.pushKV("swtotal_weight", swtotal_weight);
ret_all.pushKV("swtxs", swtxs);
@@ -2051,7 +2085,7 @@ static RPCHelpMan scantxoutset()
// no scan in progress
return NullUniValue;
}
- result.pushKV("progress", g_scan_progress);
+ result.pushKV("progress", g_scan_progress.load());
return result;
} else if (request.params[0].get_str() == "abort") {
CoinsViewScanReserver reserver;
@@ -2285,7 +2319,7 @@ UniValue CreateUTXOSnapshot(
const fs::path& temppath)
{
std::unique_ptr<CCoinsViewCursor> pcursor;
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
+ std::optional<CCoinsStats> maybe_stats;
const CBlockIndex* tip;
{
@@ -2305,19 +2339,20 @@ UniValue CreateUTXOSnapshot(
chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) {
+ maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
+ if (!maybe_stats) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
pcursor = chainstate.CoinsDB().Cursor();
- tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock));
+ tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
}
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
tip->nHeight, tip->GetBlockHash().ToString(),
fs::PathToString(path), fs::PathToString(temppath)));
- SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx};
+ SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
afile << metadata;
@@ -2339,11 +2374,11 @@ UniValue CreateUTXOSnapshot(
afile.fclose();
UniValue result(UniValue::VOBJ);
- result.pushKV("coins_written", stats.coins_count);
+ result.pushKV("coins_written", maybe_stats->coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.u8string());
- result.pushKV("txoutset_hash", stats.hashSerialized.ToString());
+ result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
// Cast required because univalue doesn't have serialization specified for
// `unsigned int`, nChainTx's type.
result.pushKV("nchaintx", uint64_t{tip->nChainTx});
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 23e9d4074c..ae0d0112ba 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -173,6 +173,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setwalletflag", 1, "value" },
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
+ { "gettxspendingprevout", 0, "outputs" },
{ "bumpfee", 1, "options" },
{ "psbtbumpfee", 1, "options" },
{ "logging", 0, "include" },
diff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp
index bfec0780aa..1873bc1587 100644
--- a/src/rpc/fees.cpp
+++ b/src/rpc/fees.cpp
@@ -7,6 +7,7 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/server.h>
@@ -16,7 +17,6 @@
#include <univalue.h>
#include <util/fees.h>
#include <util/system.h>
-#include <validation.h>
#include <algorithm>
#include <array>
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 27080d3881..97ec95a166 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -8,6 +8,7 @@
#include <core_io.h>
#include <fs.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
#include <rpc/server_util.h>
@@ -15,7 +16,6 @@
#include <txmempool.h>
#include <univalue.h>
#include <util/moneystr.h>
-#include <validation.h>
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
using node::NodeContext;
@@ -229,23 +229,12 @@ static std::vector<RPCResult> MempoolEntryDescription()
return {
RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
- RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
- "transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
- "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT +
- " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true,
- "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " +
- CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true,
- "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " +
- CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
RPCResult{RPCResult::Type::OBJ, "fees", "",
{
@@ -269,24 +258,12 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("vsize", (int)e.GetTxSize());
info.pushKV("weight", (int)e.GetTxWeight());
- // TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24
- const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")};
- if (deprecated_fee_fields_enabled) {
- info.pushKV("fee", ValueFromAmount(e.GetFee()));
- info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
- }
info.pushKV("time", count_seconds(e.GetTime()));
info.pushKV("height", (int)e.GetHeight());
info.pushKV("descendantcount", e.GetCountWithDescendants());
info.pushKV("descendantsize", e.GetSizeWithDescendants());
- if (deprecated_fee_fields_enabled) {
- info.pushKV("descendantfees", e.GetModFeesWithDescendants());
- }
info.pushKV("ancestorcount", e.GetCountWithAncestors());
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
- if (deprecated_fee_fields_enabled) {
- info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
- }
info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
UniValue fees(UniValue::VOBJ);
@@ -587,6 +564,89 @@ static RPCHelpMan getmempoolentry()
};
}
+static RPCHelpMan gettxspendingprevout()
+{
+ return RPCHelpMan{"gettxspendingprevout",
+ "Scans the mempool to find transactions spending any of the given outputs",
+ {
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
+ {
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ },
+ },
+ },
+ },
+ },
+ RPCResult{
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
+ {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
+ {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
+ }},
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheckArgument(request.params[0], UniValue::VARR);
+ const UniValue& output_params = request.params[0];
+ if (output_params.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
+ }
+
+ std::vector<COutPoint> prevouts;
+ prevouts.reserve(output_params.size());
+
+ for (unsigned int idx = 0; idx < output_params.size(); idx++) {
+ const UniValue& o = output_params[idx].get_obj();
+
+ RPCTypeCheckObj(o,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ }, /*fAllowNull=*/false, /*fStrict=*/true);
+
+ const uint256 txid(ParseHashO(o, "txid"));
+ const int nOutput{find_value(o, "vout").getInt<int>()};
+ if (nOutput < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
+ }
+
+ prevouts.emplace_back(txid, nOutput);
+ }
+
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
+ LOCK(mempool.cs);
+
+ UniValue result{UniValue::VARR};
+
+ for (const COutPoint& prevout : prevouts) {
+ UniValue o(UniValue::VOBJ);
+ o.pushKV("txid", prevout.hash.ToString());
+ o.pushKV("vout", (uint64_t)prevout.n);
+
+ const CTransaction* spendingTx = mempool.GetConflictTx(prevout);
+ if (spendingTx != nullptr) {
+ o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
+ }
+
+ result.push_back(o);
+ }
+
+ return result;
+ },
+ };
+}
+
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
// Make sure this call is atomic in the pool.
@@ -677,6 +737,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t)
{"blockchain", &getmempoolancestors},
{"blockchain", &getmempooldescendants},
{"blockchain", &getmempoolentry},
+ {"blockchain", &gettxspendingprevout},
{"blockchain", &getmempoolinfo},
{"blockchain", &getrawmempool},
{"blockchain", &savemempool},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 16b4b6728c..8fb6daf0cb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -27,6 +27,7 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <shutdown.h>
+#include <timedata.h>
#include <txmempool.h>
#include <univalue.h>
#include <util/strencodings.h>
@@ -119,9 +120,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
block_hash.SetNull();
block.hashMerkleRoot = BlockMerkleRoot(block);
- CChainParams chainparams(Params());
-
- while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()) && !ShutdownRequested()) {
+ while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainman.GetConsensus()) && !ShutdownRequested()) {
++block.nNonce;
--max_tries;
}
@@ -145,7 +144,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{
UniValue blockHashes(UniValue::VARR);
while (nGenerate > 0 && !ShutdownRequested()) {
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler{chainman.ActiveChainstate(), mempool}.CreateNewBlock(coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -349,7 +348,6 @@ static RPCHelpMan generateblock()
}
}
- CChainParams chainparams(Params());
CBlock block;
ChainstateManager& chainman = EnsureChainman(node);
@@ -357,7 +355,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(chainman.ActiveChainstate(), empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler{chainman.ActiveChainstate(), empty_mempool}.CreateNewBlock(coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -374,7 +372,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
+ if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), GetAdjustedTime, false, false)) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
}
}
@@ -429,7 +427,7 @@ static RPCHelpMan getmininginfo()
obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip()));
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
- obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("chain", chainman.GetParams().NetworkIDString());
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
},
@@ -643,7 +641,7 @@ static RPCHelpMan getblocktemplate()
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
BlockValidationState state;
- TestBlockValidity(state, Params(), active_chainstate, block, pindexPrev, false, true);
+ TestBlockValidity(state, chainman.GetParams(), active_chainstate, block, pindexPrev, GetAdjustedTime, false, true);
return BIP22ValidationResult(state);
}
@@ -665,7 +663,7 @@ static RPCHelpMan getblocktemplate()
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
- if (!Params().IsTestChain()) {
+ if (!chainman.GetParams().IsTestChain()) {
const CConnman& connman = EnsureConnman(node);
if (connman.GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
@@ -726,7 +724,7 @@ static RPCHelpMan getblocktemplate()
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ const Consensus::Params& consensusParams = chainman.GetParams().GetConsensus();
// GBT must be called with 'signet' set in the rules for signet chains
if (consensusParams.signet_blocks && setClientRules.count("signet") != 1) {
@@ -755,7 +753,7 @@ static RPCHelpMan getblocktemplate()
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler{active_chainstate, mempool}.CreateNewBlock(scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index 115a656e12..f4bb76f50f 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -163,11 +163,11 @@ static RPCHelpMan createmultisig()
result.pushKV("descriptor", descriptor->ToString());
UniValue warnings(UniValue::VARR);
- if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ if (descriptor->GetOutputType() != output_type) {
// Only warns if the user has explicitly chosen an address type we cannot generate
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
- if (warnings.size()) result.pushKV("warnings", warnings);
+ if (!warnings.empty()) result.pushKV("warnings", warnings);
return result;
},
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 84b8e8dbe0..b9b8c36bb3 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -217,7 +217,7 @@ static RPCHelpMan getrawtransaction()
uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CBlockIndex* blockindex = nullptr;
- if (hash == Params().GenesisBlock().hashMerkleRoot) {
+ if (hash == chainman.GetParams().GenesisBlock().hashMerkleRoot) {
// Special exception for the genesis block coinbase transaction
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved");
}
@@ -245,7 +245,7 @@ static RPCHelpMan getrawtransaction()
}
uint256 hash_block;
- const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block);
+ const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, chainman.GetConsensus(), hash_block);
if (!tx) {
std::string errmsg;
if (blockindex) {
diff --git a/src/rpc/txoutproof.cpp b/src/rpc/txoutproof.cpp
index d16820baeb..dcf6c6bee1 100644
--- a/src/rpc/txoutproof.cpp
+++ b/src/rpc/txoutproof.cpp
@@ -87,7 +87,7 @@ static RPCHelpMan gettxoutproof()
LOCK(cs_main);
if (pblockindex == nullptr) {
- const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), Params().GetConsensus(), hashBlock);
+ const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), chainman.GetConsensus(), hashBlock);
if (!tx || hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
}
@@ -98,7 +98,7 @@ static RPCHelpMan gettxoutproof()
}
CBlock block;
- if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pblockindex, chainman.GetConsensus())) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 2d5d4a4cb9..7517f64ea1 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -583,7 +583,7 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
}
const UniValue ret = m_fun(*this, request);
if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
- CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); }));
+ CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [&ret](const RPCResult& res) { return res.MatchesType(ret); }));
}
return ret;
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index abbc4c66fe..e883dc008e 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
-#include <node/coinstats.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <protocol.h>
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index c4d13d7283..9f56301377 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1832,6 +1832,10 @@ uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script)
uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash)
{
+ assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
+ assert(control.size() <= TAPROOT_CONTROL_MAX_SIZE);
+ assert((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE == 0);
+
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
uint256 k = tapleaf_hash;
for (int i = 0; i < path_len; ++i) {
diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp
index 019f02f159..cb4d4cb783 100644
--- a/src/script/miniscript.cpp
+++ b/src/script/miniscript.cpp
@@ -17,69 +17,67 @@ Type SanitizeType(Type e) {
int num_types = (e << "K"_mst) + (e << "V"_mst) + (e << "B"_mst) + (e << "W"_mst);
if (num_types == 0) return ""_mst; // No valid type, don't care about the rest
assert(num_types == 1); // K, V, B, W all conflict with each other
- bool ok = // Work around a GCC 4.8 bug that breaks user-defined literals in macro calls.
- (!(e << "z"_mst) || !(e << "o"_mst)) && // z conflicts with o
- (!(e << "n"_mst) || !(e << "z"_mst)) && // n conflicts with z
- (!(e << "n"_mst) || !(e << "W"_mst)) && // n conflicts with W
- (!(e << "V"_mst) || !(e << "d"_mst)) && // V conflicts with d
- (!(e << "K"_mst) || (e << "u"_mst)) && // K implies u
- (!(e << "V"_mst) || !(e << "u"_mst)) && // V conflicts with u
- (!(e << "e"_mst) || !(e << "f"_mst)) && // e conflicts with f
- (!(e << "e"_mst) || (e << "d"_mst)) && // e implies d
- (!(e << "V"_mst) || !(e << "e"_mst)) && // V conflicts with e
- (!(e << "d"_mst) || !(e << "f"_mst)) && // d conflicts with f
- (!(e << "V"_mst) || (e << "f"_mst)) && // V implies f
- (!(e << "K"_mst) || (e << "s"_mst)) && // K implies s
- (!(e << "z"_mst) || (e << "m"_mst)); // z implies m
- assert(ok);
+ assert(!(e << "z"_mst) || !(e << "o"_mst)); // z conflicts with o
+ assert(!(e << "n"_mst) || !(e << "z"_mst)); // n conflicts with z
+ assert(!(e << "n"_mst) || !(e << "W"_mst)); // n conflicts with W
+ assert(!(e << "V"_mst) || !(e << "d"_mst)); // V conflicts with d
+ assert(!(e << "K"_mst) || (e << "u"_mst)); // K implies u
+ assert(!(e << "V"_mst) || !(e << "u"_mst)); // V conflicts with u
+ assert(!(e << "e"_mst) || !(e << "f"_mst)); // e conflicts with f
+ assert(!(e << "e"_mst) || (e << "d"_mst)); // e implies d
+ assert(!(e << "V"_mst) || !(e << "e"_mst)); // V conflicts with e
+ assert(!(e << "d"_mst) || !(e << "f"_mst)); // d conflicts with f
+ assert(!(e << "V"_mst) || (e << "f"_mst)); // V implies f
+ assert(!(e << "K"_mst) || (e << "s"_mst)); // K implies s
+ assert(!(e << "z"_mst) || (e << "m"_mst)); // z implies m
return e;
}
-Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) {
+Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) {
// Sanity check on data
- if (nodetype == Fragment::SHA256 || nodetype == Fragment::HASH256) {
+ if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) {
assert(data_size == 32);
- } else if (nodetype == Fragment::RIPEMD160 || nodetype == Fragment::HASH160) {
+ } else if (fragment == Fragment::RIPEMD160 || fragment == Fragment::HASH160) {
assert(data_size == 20);
} else {
assert(data_size == 0);
}
// Sanity check on k
- if (nodetype == Fragment::OLDER || nodetype == Fragment::AFTER) {
+ if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) {
assert(k >= 1 && k < 0x80000000UL);
- } else if (nodetype == Fragment::MULTI) {
+ } else if (fragment == Fragment::MULTI) {
assert(k >= 1 && k <= n_keys);
- } else if (nodetype == Fragment::THRESH) {
+ } else if (fragment == Fragment::THRESH) {
assert(k >= 1 && k <= n_subs);
} else {
assert(k == 0);
}
// Sanity check on subs
- if (nodetype == Fragment::AND_V || nodetype == Fragment::AND_B || nodetype == Fragment::OR_B ||
- nodetype == Fragment::OR_C || nodetype == Fragment::OR_I || nodetype == Fragment::OR_D) {
+ if (fragment == Fragment::AND_V || fragment == Fragment::AND_B || fragment == Fragment::OR_B ||
+ fragment == Fragment::OR_C || fragment == Fragment::OR_I || fragment == Fragment::OR_D) {
assert(n_subs == 2);
- } else if (nodetype == Fragment::ANDOR) {
+ } else if (fragment == Fragment::ANDOR) {
assert(n_subs == 3);
- } else if (nodetype == Fragment::WRAP_A || nodetype == Fragment::WRAP_S || nodetype == Fragment::WRAP_C ||
- nodetype == Fragment::WRAP_D || nodetype == Fragment::WRAP_V || nodetype == Fragment::WRAP_J ||
- nodetype == Fragment::WRAP_N) {
+ } else if (fragment == Fragment::WRAP_A || fragment == Fragment::WRAP_S || fragment == Fragment::WRAP_C ||
+ fragment == Fragment::WRAP_D || fragment == Fragment::WRAP_V || fragment == Fragment::WRAP_J ||
+ fragment == Fragment::WRAP_N) {
assert(n_subs == 1);
- } else if (nodetype != Fragment::THRESH) {
+ } else if (fragment != Fragment::THRESH) {
assert(n_subs == 0);
}
// Sanity check on keys
- if (nodetype == Fragment::PK_K || nodetype == Fragment::PK_H) {
+ if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) {
assert(n_keys == 1);
- } else if (nodetype == Fragment::MULTI) {
+ } else if (fragment == Fragment::MULTI) {
assert(n_keys >= 1 && n_keys <= 20);
} else {
assert(n_keys == 0);
}
- // Below is the per-nodetype logic for computing the expression types.
+ // Below is the per-fragment logic for computing the expression types.
// It heavily relies on Type's << operator (where "X << a_mst" means
// "X has all properties listed in a").
- switch (nodetype) {
+ switch (fragment) {
case Fragment::PK_K: return "Konudemsxk"_mst;
case Fragment::PK_H: return "Knudemsxk"_mst;
case Fragment::OLDER: return
@@ -247,11 +245,10 @@ Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Ty
}
}
assert(false);
- return ""_mst;
}
-size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) {
- switch (nodetype) {
+size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) {
+ switch (fragment) {
case Fragment::JUST_1:
case Fragment::JUST_0: return 1;
case Fragment::PK_K: return 34;
@@ -262,7 +259,7 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
case Fragment::SHA256: return 4 + 2 + 33;
case Fragment::HASH160:
case Fragment::RIPEMD160: return 4 + 2 + 21;
- case Fragment::MULTI: return 3 + (n_keys > 16) + (k > 16) + 34 * n_keys;
+ case Fragment::MULTI: return 1 + BuildScript(n_keys).size() + BuildScript(k).size() + 34 * n_keys;
case Fragment::AND_V: return subsize;
case Fragment::WRAP_V: return subsize + (sub0typ << "x"_mst);
case Fragment::WRAP_S:
@@ -280,19 +277,17 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
case Fragment::THRESH: return subsize + n_subs + BuildScript(k).size();
}
assert(false);
- return 0;
}
-bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out)
+std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script)
{
- out.clear();
+ std::vector<Opcode> out;
CScript::const_iterator it = script.begin(), itend = script.end();
while (it != itend) {
std::vector<unsigned char> push_data;
opcodetype opcode;
if (!script.GetOp(it, opcode, push_data)) {
- out.clear();
- return false;
+ return {};
} else if (opcode >= OP_1 && opcode <= OP_16) {
// Deal with OP_n (GetOp does not turn them into pushes).
push_data.assign(1, CScript::DecodeOP_N(opcode));
@@ -309,30 +304,28 @@ bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, st
out.emplace_back(OP_EQUAL, std::vector<unsigned char>());
opcode = OP_VERIFY;
} else if (IsPushdataOp(opcode)) {
- if (!CheckMinimalPush(push_data, opcode)) return false;
+ if (!CheckMinimalPush(push_data, opcode)) return {};
} else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) {
// Rule out non minimal VERIFY sequences
- return false;
+ return {};
}
out.emplace_back(opcode, std::move(push_data));
}
std::reverse(out.begin(), out.end());
- return true;
+ return out;
}
-bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k) {
+std::optional<int64_t> ParseScriptNumber(const Opcode& in) {
if (in.first == OP_0) {
- k = 0;
- return true;
+ return 0;
}
if (!in.second.empty()) {
- if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return false;
+ if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return {};
try {
- k = CScriptNum(in.second, true).GetInt64();
- return true;
+ return CScriptNum(in.second, true).GetInt64();
} catch(const scriptnum_error&) {}
}
- return false;
+ return {};
}
int FindNextChar(Span<const char> sp, const char m)
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 5c1cc316dc..2c239c2678 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -6,6 +6,7 @@
#define BITCOIN_SCRIPT_MINISCRIPT_H
#include <algorithm>
+#include <functional>
#include <numeric>
#include <memory>
#include <optional>
@@ -40,7 +41,7 @@ namespace miniscript {
* - For example: older(n) = <n> OP_CHECKSEQUENCEVERIFY.
* - "V" Verify:
* - Takes its inputs from the top of the stack.
- * - When satisfactied, pushes nothing.
+ * - When satisfied, pushes nothing.
* - Cannot be dissatisfied.
* - This can be obtained by adding an OP_VERIFY to a B, modifying the last opcode
* of a B to its -VERIFY version (only for OP_CHECKSIG, OP_CHECKSIGVERIFY
@@ -179,6 +180,8 @@ inline constexpr Type operator"" _mst(const char* c, size_t l) {
return typ;
}
+using Opcode = std::pair<opcodetype, std::vector<unsigned char>>;
+
template<typename Key> struct Node;
template<typename Key> using NodeRef = std::shared_ptr<const Node<Key>>;
@@ -224,10 +227,10 @@ enum class Fragment {
namespace internal {
//! Helper function for Node::CalcType.
-Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys);
+Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys);
//! Helper function for Node::CalcScriptLen.
-size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys);
+size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys);
//! A helper sanitizer/checker for the output of CalcType.
Type SanitizeType(Type x);
@@ -279,7 +282,7 @@ struct StackSize {
template<typename Key>
struct Node {
//! What node type this node is.
- const Fragment nodetype;
+ const Fragment fragment;
//! The k parameter (time for OLDER/AFTER, threshold for THRESH(_M))
const uint32_t k = 0;
//! The keys used by this expression (only for PK_K/PK_H/MULTI)
@@ -298,6 +301,8 @@ private:
const Type typ;
//! Cached script length (computed by CalcScriptLen).
const size_t scriptlen;
+ //! Whether a public key appears more than once in this node.
+ const bool duplicate_key;
//! Compute the length of the script for this miniscript (including children).
size_t CalcScriptLen() const {
@@ -306,7 +311,7 @@ private:
subsize += sub->ScriptSize();
}
Type sub0type = subs.size() > 0 ? subs[0]->GetType() : ""_mst;
- return internal::ComputeScriptLen(nodetype, sub0type, subsize, k, subs.size(), keys.size());
+ return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size());
}
/* Apply a recursive algorithm to a Miniscript tree, without actual recursive calls.
@@ -329,6 +334,8 @@ private:
* computes the result of the node. If std::nullopt is returned by upfn,
* TreeEvalMaybe() immediately returns std::nullopt.
* The return value of TreeEvalMaybe is the result of the root node.
+ *
+ * Result type cannot be bool due to the std::vector<bool> specialization.
*/
template<typename Result, typename State, typename DownFn, typename UpFn>
std::optional<Result> TreeEvalMaybe(State root_state, DownFn downfn, UpFn upfn) const
@@ -393,6 +400,20 @@ private:
return std::move(results[0]);
}
+ /** Like TreeEvalMaybe, but without downfn or State type.
+ * upfn takes (const Node&, Span<Result>) and returns std::optional<Result>. */
+ template<typename Result, typename UpFn>
+ std::optional<Result> TreeEvalMaybe(UpFn upfn) const
+ {
+ struct DummyState {};
+ return TreeEvalMaybe<Result>(DummyState{},
+ [](DummyState, const Node&, size_t) { return DummyState{}; },
+ [&upfn](DummyState, const Node& node, Span<Result> subs) {
+ return upfn(node, subs);
+ }
+ );
+ }
+
/** Like TreeEvalMaybe, but always produces a result. upfn must return Result. */
template<typename Result, typename State, typename DownFn, typename UpFn>
Result TreeEval(State root_state, DownFn&& downfn, UpFn upfn) const
@@ -408,13 +429,33 @@ private:
));
}
+ /** Compare two miniscript subtrees, using a non-recursive algorithm. */
+ friend int Compare(const Node<Key>& node1, const Node<Key>& node2)
+ {
+ std::vector<std::pair<const Node<Key>&, const Node<Key>&>> queue;
+ queue.emplace_back(node1, node2);
+ while (!queue.empty()) {
+ const auto& [a, b] = queue.back();
+ queue.pop_back();
+ if (std::tie(a.fragment, a.k, a.keys, a.data) < std::tie(b.fragment, b.k, b.keys, b.data)) return -1;
+ if (std::tie(b.fragment, b.k, b.keys, b.data) < std::tie(a.fragment, a.k, a.keys, a.data)) return 1;
+ if (a.subs.size() < b.subs.size()) return -1;
+ if (b.subs.size() < a.subs.size()) return 1;
+ size_t n = a.subs.size();
+ for (size_t i = 0; i < n; ++i) {
+ queue.emplace_back(*a.subs[n - 1 - i], *b.subs[n - 1 - i]);
+ }
+ }
+ return 0;
+ }
+
//! Compute the type for this miniscript.
Type CalcType() const {
using namespace internal;
// THRESH has a variable number of subexpressions
std::vector<Type> sub_types;
- if (nodetype == Fragment::THRESH) {
+ if (fragment == Fragment::THRESH) {
for (const auto& sub : subs) sub_types.push_back(sub->GetType());
}
// All other nodes than THRESH can be computed just from the types of the 0-3 subexpressions.
@@ -422,7 +463,7 @@ private:
Type y = subs.size() > 1 ? subs[1]->GetType() : ""_mst;
Type z = subs.size() > 2 ? subs[2]->GetType() : ""_mst;
- return SanitizeType(ComputeType(nodetype, x, y, z, sub_types, k, data.size(), subs.size(), keys.size()));
+ return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size()));
}
public:
@@ -434,17 +475,17 @@ public:
// by an OP_VERIFY (which may need to be combined with the last script opcode).
auto downfn = [](bool verify, const Node& node, size_t index) {
// For WRAP_V, the subexpression is certainly followed by OP_VERIFY.
- if (node.nodetype == Fragment::WRAP_V) return true;
+ if (node.fragment == Fragment::WRAP_V) return true;
// The subexpression of WRAP_S, and the last subexpression of AND_V
// inherit the followed-by-OP_VERIFY property from the parent.
- if (node.nodetype == Fragment::WRAP_S ||
- (node.nodetype == Fragment::AND_V && index == 1)) return verify;
+ if (node.fragment == Fragment::WRAP_S ||
+ (node.fragment == Fragment::AND_V && index == 1)) return verify;
return false;
};
// The upward function computes for a node, given its followed-by-OP_VERIFY status
// and the CScripts of its child nodes, the CScript of the node.
auto upfn = [&ctx](bool verify, const Node& node, Span<CScript> subs) -> CScript {
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::PK_K: return BuildScript(ctx.ToPKBytes(node.keys[0]));
case Fragment::PK_H: return BuildScript(OP_DUP, OP_HASH160, ctx.ToPKHBytes(node.keys[0]), OP_EQUALVERIFY);
case Fragment::OLDER: return BuildScript(node.k, OP_CHECKSEQUENCEVERIFY);
@@ -491,45 +532,44 @@ public:
}
}
assert(false);
- return {};
};
return TreeEval<CScript>(false, downfn, upfn);
}
template<typename CTx>
- bool ToString(const CTx& ctx, std::string& ret) const {
+ std::optional<std::string> ToString(const CTx& ctx) const {
// To construct the std::string representation for a Miniscript object, we use
// the TreeEvalMaybe algorithm. The State is a boolean: whether the parent node is a
// wrapper. If so, non-wrapper expressions must be prefixed with a ":".
auto downfn = [](bool, const Node& node, size_t) {
- return (node.nodetype == Fragment::WRAP_A || node.nodetype == Fragment::WRAP_S ||
- node.nodetype == Fragment::WRAP_D || node.nodetype == Fragment::WRAP_V ||
- node.nodetype == Fragment::WRAP_J || node.nodetype == Fragment::WRAP_N ||
- node.nodetype == Fragment::WRAP_C ||
- (node.nodetype == Fragment::AND_V && node.subs[1]->nodetype == Fragment::JUST_1) ||
- (node.nodetype == Fragment::OR_I && node.subs[0]->nodetype == Fragment::JUST_0) ||
- (node.nodetype == Fragment::OR_I && node.subs[1]->nodetype == Fragment::JUST_0));
+ return (node.fragment == Fragment::WRAP_A || node.fragment == Fragment::WRAP_S ||
+ node.fragment == Fragment::WRAP_D || node.fragment == Fragment::WRAP_V ||
+ node.fragment == Fragment::WRAP_J || node.fragment == Fragment::WRAP_N ||
+ node.fragment == Fragment::WRAP_C ||
+ (node.fragment == Fragment::AND_V && node.subs[1]->fragment == Fragment::JUST_1) ||
+ (node.fragment == Fragment::OR_I && node.subs[0]->fragment == Fragment::JUST_0) ||
+ (node.fragment == Fragment::OR_I && node.subs[1]->fragment == Fragment::JUST_0));
};
// The upward function computes for a node, given whether its parent is a wrapper,
// and the string representations of its child nodes, the string representation of the node.
auto upfn = [&ctx](bool wrapped, const Node& node, Span<std::string> subs) -> std::optional<std::string> {
std::string ret = wrapped ? ":" : "";
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::WRAP_A: return "a" + std::move(subs[0]);
case Fragment::WRAP_S: return "s" + std::move(subs[0]);
case Fragment::WRAP_C:
- if (node.subs[0]->nodetype == Fragment::PK_K) {
+ if (node.subs[0]->fragment == Fragment::PK_K) {
// pk(K) is syntactic sugar for c:pk_k(K)
- std::string key_str;
- if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
- return std::move(ret) + "pk(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.subs[0]->keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk(" + std::move(*key_str) + ")";
}
- if (node.subs[0]->nodetype == Fragment::PK_H) {
+ if (node.subs[0]->fragment == Fragment::PK_H) {
// pkh(K) is syntactic sugar for c:pk_h(K)
- std::string key_str;
- if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
- return std::move(ret) + "pkh(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.subs[0]->keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pkh(" + std::move(*key_str) + ")";
}
return "c" + std::move(subs[0]);
case Fragment::WRAP_D: return "d" + std::move(subs[0]);
@@ -538,24 +578,24 @@ public:
case Fragment::WRAP_N: return "n" + std::move(subs[0]);
case Fragment::AND_V:
// t:X is syntactic sugar for and_v(X,1).
- if (node.subs[1]->nodetype == Fragment::JUST_1) return "t" + std::move(subs[0]);
+ if (node.subs[1]->fragment == Fragment::JUST_1) return "t" + std::move(subs[0]);
break;
case Fragment::OR_I:
- if (node.subs[0]->nodetype == Fragment::JUST_0) return "l" + std::move(subs[1]);
- if (node.subs[1]->nodetype == Fragment::JUST_0) return "u" + std::move(subs[0]);
+ if (node.subs[0]->fragment == Fragment::JUST_0) return "l" + std::move(subs[1]);
+ if (node.subs[1]->fragment == Fragment::JUST_0) return "u" + std::move(subs[0]);
break;
default: break;
}
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::PK_K: {
- std::string key_str;
- if (!ctx.ToString(node.keys[0], key_str)) return {};
- return std::move(ret) + "pk_k(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk_k(" + std::move(*key_str) + ")";
}
case Fragment::PK_H: {
- std::string key_str;
- if (!ctx.ToString(node.keys[0], key_str)) return {};
- return std::move(ret) + "pk_h(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk_h(" + std::move(*key_str) + ")";
}
case Fragment::AFTER: return std::move(ret) + "after(" + ::ToString(node.k) + ")";
case Fragment::OLDER: return std::move(ret) + "older(" + ::ToString(node.k) + ")";
@@ -573,14 +613,14 @@ public:
case Fragment::OR_I: return std::move(ret) + "or_i(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
case Fragment::ANDOR:
// and_n(X,Y) is syntactic sugar for andor(X,Y,0).
- if (node.subs[2]->nodetype == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
+ if (node.subs[2]->fragment == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
return std::move(ret) + "andor(" + std::move(subs[0]) + "," + std::move(subs[1]) + "," + std::move(subs[2]) + ")";
case Fragment::MULTI: {
auto str = std::move(ret) + "multi(" + ::ToString(node.k);
for (const auto& key : node.keys) {
- std::string key_str;
- if (!ctx.ToString(key, key_str)) return {};
- str += "," + std::move(key_str);
+ auto key_str = ctx.ToString(key);
+ if (!key_str) return {};
+ str += "," + std::move(*key_str);
}
return std::move(str) + ")";
}
@@ -591,18 +631,16 @@ public:
}
return std::move(str) + ")";
}
- default: assert(false);
+ default: break;
}
- return ""; // Should never be reached.
+ assert(false);
};
- auto res = TreeEvalMaybe<std::string>(false, downfn, upfn);
- if (res.has_value()) ret = std::move(*res);
- return res.has_value();
+ return TreeEvalMaybe<std::string>(false, downfn, upfn);
}
internal::Ops CalcOps() const {
- switch (nodetype) {
+ switch (fragment) {
case Fragment::JUST_1: return {0, 0, {}};
case Fragment::JUST_0: return {0, {}, 0};
case Fragment::PK_K: return {0, 0, 0};
@@ -672,11 +710,10 @@ public:
}
}
assert(false);
- return {0, {}, {}};
}
internal::StackSize CalcStackSize() const {
- switch (nodetype) {
+ switch (fragment) {
case Fragment::JUST_0: return {{}, 0};
case Fragment::JUST_1:
case Fragment::OLDER:
@@ -723,7 +760,42 @@ public:
}
}
assert(false);
- return {{}, {}};
+ }
+
+ /** Check whether any key is repeated.
+ * This uses a custom key comparator provided by the context in order to still detect duplicates
+ * for more complicated types.
+ */
+ template<typename Ctx> bool ContainsDuplicateKey(const Ctx& ctx) const {
+ // We cannot use a lambda here, as lambdas are non assignable, and the set operations
+ // below require moving the comparators around.
+ struct Comp {
+ const Ctx* ctx_ptr;
+ Comp(const Ctx& ctx) : ctx_ptr(&ctx) {}
+ bool operator()(const Key& a, const Key& b) const { return ctx_ptr->KeyCompare(a, b); }
+ };
+ using set = std::set<Key, Comp>;
+
+ auto upfn = [this, &ctx](const Node& node, Span<set> subs) -> std::optional<set> {
+ if (&node != this && node.duplicate_key) return {};
+
+ size_t keys_count = node.keys.size();
+ set key_set{node.keys.begin(), node.keys.end(), Comp(ctx)};
+ if (key_set.size() != keys_count) return {};
+
+ for (auto& sub: subs) {
+ keys_count += sub.size();
+ // Small optimization: std::set::merge is linear in the size of the second arg but
+ // logarithmic in the size of the first.
+ if (key_set.size() < sub.size()) std::swap(key_set, sub);
+ key_set.merge(sub);
+ if (key_set.size() != keys_count) return {};
+ }
+
+ return key_set;
+ };
+
+ return !TreeEvalMaybe<set>(upfn);
}
public:
@@ -758,35 +830,31 @@ public:
//! Check whether this script always needs a signature.
bool NeedsSignature() const { return GetType() << "s"_mst; }
- //! Do all sanity checks.
- bool IsSane() const { return IsValid() && GetType() << "mk"_mst && CheckOpsLimit() && CheckStackSize(); }
+ //! Check whether there is no satisfaction path that contains both timelocks and heightlocks
+ bool CheckTimeLocksMix() const { return GetType() << "k"_mst; }
+
+ //! Check whether there is no duplicate key across this fragment and all its sub-fragments.
+ bool CheckDuplicateKey() const { return !duplicate_key; }
+
+ //! Whether successful non-malleable satisfactions are guaranteed to be valid.
+ bool ValidSatisfactions() const { return IsValid() && CheckOpsLimit() && CheckStackSize(); }
+
+ //! Whether the apparent policy of this node matches its script semantics. Doesn't guarantee it is a safe script on its own.
+ bool IsSaneSubexpression() const { return ValidSatisfactions() && IsNonMalleable() && CheckTimeLocksMix() && CheckDuplicateKey(); }
//! Check whether this node is safe as a script on its own.
- bool IsSaneTopLevel() const { return IsValidTopLevel() && IsSane() && NeedsSignature(); }
+ bool IsSane() const { return IsValidTopLevel() && IsSaneSubexpression() && NeedsSignature(); }
//! Equality testing.
- bool operator==(const Node<Key>& arg) const
- {
- if (nodetype != arg.nodetype) return false;
- if (k != arg.k) return false;
- if (data != arg.data) return false;
- if (keys != arg.keys) return false;
- if (subs.size() != arg.subs.size()) return false;
- for (size_t i = 0; i < subs.size(); ++i) {
- if (!(*subs[i] == *arg.subs[i])) return false;
- }
- assert(scriptlen == arg.scriptlen);
- assert(typ == arg.typ);
- return true;
- }
+ bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; }
// Constructors with various argument combinations.
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : nodetype(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : nodetype(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : nodetype(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<Key> key, uint32_t val = 0) : nodetype(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : nodetype(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, uint32_t val = 0) : nodetype(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
};
namespace internal {
@@ -847,15 +915,15 @@ enum class ParseContext {
int FindNextChar(Span<const char> in, const char m);
-/** Parse a key string ending with a ')' or ','. */
+/** Parse a key string ending at the end of the fragment's text representation. */
template<typename Key, typename Ctx>
std::optional<std::pair<Key, int>> ParseKeyEnd(Span<const char> in, const Ctx& ctx)
{
- Key key;
int key_size = FindNextChar(in, ')');
if (key_size < 1) return {};
- if (!ctx.FromString(in.begin(), in.begin() + key_size, key)) return {};
- return {{std::move(key), key_size}};
+ auto key = ctx.FromString(in.begin(), in.begin() + key_size);
+ if (!key) return {};
+ return {{std::move(*key), key_size}};
}
/** Parse a hex string ending at the end of the fragment's text representation. */
@@ -873,15 +941,15 @@ std::optional<std::pair<std::vector<unsigned char>, int>> ParseHexStrEnd(Span<co
}
/** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */
-template<typename Key>
-void BuildBack(Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
+template<typename Key, typename Ctx>
+void BuildBack(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
{
NodeRef<Key> child = std::move(constructed.back());
constructed.pop_back();
if (reverse) {
- constructed.back() = MakeNodeRef<Key>(nt, Vector(std::move(child), std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(child), std::move(constructed.back())));
} else {
- constructed.back() = MakeNodeRef<Key>(nt, Vector(std::move(constructed.back()), std::move(child)));
+ constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(constructed.back()), std::move(child)));
}
}
@@ -934,7 +1002,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
to_parse.emplace_back(ParseContext::WRAP_T, -1, -1);
} else if (in[j] == 'l') {
// The l: wrapper is equivalent to or_i(0,X)
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
to_parse.emplace_back(ParseContext::OR_I, -1, -1);
} else {
return {};
@@ -946,56 +1014,56 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
}
case ParseContext::EXPR: {
if (Const("0", in)) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
} else if (Const("1", in)) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
} else if (Const("pk(", in)) {
auto res = ParseKeyEnd<Key, Ctx>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::WRAP_C, Vector(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
} else if (Const("pkh(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::WRAP_C, Vector(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
} else if (Const("pk_k(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))));
in = in.subspan(key_size + 1);
} else if (Const("pk_h(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))));
in = in.subspan(key_size + 1);
} else if (Const("sha256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::SHA256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("ripemd160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::RIPEMD160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("hash256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("hash160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("after(", in)) {
int arg_size = FindNextChar(in, ')');
@@ -1003,7 +1071,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, num));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, num));
in = in.subspan(arg_size + 1);
} else if (Const("older(", in)) {
int arg_size = FindNextChar(in, ')');
@@ -1011,7 +1079,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, num));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, num));
in = in.subspan(arg_size + 1);
} else if (Const("multi(", in)) {
// Get threshold
@@ -1022,17 +1090,17 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
// Get keys
std::vector<Key> keys;
while (next_comma != -1) {
- Key key;
next_comma = FindNextChar(in, ',');
int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma;
if (key_length < 1) return {};
- if (!ctx.FromString(in.begin(), in.begin() + key_length, key)) return {};
- keys.push_back(std::move(key));
+ auto key = ctx.FromString(in.begin(), in.begin() + key_length);
+ if (!key) return {};
+ keys.push_back(std::move(*key));
in = in.subspan(key_length + 1);
}
if (keys.size() < 1 || keys.size() > 20) return {};
if (k < 1 || k > (int64_t)keys.size()) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), k));
} else if (Const("thresh(", in)) {
int next_comma = FindNextChar(in, ',');
if (next_comma < 1) return {};
@@ -1076,69 +1144,69 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
break;
}
case ParseContext::ALT: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case ParseContext::SWAP: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case ParseContext::CHECK: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case ParseContext::DUP_IF: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case ParseContext::NON_ZERO: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case ParseContext::ZERO_NOTEQUAL: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case ParseContext::VERIFY: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case ParseContext::WRAP_U: {
- constructed.back() = MakeNodeRef<Key>(Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
break;
}
case ParseContext::WRAP_T: {
- constructed.back() = MakeNodeRef<Key>(Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(Fragment::JUST_1)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_1)));
break;
}
case ParseContext::AND_B: {
- BuildBack(Fragment::AND_B, constructed);
+ BuildBack(ctx, Fragment::AND_B, constructed);
break;
}
case ParseContext::AND_N: {
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
break;
}
case ParseContext::AND_V: {
- BuildBack(Fragment::AND_V, constructed);
+ BuildBack(ctx, Fragment::AND_V, constructed);
break;
}
case ParseContext::OR_B: {
- BuildBack(Fragment::OR_B, constructed);
+ BuildBack(ctx, Fragment::OR_B, constructed);
break;
}
case ParseContext::OR_C: {
- BuildBack(Fragment::OR_C, constructed);
+ BuildBack(ctx, Fragment::OR_C, constructed);
break;
}
case ParseContext::OR_D: {
- BuildBack(Fragment::OR_D, constructed);
+ BuildBack(ctx, Fragment::OR_D, constructed);
break;
}
case ParseContext::OR_I: {
- BuildBack(Fragment::OR_I, constructed);
+ BuildBack(ctx, Fragment::OR_I, constructed);
break;
}
case ParseContext::ANDOR: {
@@ -1146,7 +1214,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
break;
}
case ParseContext::THRESH: {
@@ -1165,7 +1233,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
}
std::reverse(subs.begin(), subs.end());
- constructed.push_back(MakeNodeRef<Key>(Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
} else {
return {};
}
@@ -1200,10 +1268,10 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
* and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, OP_EQUAL
* respectively, plus OP_VERIFY.
*/
-bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out);
+std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script);
/** Determine whether the passed pair (created by DecomposeScript) is pushing a number. */
-bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k);
+std::optional<int64_t> ParseScriptNumber(const Opcode& in);
enum class DecodeContext {
/** A single expression of type B, K, or V. Specifically, this can't be an
@@ -1300,58 +1368,59 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
// Constants
if (in[0].first == OP_1) {
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
break;
}
if (in[0].first == OP_0) {
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
break;
}
// Public keys
if (in[0].second.size() == 33) {
- Key key;
- if (!ctx.FromPKBytes(in[0].second.begin(), in[0].second.end(), key)) return {};
+ auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end());
+ if (!key) return {};
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(*key))));
break;
}
if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) {
- Key key;
- if (!ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end(), key)) return {};
+ auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end());
+ if (!key) return {};
in += 5;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(*key))));
break;
}
// Time locks
- if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && ParseScriptNumber(in[1], k)) {
+ std::optional<int64_t> num;
+ if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
- if (k < 1 || k > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, k));
+ if (*num < 1 || *num > 0x7FFFFFFFL) return {};
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, *num));
break;
}
- if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) {
+ if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
- if (k < 1 || k > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, k));
+ if (num < 1 || num > 0x7FFFFFFFL) return {};
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, *num));
break;
}
// Hashes
- if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) {
+ if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && (num = ParseScriptNumber(in[5])) && num == 32 && in[6].first == OP_SIZE) {
if (in[2].first == OP_SHA256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::SHA256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_RIPEMD160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::RIPEMD160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, in[1].second));
in += 7;
break;
}
@@ -1359,20 +1428,20 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
// Multi
if (last - in >= 3 && in[0].first == OP_CHECKMULTISIG) {
std::vector<Key> keys;
- if (!ParseScriptNumber(in[1], n)) return {};
- if (last - in < 3 + n) return {};
- if (n < 1 || n > 20) return {};
- for (int i = 0; i < n; ++i) {
- Key key;
+ const auto n = ParseScriptNumber(in[1]);
+ if (!n || last - in < 3 + *n) return {};
+ if (*n < 1 || *n > 20) return {};
+ for (int i = 0; i < *n; ++i) {
if (in[2 + i].second.size() != 33) return {};
- if (!ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end(), key)) return {};
- keys.push_back(std::move(key));
+ auto key = ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end());
+ if (!key) return {};
+ keys.push_back(std::move(*key));
}
- if (!ParseScriptNumber(in[2 + n], k)) return {};
- if (k < 1 || k > n) return {};
- in += 3 + n;
+ const auto k = ParseScriptNumber(in[2 + *n]);
+ if (!k || *k < 1 || *k > *n) return {};
+ in += 3 + *n;
std::reverse(keys.begin(), keys.end());
- constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), *k));
break;
}
/** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather
@@ -1400,10 +1469,10 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
break;
}
// Thresh
- if (last - in >= 3 && in[0].first == OP_EQUAL && ParseScriptNumber(in[1], k)) {
- if (k < 1) return {};
+ if (last - in >= 3 && in[0].first == OP_EQUAL && (num = ParseScriptNumber(in[1]))) {
+ if (*num < 1) return {};
in += 2;
- to_parse.emplace_back(DecodeContext::THRESH_W, 0, k);
+ to_parse.emplace_back(DecodeContext::THRESH_W, 0, *num);
break;
}
// OP_ENDIF can be WRAP_J, WRAP_D, ANDOR, OR_C, OR_D, or OR_I
@@ -1467,63 +1536,63 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
case DecodeContext::SWAP: {
if (in >= last || in[0].first != OP_SWAP || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ALT: {
if (in >= last || in[0].first != OP_TOALTSTACK || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::CHECK: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::DUP_IF: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::VERIFY: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::NON_ZERO: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ZERO_NOTEQUAL: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::AND_V: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::AND_V, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::AND_V, constructed, /*reverse=*/true);
break;
}
case DecodeContext::AND_B: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::AND_B, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::AND_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_B: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_B, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_C: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_C, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_C, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_D: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_D, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_D, constructed, /*reverse=*/true);
break;
}
case DecodeContext::ANDOR: {
@@ -1533,7 +1602,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
NodeRef<Key> right = std::move(constructed.back());
constructed.pop_back();
NodeRef<Key> mid = std::move(constructed.back());
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
break;
}
case DecodeContext::THRESH_W: {
@@ -1557,7 +1626,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
constructed.pop_back();
subs.push_back(std::move(sub));
}
- constructed.push_back(MakeNodeRef<Key>(Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
break;
}
case DecodeContext::ENDIF: {
@@ -1607,7 +1676,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
if (in >= last) return {};
if (in[0].first == OP_IF) {
++in;
- BuildBack(Fragment::OR_I, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_I, constructed, /*reverse=*/true);
} else if (in[0].first == OP_NOTIF) {
++in;
to_parse.emplace_back(DecodeContext::ANDOR, -1, -1);
@@ -1638,12 +1707,12 @@ inline NodeRef<typename Ctx::Key> FromString(const std::string& str, const Ctx&
template<typename Ctx>
inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) {
using namespace internal;
- std::vector<std::pair<opcodetype, std::vector<unsigned char>>> decomposed;
- if (!DecomposeScript(script, decomposed)) return {};
- auto it = decomposed.begin();
- auto ret = DecodeScript<typename Ctx::Key>(it, decomposed.end(), ctx);
+ auto decomposed = DecomposeScript(script);
+ if (!decomposed) return {};
+ auto it = decomposed->begin();
+ auto ret = DecodeScript<typename Ctx::Key>(it, decomposed->end(), ctx);
if (!ret) return {};
- if (it != decomposed.end()) return {};
+ if (it != decomposed->end()) return {};
return ret;
}
diff --git a/src/script/standard.h b/src/script/standard.h
index f0b143c52b..6a15ba4e3d 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
+#include <attributes.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <uint256.h>
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index ffb9fb55d9..ba1eacfc78 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -65,8 +65,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey)
{
- const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -83,7 +82,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
block.hashMerkleRoot = BlockMerkleRoot(block);
}
- while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
return block;
}
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 82e4e1c90f..b333a9f72d 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <attributes.h>
#include <clientversion.h>
#include <coins.h>
#include <script/standard.h>
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 5b73481bc1..50eb479035 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -13,8 +13,8 @@
#include <chrono>
-using node::CCoinsStats;
-using node::CoinStatsHashType;
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
@@ -33,7 +33,6 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
{
CoinStatsIndex coin_stats_index{1 << 20, true};
- CCoinsStats coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* block_index;
{
LOCK(cs_main);
@@ -41,7 +40,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
}
// CoinStatsIndex should not be found before it is started.
- BOOST_CHECK(!coin_stats_index.LookUpStats(block_index, coin_stats));
+ BOOST_CHECK(!coin_stats_index.LookUpStats(block_index));
// BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
// is started.
@@ -57,10 +56,10 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
LOCK(cs_main);
genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
- BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
+ BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index));
// Check that CoinStatsIndex updates with new blocks.
- coin_stats_index.LookUpStats(block_index, coin_stats);
+ BOOST_CHECK(coin_stats_index.LookUpStats(block_index));
const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
std::vector<CMutableTransaction> noTxns;
@@ -69,13 +68,12 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// Let the CoinStatsIndex to catch up again.
BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain());
- CCoinsStats new_coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
new_block_index = m_node.chainman->ActiveChain().Tip();
}
- coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
+ BOOST_CHECK(coin_stats_index.LookUpStats(new_block_index));
BOOST_CHECK(block_index != new_block_index);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 7b492fd1da..3b4a6f2637 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -44,11 +44,10 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
// Disable inactivity checks for this test to avoid interference
static_cast<ConnmanTestMsg*>(connman.get())->SetPeerConnectTimeout(99999s);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
// Mock an outbound peer
@@ -134,9 +133,8 @@ static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerM
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
NodeId id{0};
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
@@ -147,7 +145,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
const auto time_init{GetTime<std::chrono::seconds>()};
SetMockTime(time_init);
- const auto time_later{time_init + 3 * std::chrono::seconds{chainparams.GetConsensus().nPowTargetSpacing} + 1s};
+ const auto time_later{time_init + 3 * std::chrono::seconds{m_node.chainman->GetConsensus().nPowTargetSpacing} + 1s};
connman->Init(options);
std::vector<CNode *> vNodes;
@@ -212,9 +210,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
{
NodeId id{0};
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS};
@@ -274,10 +271,9 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
- const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, false);
CNetAddr tor_netaddr;
@@ -390,10 +386,9 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
- const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, false);
banman->ClearBanned();
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 360dc00307..6c96702f1e 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -10,7 +10,6 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <key.h>
-#include <node/coinstats.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <pubkey.h>
@@ -26,10 +25,6 @@
#include <string>
#include <vector>
-using node::CCoinsStats;
-using node::CoinStatsHashType;
-using node::GetUTXOStats;
-
namespace {
const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
@@ -270,16 +265,6 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
(void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
},
[&] {
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
- bool expected_code_path = false;
- try {
- (void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats);
- } catch (const std::logic_error&) {
- expected_code_path = true;
- }
- assert(expected_code_path);
- },
- [&] {
(void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
});
}
diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp
new file mode 100644
index 0000000000..6be75322b4
--- /dev/null
+++ b/src/test/fuzz/miniscript.cpp
@@ -0,0 +1,167 @@
+// Copyright (c) 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 <core_io.h>
+#include <hash.h>
+#include <key.h>
+#include <script/miniscript.h>
+#include <script/script.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <util/strencodings.h>
+
+namespace {
+
+//! Some pre-computed data for more efficient string roundtrips.
+struct TestData {
+ typedef CPubKey Key;
+
+ // Precomputed public keys.
+ std::vector<Key> dummy_keys;
+ std::map<Key, int> dummy_key_idx_map;
+ std::map<CKeyID, Key> dummy_keys_map;
+
+ //! Set the precomputed data.
+ void Init() {
+ unsigned char keydata[32] = {1};
+ for (size_t i = 0; i < 256; i++) {
+ keydata[31] = i;
+ CKey privkey;
+ privkey.Set(keydata, keydata + 32, true);
+ const Key pubkey = privkey.GetPubKey();
+
+ dummy_keys.push_back(pubkey);
+ dummy_key_idx_map.emplace(pubkey, i);
+ dummy_keys_map.insert({pubkey.GetID(), pubkey});
+ }
+ }
+} TEST_DATA;
+
+/**
+ * Context to parse a Miniscript node to and from Script or text representation.
+ * Uses an integer (an index in the dummy keys array from the test data) as keys in order
+ * to focus on fuzzing the Miniscript nodes' test representation, not the key representation.
+ */
+struct ParserContext {
+ typedef CPubKey Key;
+
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a < b;
+ }
+
+ std::optional<std::string> ToString(const Key& key) const
+ {
+ auto it = TEST_DATA.dummy_key_idx_map.find(key);
+ if (it == TEST_DATA.dummy_key_idx_map.end()) return {};
+ uint8_t idx = it->second;
+ return HexStr(Span{&idx, 1});
+ }
+
+ template<typename I>
+ std::optional<Key> FromString(I first, I last) const {
+ if (last - first != 2) return {};
+ auto idx = ParseHex(std::string(first, last));
+ if (idx.size() != 1) return {};
+ return TEST_DATA.dummy_keys[idx[0]];
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKBytes(I first, I last) const {
+ Key key;
+ key.Set(first, last);
+ if (!key.IsValid()) return {};
+ return key;
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKHBytes(I first, I last) const {
+ assert(last - first == 20);
+ CKeyID keyid;
+ std::copy(first, last, keyid.begin());
+ const auto it = TEST_DATA.dummy_keys_map.find(keyid);
+ if (it == TEST_DATA.dummy_keys_map.end()) return {};
+ return it->second;
+ }
+} PARSER_CTX;
+
+//! Context that implements naive conversion from/to script only, for roundtrip testing.
+struct ScriptParserContext {
+ //! For Script roundtrip we never need the key from a key hash.
+ struct Key {
+ bool is_hash;
+ std::vector<unsigned char> data;
+ };
+
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a.data < b.data;
+ }
+
+ const std::vector<unsigned char>& ToPKBytes(const Key& key) const
+ {
+ assert(!key.is_hash);
+ return key.data;
+ }
+
+ const std::vector<unsigned char> ToPKHBytes(const Key& key) const
+ {
+ if (key.is_hash) return key.data;
+ const auto h = Hash160(key.data);
+ return {h.begin(), h.end()};
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKBytes(I first, I last) const
+ {
+ Key key;
+ key.data.assign(first, last);
+ key.is_hash = false;
+ return key;
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKHBytes(I first, I last) const
+ {
+ Key key;
+ key.data.assign(first, last);
+ key.is_hash = true;
+ return key;
+ }
+} SCRIPT_PARSER_CONTEXT;
+
+} // namespace
+
+void FuzzInit()
+{
+ ECC_Start();
+ TEST_DATA.Init();
+}
+
+/* Fuzz tests that test parsing from a string, and roundtripping via string. */
+FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ auto str = provider.ConsumeRemainingBytesAsString();
+ auto parsed = miniscript::FromString(str, PARSER_CTX);
+ if (!parsed) return;
+
+ const auto str2 = parsed->ToString(PARSER_CTX);
+ assert(str2);
+ auto parsed2 = miniscript::FromString(*str2, PARSER_CTX);
+ assert(parsed2);
+ assert(*parsed == *parsed2);
+}
+
+/* Fuzz tests that test parsing from a script, and roundtripping via script. */
+FUZZ_TARGET(miniscript_script)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
+ if (!script) return;
+
+ const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT);
+ if (!ms) return;
+
+ assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script);
+}
diff --git a/src/test/fuzz/miniscript_decode.cpp b/src/test/fuzz/miniscript_decode.cpp
deleted file mode 100644
index 4cc0a1be8f..0000000000
--- a/src/test/fuzz/miniscript_decode.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 <core_io.h>
-#include <hash.h>
-#include <key.h>
-#include <script/miniscript.h>
-#include <script/script.h>
-#include <span.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <util/strencodings.h>
-
-#include <optional>
-
-using miniscript::operator""_mst;
-
-
-struct Converter {
- typedef CPubKey Key;
-
- bool ToString(const Key& key, std::string& ret) const {
- ret = HexStr(key);
- return true;
- }
- const std::vector<unsigned char> ToPKBytes(const Key& key) const {
- return {key.begin(), key.end()};
- }
- const std::vector<unsigned char> ToPKHBytes(const Key& key) const {
- const auto h = Hash160(key);
- return {h.begin(), h.end()};
- }
-
- template<typename I>
- bool FromString(I first, I last, Key& key) const {
- const auto bytes = ParseHex(std::string(first, last));
- key.Set(bytes.begin(), bytes.end());
- return key.IsValid();
- }
- template<typename I>
- bool FromPKBytes(I first, I last, CPubKey& key) const {
- key.Set(first, last);
- return key.IsValid();
- }
- template<typename I>
- bool FromPKHBytes(I first, I last, CPubKey& key) const {
- assert(last - first == 20);
- return false;
- }
-};
-
-const Converter CONVERTER;
-
-FUZZ_TARGET(miniscript_decode)
-{
- FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
- if (!script) return;
-
- const auto ms = miniscript::FromScript(*script, CONVERTER);
- if (!ms) return;
-
- // We can roundtrip it to its string representation.
- std::string ms_str;
- assert(ms->ToString(CONVERTER, ms_str));
- assert(*miniscript::FromString(ms_str, CONVERTER) == *ms);
- // The Script representation must roundtrip since we parsed it this way the first time.
- const CScript ms_script = ms->ToScript(CONVERTER);
- assert(ms_script == *script);
-}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 03a84b697d..e4e83c3f32 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -128,6 +128,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getmempoolancestors",
"getmempooldescendants",
"getmempoolentry",
+ "gettxspendingprevout",
"getmempoolinfo",
"getmininginfo",
"getnettotals",
diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp
index 39a50b6c80..a2421ff582 100644
--- a/src/test/fuzz/tx_out.cpp
+++ b/src/test/fuzz/tx_out.cpp
@@ -4,6 +4,7 @@
#include <consensus/validation.h>
#include <core_memusage.h>
+#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <streams.h>
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index f686f4fd86..771d7a11cb 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
- auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), chainstate.m_params, options};
+ auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), options};
auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
Assert(block_template->block.vtx.size() >= 1);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 580105e442..3fc6fa1cd5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -6,7 +6,6 @@
#define BITCOIN_TEST_FUZZ_UTIL_H
#include <arith_uint256.h>
-#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
#include <compat.h>
diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp
index cbfb6c67c3..3f6a605945 100644
--- a/src/test/logging_tests.cpp
+++ b/src/test/logging_tests.cpp
@@ -5,13 +5,55 @@
#include <logging.h>
#include <logging/timer.h>
#include <test/util/setup_common.h>
+#include <util/string.h>
#include <chrono>
+#include <fstream>
+#include <iostream>
+#include <utility>
+#include <vector>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
+struct LogSetup : public BasicTestingSetup {
+ fs::path prev_log_path;
+ fs::path tmp_log_path;
+ bool prev_reopen_file;
+ bool prev_print_to_file;
+ bool prev_log_timestamps;
+ bool prev_log_threadnames;
+ bool prev_log_sourcelocations;
+
+ LogSetup() : prev_log_path{LogInstance().m_file_path},
+ tmp_log_path{m_args.GetDataDirBase() / "tmp_debug.log"},
+ prev_reopen_file{LogInstance().m_reopen_file},
+ prev_print_to_file{LogInstance().m_print_to_file},
+ prev_log_timestamps{LogInstance().m_log_timestamps},
+ prev_log_threadnames{LogInstance().m_log_threadnames},
+ prev_log_sourcelocations{LogInstance().m_log_sourcelocations}
+ {
+ LogInstance().m_file_path = tmp_log_path;
+ LogInstance().m_reopen_file = true;
+ LogInstance().m_print_to_file = true;
+ LogInstance().m_log_timestamps = false;
+ LogInstance().m_log_threadnames = false;
+ LogInstance().m_log_sourcelocations = true;
+ }
+
+ ~LogSetup()
+ {
+ LogInstance().m_file_path = prev_log_path;
+ LogPrintf("Sentinel log to reopen log file\n");
+ LogInstance().m_print_to_file = prev_print_to_file;
+ LogInstance().m_reopen_file = prev_reopen_file;
+ LogInstance().m_log_timestamps = prev_log_timestamps;
+ LogInstance().m_log_threadnames = prev_log_threadnames;
+ LogInstance().m_log_sourcelocations = prev_log_sourcelocations;
+ }
+};
+
BOOST_AUTO_TEST_CASE(logging_timer)
{
SetMockTime(1);
@@ -30,4 +72,82 @@ BOOST_AUTO_TEST_CASE(logging_timer)
BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
}
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintf_, LogSetup)
+{
+ LogPrintf_("fn1", "src1", 1, BCLog::LogFlags::NET, BCLog::Level::Debug, "foo1: %s", "bar1\n");
+ LogPrintf_("fn2", "src2", 2, BCLog::LogFlags::NET, BCLog::Level::None, "foo2: %s", "bar2\n");
+ LogPrintf_("fn3", "src3", 3, BCLog::LogFlags::NONE, BCLog::Level::Debug, "foo3: %s", "bar3\n");
+ LogPrintf_("fn4", "src4", 4, BCLog::LogFlags::NONE, BCLog::Level::None, "foo4: %s", "bar4\n");
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ std::vector<std::string> expected = {
+ "[src1:1] [fn1] [net:debug] foo1: bar1",
+ "[src2:2] [fn2] [net] foo2: bar2",
+ "[src3:3] [fn3] [debug] foo3: bar3",
+ "[src4:4] [fn4] foo4: bar4",
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup)
+{
+ // Prevent tests from failing when the line number of the following log calls changes.
+ LogInstance().m_log_sourcelocations = false;
+
+ LogPrintf("foo5: %s\n", "bar5");
+ LogPrint(BCLog::NET, "foo6: %s\n", "bar6");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "foo7: %s\n", "bar7");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Info, "foo8: %s\n", "bar8");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "foo9: %s\n", "bar9");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "foo10: %s\n", "bar10");
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ std::vector<std::string> expected = {
+ "foo5: bar5",
+ "[net] foo6: bar6",
+ "[net:debug] foo7: bar7",
+ "[net:info] foo8: bar8",
+ "[net:warning] foo9: bar9",
+ "[net:error] foo10: bar10"};
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)
+{
+ // Prevent tests from failing when the line number of the following log calls changes.
+ LogInstance().m_log_sourcelocations = false;
+ LogInstance().EnableCategory(BCLog::LogFlags::ALL);
+ const auto concated_categery_names = LogInstance().LogCategoriesString();
+ std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names;
+ const auto category_names = SplitString(concated_categery_names, ',');
+ for (const auto& category_name : category_names) {
+ BCLog::LogFlags category = BCLog::NONE;
+ const auto trimmed_category_name = TrimString(category_name);
+ BOOST_TEST(GetLogCategory(category, trimmed_category_name));
+ expected_category_names.emplace_back(category, trimmed_category_name);
+ }
+
+ std::vector<std::string> expected;
+ for (const auto& [category, name] : expected_category_names) {
+ LogPrint(category, "foo: %s\n", "bar");
+ std::string expected_log = "[";
+ expected_log += name;
+ expected_log += "] foo: bar";
+ expected.push_back(expected_log);
+ }
+
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 0c3a13c5f8..439ad174b3 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -10,6 +10,7 @@
#include <node/miner.h>
#include <policy/policy.h>
#include <script/standard.h>
+#include <timedata.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
@@ -51,7 +52,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
+ return BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool, options};
}
constexpr static struct {
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 930582ea24..3877fea907 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -71,6 +71,10 @@ std::unique_ptr<const TestData> g_testdata;
struct KeyConverter {
typedef CPubKey Key;
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a < b;
+ }
+
//! Convert a public key to bytes.
std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; }
@@ -84,27 +88,28 @@ struct KeyConverter {
//! Parse a public key from a range of hex characters.
template<typename I>
- bool FromString(I first, I last, CPubKey& key) const {
+ std::optional<Key> FromString(I first, I last) const {
auto bytes = ParseHex(std::string(first, last));
- key.Set(bytes.begin(), bytes.end());
- return key.IsValid();
+ Key key{bytes.begin(), bytes.end()};
+ if (key.IsValid()) return key;
+ return {};
}
template<typename I>
- bool FromPKBytes(I first, I last, CPubKey& key) const {
- key.Set(first, last);
- return key.IsValid();
+ std::optional<Key> FromPKBytes(I first, I last) const {
+ Key key{first, last};
+ if (key.IsValid()) return key;
+ return {};
}
template<typename I>
- bool FromPKHBytes(I first, I last, CPubKey& key) const {
+ std::optional<Key> FromPKHBytes(I first, I last) const {
assert(last - first == 20);
CKeyID keyid;
std::copy(first, last, keyid.begin());
auto it = g_testdata->pkmap.find(keyid);
assert(it != g_testdata->pkmap.end());
- key = it->second;
- return true;
+ return it->second;
}
};
@@ -272,6 +277,19 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
// its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341).
const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER);
BOOST_CHECK(!ms_minimalif);
+ // A Miniscript with duplicate keys is not sane
+ const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ BOOST_CHECK(ms_dup1);
+ BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey());
+ // Same with a disjunction, and different key nodes (pk and pkh)
+ const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey());
+ // Same when the duplicates are leaves or a larger tree
+ const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey());
+ // Same when the duplicates are on different levels in the tree
+ const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey());
// Timelock tests
Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index a7057f8361..907a3fd15b 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <compat/sanity.h>
#include <key.h>
#include <test/util/setup_common.h>
#include <util/time.h>
@@ -13,7 +12,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(basic_sanity)
{
- BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 15ffd068c7..0feb68b9b1 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(ReadWrite)
//! Check settings struct contents against expected json strings.
static void CheckValues(const util::Settings& settings, const std::string& single_val, const std::string& list_val)
{
- util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false);
+ util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false, false);
util::SettingsValue list_value(util::SettingsValue::VARR);
for (const auto& item : GetSettingsList(settings, "section", "name", false)) {
list_value.push_back(item);
@@ -141,9 +141,9 @@ BOOST_AUTO_TEST_CASE(NullOverride)
{
util::Settings settings;
settings.command_line_options["name"].push_back("value");
- BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false).write().c_str());
+ BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false, false).write().c_str());
settings.forced_settings["name"] = {};
- BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false).write().c_str());
+ BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false, false).write().c_str());
}
// Test different ways settings can be merged, and verify results. This test can
@@ -224,7 +224,7 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup)
}
desc += " || ";
- desc += GetSetting(settings, network, name, ignore_default_section_config, /* get_chain_name= */ false).write();
+ desc += GetSetting(settings, network, name, ignore_default_section_config, /*ignore_nonpersistent=*/false, /*get_chain_name=*/false).write();
desc += " |";
for (const auto& s : GetSettingsList(settings, network, name, ignore_default_section_config)) {
desc += " ";
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index d41b54af20..dd4bc5af75 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -15,7 +15,7 @@
struct Dersig100Setup : public TestChain100Setup {
Dersig100Setup()
- : TestChain100Setup{{"-testactivationheight=dersig@102"}} {}
+ : TestChain100Setup{CBaseChainParams::REGTEST, {"-testactivationheight=dersig@102"}} {}
};
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 52e02c4fa4..a6d624fe84 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -77,7 +77,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool)}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 6891629f8e..8f03481f72 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -12,6 +12,7 @@
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <init.h>
+#include <init/common.h>
#include <interfaces/chain.h>
#include <net.h>
#include <net_processing.h>
@@ -29,6 +30,7 @@
#include <shutdown.h>
#include <streams.h>
#include <test/util/net.h>
+#include <timedata.h>
#include <txdb.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -42,6 +44,7 @@
#include <validationinterface.h>
#include <walletinitinterface.h>
+#include <algorithm>
#include <functional>
#include <stdexcept>
@@ -124,8 +127,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
InitLogging(*m_node.args);
AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
- SHA256AutoDetect();
- ECC_Start();
+ m_node.kernel = std::make_unique<kernel::Context>();
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
@@ -145,7 +147,6 @@ BasicTestingSetup::~BasicTestingSetup()
LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
gArgs.ClearArgs();
- ECC_Stop();
}
ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
@@ -160,11 +161,15 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
- m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
+ m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), m_node.args->GetIntArg("-checkmempool", 1));
m_cache_sizes = CalculateCacheSizes(m_args);
- m_node.chainman = std::make_unique<ChainstateManager>(chainparams);
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ GetAdjustedTime,
+ };
+ 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);
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
@@ -192,7 +197,6 @@ ChainTestingSetup::~ChainTestingSetup()
TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
: ChainTestingSetup(chainName, extra_args)
{
- const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
@@ -201,7 +205,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
*Assert(m_node.chainman.get()),
Assert(m_node.mempool.get()),
fPruneMode,
- chainparams.GetConsensus(),
m_args.GetBoolArg("-reindex-chainstate", false),
m_cache_sizes.block_tree_db,
m_cache_sizes.coins_db,
@@ -214,10 +217,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
*Assert(m_node.chainman),
fReindex.load(),
m_args.GetBoolArg("-reindex-chainstate", false),
- chainparams.GetConsensus(),
m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS),
- m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
+ m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL));
assert(!maybe_verify_error.has_value());
BlockValidationState state;
@@ -231,7 +232,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
m_node.args->GetIntArg("-checkaddrman", 0));
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); // Deterministic randomness for tests.
- m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
+ m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
m_node.banman.get(), *m_node.chainman,
*m_node.mempool, false);
{
@@ -241,8 +242,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup(const std::vector<const char*>& extra_args)
- : TestingSetup{CBaseChainParams::REGTEST, extra_args}
+TestChain100Setup::TestChain100Setup(const std::string& chain_name, const std::vector<const char*>& extra_args)
+ : TestingSetup{chain_name, extra_args}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -276,9 +277,8 @@ CBlock TestChain100Setup::CreateBlock(
const CScript& scriptPubKey,
CChainState& chainstate)
{
- const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(chainstate, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler{chainstate, empty_pool}.CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -286,7 +286,7 @@ CBlock TestChain100Setup::CreateBlock(
}
RegenerateCommitments(block, *Assert(m_node.chainman));
- while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
return block;
}
@@ -357,6 +357,52 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
return mempool_txn;
}
+std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit)
+{
+ std::vector<CTransactionRef> mempool_transactions;
+ std::deque<std::pair<COutPoint, CAmount>> unspent_prevouts;
+ std::transform(m_coinbase_txns.begin(), m_coinbase_txns.end(), std::back_inserter(unspent_prevouts),
+ [](const auto& tx){ return std::make_pair(COutPoint(tx->GetHash(), 0), tx->vout[0].nValue); });
+ while (num_transactions > 0 && !unspent_prevouts.empty()) {
+ // The number of inputs and outputs are random, between 1 and 24.
+ CMutableTransaction mtx = CMutableTransaction();
+ const size_t num_inputs = det_rand.randrange(24) + 1;
+ CAmount total_in{0};
+ for (size_t n{0}; n < num_inputs; ++n) {
+ if (unspent_prevouts.empty()) break;
+ const auto& [prevout, amount] = unspent_prevouts.front();
+ mtx.vin.push_back(CTxIn(prevout, CScript()));
+ total_in += amount;
+ unspent_prevouts.pop_front();
+ }
+ const size_t num_outputs = det_rand.randrange(24) + 1;
+ // Approximately 1000sat "fee," equal output amounts.
+ const CAmount amount_per_output = (total_in - 1000) / num_outputs;
+ for (size_t n{0}; n < num_outputs; ++n) {
+ CScript spk = CScript() << CScriptNum(num_transactions + n);
+ mtx.vout.push_back(CTxOut(amount_per_output, spk));
+ }
+ CTransactionRef ptx = MakeTransactionRef(mtx);
+ mempool_transactions.push_back(ptx);
+ if (amount_per_output > 2000) {
+ // If the value is high enough to fund another transaction + fees, keep track of it so
+ // it can be used to build a more complex transaction graph. Insert randomly into
+ // unspent_prevouts for extra randomness in the resulting structures.
+ for (size_t n{0}; n < num_outputs; ++n) {
+ unspent_prevouts.push_back(std::make_pair(COutPoint(ptx->GetHash(), n), amount_per_output));
+ std::swap(unspent_prevouts.back(), unspent_prevouts[det_rand.randrange(unspent_prevouts.size())]);
+ }
+ }
+ if (submit) {
+ LOCK2(m_node.mempool->cs, cs_main);
+ LockPoints lp;
+ m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, 1000, 0, 1, false, 4, lp));
+ }
+ --num_transactions;
+ }
+ return mempool_transactions;
+}
+
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
{
return FromTx(MakeTransactionRef(tx));
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index a1b7525cf4..37407bcb92 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -81,8 +81,7 @@ static constexpr CAmount CENT{1000000};
* This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
- ECCVerifyHandle globalVerifyHandle;
- node::NodeContext m_node;
+ node::NodeContext m_node; // keep as first member to be destructed last
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
~BasicTestingSetup();
@@ -122,7 +121,8 @@ class CScript;
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public TestingSetup {
- TestChain100Setup(const std::vector<const char*>& extra_args = {});
+ TestChain100Setup(const std::string& chain_name = CBaseChainParams::REGTEST,
+ const std::vector<const char*>& extra_args = {});
/**
* Create a new block with just given transactions, coinbase paying to
@@ -164,6 +164,19 @@ struct TestChain100Setup : public TestingSetup {
CAmount output_amount = CAmount(1 * COIN),
bool submit = true);
+ /** Create transactions spending from m_coinbase_txns. These transactions will only spend coins
+ * that exist in the current chain, but may be premature coinbase spends, have missing
+ * signatures, or violate some other consensus rules. They should only be used for testing
+ * mempool consistency. All transactions will have some random number of inputs and outputs
+ * (between 1 and 24). Transactions may or may not be dependent upon each other; if dependencies
+ * exit, every parent will always be somewhere in the list before the child so each transaction
+ * can be submitted in the same order they appear in the list.
+ * @param[in] submit When true, submit transactions to the mempool.
+ * When false, return them but don't submit them.
+ * @returns A vector of transactions that can be submitted to the mempool.
+ */
+ std::vector<CTransactionRef> PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit);
+
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 18882929a8..331da691b5 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -65,7 +65,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
- auto ptemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(CScript{} << i++ << OP_TRUE);
+ auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(CScript{} << i++ << OP_TRUE);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
@@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
{
CScript pubKey;
pubKey << 1 << OP_TRUE;
- auto ptemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(pubKey);
+ auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(pubKey);
CBlock pblock = ptemplate->block;
CTxOut witness;
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index e7c7584f1c..98cb713a81 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <chainparams.h>
-#include <random.h>
-#include <uint256.h>
#include <consensus/validation.h>
-#include <sync.h>
+#include <random.h>
#include <rpc/blockchain.h>
+#include <sync.h>
#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
+#include <timedata.h>
+#include <uint256.h>
#include <validation.h>
#include <vector>
@@ -22,8 +23,12 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, TestingSetup)
//!
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
- const CChainParams& chainparams = Params();
- ChainstateManager manager(chainparams);
+ const ChainstateManager::Options chainman_opts{
+ Params(),
+ GetAdjustedTime,
+ };
+ ChainstateManager manager{chainman_opts};
+
WITH_LOCK(::cs_main, manager.m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(1 << 20, true));
CTxMemPool mempool;
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 129976ec15..e6203af4b4 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
/** Check that ComputeBlockVersion will set the appropriate bit correctly */
static void check_computeblockversion(VersionBitsCache& versionbitscache, const Consensus::Params& params, Consensus::DeploymentPos dep)
{
- // Clear the cache everytime
+ // Clear the cache every time
versionbitscache.Clear();
int64_t bit = params.vDeployments[dep].bit;
@@ -273,6 +273,7 @@ static void check_computeblockversion(VersionBitsCache& versionbitscache, const
nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE)
{
BOOST_CHECK_EQUAL(min_activation_height, 0);
+ BOOST_CHECK_EQUAL(nTimeout, Consensus::BIP9Deployment::NO_TIMEOUT);
return;
}
diff --git a/src/timedata.cpp b/src/timedata.cpp
index d881aa8043..d0ca609a48 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -99,7 +99,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
}
}
- if (LogAcceptCategory(BCLog::NET)) {
+ if (LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
std::string log_message{"time data samples: "};
for (const int64_t n : vSorted) {
log_message += strprintf("%+d ", n);
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 2064f0fb8a..82b8529d76 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -94,7 +94,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
self->reply_handlers.front()(*self, self->message);
self->reply_handlers.pop_front();
} else {
- LogPrint(BCLog::TOR, "tor: Received unexpected sync reply %i\n", self->message.code);
+ LogPrint(BCLog::TOR, "Received unexpected sync reply %i\n", self->message.code);
}
}
self->message.Clear();
@@ -113,13 +113,13 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
{
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
if (what & BEV_EVENT_CONNECTED) {
- LogPrint(BCLog::TOR, "tor: Successfully connected!\n");
+ LogPrint(BCLog::TOR, "Successfully connected!\n");
self->connected(*self);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
if (what & BEV_EVENT_ERROR) {
- LogPrint(BCLog::TOR, "tor: Error connecting to Tor control socket\n");
+ LogPrint(BCLog::TOR, "Error connecting to Tor control socket\n");
} else {
- LogPrint(BCLog::TOR, "tor: End of stream\n");
+ LogPrint(BCLog::TOR, "End of stream\n");
}
self->Disconnect();
self->disconnected(*self);
@@ -318,7 +318,7 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
- LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
+ LogPrint(BCLog::TOR, "Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
private_key = pkf.second;
}
}
@@ -359,7 +359,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
}
if (!socks_location.empty()) {
- LogPrint(BCLog::TOR, "tor: Get SOCKS port command yielded %s\n", socks_location);
+ LogPrint(BCLog::TOR, "Get SOCKS port command yielded %s\n", socks_location);
} else {
LogPrintf("tor: Get SOCKS port command returned nothing\n");
}
@@ -380,7 +380,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
Assume(resolved.IsValid());
- LogPrint(BCLog::TOR, "tor: Configuring onion proxy for %s\n", resolved.ToStringIPPort());
+ LogPrint(BCLog::TOR, "Configuring onion proxy for %s\n", resolved.ToStringIPPort());
Proxy addrOnion = Proxy(resolved, true);
SetProxy(NET_ONION, addrOnion);
@@ -404,7 +404,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n");
+ LogPrint(BCLog::TOR, "ADD_ONION successful\n");
for (const std::string &s : reply.lines) {
std::map<std::string,std::string> m = ParseTorReplyMapping(s);
std::map<std::string,std::string>::iterator i;
@@ -423,7 +423,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
- LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
+ LogPrint(BCLog::TOR, "Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
} else {
LogPrintf("tor: Error writing service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
}
@@ -439,7 +439,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: Authentication successful\n");
+ LogPrint(BCLog::TOR, "Authentication successful\n");
// Now that we know Tor is running setup the proxy for onion addresses
// if -onion isn't set to something else.
@@ -490,7 +490,7 @@ static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::v
void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: SAFECOOKIE authentication challenge successful\n");
+ LogPrint(BCLog::TOR, "SAFECOOKIE authentication challenge successful\n");
std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]);
if (l.first == "AUTHCHALLENGE") {
std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
@@ -500,7 +500,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr
}
std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
- LogPrint(BCLog::TOR, "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce));
+ LogPrint(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce));
if (serverNonce.size() != 32) {
LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n");
return;
@@ -547,12 +547,12 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
std::map<std::string,std::string>::iterator i;
if ((i = m.find("Tor")) != m.end()) {
- LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n", i->second);
+ LogPrint(BCLog::TOR, "Connected to Tor version %s\n", i->second);
}
}
}
for (const std::string &s : methods) {
- LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n", s);
+ LogPrint(BCLog::TOR, "Supported authentication method: %s\n", s);
}
// Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD
/* Authentication:
@@ -562,18 +562,18 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
std::string torpassword = gArgs.GetArg("-torpassword", "");
if (!torpassword.empty()) {
if (methods.count("HASHEDPASSWORD")) {
- LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n");
+ LogPrint(BCLog::TOR, "Using HASHEDPASSWORD authentication\n");
ReplaceAll(torpassword, "\"", "\\\"");
_conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
}
} else if (methods.count("NULL")) {
- LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
+ LogPrint(BCLog::TOR, "Using NULL authentication\n");
_conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
- LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
+ LogPrint(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
@@ -615,7 +615,7 @@ void TorController::disconnected_cb(TorControlConnection& _conn)
if (!reconnect)
return;
- LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center);
+ LogPrint(BCLog::TOR, "Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center);
// Single-shot timer for reconnect. Use exponential backoff.
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
diff --git a/src/univalue/TODO b/src/univalue/TODO
deleted file mode 100644
index 5530048e92..0000000000
--- a/src/univalue/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-
-Rearrange tree for easier 'git subtree' style use
-
-Move towards C++11 etc.
-
-Namespace support - must come up with useful shorthand, avoiding
-long Univalue::Univalue::Univalue usages forced upon library users.
-
-Improve test suite
-
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 35eaa2dd0d..f0d4de2035 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -83,66 +83,10 @@ public:
bool isObject() const { return (typ == VOBJ); }
bool push_back(const UniValue& val);
- bool push_back(const std::string& val_) {
- UniValue tmpVal(VSTR, val_);
- return push_back(tmpVal);
- }
- bool push_back(const char *val_) {
- std::string s(val_);
- return push_back(s);
- }
- bool push_back(uint64_t val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(int64_t val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(bool val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(int val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(double val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
bool push_backV(const std::vector<UniValue>& vec);
void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val);
- bool pushKV(const std::string& key, const std::string& val_) {
- UniValue tmpVal(VSTR, val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, const char *val_) {
- std::string _val(val_);
- return pushKV(key, _val);
- }
- bool pushKV(const std::string& key, int64_t val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, uint64_t val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, bool val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, int val_) {
- UniValue tmpVal((int64_t)val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, double val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
bool pushKVs(const UniValue& obj);
std::string write(unsigned int prettyIndent = 0,
@@ -185,8 +129,6 @@ public:
}
bool get_bool() const;
const std::string& get_str() const;
- auto get_int() const { return getInt<int>(); };
- auto get_int64() const { return getInt<int64_t>(); };
double get_real() const;
const UniValue& get_obj() const;
const UniValue& get_array() const;
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
index b9697a8cb7..8a35bf914d 100644
--- a/src/univalue/test/object.cpp
+++ b/src/univalue/test/object.cpp
@@ -92,23 +92,30 @@ BOOST_AUTO_TEST_CASE(univalue_typecheck)
BOOST_CHECK(v1.isNum());
BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error);
+ {
+ UniValue v_negative;
+ BOOST_CHECK(v_negative.setNumStr("-1"));
+ BOOST_CHECK_THROW(v_negative.getInt<uint8_t>(), std::runtime_error);
+ BOOST_CHECK_EQUAL(v_negative.getInt<int8_t>(), -1);
+ }
+
UniValue v2;
BOOST_CHECK(v2.setBool(true));
BOOST_CHECK_EQUAL(v2.get_bool(), true);
- BOOST_CHECK_THROW(v2.get_int(), std::runtime_error);
+ BOOST_CHECK_THROW(v2.getInt<int>(), std::runtime_error);
UniValue v3;
BOOST_CHECK(v3.setNumStr("32482348723847471234"));
- BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error);
+ BOOST_CHECK_THROW(v3.getInt<int64_t>(), std::runtime_error);
BOOST_CHECK(v3.setNumStr("1000"));
- BOOST_CHECK_EQUAL(v3.get_int64(), 1000);
+ BOOST_CHECK_EQUAL(v3.getInt<int64_t>(), 1000);
UniValue v4;
BOOST_CHECK(v4.setNumStr("2147483648"));
- BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648);
- BOOST_CHECK_THROW(v4.get_int(), std::runtime_error);
+ BOOST_CHECK_EQUAL(v4.getInt<int64_t>(), 2147483648);
+ BOOST_CHECK_THROW(v4.getInt<int>(), std::runtime_error);
BOOST_CHECK(v4.setNumStr("1000"));
- BOOST_CHECK_EQUAL(v4.get_int(), 1000);
+ BOOST_CHECK_EQUAL(v4.getInt<int>(), 1000);
BOOST_CHECK_THROW(v4.get_str(), std::runtime_error);
BOOST_CHECK_EQUAL(v4.get_real(), 1000);
BOOST_CHECK_THROW(v4.get_array(), std::runtime_error);
@@ -120,10 +127,10 @@ BOOST_AUTO_TEST_CASE(univalue_typecheck)
BOOST_CHECK(v5.read("[true, 10]"));
BOOST_CHECK_NO_THROW(v5.get_array());
std::vector<UniValue> vals = v5.getValues();
- BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error);
+ BOOST_CHECK_THROW(vals[0].getInt<int>(), std::runtime_error);
BOOST_CHECK_EQUAL(vals[0].get_bool(), true);
- BOOST_CHECK_EQUAL(vals[1].get_int(), 10);
+ BOOST_CHECK_EQUAL(vals[1].getInt<int>(), 10);
BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error);
}
diff --git a/src/util/bip32.h b/src/util/bip32.h
index 8f86f2aaa6..aa4eac3791 100644
--- a/src/util/bip32.h
+++ b/src/util/bip32.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_UTIL_BIP32_H
#define BITCOIN_UTIL_BIP32_H
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/util/designator.h b/src/util/designator.h
new file mode 100644
index 0000000000..3670b11e00
--- /dev/null
+++ b/src/util/designator.h
@@ -0,0 +1,21 @@
+// 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_UTIL_DESIGNATOR_H
+#define BITCOIN_UTIL_DESIGNATOR_H
+
+/**
+ * Designated initializers can be used to avoid ordering mishaps in aggregate
+ * initialization. However, they do not prevent uninitialized members. The
+ * checks can be disabled by defining DISABLE_DESIGNATED_INITIALIZER_ERRORS.
+ * This should only be needed on MSVC 2019. MSVC 2022 supports them with the
+ * option "/std:c++20"
+ */
+#ifndef DISABLE_DESIGNATED_INITIALIZER_ERRORS
+#define Desig(field_name) .field_name =
+#else
+#define Desig(field_name)
+#endif
+
+#endif // BITCOIN_UTIL_DESIGNATOR_H
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index 8180604342..3d33bd7f99 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_MONEYSTR_H
#define BITCOIN_UTIL_MONEYSTR_H
-#include <attributes.h>
#include <consensus/amount.h>
#include <optional>
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
index 26439b010b..924a9cfab2 100644
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -127,6 +127,7 @@ SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
+ bool ignore_nonpersistent,
bool get_chain_name)
{
SettingsValue result;
@@ -162,6 +163,9 @@ SettingsValue GetSetting(const Settings& settings,
return;
}
+ // Ignore nonpersistent settings if requested.
+ if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
+
// Skip negated command line settings.
if (skip_negated_command_line && span.last_negated()) return;
diff --git a/src/util/settings.h b/src/util/settings.h
index 261a0a032f..e97158dc09 100644
--- a/src/util/settings.h
+++ b/src/util/settings.h
@@ -55,12 +55,18 @@ bool WriteSettings(const fs::path& path,
//! @param ignore_default_section_config - ignore values in the default section
//! of the config file (part before any
//! [section] keywords)
+//! @param ignore_nonpersistent - ignore non-persistent settings values (forced
+//! settings values and values specified on the
+//! command line). Only return settings in the
+//! read-only config and read-write settings
+//! files.
//! @param get_chain_name - enable special backwards compatible behavior
//! for GetChainName
SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
+ bool ignore_nonpersistent,
bool get_chain_name);
//! Get combined setting value similar to GetSetting(), except if setting was
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index ee58383528..9a96bbe67b 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
-#include <attributes.h>
#include <span.h>
#include <util/string.h>
diff --git a/src/util/system.cpp b/src/util/system.cpp
index ed90874aca..6e2638a5d6 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -530,12 +530,15 @@ bool ArgsManager::InitSettings(std::string& error)
return true;
}
-bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
+bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
{
fs::path settings = GetPathArg("-settings", fs::path{BITCOIN_SETTINGS_FILENAME});
if (settings.empty()) {
return false;
}
+ if (backup) {
+ settings += ".bak";
+ }
if (filepath) {
*filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
}
@@ -576,10 +579,10 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
return true;
}
-bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
+bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
{
fs::path path, path_tmp;
- if (!GetSettingsPath(&path, /* temp= */ false) || !GetSettingsPath(&path_tmp, /* temp= */ true)) {
+ if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
}
@@ -596,6 +599,13 @@ bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
return true;
}
+util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
+{
+ LOCK(cs_args);
+ return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
+ /*ignore_nonpersistent=*/true, /*get_chain_name=*/false);
+}
+
bool ArgsManager::IsArgNegated(const std::string& strArg) const
{
return GetSetting(strArg).isFalse();
@@ -604,18 +614,33 @@ bool ArgsManager::IsArgNegated(const std::string& strArg) const
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
+ return SettingToString(value, strDefault);
+}
+
+std::string SettingToString(const util::SettingsValue& value, const std::string& strDefault)
+{
return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.isNum() ? value.getValStr() : value.get_str();
}
int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
+ return SettingToInt(value, nDefault);
+}
+
+int64_t SettingToInt(const util::SettingsValue& value, int64_t nDefault)
+{
return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.getInt<int64_t>() : LocaleIndependentAtoi<int64_t>(value.get_str());
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
+ return SettingToBool(value, fDefault);
+}
+
+bool SettingToBool(const util::SettingsValue& value, bool fDefault)
+{
return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
}
@@ -1006,6 +1031,7 @@ std::string ArgsManager::GetChainName() const
LOCK(cs_args);
util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
/* ignore_default_section_config= */ false,
+ /*ignore_nonpersistent=*/false,
/* get_chain_name= */ true);
return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
};
@@ -1038,7 +1064,8 @@ util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
{
LOCK(cs_args);
return util::GetSetting(
- m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), /* get_chain_name= */ false);
+ m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
+ /*ignore_nonpersistent=*/false, /*get_chain_name=*/false);
}
std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
diff --git a/src/util/system.h b/src/util/system.h
index 64585cbfac..07d7a533aa 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -14,7 +14,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <compat/assumptions.h>
#include <fs.h>
@@ -161,6 +160,10 @@ struct SectionInfo
int m_line;
};
+std::string SettingToString(const util::SettingsValue&, const std::string&);
+int64_t SettingToInt(const util::SettingsValue&, int64_t);
+bool SettingToBool(const util::SettingsValue&, bool);
+
class ArgsManager
{
public:
@@ -437,7 +440,7 @@ protected:
* Get settings file path, or return false if read-write settings were
* disabled with -nosettings.
*/
- bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false) const;
+ bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const;
/**
* Read settings file. Push errors to vector, or log them if null.
@@ -445,9 +448,16 @@ protected:
bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
/**
- * Write settings file. Push errors to vector, or log them if null.
+ * Write settings file or backup settings file. Push errors to vector, or
+ * log them if null.
+ */
+ bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const;
+
+ /**
+ * Get current setting from config file or read/write settings file,
+ * ignoring nonpersistent command line or forced settings values.
*/
- bool WriteSettingsFile(std::vector<std::string>* errors = nullptr) const;
+ util::SettingsValue GetPersistentSetting(const std::string& name) const;
/**
* Access settings with lock held.
diff --git a/src/util/time.h b/src/util/time.h
index 14df3fe53a..ad91a72860 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -86,8 +86,8 @@ void SetMockTime(std::chrono::seconds mock_time_in);
std::chrono::seconds GetMockTime();
/**
- * Return the current time point cast to the given precicion. Only use this
- * when an exact precicion is needed, otherwise use T::clock::now() directly.
+ * Return the current time point cast to the given precision. Only use this
+ * when an exact precision is needed, otherwise use T::clock::now() directly.
*/
template <typename T>
T Now()
diff --git a/src/validation.cpp b/src/validation.cpp
index 62bfb88b8a..1a8f501863 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -18,10 +18,10 @@
#include <cuckoocache.h>
#include <flatfile.h>
#include <hash.h>
+#include <kernel/coinstats.h>
#include <logging.h>
#include <logging/timer.h>
#include <node/blockstorage.h>
-#include <node/coinstats.h>
#include <node/ui_interface.h>
#include <node/utxo_snapshot.h>
#include <policy/policy.h>
@@ -36,7 +36,6 @@
#include <script/sigcache.h>
#include <shutdown.h>
#include <signet.h>
-#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
@@ -59,17 +58,18 @@
#include <optional>
#include <string>
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
+using kernel::ComputeUTXOStats;
+
using node::BLOCKFILE_CHUNK_SIZE;
using node::BlockManager;
using node::BlockMap;
using node::CBlockIndexHeightOnlyComparator;
using node::CBlockIndexWorkComparator;
-using node::CCoinsStats;
-using node::CoinStatsHashType;
using node::fImporting;
using node::fPruneMode;
using node::fReindex;
-using node::GetUTXOStats;
using node::nPruneTarget;
using node::OpenBlockFile;
using node::ReadBlockFromDisk;
@@ -134,8 +134,6 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;
-CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-
const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -814,7 +812,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return false; // state filled in by CheckTxInputs
}
- // Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}
@@ -2006,7 +2003,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
- // GetAdjustedTime() to go backward).
+ // m_adjusted_time_callback() to go backward).
if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
@@ -3613,7 +3610,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, GetAdjustedTime())) {
+ if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_adjusted_time_callback())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3837,6 +3834,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -3850,7 +3848,7 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, GetAdjustedTime()))
+ if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, adjusted_time_callback()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
@@ -4158,7 +4156,7 @@ bool ChainstateManager::LoadBlockIndex()
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = m_blockman.LoadBlockIndexDB();
+ bool ret = m_blockman.LoadBlockIndexDB(GetConsensus());
if (!ret) return false;
std::vector<CBlockIndex*> vSortedByHeight{m_blockman.GetAllBlockIndices()};
@@ -4987,7 +4985,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));
if (!snapshot_start_block) {
- // Needed for GetUTXOStats and ExpectedAssumeutxo to determine the height and to avoid a crash when base_blockhash.IsNull()
+ // Needed for ComputeUTXOStats and ExpectedAssumeutxo to determine the
+ // height and to avoid a crash when base_blockhash.IsNull()
LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
base_blockhash.ToString());
return false;
@@ -5095,22 +5094,22 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(coins_cache.GetBestBlock() == base_blockhash);
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
// As above, okay to immediately release cs_main here since no other context knows
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
+ const std::optional<CCoinsStats> maybe_stats = ComputeUTXOStats(CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, breakpoint_fnc);
+ if (!maybe_stats.has_value()) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
- if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) {
+ if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
- au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
+ au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
return false;
}
diff --git a/src/validation.h b/src/validation.h
index 1b80d0ccf4..70b6c072e6 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -14,6 +14,7 @@
#include <attributes.h>
#include <chain.h>
#include <chainparams.h>
+#include <kernel/chainstatemanager_opts.h>
#include <consensus/amount.h>
#include <deploymentstatus.h>
#include <fs.h>
@@ -57,8 +58,6 @@ namespace Consensus {
struct Params;
} // namespace Consensus
-/** Default for -minrelaytxfee, minimum relay fee for transactions */
-static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
@@ -125,8 +124,6 @@ extern bool g_parallel_script_checks;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
-/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
-extern CFeeRate minRelayTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
extern int64_t nMaxTipAge;
@@ -361,6 +358,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW = true,
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -834,7 +832,9 @@ private:
CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr};
- const CChainParams& m_chainparams;
+ const CChainParams m_chainparams;
+
+ const std::function<int64_t()> m_adjusted_time_callback;
//! Internal helper for ActivateSnapshot().
[[nodiscard]] bool PopulateAndValidateSnapshot(
@@ -853,7 +853,11 @@ private:
friend CChainState;
public:
- explicit ChainstateManager(const CChainParams& chainparams) : m_chainparams{chainparams} { }
+ using Options = ChainstateManagerOpts;
+
+ explicit ChainstateManager(const Options& opts)
+ : m_chainparams{opts.chainparams},
+ m_adjusted_time_callback{Assert(opts.adjusted_time_callback)} {};
const CChainParams& GetParams() const { return m_chainparams; }
const Consensus::Params& GetConsensus() const { return m_chainparams.GetConsensus(); }
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index fffab39cc1..613c5b65ef 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -5,6 +5,7 @@
#include <validationinterface.h>
+#include <attributes.h>
#include <chain.h>
#include <consensus/validation.h>
#include <logging.h>
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 0b236e2e48..07df8d9fc8 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -307,7 +307,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
}
}
- if (LogAcceptCategory(BCLog::SELECTCOINS)) {
+ if (LogAcceptCategory(BCLog::SELECTCOINS, BCLog::Level::Debug)) {
std::string log_message{"Coin selection best subset: "};
for (unsigned int i = 0; i < applicable_groups.size(); i++) {
if (vfBest[i]) {
@@ -328,24 +328,18 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
******************************************************************************/
void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only) {
- // Compute the effective value first
- const CAmount coin_fee = output.input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.input_bytes);
- const CAmount ev = output.txout.nValue - coin_fee;
-
// Filter for positive only here before adding the coin
- if (positive_only && ev <= 0) return;
+ if (positive_only && output.GetEffectiveValue() <= 0) return;
m_outputs.push_back(output);
COutput& coin = m_outputs.back();
- coin.fee = coin_fee;
- fee += coin.fee;
+ fee += coin.GetFee();
coin.long_term_fee = coin.input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.input_bytes);
long_term_fee += coin.long_term_fee;
- coin.effective_value = ev;
- effective_value += coin.effective_value;
+ effective_value += coin.GetEffectiveValue();
m_from_me &= coin.from_me;
m_value += coin.txout.nValue;
@@ -380,8 +374,8 @@ CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost,
CAmount waste = 0;
CAmount selected_effective_value = 0;
for (const COutput& coin : inputs) {
- waste += coin.fee - coin.long_term_fee;
- selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
+ waste += coin.GetFee() - coin.long_term_fee;
+ selected_effective_value += use_effective_value ? coin.GetEffectiveValue() : coin.txout.nValue;
}
if (change_cost) {
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 25c672eda0..9135e48104 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -20,6 +20,14 @@ static constexpr CAmount CHANGE_UPPER{1000000};
/** A UTXO under consideration for use in funding a new transaction. */
struct COutput {
+private:
+ /** The output's value minus fees required to spend it.*/
+ std::optional<CAmount> effective_value;
+
+ /** The fee required to spend this output at the transaction's target feerate. */
+ std::optional<CAmount> fee;
+
+public:
/** The outpoint identifying this UTXO */
COutPoint outpoint;
@@ -55,16 +63,10 @@ struct COutput {
/** Whether the transaction containing this output is sent from the owning wallet */
bool from_me;
- /** The output's value minus fees required to spend it. Initialized as the output's absolute value. */
- CAmount effective_value;
-
- /** The fee required to spend this output at the transaction's target feerate. */
- CAmount fee{0};
-
/** The fee required to spend this output at the consolidation feerate. */
CAmount long_term_fee{0};
- COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me)
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
: outpoint{outpoint},
txout{txout},
depth{depth},
@@ -73,9 +75,22 @@ struct COutput {
solvable{solvable},
safe{safe},
time{time},
- from_me{from_me},
- effective_value{txout.nValue}
- {}
+ from_me{from_me}
+ {
+ if (feerate) {
+ fee = input_bytes < 0 ? 0 : feerate.value().GetFee(input_bytes);
+ effective_value = txout.nValue - fee.value();
+ }
+ }
+
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
+ : COutput(outpoint, txout, depth, input_bytes, spendable, solvable, safe, time, from_me)
+ {
+ // if input_bytes is unknown, then fees should be 0, if input_bytes is known, then the fees should be a positive integer or 0 (input_bytes known and fees = 0 only happens in the tests)
+ assert((input_bytes < 0 && fees == 0) || (input_bytes > 0 && fees >= 0));
+ fee = fees;
+ effective_value = txout.nValue - fee.value();
+ }
std::string ToString() const;
@@ -83,6 +98,18 @@ struct COutput {
{
return outpoint < rhs.outpoint;
}
+
+ CAmount GetFee() const
+ {
+ assert(fee.has_value());
+ return fee.value();
+ }
+
+ CAmount GetEffectiveValue() const
+ {
+ assert(effective_value.has_value());
+ return effective_value.value();
+ }
};
/** Parameters for one iteration of Coin Selection. */
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index d5444f5051..f25ad59528 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -302,11 +302,11 @@ RPCHelpMan addmultisigaddress()
result.pushKV("descriptor", descriptor->ToString());
UniValue warnings(UniValue::VARR);
- if (!request.params[3].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ if (descriptor->GetOutputType() != output_type) {
// Only warns if the user has explicitly chosen an address type we cannot generate
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
- if (warnings.size()) result.pushKV("warnings", warnings);
+ if (!warnings.empty()) result.pushKV("warnings", warnings);
return result;
},
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index c289c05f2c..aa9f23c886 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -1621,7 +1621,7 @@ RPCHelpMan importdescriptors()
},
RPCExamples{
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
- "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
+ "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index 95296b4c9d..2649fa586c 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -18,28 +18,31 @@
namespace wallet {
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
- std::set<CScript> output_scripts;
-
+ std::set<CTxDestination> addresses;
if (by_label) {
// Get the set of addresses assigned to label
- std::string label = LabelFromValue(params[0]);
- for (const auto& address : wallet.GetLabelAddresses(label)) {
- auto output_script{GetScriptForDestination(address)};
- if (wallet.IsMine(output_script)) {
- output_scripts.insert(output_script);
- }
- }
+ addresses = wallet.GetLabelAddresses(LabelFromValue(params[0]));
+ if (addresses.empty()) throw JSONRPCError(RPC_WALLET_ERROR, "Label not found in wallet");
} else {
// Get the address
CTxDestination dest = DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
- CScript script_pub_key = GetScriptForDestination(dest);
- if (!wallet.IsMine(script_pub_key)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
+ addresses.insert(dest);
+ }
+
+ // Filter by own scripts only
+ std::set<CScript> output_scripts;
+ for (const auto& address : addresses) {
+ auto output_script{GetScriptForDestination(address)};
+ if (wallet.IsMine(output_script)) {
+ output_scripts.insert(output_script);
}
- output_scripts.insert(script_pub_key);
+ }
+
+ if (output_scripts.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
@@ -635,7 +638,7 @@ RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet);
- AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ AvailableCoinsListUnspent(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index c7b57ba4be..d1a0ba50f6 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -1398,7 +1398,7 @@ RPCHelpMan sendall()
total_input_value += tx->tx->vout[input.prevout.n].nValue;
}
} else {
- AvailableCoins(*pwallet, all_the_utxos, &coin_control, /*nMinimumAmount=*/0);
+ AvailableCoins(*pwallet, all_the_utxos, &coin_control, fee_rate, /*nMinimumAmount=*/0);
for (const COutput& output : all_the_utxos) {
CHECK_NONFATAL(output.input_bytes > 0);
if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index e5fd7b0eb4..6f79ae4e9b 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -84,7 +84,7 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
}
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, std::optional<CFeeRate> feerate, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{
AssertLockHeld(wallet.cs_wallet);
@@ -192,7 +192,7 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly));
- vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me);
+ vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@@ -211,13 +211,18 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
}
}
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+{
+ AvailableCoins(wallet, vCoins, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+}
+
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
{
LOCK(wallet.cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(wallet, vCoins, coinControl);
+ AvailableCoinsListUnspent(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
if (out.spendable) {
balance += out.txout.nValue;
@@ -257,7 +262,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(wallet, availableCoins);
+ AvailableCoinsListUnspent(wallet, availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
@@ -477,12 +482,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
/* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
- COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- output.effective_value = output.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(output.input_bytes);
+ COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, coin_selection_params.m_effective_feerate);
if (coin_selection_params.m_subtract_fee_outputs) {
value_to_select -= output.txout.nValue;
} else {
- value_to_select -= output.effective_value;
+ value_to_select -= output.GetEffectiveValue();
}
preset_coins.insert(outpoint);
/* Set ancestors and descendants to 0 as they don't matter for preset inputs since no actual selection is being done.
@@ -788,7 +792,7 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal(
// Get available coins
std::vector<COutput> vAvailableCoins;
- AvailableCoins(wallet, vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ AvailableCoins(wallet, vAvailableCoins, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
@@ -993,12 +997,13 @@ std::optional<CreatedTransactionResult> CreateTransaction(
tmp_cc.m_avoid_partial_spends = true;
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, error2, tmp_cc, fee_calc_out, 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};
+ TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(),
+ txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() ? txr_grouped->change_pos : 0);
if (txr_grouped) {
- // if fee of this alternative one is within the range of the max fee, we use this one
- const bool use_aps = txr_grouped->fee <= txr_ungrouped->fee + wallet.m_max_aps_fee;
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n",
txr_ungrouped->fee, txr_grouped->fee, use_aps ? "grouped" : "non-grouped");
- TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, true, txr_grouped->fee, txr_grouped->change_pos);
if (use_aps) return txr_grouped;
}
}
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index 8af712110d..988058a25a 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -37,7 +37,13 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* walle
/**
* populate vCoins with vector of available COutputs.
*/
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/**
+ * Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function
+ * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
+ */
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 72e749477b..76f28917a4 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -41,7 +41,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& se
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
}
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
@@ -50,7 +50,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
OutputGroup group;
group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true);
result.AddInput(group);
@@ -62,14 +62,12 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- coin.effective_value = nValue - fee;
- coin.fee = fee;
+ COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ 148, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fee);
coin.long_term_fee = long_term_fee;
set.insert(coin);
}
-static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -88,7 +86,7 @@ static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount
auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
assert(ret.second);
CWalletTx& wtx = (*ret.first).second;
- coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe);
+ coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate);
}
/** Check if SelectionResult a is equivalent to SelectionResult b.
@@ -311,13 +309,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 1);
+ add_coin(coins, *wallet, 1, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change));
// Test fees subtracted from output:
coins.clear();
- add_coin(coins, *wallet, 1 * CENT);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40;
coin_selection_params_bnb.m_subtract_fee_outputs = true;
const auto result9 = SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change);
@@ -334,9 +332,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
coin_control.Select(coins.at(0).outpoint);
@@ -344,6 +342,61 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(result10);
}
+ {
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+
+ // single coin should be selected when effective fee > long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(10 * CENT, 2, expected_result);
+ CCoinControl coin_control;
+ const auto result11 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result11));
+ coins.clear();
+
+ // more coins should be selected when effective fee < long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(5000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(9 * CENT, 2, expected_result);
+ add_coin(1 * CENT, 2, expected_result);
+ const auto result12 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result12));
+ coins.clear();
+
+ // pre selected coin should be selected even if disadvantageous
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(9 * CENT, 2, expected_result);
+ add_coin(1 * CENT, 2, expected_result);
+ coin_control.fAllowOtherInputs = true;
+ coin_control.Select(coins.at(1).outpoint); // pre select 9 coin
+ const auto result13 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result13));
+ }
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
@@ -367,7 +420,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// with an empty wallet we can't even pay one cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
- add_coin(coins, *wallet, 1*CENT, 4); // add a new 1 cent coin
+ add_coin(coins, *wallet, 1*CENT, CFeeRate(0), 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
@@ -388,7 +441,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT);
add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
- add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
+ add_coin(coins, *wallet, 10*CENT, CFeeRate(0), 3, true); // a new 10 cent coin sent from one of our own addresses
add_coin(coins, *wallet, 20*CENT); // and a mature 20 cent coin
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
@@ -815,5 +868,40 @@ BOOST_AUTO_TEST_CASE(waste_test)
BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
}
+BOOST_AUTO_TEST_CASE(effective_value_test)
+{
+ const int input_bytes = 148;
+ const CFeeRate feerate(1000);
+ const CAmount nValue = 10000;
+ const int nInput = 0;
+
+ CMutableTransaction tx;
+ tx.vout.resize(1);
+ tx.vout[nInput].nValue = nValue;
+
+ // standard case, pass feerate in constructor
+ COutput output1(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ const CAmount expected_ev1 = 9852; // 10000 - 148
+ BOOST_CHECK_EQUAL(output1.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass feerate in constructor
+ COutput output2(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ BOOST_CHECK_EQUAL(output2.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+
+ // negative effective value, pass feerate in constructor
+ COutput output3(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, CFeeRate(100000));
+ const CAmount expected_ev3 = -4800; // 10000 - 14800
+ BOOST_CHECK_EQUAL(output3.GetEffectiveValue(), expected_ev3);
+
+ // standard case, pass fees in constructor
+ const CAmount fees = 148;
+ COutput output4(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fees);
+ BOOST_CHECK_EQUAL(output4.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass fees in constructor
+ COutput output5(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
+ BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 2693b68cca..3465f2f331 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -14,13 +14,13 @@
namespace wallet {
-static void AddCoin(const CAmount& value, int n_input, int n_input_bytes, int locktime, std::vector<COutput>& coins)
+static void AddCoin(const CAmount& value, int n_input, int n_input_bytes, int locktime, std::vector<COutput>& coins, CFeeRate fee_rate)
{
CMutableTransaction tx;
tx.vout.resize(n_input + 1);
tx.vout[n_input].nValue = value;
tx.nLockTime = locktime; // all transactions get different hashes
- coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true);
+ coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, fee_rate);
}
// Randomly distribute coins to instances of OutputGroup
@@ -70,7 +70,7 @@ FUZZ_TARGET(coinselection)
if (total_balance + amount >= MAX_MONEY) {
break;
}
- AddCoin(amount, n_input, n_input_bytes, ++next_locktime, utxo_pool);
+ AddCoin(amount, n_input, n_input_bytes, ++next_locktime, utxo_pool, coin_params.m_effective_feerate);
total_balance += amount;
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index d4406fd5dd..70863f5464 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -584,7 +584,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -596,7 +596,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7d9f8b05bb..910562e669 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2106,7 +2106,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
- AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
+ CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
CHECK_NONFATAL(wtx.mapValue.empty());
CHECK_NONFATAL(wtx.vOrderForm.empty());
wtx.mapValue = std::move(mapValue);
@@ -2116,6 +2116,11 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
return true;
});
+ // wtx can only be null if the db write failed.
+ if (!wtx) {
+ throw std::runtime_error(std::string(__func__) + ": Wallet db error, transaction commit failed");
+ }
+
// Notify that old coins are spent
for (const CTxIn& txin : tx->vin) {
CWalletTx &coin = mapWallet.at(txin.prevout.hash);
@@ -2123,17 +2128,13 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
NotifyTransactionChanged(coin.GetHash(), CT_UPDATED);
}
- // Get the inserted-CWalletTx from mapWallet so that the
- // wtx cached mempool state is updated correctly
- CWalletTx& wtx = mapWallet.at(tx->GetHash());
-
if (!fBroadcastTransactions) {
// Don't submit tx to the mempool
return;
}
std::string err_string;
- if (!SubmitTxMemoryPoolAndRelay(wtx, err_string, true)) {
+ if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index a53de34db4..26618735f6 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -70,9 +70,9 @@ bool CZMQNotificationInterface::Initialize()
{
int major = 0, minor = 0, patch = 0;
zmq_version(&major, &minor, &patch);
- LogPrint(BCLog::ZMQ, "zmq: version %d.%d.%d\n", major, minor, patch);
+ LogPrint(BCLog::ZMQ, "version %d.%d.%d\n", major, minor, patch);
- LogPrint(BCLog::ZMQ, "zmq: Initialize notification interface\n");
+ LogPrint(BCLog::ZMQ, "Initialize notification interface\n");
assert(!pcontext);
pcontext = zmq_ctx_new();
@@ -85,9 +85,9 @@ bool CZMQNotificationInterface::Initialize()
for (auto& notifier : notifiers) {
if (notifier->Initialize(pcontext)) {
- LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
} else {
- LogPrint(BCLog::ZMQ, "zmq: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
return false;
}
}
@@ -98,11 +98,11 @@ bool CZMQNotificationInterface::Initialize()
// Called during shutdown sequence
void CZMQNotificationInterface::Shutdown()
{
- LogPrint(BCLog::ZMQ, "zmq: Shutdown notification interface\n");
+ LogPrint(BCLog::ZMQ, "Shutdown notification interface\n");
if (pcontext)
{
for (auto& notifier : notifiers) {
- LogPrint(BCLog::ZMQ, "zmq: Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
notifier->Shutdown();
}
zmq_ctx_term(pcontext);
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 2c6f24a239..011336bf72 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -106,7 +106,7 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
- LogPrint(BCLog::ZMQ, "zmq: Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
+ LogPrint(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
int rc = zmq_setsockopt(psocket, ZMQ_SNDHWM, &outbound_message_high_water_mark, sizeof(outbound_message_high_water_mark));
if (rc != 0)
@@ -147,8 +147,8 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
}
else
{
- LogPrint(BCLog::ZMQ, "zmq: Reusing socket for address %s\n", address);
- LogPrint(BCLog::ZMQ, "zmq: Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
+ LogPrint(BCLog::ZMQ, "Reusing socket for address %s\n", address);
+ LogPrint(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
psocket = i->second->psocket;
mapPublishNotifiers.insert(std::make_pair(address, this));
@@ -179,7 +179,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
if (count == 1)
{
- LogPrint(BCLog::ZMQ, "zmq: Close socket at address %s\n", address);
+ LogPrint(BCLog::ZMQ, "Close socket at address %s\n", address);
int linger = 0;
zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger));
zmq_close(psocket);
@@ -208,7 +208,7 @@ bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashblock %s to %s\n", hash.GetHex(), this->address);
uint8_t data[32];
for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
@@ -219,7 +219,7 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx %s to %s\n", hash.GetHex(), this->address);
uint8_t data[32];
for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
@@ -229,7 +229,7 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
- LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
@@ -251,7 +251,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish rawtx %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ss << transaction;
return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
@@ -273,27 +273,27 @@ static bool SendSequenceMsg(CZMQAbstractPublishNotifier& notifier, uint256 hash,
bool CZMQPublishSequenceNotifier::NotifyBlockConnect(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish sequence block connect %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish sequence block connect %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Block (C)onnect */ 'C');
}
bool CZMQPublishSequenceNotifier::NotifyBlockDisconnect(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish sequence block disconnect %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish sequence block disconnect %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Block (D)isconnect */ 'D');
}
bool CZMQPublishSequenceNotifier::NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool acceptance %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx mempool acceptance %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Mempool (A)cceptance */ 'A', mempool_sequence);
}
bool CZMQPublishSequenceNotifier::NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool removal %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx mempool removal %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Mempool (R)emoval */ 'R', mempool_sequence);
}
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
index f0568634d4..cf3a0b2d71 100644
--- a/src/zmq/zmqutil.cpp
+++ b/src/zmq/zmqutil.cpp
@@ -12,5 +12,5 @@
void zmqError(const std::string& str)
{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, msg: %s\n", str, zmq_strerror(errno));
+ LogPrint(BCLog::ZMQ, "Error: %s, msg: %s\n", str, zmq_strerror(errno));
}
diff --git a/test/README.md b/test/README.md
index 0d9b9fb89b..6ca7cc0016 100644
--- a/test/README.md
+++ b/test/README.md
@@ -327,7 +327,7 @@ test/lint/lint-files.py
You can run all the shell-based lint tests by running:
```
-test/lint/lint-all.py
+test/lint/all-lint.py
```
# Writing functional tests
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 3e60efbb3c..a3a5e9e27d 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -30,17 +30,17 @@ import http.client
import random
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
COIN,
- COutPoint,
- CTransaction,
- CTxIn,
- CTxOut,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- create_confirmed_utxos,
+)
+from test_framework.wallet import (
+ MiniWallet,
+ getnewdestination,
)
@@ -66,13 +66,9 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=self.extra_args)
self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
# Leave them unconnected, we'll use submitblock directly in this test
def restart_node(self, node_index, expected_tip):
@@ -190,34 +186,36 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
num_transactions = 0
random.shuffle(utxo_list)
while len(utxo_list) >= 2 and num_transactions < count:
- tx = CTransaction()
- input_amount = 0
- for _ in range(2):
- utxo = utxo_list.pop()
- tx.vin.append(CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout'])))
- input_amount += int(utxo['amount'] * COIN)
- output_amount = (input_amount - FEE) // 3
-
- if output_amount <= 0:
+ utxos_to_spend = [utxo_list.pop() for _ in range(2)]
+ input_amount = int(sum([utxo['value'] for utxo in utxos_to_spend]) * COIN)
+ if input_amount < FEE:
# Sanity check -- if we chose inputs that are too small, skip
continue
- for _ in range(3):
- tx.vout.append(CTxOut(output_amount, bytes.fromhex(utxo['scriptPubKey'])))
+ tx = self.wallet.create_self_transfer_multi(
+ from_node=node,
+ utxos_to_spend=utxos_to_spend,
+ num_outputs=3,
+ fee_per_output=FEE // 3)
- # Sign and send the transaction to get into the mempool
- tx_signed_hex = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
- node.sendrawtransaction(tx_signed_hex)
+ # Send the transaction to get into the mempool (skip fee-checks to run faster)
+ node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
num_transactions += 1
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[3])
+ self.wallet.rescan_utxos()
+ initial_height = self.nodes[3].getblockcount()
+ self.generate(self.nodes[3], COINBASE_MATURITY, sync_fun=self.no_op)
+
# Track test coverage statistics
self.restart_counts = [0, 0, 0] # Track the restarts for nodes 0-2
self.crashed_on_restart = 0 # Track count of crashes during recovery
# Start by creating a lot of utxos on node3
- initial_height = self.nodes[3].getblockcount()
- utxo_list = create_confirmed_utxos(self, self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000, sync_fun=self.no_op)
+ utxo_list = self.wallet.send_self_transfer_multi(from_node=self.nodes[3], num_outputs=5000)['new_utxos']
+ self.generate(self.nodes[3], 1, sync_fun=self.no_op)
+ assert_equal(len(self.nodes[3].getrawmempool()), 0)
self.log.info(f"Prepped {len(utxo_list)} utxo entries")
# Sync these blocks with the other nodes
@@ -257,13 +255,14 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.nodes[3],
nblocks=min(10, current_height + 1 - self.nodes[3].getblockcount()),
# new address to avoid mining a block that has just been invalidated
- address=self.nodes[3].getnewaddress(),
+ address=getnewdestination()[2],
sync_fun=self.no_op,
))
self.log.debug(f"Syncing {len(block_hashes)} new blocks...")
self.sync_node3blocks(block_hashes)
- utxo_list = self.nodes[3].listunspent()
- self.log.debug(f"Node3 utxo count: {len(utxo_list)}")
+ self.wallet.rescan_utxos()
+ utxo_list = self.wallet.get_utxos()
+ self.log.debug(f"MiniWallet utxo count: {len(utxo_list)}")
# Check that the utxo hashes agree with node3
# Useful side effect: each utxo cache gets flushed here, so that we
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
index 3ee6a8036c..bc85e43a57 100755
--- a/test/functional/feature_index_prune.py
+++ b/test/functional/feature_index_prune.py
@@ -73,7 +73,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
pruneheight_new = node.pruneblockchain(400)
# the prune heights used here and below are magic numbers that are determined by the
# thresholds at which block files wrap, so they depend on disk serialization and default block file size.
- assert_equal(pruneheight_new, 249)
+ assert_equal(pruneheight_new, 248)
self.log.info("check if we can access the tips blockfilter and coinstats when we have pruned some blocks")
tip = self.nodes[0].getbestblockhash()
@@ -108,7 +108,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("prune exactly up to the indices best blocks while the indices are disabled")
for i in range(3):
pruneheight_2 = self.nodes[i].pruneblockchain(1000)
- assert_equal(pruneheight_2, 751)
+ assert_equal(pruneheight_2, 750)
# Restart the nodes again with the indices activated
self.restart_node(i, extra_args=self.extra_args[i])
@@ -142,7 +142,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
for node in self.nodes[:2]:
with node.assert_debug_log(['limited pruning to height 2489']):
pruneheight_new = node.pruneblockchain(2500)
- assert_equal(pruneheight_new, 2006)
+ assert_equal(pruneheight_new, 2005)
self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index d0cb1e10e2..13c7326519 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -49,33 +49,33 @@ class InitStressTest(BitcoinTestFramework):
assert_equal(200, node.getblockcount())
lines_to_terminate_after = [
- 'Validating signatures for all blocks',
- 'scheduler thread start',
- 'Starting HTTP server',
- 'Loading P2P addresses',
- 'Loading banlist',
- 'Loading block index',
- 'Switching active chainstate',
- 'Checking all blk files are present',
- 'Loaded best chain:',
- 'init message: Verifying blocks',
- 'init message: Starting network threads',
- 'net thread start',
- 'addcon thread start',
- 'loadblk thread start',
- 'txindex thread start',
- 'block filter index thread start',
- 'coinstatsindex thread start',
- 'msghand thread start',
- 'net thread start',
- 'addcon thread start',
+ b'Validating signatures for all blocks',
+ b'scheduler thread start',
+ b'Starting HTTP server',
+ b'Loading P2P addresses',
+ b'Loading banlist',
+ b'Loading block index',
+ b'Switching active chainstate',
+ b'Checking all blk files are present',
+ b'Loaded best chain:',
+ b'init message: Verifying blocks',
+ b'init message: Starting network threads',
+ b'net thread start',
+ b'addcon thread start',
+ b'loadblk thread start',
+ b'txindex thread start',
+ b'block filter index thread start',
+ b'coinstatsindex thread start',
+ b'msghand thread start',
+ b'net thread start',
+ b'addcon thread start',
]
if self.is_wallet_compiled():
- lines_to_terminate_after.append('Verifying wallet')
+ lines_to_terminate_after.append(b'Verifying wallet')
for terminate_line in lines_to_terminate_after:
- self.log.info(f"Starting node and will exit after line '{terminate_line}'")
- with node.wait_for_debug_log([terminate_line], ignore_case=True):
+ self.log.info(f"Starting node and will exit after line {terminate_line}")
+ with node.wait_for_debug_log([terminate_line]):
node.start(extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'])
self.log.debug("Terminating node after terminate line was found")
sigterm_node()
diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py
index 489a729cfc..fa10855a98 100755
--- a/test/functional/feature_minchainwork.py
+++ b/test/functional/feature_minchainwork.py
@@ -17,6 +17,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded.
import time
+from test_framework.p2p import P2PInterface, msg_getheaders
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -41,6 +42,9 @@ class MinimumChainWorkTest(BitcoinTestFramework):
for i in range(self.num_nodes-1):
self.connect_nodes(i+1, i)
+ # Set clock of node2 2 days ahead, to keep it in IBD during this test.
+ self.nodes[2].setmocktime(int(time.time()) + 48*60*60)
+
def run_test(self):
# Start building a chain on node0. node2 shouldn't be able to sync until node1's
# minchainwork is exceeded
@@ -71,6 +75,15 @@ class MinimumChainWorkTest(BitcoinTestFramework):
assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
assert_equal(self.nodes[2].getblockcount(), starting_blockcount)
+ self.log.info("Check that getheaders requests to node2 are ignored")
+ peer = self.nodes[2].add_p2p_connection(P2PInterface())
+ msg = msg_getheaders()
+ msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)]
+ msg.hashstop = 0
+ peer.send_and_ping(msg)
+ time.sleep(5)
+ assert "headers" not in peer.last_message
+
self.log.info("Generating one more block")
self.generate(self.nodes[0], 1)
@@ -85,5 +98,21 @@ class MinimumChainWorkTest(BitcoinTestFramework):
self.sync_all()
self.log.info(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}")
+ self.log.info("Test that getheaders requests to node2 are not ignored")
+ peer.send_and_ping(msg)
+ assert "headers" in peer.last_message
+
+ # Verify that node2 is in fact still in IBD (otherwise this test may
+ # not be exercising the logic we want!)
+ assert_equal(self.nodes[2].getblockchaininfo()['initialblockdownload'], True)
+
+ self.log.info("Test -minimumchainwork with a non-hex value")
+ self.stop_node(0)
+ self.nodes[0].assert_start_raises_init_error(
+ ["-minimumchainwork=test"],
+ expected_msg='Error: Invalid non-hex (test) minimum chain work value specified',
+ )
+
+
if __name__ == '__main__':
MinimumChainWorkTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 77524e85a3..7dbeccbc09 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -291,7 +291,7 @@ class PruneTest(BitcoinTestFramework):
def prune(index):
ret = node.pruneblockchain(height=height(index))
- assert_equal(ret, node.getblockchaininfo()['pruneheight'])
+ assert_equal(ret + 1, node.getblockchaininfo()['pruneheight'])
def has_block(index):
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, self.chain, "blocks", f"blk{index:05}.dat"))
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index f0ed914461..a8492bd6eb 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -32,7 +32,7 @@ from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
MAX_REPLACEMENT_LIMIT = 100
class ReplaceByFeeTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 1
+ self.num_nodes = 2
self.extra_args = [
[
"-acceptnonstdtxn=1",
@@ -42,6 +42,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
"-limitdescendantcount=200",
"-limitdescendantsize=101",
],
+ # second node has default mempool parameters
+ [
+ ],
]
self.supports_cli = False
@@ -73,6 +76,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.log.info("Running test too many replacements...")
self.test_too_many_replacements()
+ self.log.info("Running test too many replacements using default mempool params...")
+ self.test_too_many_replacements_with_default_mempool_params()
+
self.log.info("Running test opt-in...")
self.test_opt_in()
@@ -397,6 +403,94 @@ class ReplaceByFeeTest(BitcoinTestFramework):
double_tx_hex = double_tx.serialize().hex()
self.nodes[0].sendrawtransaction(double_tx_hex, 0)
+ def test_too_many_replacements_with_default_mempool_params(self):
+ """
+ Test rule 5 of BIP125 (do not allow replacements that cause more than 100
+ evictions) without having to rely on non-default mempool parameters.
+
+ In order to do this, create a number of "root" UTXOs, and then hang
+ enough transactions off of each root UTXO to exceed the MAX_REPLACEMENT_LIMIT.
+ Then create a conflicting RBF replacement transaction.
+ """
+ normal_node = self.nodes[1]
+ wallet = MiniWallet(normal_node)
+ wallet.rescan_utxos()
+ # Clear mempools to avoid cross-node sync failure.
+ for node in self.nodes:
+ self.generate(node, 1)
+
+ # This has to be chosen so that the total number of transactions can exceed
+ # MAX_REPLACEMENT_LIMIT without having any one tx graph run into the descendant
+ # limit; 10 works.
+ num_tx_graphs = 10
+
+ # (Number of transactions per graph, BIP125 rule 5 failure expected)
+ cases = [
+ # Test the base case of evicting fewer than MAX_REPLACEMENT_LIMIT
+ # transactions.
+ ((MAX_REPLACEMENT_LIMIT // num_tx_graphs) - 1, False),
+
+ # Test hitting the rule 5 eviction limit.
+ (MAX_REPLACEMENT_LIMIT // num_tx_graphs, True),
+ ]
+
+ for (txs_per_graph, failure_expected) in cases:
+ self.log.debug(f"txs_per_graph: {txs_per_graph}, failure: {failure_expected}")
+ # "Root" utxos of each txn graph that we will attempt to double-spend with
+ # an RBF replacement.
+ root_utxos = []
+
+ # For each root UTXO, create a package that contains the spend of that
+ # UTXO and `txs_per_graph` children tx.
+ for graph_num in range(num_tx_graphs):
+ root_utxos.append(wallet.get_utxo())
+
+ optin_parent_tx = wallet.send_self_transfer_multi(
+ from_node=normal_node,
+ sequence=BIP125_SEQUENCE_NUMBER,
+ utxos_to_spend=[root_utxos[graph_num]],
+ num_outputs=txs_per_graph,
+ )
+ assert_equal(True, normal_node.getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
+ new_utxos = optin_parent_tx['new_utxos']
+
+ for utxo in new_utxos:
+ # Create spends for each output from the "root" of this graph.
+ child_tx = wallet.send_self_transfer(
+ from_node=normal_node,
+ utxo_to_spend=utxo,
+ )
+
+ assert normal_node.getmempoolentry(child_tx['txid'])
+
+ num_txs_invalidated = len(root_utxos) + (num_tx_graphs * txs_per_graph)
+
+ if failure_expected:
+ assert num_txs_invalidated > MAX_REPLACEMENT_LIMIT
+ else:
+ assert num_txs_invalidated <= MAX_REPLACEMENT_LIMIT
+
+ # Now attempt to submit a tx that double-spends all the root tx inputs, which
+ # would invalidate `num_txs_invalidated` transactions.
+ double_tx = wallet.create_self_transfer_multi(
+ from_node=normal_node,
+ utxos_to_spend=root_utxos,
+ fee_per_output=10_000_000, # absurdly high feerate
+ )
+ tx_hex = double_tx.serialize().hex()
+
+ if failure_expected:
+ assert_raises_rpc_error(
+ -26, "too many potential replacements", normal_node.sendrawtransaction, tx_hex, 0)
+ else:
+ txid = normal_node.sendrawtransaction(tx_hex, 0)
+ assert normal_node.getmempoolentry(txid)
+
+ # Clear the mempool once finished, and rescan the other nodes' wallet
+ # to account for the spends we've made on `normal_node`.
+ self.generate(normal_node, 1)
+ self.wallet.rescan_utxos()
+
def test_opt_in(self):
"""Replacing should only work if orig tx opted in"""
tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index d3961e753d..85464b8d0d 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -76,7 +76,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout[0].nValue = int(0.3 * COIN)
tx.vout[1].nValue = int(49 * COIN)
raw_tx_in_block = tx.serialize().hex()
- txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block, maxfeerate=0)
+ txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block)
self.generate(node, 1)
self.mempool_size = 0
self.check_mempool_result(
@@ -166,7 +166,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vin[1].prevout = COutPoint(hash=int(txid_1, 16), n=0)
tx.vout[0].nValue = int(0.1 * COIN)
raw_tx_spend_both = tx.serialize().hex()
- txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both, maxfeerate=0)
+ txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both)
self.generate(node, 1)
self.mempool_size = 0
# Now see if we can add the coins back to the utxo set by sending the exact txs again
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 068fdc0b65..a2a2caf324 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -100,6 +100,12 @@ class MempoolPackagesTest(BitcoinTestFramework):
entry = self.nodes[0].getmempoolentry(x)
assert_equal(entry, mempool[x])
+ # Check that gettxspendingprevout is consistent with getrawmempool
+ witnesstx = self.nodes[0].gettransaction(txid=x, verbose=True)['decoded']
+ for tx_in in witnesstx["vin"]:
+ spending_result = self.nodes[0].gettxspendingprevout([ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"]} ])
+ assert_equal(spending_result, [ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"], 'spendingtxid' : x} ])
+
# Check that the descendant calculations are correct
assert_equal(entry['descendantcount'], descendant_count)
descendant_fees += entry['fees']['base']
diff --git a/test/functional/p2p_block_sync.py b/test/functional/p2p_block_sync.py
new file mode 100755
index 0000000000..d821edc1b1
--- /dev/null
+++ b/test/functional/p2p_block_sync.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+# 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.
+"""Test block download
+
+Ensure that even in IBD, we'll eventually sync chain from inbound peers
+(whether we have only inbound peers or both inbound and outbound peers).
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+
+class BlockSyncTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ def setup_network(self):
+ self.setup_nodes()
+ # Construct a network:
+ # node0 -> node1 -> node2
+ # So node1 has both an inbound and outbound peer.
+ # In our test, we will mine a block on node0, and ensure that it makes
+ # to to both node1 and node2.
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+
+ def run_test(self):
+ self.log.info("Setup network: node0->node1->node2")
+ self.log.info("Mining one block on node0 and verify all nodes sync")
+ self.generate(self.nodes[0], 1)
+ self.log.info("Success!")
+
+
+if __name__ == '__main__':
+ BlockSyncTest().main()
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index 2ffc825acd..ef12b5f6b7 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -250,6 +250,11 @@ class CompactFiltersTest(BitcoinTestFramework):
msg = "Error: Cannot set -peerblockfilters without -blockfilterindex."
self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
+ self.log.info("Test unknown value to -blockfilterindex raises an error")
+ self.nodes[0].extra_args = ["-blockfilterindex=abc"]
+ msg = "Error: Unknown -blockfilterindex value abc."
+ self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
+
self.log.info("Test -blockfilterindex with -reindex-chainstate raises an error")
self.nodes[0].assert_start_raises_init_error(
expected_msg='Error: -reindex-chainstate option is not compatible with -blockfilterindex. '
diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py
index d375af6fe1..0d42bd9d9e 100755
--- a/test/functional/p2p_getaddr_caching.py
+++ b/test/functional/p2p_getaddr_caching.py
@@ -14,6 +14,7 @@ from test_framework.p2p import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ p2p_port,
)
# As defined in net_processing.
@@ -42,6 +43,12 @@ class AddrReceiver(P2PInterface):
class AddrTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ # Use some of the remaining p2p ports for the onion binds.
+ self.onion_port1 = p2p_port(1)
+ self.onion_port2 = p2p_port(2)
+ self.extra_args = [
+ [f"-bind=127.0.0.1:{self.onion_port1}=onion", f"-bind=127.0.0.1:{self.onion_port2}=onion"],
+ ]
def run_test(self):
self.log.info('Fill peer AddrMan with a lot of records')
@@ -55,35 +62,66 @@ class AddrTest(BitcoinTestFramework):
# only a fraction of all known addresses can be cached and returned.
assert(len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100)))
- responses = []
+ last_response_on_local_bind = None
+ last_response_on_onion_bind1 = None
+ last_response_on_onion_bind2 = None
self.log.info('Send many addr requests within short time to receive same response')
N = 5
cur_mock_time = int(time.time())
for i in range(N):
- addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
- addr_receiver.send_and_ping(msg_getaddr())
+ addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
+ addr_receiver_local.send_and_ping(msg_getaddr())
+ addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
+ addr_receiver_onion1.send_and_ping(msg_getaddr())
+ addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
+ addr_receiver_onion2.send_and_ping(msg_getaddr())
+
# Trigger response
cur_mock_time += 5 * 60
self.nodes[0].setmocktime(cur_mock_time)
- addr_receiver.wait_until(addr_receiver.addr_received)
- responses.append(addr_receiver.get_received_addrs())
- for response in responses[1:]:
- assert_equal(response, responses[0])
- assert(len(response) == MAX_ADDR_TO_SEND)
+ addr_receiver_local.wait_until(addr_receiver_local.addr_received)
+ addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
+ addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
+
+ if i > 0:
+ # Responses from different binds should be unique
+ assert(last_response_on_local_bind != addr_receiver_onion1.get_received_addrs())
+ assert(last_response_on_local_bind != addr_receiver_onion2.get_received_addrs())
+ assert(last_response_on_onion_bind1 != addr_receiver_onion2.get_received_addrs())
+ # Responses on from the same bind should be the same
+ assert_equal(last_response_on_local_bind, addr_receiver_local.get_received_addrs())
+ assert_equal(last_response_on_onion_bind1, addr_receiver_onion1.get_received_addrs())
+ assert_equal(last_response_on_onion_bind2, addr_receiver_onion2.get_received_addrs())
+
+ last_response_on_local_bind = addr_receiver_local.get_received_addrs()
+ last_response_on_onion_bind1 = addr_receiver_onion1.get_received_addrs()
+ last_response_on_onion_bind2 = addr_receiver_onion2.get_received_addrs()
+
+ for response in [last_response_on_local_bind, last_response_on_onion_bind1, last_response_on_onion_bind2]:
+ assert_equal(len(response), MAX_ADDR_TO_SEND)
cur_mock_time += 3 * 24 * 60 * 60
self.nodes[0].setmocktime(cur_mock_time)
self.log.info('After time passed, see a new response to addr request')
- last_addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
- last_addr_receiver.send_and_ping(msg_getaddr())
+ addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
+ addr_receiver_local.send_and_ping(msg_getaddr())
+ addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
+ addr_receiver_onion1.send_and_ping(msg_getaddr())
+ addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
+ addr_receiver_onion2.send_and_ping(msg_getaddr())
+
# Trigger response
cur_mock_time += 5 * 60
self.nodes[0].setmocktime(cur_mock_time)
- last_addr_receiver.wait_until(last_addr_receiver.addr_received)
- # new response is different
- assert(set(responses[0]) != set(last_addr_receiver.get_received_addrs()))
+ addr_receiver_local.wait_until(addr_receiver_local.addr_received)
+ addr_receiver_onion1.wait_until(addr_receiver_onion1.addr_received)
+ addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received)
+ # new response is different
+ assert(set(last_response_on_local_bind) != set(addr_receiver_local.get_received_addrs()))
+ assert(set(last_response_on_onion_bind1) != set(addr_receiver_onion1.get_received_addrs()))
+ assert(set(last_response_on_onion_bind2) != set(addr_receiver_onion2.get_received_addrs()))
if __name__ == '__main__':
AddrTest().main()
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 1695acaaa8..716ee8f7ef 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -91,15 +91,17 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
assert 'warnings' not in result
# Generate addresses with the segwit types. These should all make legacy addresses
+ err_msg = ["Unable to make chosen address type, please ensure no uncompressed public keys are present."]
+
for addr_type in ['bech32', 'p2sh-segwit']:
- result = self.nodes[0].createmultisig(2, keys, addr_type)
+ result = self.nodes[0].createmultisig(nrequired=2, keys=keys, address_type=addr_type)
assert_equal(legacy_addr, result['address'])
- assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
+ assert_equal(result['warnings'], err_msg)
if self.is_bdb_compiled():
- result = wmulti0.addmultisigaddress(2, keys, '', addr_type)
+ result = wmulti0.addmultisigaddress(nrequired=2, keys=keys, address_type=addr_type)
assert_equal(legacy_addr, result['address'])
- assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
+ assert_equal(result['warnings'], err_msg)
self.log.info('Testing sortedmulti descriptors with BIP 67 test vectors')
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f:
@@ -173,6 +175,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
desc = descsum_create(desc)
msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
+ assert 'warnings' not in msig
madd = msig["address"]
mredeem = msig["redeemScript"]
assert_equal(desc, msig['descriptor'])
diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py
index b65322d920..a7628b5591 100755
--- a/test/functional/rpc_getblockfrompeer.py
+++ b/test/functional/rpc_getblockfrompeer.py
@@ -5,6 +5,11 @@
"""Test the getblockfrompeer RPC."""
from test_framework.authproxy import JSONRPCException
+from test_framework.messages import NODE_WITNESS
+from test_framework.p2p import (
+ P2P_SERVICES,
+ P2PInterface,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -58,6 +63,13 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
self.log.info("Non-existent peer generates error")
assert_raises_rpc_error(-1, "Peer does not exist", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id + 1)
+ self.log.info("Fetching from pre-segwit peer generates error")
+ self.nodes[0].add_p2p_connection(P2PInterface(), services=P2P_SERVICES & ~NODE_WITNESS)
+ peers = self.nodes[0].getpeerinfo()
+ assert_equal(len(peers), 2)
+ presegwit_peer_id = peers[1]["id"]
+ assert_raises_rpc_error(-1, "Pre-SegWit peer", self.nodes[0].getblockfrompeer, short_tip, presegwit_peer_id)
+
self.log.info("Successful fetch")
result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id)
self.wait_until(lambda: self.check_for_block(short_tip), timeout=1)
@@ -66,5 +78,6 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
self.log.info("Don't fetch blocks we already have")
assert_raises_rpc_error(-1, "Block already downloaded", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id)
+
if __name__ == '__main__':
GetBlockFromPeerTest().main()
diff --git a/test/functional/rpc_mempool_entry_fee_fields_deprecation.py b/test/functional/rpc_mempool_entry_fee_fields_deprecation.py
deleted file mode 100755
index 82761ff7c8..0000000000
--- a/test/functional/rpc_mempool_entry_fee_fields_deprecation.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 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.
-"""Test deprecation of fee fields from top level mempool entry object"""
-
-from test_framework.blocktools import COIN
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-from test_framework.wallet import MiniWallet
-
-
-def assertions_helper(new_object, deprecated_object, deprecated_fields):
- for field in deprecated_fields:
- assert field in deprecated_object
- assert field not in new_object
-
-
-class MempoolFeeFieldsDeprecationTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 2
- self.extra_args = [[], ["-deprecatedrpc=fees"]]
-
- def run_test(self):
- # we get spendable outputs from the premined chain starting
- # at block 76. see BitcoinTestFramework._initialize_chain() for details
- self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
-
- # we create the tx on the first node and wait until it syncs to node_deprecated
- # thus, any differences must be coming from getmempoolentry or getrawmempool
- tx = self.wallet.send_self_transfer(from_node=self.nodes[0])
- self.nodes[1].sendrawtransaction(tx["hex"])
-
- deprecated_fields = ["ancestorfees", "descendantfees", "modifiedfee", "fee"]
- self.test_getmempoolentry(tx["txid"], deprecated_fields)
- self.test_getrawmempool(tx["txid"], deprecated_fields)
- self.test_deprecated_fields_match(tx["txid"])
-
- def test_getmempoolentry(self, txid, deprecated_fields):
-
- self.log.info("Test getmempoolentry rpc")
- entry = self.nodes[0].getmempoolentry(txid)
- deprecated_entry = self.nodes[1].getmempoolentry(txid)
- assertions_helper(entry, deprecated_entry, deprecated_fields)
-
- def test_getrawmempool(self, txid, deprecated_fields):
-
- self.log.info("Test getrawmempool rpc")
- entry = self.nodes[0].getrawmempool(verbose=True)[txid]
- deprecated_entry = self.nodes[1].getrawmempool(verbose=True)[txid]
- assertions_helper(entry, deprecated_entry, deprecated_fields)
-
- def test_deprecated_fields_match(self, txid):
-
- self.log.info("Test deprecated fee fields match new fees object")
- entry = self.nodes[0].getmempoolentry(txid)
- deprecated_entry = self.nodes[1].getmempoolentry(txid)
-
- assert_equal(deprecated_entry["fee"], entry["fees"]["base"])
- assert_equal(deprecated_entry["modifiedfee"], entry["fees"]["modified"])
- assert_equal(deprecated_entry["descendantfees"], entry["fees"]["descendant"] * COIN)
- assert_equal(deprecated_entry["ancestorfees"], entry["fees"]["ancestor"] * COIN)
-
-
-if __name__ == "__main__":
- MempoolFeeFieldsDeprecationTest().main()
diff --git a/test/functional/rpc_mempool_info.py b/test/functional/rpc_mempool_info.py
new file mode 100755
index 0000000000..cd7a48d387
--- /dev/null
+++ b/test/functional/rpc_mempool_info.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-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.
+"""Test RPCs that retrieve information from the mempool."""
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+from test_framework.wallet import MiniWallet
+
+
+class RPCMempoolInfoTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+ self.generate(self.wallet, COINBASE_MATURITY + 1)
+ self.wallet.rescan_utxos()
+ confirmed_utxo = self.wallet.get_utxo()
+
+ # Create a tree of unconfirmed transactions in the mempool:
+ # txA
+ # / \
+ # / \
+ # / \
+ # / \
+ # / \
+ # txB txC
+ # / \ / \
+ # / \ / \
+ # txD txE txF txG
+ # \ /
+ # \ /
+ # txH
+
+ def create_tx(**kwargs):
+ return self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ **kwargs,
+ )
+
+ txA = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
+ txB = create_tx(utxos_to_spend=[txA["new_utxos"][0]], num_outputs=2)
+ txC = create_tx(utxos_to_spend=[txA["new_utxos"][1]], num_outputs=2)
+ txD = create_tx(utxos_to_spend=[txB["new_utxos"][0]], num_outputs=1)
+ txE = create_tx(utxos_to_spend=[txB["new_utxos"][1]], num_outputs=1)
+ txF = create_tx(utxos_to_spend=[txC["new_utxos"][0]], num_outputs=2)
+ txG = create_tx(utxos_to_spend=[txC["new_utxos"][1]], num_outputs=1)
+ txH = create_tx(utxos_to_spend=[txE["new_utxos"][0],txF["new_utxos"][0]], num_outputs=1)
+ txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH = [
+ tx["txid"] for tx in [txA, txB, txC, txD, txE, txF, txG, txH]
+ ]
+
+ mempool = self.nodes[0].getrawmempool()
+ assert_equal(len(mempool), 8)
+ for txid in [txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH]:
+ assert_equal(txid in mempool, True)
+
+ self.log.info("Find transactions spending outputs")
+ result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ])
+ assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : txidA}, {'txid' : txidA, 'vout' : 1, 'spendingtxid' : txidC} ])
+
+ self.log.info("Find transaction spending multiple outputs")
+ result = self.nodes[0].gettxspendingprevout([ {'txid' : txidE, 'vout' : 0}, {'txid' : txidF, 'vout' : 0} ])
+ assert_equal(result, [ {'txid' : txidE, 'vout' : 0, 'spendingtxid' : txidH}, {'txid' : txidF, 'vout' : 0, 'spendingtxid' : txidH} ])
+
+ self.log.info("Find no transaction when output is unspent")
+ result = self.nodes[0].gettxspendingprevout([ {'txid' : txidH, 'vout' : 0} ])
+ assert_equal(result, [ {'txid' : txidH, 'vout' : 0} ])
+ result = self.nodes[0].gettxspendingprevout([ {'txid' : txidA, 'vout' : 5} ])
+ assert_equal(result, [ {'txid' : txidA, 'vout' : 5} ])
+
+ self.log.info("Mixed spent and unspent outputs")
+ result = self.nodes[0].gettxspendingprevout([ {'txid' : txidB, 'vout' : 0}, {'txid' : txidG, 'vout' : 3} ])
+ assert_equal(result, [ {'txid' : txidB, 'vout' : 0, 'spendingtxid' : txidD}, {'txid' : txidG, 'vout' : 3} ])
+
+ self.log.info("Unknown input fields")
+ assert_raises_rpc_error(-3, "Unexpected key unknown", self.nodes[0].gettxspendingprevout, [{'txid' : txidC, 'vout' : 1, 'unknown' : 42}])
+
+ self.log.info("Invalid vout provided")
+ assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].gettxspendingprevout, [{'txid' : txidA, 'vout' : -1}])
+
+ self.log.info("Invalid txid provided")
+ assert_raises_rpc_error(-3, "Expected type string for txid, got number", self.nodes[0].gettxspendingprevout, [{'txid' : 42, 'vout' : 0}])
+
+ self.log.info("Missing outputs")
+ assert_raises_rpc_error(-8, "Invalid parameter, outputs are missing", self.nodes[0].gettxspendingprevout, [])
+
+ self.log.info("Missing vout")
+ assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].gettxspendingprevout, [{'txid' : txidA}])
+
+ self.log.info("Missing txid")
+ assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].gettxspendingprevout, [{'vout' : 3}])
+
+
+if __name__ == '__main__':
+ RPCMempoolInfoTest().main()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index a839af0288..1f95814e18 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -17,6 +17,7 @@ from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
CTransaction,
tx_from_hex,
)
@@ -24,7 +25,10 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- find_vout_for_address,
+)
+from test_framework.wallet import (
+ getnewdestination,
+ MiniWallet,
)
@@ -52,79 +56,67 @@ class multidict(dict):
class RawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 4
+ self.num_nodes = 3
self.extra_args = [
["-txindex"],
["-txindex"],
- ["-txindex"],
[],
]
# whitelist all peers to speed up tx relay / mempool sync
for args in self.extra_args:
args.append("-whitelist=noban@127.0.0.1")
+ self.requires_wallet = self.is_specified_wallet_compiled()
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def setup_network(self):
super().setup_network()
self.connect_nodes(0, 2)
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
self.log.info("Prepare some coins for multiple *rawtransaction commands")
- self.generate(self.nodes[2], 1)
+ self.generate(self.wallet, 10)
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
- for amount in [1.5, 1.0, 5.0]:
- self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), amount)
- self.sync_all()
- self.generate(self.nodes[0], 5)
self.getrawtransaction_tests()
self.createrawtransaction_tests()
- self.signrawtransactionwithwallet_tests()
self.sendrawtransaction_tests()
self.sendrawtransaction_testmempoolaccept_tests()
self.decoderawtransaction_tests()
self.transaction_version_number_tests()
- if not self.options.descriptors:
+ if self.requires_wallet and not self.options.descriptors:
self.raw_multisig_transaction_legacy_tests()
def getrawtransaction_tests(self):
- addr = self.nodes[1].getnewaddress()
- txid = self.nodes[0].sendtoaddress(addr, 10)
+ tx = self.wallet.send_self_transfer(from_node=self.nodes[0])
self.generate(self.nodes[0], 1)
- vout = find_vout_for_address(self.nodes[1], txid, addr)
- rawTx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): 9.999})
- rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx)
- txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex'])
- self.generateblock(self.nodes[0], output=self.nodes[0].getnewaddress(), transactions=[rawTxSigned['hex']])
+ txId = tx['txid']
err_msg = (
"No such mempool transaction. Use -txindex or provide a block hash to enable"
" blockchain transaction queries. Use gettransaction for wallet transactions."
)
- for n in [0, 3]:
+ for n in [0, 2]:
self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex")
if n == 0:
# With -txindex.
# 1. valid parameters - only supply txid
- assert_equal(self.nodes[n].getrawtransaction(txId), rawTxSigned['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId), tx['hex'])
# 2. valid parameters - supply txid and 0 for non-verbose
- assert_equal(self.nodes[n].getrawtransaction(txId, 0), rawTxSigned['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId, 0), tx['hex'])
# 3. valid parameters - supply txid and False for non-verbose
- assert_equal(self.nodes[n].getrawtransaction(txId, False), rawTxSigned['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId, False), tx['hex'])
# 4. valid parameters - supply txid and 1 for verbose.
# We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
- assert_equal(self.nodes[n].getrawtransaction(txId, 1)["hex"], rawTxSigned['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId, 1)["hex"], tx['hex'])
# 5. valid parameters - supply txid and True for non-verbose
- assert_equal(self.nodes[n].getrawtransaction(txId, True)["hex"], rawTxSigned['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId, True)["hex"], tx['hex'])
else:
# Without -txindex, expect to raise.
for verbose in [None, 0, False, 1, True]:
@@ -141,9 +133,9 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, "not a boolean", self.nodes[n].getrawtransaction, txId, {})
# Make a tx by sending, then generate 2 blocks; block1 has the tx in it
- tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+ tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid']
block1, block2 = self.generate(self.nodes[2], 2)
- for n in [0, 3]:
+ for n in [0, 2]:
self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex, with blockhash")
# We should be able to get the raw transaction by providing the correct block
gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
@@ -200,20 +192,21 @@ class RawTransactionsTest(BitcoinTestFramework):
# sequence number out of range
for invalid_seq in [-1, 4294967296]:
inputs = [{'txid': TXID, 'vout': 1, 'sequence': invalid_seq}]
- outputs = {self.nodes[0].getnewaddress(): 1}
+ address = getnewdestination()[2]
+ outputs = {address: 1}
assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range',
self.nodes[0].createrawtransaction, inputs, outputs)
# with valid sequence number
for valid_seq in [1000, 4294967294]:
inputs = [{'txid': TXID, 'vout': 1, 'sequence': valid_seq}]
- outputs = {self.nodes[0].getnewaddress(): 1}
+ address = getnewdestination()[2]
+ outputs = {address: 1}
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['vin'][0]['sequence'], valid_seq)
# Test `createrawtransaction` invalid `outputs`
- address = self.nodes[0].getnewaddress()
- address2 = self.nodes[0].getnewaddress()
+ address = getnewdestination()[2]
assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo')
self.nodes[0].createrawtransaction(inputs=[], outputs={}) # Should not throw for backwards compatibility
self.nodes[0].createrawtransaction(inputs=[], outputs=[])
@@ -228,6 +221,10 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}])
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']])
+ # Test `createrawtransaction` mismatch between sequence number(s) and `replaceable` option
+ assert_raises_rpc_error(-8, "Invalid parameter combination: Sequence number(s) contradict replaceable option",
+ self.nodes[0].createrawtransaction, [{'txid': TXID, 'vout': 0, 'sequence': BIP125_SEQUENCE_NUMBER+1}], {}, 0, True)
+
# Test `createrawtransaction` invalid `locktime`
assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo')
assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1)
@@ -245,6 +242,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[2].createrawtransaction(inputs=[{'txid': TXID, 'vout': 9}], outputs=[{address: 99}]),
)
# Two outputs
+ address2 = getnewdestination()[2]
tx = tx_from_hex(self.nodes[2].createrawtransaction(inputs=[{'txid': TXID, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)])))
assert_equal(len(tx.vout), 2)
assert_equal(
@@ -259,122 +257,53 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[2].createrawtransaction(inputs=[{'txid': TXID, 'vout': 9}], outputs=[{address: 99}, {address2: 99}, {'data': '99'}]),
)
- def signrawtransactionwithwallet_tests(self):
- for type in ["bech32", "p2sh-segwit", "legacy"]:
- self.log.info(f"Test signrawtransactionwithwallet with missing prevtx info ({type})")
- addr = self.nodes[0].getnewaddress("", type)
- addrinfo = self.nodes[0].getaddressinfo(addr)
- pubkey = addrinfo["scriptPubKey"]
- inputs = [{'txid': TXID, 'vout': 3, 'sequence': 1000}]
- outputs = {self.nodes[0].getnewaddress(): 1}
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
-
- prevtx = dict(txid=TXID, scriptPubKey=pubkey, vout=3, amount=1)
- succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
- assert succ["complete"]
-
- if type == "legacy":
- del prevtx["amount"]
- succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
- assert succ["complete"]
- else:
- assert_raises_rpc_error(-3, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [
- {
- "txid": TXID,
- "scriptPubKey": pubkey,
- "vout": 3,
- }
- ])
-
- assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [
- {
- "txid": TXID,
- "scriptPubKey": pubkey,
- "amount": 1,
- }
- ])
- assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [
- {
- "scriptPubKey": pubkey,
- "vout": 3,
- "amount": 1,
- }
- ])
- assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [
- {
- "txid": TXID,
- "vout": 3,
- "amount": 1
- }
- ])
-
def sendrawtransaction_tests(self):
self.log.info("Test sendrawtransaction with missing input")
inputs = [{'txid': TXID, 'vout': 1}] # won't exist
- outputs = {self.nodes[0].getnewaddress(): 4.998}
+ address = getnewdestination()[2]
+ outputs = {address: 4.998}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx)
- assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx['hex'])
+ assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx)
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)"
# Test a transaction with a small fee.
- txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
- rawTx = self.nodes[0].getrawtransaction(txId, True)
- vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
-
- self.sync_all()
- inputs = [{"txid": txId, "vout": vout['n']}]
- # Fee 10,000 satoshis, (1 - (10000 sat * 0.00000001 BTC/sat)) = 0.9999
- outputs = {self.nodes[0].getnewaddress(): Decimal("0.99990000")}
- rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
- rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
- assert_equal(rawTxSigned['complete'], True)
- # Fee 10,000 satoshis, ~100 b transaction, fee rate should land around 100 sat/byte = 0.00100000 BTC/kB
+ # Fee rate is 0.00100000 BTC/kvB
+ tx = self.wallet.create_self_transfer(fee_rate=Decimal('0.00100000'))
# Thus, testmempoolaccept should reject
- testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0]
+ testres = self.nodes[2].testmempoolaccept([tx['hex']], 0.00001000)[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
- assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
+ assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'], 0.00001000)
# and the following calls should both succeed
- testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']])[0]
+ testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']])[0]
assert_equal(testres['allowed'], True)
- self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'])
+ self.nodes[2].sendrawtransaction(hexstring=tx['hex'])
# Test a transaction with a large fee.
- txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
- rawTx = self.nodes[0].getrawtransaction(txId, True)
- vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
-
- self.sync_all()
- inputs = [{"txid": txId, "vout": vout['n']}]
- # Fee 2,000,000 satoshis, (1 - (2000000 sat * 0.00000001 BTC/sat)) = 0.98
- outputs = {self.nodes[0].getnewaddress() : Decimal("0.98000000")}
- rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
- rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
- assert_equal(rawTxSigned['complete'], True)
- # Fee 2,000,000 satoshis, ~100 b transaction, fee rate should land around 20,000 sat/byte = 0.20000000 BTC/kB
+ # Fee rate is 0.20000000 BTC/kvB
+ tx = self.wallet.create_self_transfer(mempool_valid=False, from_node=self.nodes[0], fee_rate=Decimal('0.20000000'))
# Thus, testmempoolaccept should reject
- testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']])[0]
+ testres = self.nodes[2].testmempoolaccept([tx['hex']])[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
- assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, rawTxSigned['hex'])
+ assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'])
# and the following calls should both succeed
- testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.20000000')[0]
+ testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']], maxfeerate='0.20000000')[0]
assert_equal(testres['allowed'], True)
- self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000')
+ self.nodes[2].sendrawtransaction(hexstring=tx['hex'], maxfeerate='0.20000000')
self.log.info("Test sendrawtransaction/testmempoolaccept with tx already in the chain")
self.generate(self.nodes[2], 1)
for node in self.nodes:
- testres = node.testmempoolaccept([rawTxSigned['hex']])[0]
+ testres = node.testmempoolaccept([tx['hex']])[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'txn-already-known')
- assert_raises_rpc_error(-27, 'Transaction already in block chain', node.sendrawtransaction, rawTxSigned['hex'])
+ assert_raises_rpc_error(-27, 'Transaction already in block chain', node.sendrawtransaction, tx['hex'])
def decoderawtransaction_tests(self):
self.log.info("Test decoderawtransaction")
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index a2091b4ece..8da2cfa72b 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -334,6 +334,56 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert_equal(signed["complete"], True)
self.nodes[0].sendrawtransaction(signed["hex"])
+ def test_signing_with_missing_prevtx_info(self):
+ txid = "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000"
+ for type in ["bech32", "p2sh-segwit", "legacy"]:
+ self.log.info(f"Test signing with missing prevtx info ({type})")
+ addr = self.nodes[0].getnewaddress("", type)
+ addrinfo = self.nodes[0].getaddressinfo(addr)
+ pubkey = addrinfo["scriptPubKey"]
+ inputs = [{'txid': txid, 'vout': 3, 'sequence': 1000}]
+ outputs = {self.nodes[0].getnewaddress(): 1}
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+
+ prevtx = dict(txid=txid, scriptPubKey=pubkey, vout=3, amount=1)
+ succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
+ assert succ["complete"]
+
+ if type == "legacy":
+ del prevtx["amount"]
+ succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
+ assert succ["complete"]
+ else:
+ assert_raises_rpc_error(-3, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [
+ {
+ "txid": txid,
+ "scriptPubKey": pubkey,
+ "vout": 3,
+ }
+ ])
+
+ assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [
+ {
+ "txid": txid,
+ "scriptPubKey": pubkey,
+ "amount": 1,
+ }
+ ])
+ assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [
+ {
+ "scriptPubKey": pubkey,
+ "vout": 3,
+ "amount": 1,
+ }
+ ])
+ assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [
+ {
+ "txid": txid,
+ "vout": 3,
+ "amount": 1
+ }
+ ])
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
@@ -343,6 +393,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
self.test_fully_signed_tx()
self.test_signing_with_csv()
self.test_signing_with_cltv()
+ self.test_signing_with_missing_prevtx_info()
if __name__ == '__main__':
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index a39ee003ef..3f02d21d42 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -223,11 +223,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# It still needs to exist and be None in order for tests to work however.
self.options.descriptors = None
+ PortSeed.n = self.options.port_seed
+
def setup(self):
"""Call this method to start up the test framework object with options set."""
- PortSeed.n = self.options.port_seed
-
check_json_precision()
self.options.cachedir = os.path.abspath(self.options.cachedir)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 7d2db391b6..03f6c8adea 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -423,7 +423,7 @@ class TestNode():
self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
- def wait_for_debug_log(self, expected_msgs, timeout=60, ignore_case=False):
+ def wait_for_debug_log(self, expected_msgs, timeout=60):
"""
Block until we see a particular debug log message fragment or until we exceed the timeout.
Return:
@@ -431,18 +431,17 @@ class TestNode():
"""
time_end = time.time() + timeout * self.timeout_factor
prev_size = self.debug_log_bytes()
- re_flags = re.MULTILINE | (re.IGNORECASE if ignore_case else 0)
yield
while True:
found = True
- with open(self.debug_log_path, encoding='utf-8') as dl:
+ with open(self.debug_log_path, "rb") as dl:
dl.seek(prev_size)
log = dl.read()
for expected_msg in expected_msgs:
- if re.search(re.escape(expected_msg), log, flags=re_flags) is None:
+ if expected_msg not in log:
found = False
if found:
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 6901bcfe66..e43dd9f61a 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -10,6 +10,7 @@ from enum import Enum
from random import choice
from typing import (
Any,
+ List,
Optional,
)
from test_framework.address import (
@@ -147,7 +148,7 @@ class MiniWallet:
def get_address(self):
return self._address
- def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True):
+ def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True) -> dict:
"""
Returns a utxo and marks it as spent (pops it from the internal list)
@@ -167,6 +168,13 @@ class MiniWallet:
else:
return self._utxos[index]
+ def get_utxos(self, *, mark_as_spent=True):
+ """Returns the list of all utxos and optionally mark them as spent"""
+ utxos = deepcopy(self._utxos)
+ if mark_as_spent:
+ self._utxos = []
+ return utxos
+
def send_self_transfer(self, **kwargs):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
tx = self.create_self_transfer(**kwargs)
@@ -208,14 +216,21 @@ class MiniWallet:
return {'new_utxos': [self.get_utxo(txid=txid, vout=vout) for vout in range(len(tx.vout))],
'txid': txid, 'hex': tx.serialize().hex(), 'tx': tx}
- def create_self_transfer_multi(self, *, from_node, utxos_to_spend=None, num_outputs=1, fee_per_output=1000):
+ def create_self_transfer_multi(
+ self, *, from_node,
+ utxos_to_spend: Optional[List[dict]] = None,
+ num_outputs=1,
+ sequence=0,
+ fee_per_output=1000):
"""
Create and return a transaction that spends the given UTXOs and creates a
certain number of outputs with equal amounts.
"""
utxos_to_spend = utxos_to_spend or [self.get_utxo()]
# create simple tx template (1 input, 1 output)
- tx = self.create_self_transfer(fee_rate=0, from_node=from_node, utxo_to_spend=utxos_to_spend[0], mempool_valid=False)['tx']
+ tx = self.create_self_transfer(
+ fee_rate=0, from_node=from_node,
+ utxo_to_spend=utxos_to_spend[0], sequence=sequence, mempool_valid=False)['tx']
# duplicate inputs, witnesses and outputs
tx.vin = [deepcopy(tx.vin[0]) for _ in range(len(utxos_to_spend))]
@@ -270,8 +285,8 @@ class MiniWallet:
return {'txid': tx.rehash(), 'wtxid': tx.getwtxid(), 'hex': tx_hex, 'tx': tx}
- def sendrawtransaction(self, *, from_node, tx_hex, **kwargs):
- txid = from_node.sendrawtransaction(hexstring=tx_hex, **kwargs)
+ def sendrawtransaction(self, *, from_node, tx_hex, maxfeerate=0, **kwargs):
+ txid = from_node.sendrawtransaction(hexstring=tx_hex, maxfeerate=maxfeerate, **kwargs)
self.scan_tx(from_node.decoderawtransaction(tx_hex))
return txid
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 8416a5881d..6a44f9d21d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -157,6 +157,7 @@ BASE_SCRIPTS = [
'wallet_avoidreuse.py --descriptors',
'mempool_reorg.py',
'mempool_persist.py',
+ 'p2p_block_sync.py',
'wallet_multiwallet.py --legacy-wallet',
'wallet_multiwallet.py --descriptors',
'wallet_multiwallet.py --usecli',
@@ -183,7 +184,6 @@ BASE_SCRIPTS = [
'rpc_signrawtransaction.py --legacy-wallet',
'rpc_signrawtransaction.py --descriptors',
'rpc_rawtransaction.py --legacy-wallet',
- 'rpc_rawtransaction.py --descriptors',
'wallet_groups.py --legacy-wallet',
'wallet_transactiontime_rescan.py --descriptors',
'wallet_transactiontime_rescan.py --legacy-wallet',
@@ -328,7 +328,7 @@ BASE_SCRIPTS = [
'feature_presegwit_node_upgrade.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
- 'rpc_mempool_entry_fee_fields_deprecation.py',
+ 'rpc_mempool_info.py',
'rpc_help.py',
'feature_dirsymlinks.py',
'feature_help.py',
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index c52a127c86..db1d8eb54a 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -129,6 +129,9 @@ class ReceivedByTest(BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
+ # getreceivedbylabel returns an error if the wallet doesn't own the label
+ assert_raises_rpc_error(-4, "Label not found in wallet", self.nodes[0].getreceivedbylabel, "dummy")
+
# listreceivedbylabel should return received_by_label_json because of 0 confirmations
assert_array_result(self.nodes[1].listreceivedbylabel(),
{"label": label},
diff --git a/test/lint/README.md b/test/lint/README.md
index 1f683c10b3..a23211a72b 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -39,6 +39,6 @@ To do so, add the upstream repository as remote:
git remote add --fetch secp256k1 https://github.com/bitcoin-core/secp256k1.git
```
-lint-all.py
+all-lint.py
===========
Calls other scripts with the `lint-` prefix.
diff --git a/test/lint/lint-all.py b/test/lint/all-lint.py
index c280ba2db2..34a7b9742a 100755
--- a/test/lint/lint-all.py
+++ b/test/lint/all-lint.py
@@ -13,11 +13,10 @@ from subprocess import run
exit_code = 0
mod_path = Path(__file__).parent
-for lint in glob(f"{mod_path}/lint-*"):
- if lint != __file__:
- result = run([lint])
- if result.returncode != 0:
- print(f"^---- failure generated from {lint.split('/')[-1]}")
- exit_code |= result.returncode
+for lint in glob(f"{mod_path}/lint-*.py"):
+ result = run([lint])
+ if result.returncode != 0:
+ print(f"^---- failure generated from {lint.split('/')[-1]}")
+ exit_code |= result.returncode
exit(exit_code)
diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py
index 7ca2ec994b..5d157eb4b1 100755
--- a/test/lint/lint-circular-dependencies.py
+++ b/test/lint/lint-circular-dependencies.py
@@ -14,7 +14,6 @@ import sys
EXPECTED_CIRCULAR_DEPENDENCIES = (
"chainparamsbase -> util/system -> chainparamsbase",
"node/blockstorage -> validation -> node/blockstorage",
- "index/coinstatsindex -> node/coinstats -> index/coinstatsindex",
"policy/fees -> txmempool -> policy/fees",
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel",
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel",
@@ -22,7 +21,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES = (
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel",
"wallet/fees -> wallet/wallet -> wallet/fees",
"wallet/wallet -> wallet/walletdb -> wallet/wallet",
- "node/coinstats -> validation -> node/coinstats",
+ "kernel/coinstats -> validation -> kernel/coinstats",
)
CODE_DIR = "src"
diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py
index 3723e0ee6a..dbb51ce54e 100755
--- a/test/lint/lint-files.py
+++ b/test/lint/lint-files.py
@@ -11,19 +11,20 @@ import os
import re
import sys
from subprocess import check_output
-from typing import Optional, NoReturn
+from typing import Dict, Optional, NoReturn
CMD_TOP_LEVEL = ["git", "rev-parse", "--show-toplevel"]
-CMD_ALL_FILES = "git ls-files -z --full-name"
-CMD_SOURCE_FILES = 'git ls-files -z --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]"'
-CMD_SHEBANG_FILES = "git grep --full-name --line-number -I '^#!'"
+CMD_ALL_FILES = ["git", "ls-files", "-z", "--full-name", "--stage"]
+CMD_SHEBANG_FILES = ["git", "grep", "--full-name", "--line-number", "-I", "^#!"]
+
+ALL_SOURCE_FILENAMES_REGEXP = r"^.*\.(cpp|h|py|sh)$"
ALLOWED_FILENAME_REGEXP = "^[a-zA-Z0-9/_.@][a-zA-Z0-9/_.@-]*$"
ALLOWED_SOURCE_FILENAME_REGEXP = "^[a-z0-9_./-]+$"
ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXP = (
"^src/(secp256k1/|minisketch/|univalue/|test/fuzz/FuzzedDataProvider.h)"
)
-ALLOWED_PERMISSION_NON_EXECUTABLES = 644
-ALLOWED_PERMISSION_EXECUTABLES = 755
+ALLOWED_PERMISSION_NON_EXECUTABLES = 0o644
+ALLOWED_PERMISSION_EXECUTABLES = 0o755
ALLOWED_EXECUTABLE_SHEBANG = {
"py": [b"#!/usr/bin/env python3"],
"sh": [b"#!/usr/bin/env bash", b"#!/bin/sh"],
@@ -31,8 +32,15 @@ ALLOWED_EXECUTABLE_SHEBANG = {
class FileMeta(object):
- def __init__(self, file_path: str):
- self.file_path = file_path
+ def __init__(self, file_spec: str):
+ '''Parse a `git ls files --stage` output line.'''
+ # 100755 5a150d5f8031fcd75e80a4dd9843afa33655f579 0 ci/test/00_setup_env.sh
+ meta, self.file_path = file_spec.split('\t', 2)
+ meta = meta.split()
+ # The octal file permission of the file. Internally, git only
+ # keeps an 'executable' bit, so this will always be 0o644 or 0o755.
+ self.permissions = int(meta[0], 8) & 0o7777
+ # We don't currently care about the other fields
@property
def extension(self) -> Optional[str]:
@@ -60,20 +68,24 @@ class FileMeta(object):
except IndexError:
return None
- @property
- def permissions(self) -> int:
- """
- Returns the octal file permission of the file
- """
- return int(oct(os.stat(self.file_path).st_mode)[-3:])
+def get_git_file_metadata() -> Dict[str, FileMeta]:
+ '''
+ Return a dictionary mapping the name of all files in the repository to git tree metadata.
+ '''
+ files_raw = check_output(CMD_ALL_FILES).decode("utf8").rstrip("\0").split("\0")
+ files = {}
+ for file_spec in files_raw:
+ meta = FileMeta(file_spec)
+ files[meta.file_path] = meta
+ return files
-def check_all_filenames() -> int:
+def check_all_filenames(files) -> int:
"""
Checks every file in the repository against an allowed regexp to make sure only lowercase or uppercase
alphanumerics (a-zA-Z0-9), underscores (_), hyphens (-), at (@) and dots (.) are used in repository filenames.
"""
- filenames = check_output(CMD_ALL_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
+ filenames = files.keys()
filename_regex = re.compile(ALLOWED_FILENAME_REGEXP)
failed_tests = 0
for filename in filenames:
@@ -85,14 +97,14 @@ def check_all_filenames() -> int:
return failed_tests
-def check_source_filenames() -> int:
+def check_source_filenames(files) -> int:
"""
Checks only source files (*.cpp, *.h, *.py, *.sh) against a stricter allowed regexp to make sure only lowercase
alphanumerics (a-z0-9), underscores (_), hyphens (-) and dots (.) are used in source code filenames.
Additionally there is an exception regexp for directories or files which are excepted from matching this regexp.
"""
- filenames = check_output(CMD_SOURCE_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
+ filenames = [filename for filename in files.keys() if re.match(ALL_SOURCE_FILENAMES_REGEXP, filename, re.IGNORECASE)]
filename_regex = re.compile(ALLOWED_SOURCE_FILENAME_REGEXP)
filename_exception_regex = re.compile(ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXP)
failed_tests = 0
@@ -105,16 +117,14 @@ def check_source_filenames() -> int:
return failed_tests
-def check_all_file_permissions() -> int:
+def check_all_file_permissions(files) -> int:
"""
Checks all files in the repository match an allowed executable or non-executable file permission octal.
Additionally checks that for executable files, the file contains a shebang line
"""
- filenames = check_output(CMD_ALL_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
failed_tests = 0
- for filename in filenames:
- file_meta = FileMeta(filename)
+ for filename, file_meta in files.items():
if file_meta.permissions == ALLOWED_PERMISSION_EXECUTABLES:
with open(filename, "rb") as f:
shebang = f.readline().rstrip(b"\n")
@@ -122,7 +132,7 @@ def check_all_file_permissions() -> int:
# For any file with executable permissions the first line must contain a shebang
if not shebang.startswith(b"#!"):
print(
- f"""File "{filename}" has permission {ALLOWED_PERMISSION_EXECUTABLES} (executable) and is thus expected to contain a shebang '#!'. Add shebang or do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES} {filename}" to make it non-executable."""
+ f"""File "{filename}" has permission {ALLOWED_PERMISSION_EXECUTABLES:03o} (executable) and is thus expected to contain a shebang '#!'. Add shebang or do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES:03o} {filename}" to make it non-executable."""
)
failed_tests += 1
@@ -145,18 +155,18 @@ def check_all_file_permissions() -> int:
continue
else:
print(
- f"""File "{filename}" has unexpected permission {file_meta.permissions}. Do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES} {filename}" (if non-executable) or "chmod {ALLOWED_PERMISSION_EXECUTABLES} {filename}" (if executable)."""
+ f"""File "{filename}" has unexpected permission {file_meta.permissions:03o}. Do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES:03o} {filename}" (if non-executable) or "chmod {ALLOWED_PERMISSION_EXECUTABLES:03o} {filename}" (if executable)."""
)
failed_tests += 1
return failed_tests
-def check_shebang_file_permissions() -> int:
+def check_shebang_file_permissions(files_meta) -> int:
"""
Checks every file that contains a shebang line to ensure it has an executable permission
"""
- filenames = check_output(CMD_SHEBANG_FILES, shell=True).decode("utf8").strip().split("\n")
+ filenames = check_output(CMD_SHEBANG_FILES).decode("utf8").strip().split("\n")
# The git grep command we use returns files which contain a shebang on any line within the file
# so we need to filter the list to only files with the shebang on the first line
@@ -164,7 +174,7 @@ def check_shebang_file_permissions() -> int:
failed_tests = 0
for filename in filenames:
- file_meta = FileMeta(filename)
+ file_meta = files_meta[filename]
if file_meta.permissions != ALLOWED_PERMISSION_EXECUTABLES:
# These file types are typically expected to be sourced and not executed directly
if file_meta.full_extension in ["bash", "init", "openrc", "sh.in"]:
@@ -178,7 +188,7 @@ def check_shebang_file_permissions() -> int:
continue
print(
- f"""File "{filename}" contains a shebang line, but has the file permission {file_meta.permissions} instead of the expected executable permission {ALLOWED_PERMISSION_EXECUTABLES}. Do "chmod {ALLOWED_PERMISSION_EXECUTABLES} {filename}" (or remove the shebang line)."""
+ f"""File "{filename}" contains a shebang line, but has the file permission {file_meta.permissions:03o} instead of the expected executable permission {ALLOWED_PERMISSION_EXECUTABLES:03o}. Do "chmod {ALLOWED_PERMISSION_EXECUTABLES:03o} {filename}" (or remove the shebang line)."""
)
failed_tests += 1
return failed_tests
@@ -187,11 +197,14 @@ def check_shebang_file_permissions() -> int:
def main() -> NoReturn:
root_dir = check_output(CMD_TOP_LEVEL).decode("utf8").strip()
os.chdir(root_dir)
+
+ files = get_git_file_metadata()
+
failed_tests = 0
- failed_tests += check_all_filenames()
- failed_tests += check_source_filenames()
- failed_tests += check_all_file_permissions()
- failed_tests += check_shebang_file_permissions()
+ failed_tests += check_all_filenames(files)
+ failed_tests += check_source_filenames(files)
+ failed_tests += check_all_file_permissions(files)
+ failed_tests += check_shebang_file_permissions(files)
if failed_tests:
print(
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 28e7b1e4ff..412cf86791 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -22,6 +22,7 @@ FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS = [
'LogConnectFailure,1',
'LogPrint,1',
'LogPrintf,0',
+ 'LogPrintLevel,2',
'printf,0',
'snprintf,2',
'sprintf,1',
diff --git a/test/lint/lint-logs.py b/test/lint/lint-logs.py
index e6c4c068fb..de53729b4e 100755
--- a/test/lint/lint-logs.py
+++ b/test/lint/lint-logs.py
@@ -16,12 +16,12 @@ from subprocess import check_output
def main():
- logs_list = check_output(["git", "grep", "--extended-regexp", r"LogPrintf?\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines()
+ logs_list = check_output(["git", "grep", "--extended-regexp", r"(LogPrintLevel|LogPrintf?)\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines()
unterminated_logs = [line for line in logs_list if not re.search(r'(\\n"|/\* Continued \*/)', line)]
if unterminated_logs != []:
- print("All calls to LogPrintf() and LogPrint() should be terminated with \\n")
+ print("All calls to LogPrintf(), LogPrint(), LogPrintLevel(), and WalletLogPrintf() should be terminated with \"\\n\".")
print("")
for line in unterminated_logs:
diff --git a/test/lint/spelling.ignore-words.txt b/test/lint/spelling.ignore-words.txt
index afdb0692d8..0efd298408 100644
--- a/test/lint/spelling.ignore-words.txt
+++ b/test/lint/spelling.ignore-words.txt
@@ -3,6 +3,7 @@ ba
blockin
cachable
creat
+desig
fo
fpr
hights
@@ -11,6 +12,7 @@ inout
invokable
keypair
mor
+nd
nin
ser
unparseable