diff options
159 files changed, 1151 insertions, 744 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 2d3efee8cf..72a5438fbe 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -262,7 +262,7 @@ task: MAKEJOBS: "-j4" # Avoid excessive memory use due to MSan task: - name: '[ASan + LSan + UBSan + integer, no depends, USDT] [jammy]' + name: '[ASan + LSan + UBSan + integer, no depends, USDT] [lunar]' << : *GLOBAL_TASK_TEMPLATE # We can't use a 'container' for the USDT interface tests as the CirrusCI # containers don't have privileges to hook into bitcoind. CirrusCI uses diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj index 0b90f341a7..585c28e503 100644 --- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj +++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj @@ -15,6 +15,7 @@ <ItemDefinitionGroup> <ClCompile> <PreprocessorDefinitions>ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <UndefinePreprocessorDefinitions>USE_ASM_X86_64;%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions> <AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <DisableSpecificWarnings>4146;4244;4267;4334</DisableSpecificWarnings> </ClCompile> diff --git a/build_msvc/libsecp256k1_config.h b/build_msvc/libsecp256k1_config.h deleted file mode 100644 index 2b1a980e27..0000000000 --- a/build_msvc/libsecp256k1_config.h +++ /dev/null @@ -1,15 +0,0 @@ -/********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef BITCOIN_LIBSECP256K1_CONFIG_H -#define BITCOIN_LIBSECP256K1_CONFIG_H - -#undef USE_ASM_X86_64 - -#define ECMULT_GEN_PREC_BITS 4 -#define ECMULT_WINDOW_SIZE 15 - -#endif // BITCOIN_LIBSECP256K1_CONFIG_H diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py index e02e3abdfa..9484f0cb89 100755 --- a/build_msvc/msvc-autogen.py +++ b/build_msvc/msvc-autogen.py @@ -111,7 +111,6 @@ def main(): set_properties(vcxproj_filename, '@SOURCE_FILES@\n', content) parse_config_into_btc_config() copyfile(os.path.join(SOURCE_DIR,'../build_msvc/bitcoin_config.h'), os.path.join(SOURCE_DIR, 'config/bitcoin-config.h')) - copyfile(os.path.join(SOURCE_DIR,'../build_msvc/libsecp256k1_config.h'), os.path.join(SOURCE_DIR, 'secp256k1/src/libsecp256k1-config.h')) if __name__ == '__main__': main() diff --git a/ci/README.md b/ci/README.md index de798607df..d014565f44 100644 --- a/ci/README.md +++ b/ci/README.md @@ -14,10 +14,10 @@ testing compared to other parts of the codebase. If you want to keep the work tr system in a virtual machine with a Linux operating system of your choice. To allow for a wide range of tested environments, but also ensure reproducibility to some extent, the test stage -requires `docker` to be installed. To install all requirements on Ubuntu, run +requires `bash`, `docker`, and `python3` to be installed. To install all requirements on Ubuntu, run ``` -sudo apt install docker.io bash +sudo apt install bash docker.io python3 ``` To run the default test stage, diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index 1024222e96..6390667394 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -18,7 +18,6 @@ export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order= export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan" export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan" export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1" -env | grep -E '^(BITCOIN_CONFIG|BASE_|QEMU_|CCACHE_|LC_ALL|BOOST_TEST_RANDOM|DEBIAN_FRONTEND|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS|PREVIOUS_RELEASES_DIR)' | tee /tmp/env if [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764) CI_CONTAINER_CAP="--cap-add SYS_PTRACE" fi @@ -27,6 +26,9 @@ export P_CI_DIR="$PWD" export BINS_SCRATCH_DIR="${BASE_SCRATCH_DIR}/bins/" if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then + # Export all env vars to avoid missing some. + # Though, exclude those with newlines to avoid parsing problems. + python3 -c 'import os; [print(f"{key}={value}") for key, value in os.environ.items() if "\n" not in value]' | tee /tmp/env echo "Creating $CI_IMAGE_NAME_TAG container to run in" DOCKER_BUILDKIT=1 ${CI_RETRY_EXE} docker build \ --file "${BASE_ROOT_DIR}/ci/test_imagefile" \ diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh index e0fae18b8e..3efbe8afc6 100755 --- a/ci/test/06_script_b.sh +++ b/ci/test/06_script_b.sh @@ -6,101 +6,98 @@ export LC_ALL=C.UTF-8 +set -ex + if [[ $HOST = *-mingw32 ]]; then # Generate all binaries, so that they can be wrapped - CI_EXEC make "$MAKEJOBS" -C src/secp256k1 VERBOSE=1 - CI_EXEC make "$MAKEJOBS" -C src minisketch/test.exe VERBOSE=1 - CI_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-wine.sh" + make "$MAKEJOBS" -C src/secp256k1 VERBOSE=1 + make "$MAKEJOBS" -C src minisketch/test.exe VERBOSE=1 + "${BASE_ROOT_DIR}/ci/test/wrap-wine.sh" fi if [ -n "$QEMU_USER_CMD" ]; then # Generate all binaries, so that they can be wrapped - CI_EXEC make "$MAKEJOBS" -C src/secp256k1 VERBOSE=1 - CI_EXEC make "$MAKEJOBS" -C src minisketch/test VERBOSE=1 - CI_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-qemu.sh" + make "$MAKEJOBS" -C src/secp256k1 VERBOSE=1 + make "$MAKEJOBS" -C src minisketch/test VERBOSE=1 + "${BASE_ROOT_DIR}/ci/test/wrap-qemu.sh" fi if [ -n "$USE_VALGRIND" ]; then - CI_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-valgrind.sh" + "${BASE_ROOT_DIR}/ci/test/wrap-valgrind.sh" fi if [ "$RUN_UNIT_TESTS" = "true" ]; then - CI_EXEC "${TEST_RUNNER_ENV}" DIR_UNIT_TEST_DATA="${DIR_UNIT_TEST_DATA}" LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" make "$MAKEJOBS" check VERBOSE=1 + bash -c "${TEST_RUNNER_ENV} DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=${DEPENDS_DIR}/${HOST}/lib make $MAKEJOBS check VERBOSE=1" fi if [ "$RUN_UNIT_TESTS_SEQUENTIAL" = "true" ]; then - CI_EXEC "${TEST_RUNNER_ENV}" DIR_UNIT_TEST_DATA="${DIR_UNIT_TEST_DATA}" LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" "${BASE_OUTDIR}/bin/test_bitcoin" --catch_system_errors=no -l test_suite + bash -c "${TEST_RUNNER_ENV} DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=${DEPENDS_DIR}/${HOST}/lib ${BASE_OUTDIR}/bin/test_bitcoin --catch_system_errors=no -l test_suite" fi if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then - CI_EXEC LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" "${TEST_RUNNER_ENV}" test/functional/test_runner.py --ci "$MAKEJOBS" --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" --ansi --combinedlogslen=99999999 --timeout-factor="${TEST_RUNNER_TIMEOUT_FACTOR}" "${TEST_RUNNER_EXTRA}" --quiet --failfast + bash -c "LD_LIBRARY_PATH=${DEPENDS_DIR}/${HOST}/lib ${TEST_RUNNER_ENV} test/functional/test_runner.py --ci $MAKEJOBS --tmpdirprefix ${BASE_SCRATCH_DIR}/test_runner/ --ansi --combinedlogslen=99999999 --timeout-factor=${TEST_RUNNER_TIMEOUT_FACTOR} ${TEST_RUNNER_EXTRA} --quiet --failfast" fi if [ "${RUN_TIDY}" = "true" ]; then set -eo pipefail - export P_CI_DIR="${BASE_BUILD_DIR}/bitcoin-$HOST/src/" - ( CI_EXEC run-clang-tidy-16 -quiet "${MAKEJOBS}" ) | grep -C5 "error" - export P_CI_DIR="${BASE_BUILD_DIR}/bitcoin-$HOST/" - CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/iwyu_tool.py"\ - " src/common/args.cpp"\ - " src/common/config.cpp"\ - " src/common/init.cpp"\ - " src/common/url.cpp"\ - " src/compat"\ - " src/dbwrapper.cpp"\ - " src/init"\ - " src/kernel"\ - " src/node/chainstate.cpp"\ - " src/node/chainstatemanager_args.cpp"\ - " src/node/mempool_args.cpp"\ - " src/node/minisketchwrapper.cpp"\ - " src/node/utxo_snapshot.cpp"\ - " src/node/validation_cache_args.cpp"\ - " src/policy/feerate.cpp"\ - " src/policy/packages.cpp"\ - " src/policy/settings.cpp"\ - " src/primitives/transaction.cpp"\ - " src/random.cpp"\ - " src/rpc/fees.cpp"\ - " src/rpc/signmessage.cpp"\ - " src/test/fuzz/string.cpp"\ - " src/test/fuzz/txorphan.cpp"\ - " src/test/fuzz/util/"\ - " src/test/util/coins.cpp"\ - " src/uint256.cpp"\ - " src/util/bip32.cpp"\ - " src/util/bytevectorhash.cpp"\ - " src/util/check.cpp"\ - " src/util/error.cpp"\ - " src/util/exception.cpp"\ - " src/util/getuniquepath.cpp"\ - " src/util/hasher.cpp"\ - " src/util/message.cpp"\ - " src/util/moneystr.cpp"\ - " src/util/serfloat.cpp"\ - " src/util/spanparsing.cpp"\ - " src/util/strencodings.cpp"\ - " src/util/string.cpp"\ - " src/util/syserror.cpp"\ - " src/util/threadinterrupt.cpp"\ - " src/zmq"\ - " -p . ${MAKEJOBS}"\ - " -- -Xiwyu --cxx17ns -Xiwyu --mapping_file=${BASE_BUILD_DIR}/bitcoin-$HOST/contrib/devtools/iwyu/bitcoin.core.imp"\ - " |& tee /tmp/iwyu_ci.out" - export P_CI_DIR="${BASE_ROOT_DIR}/src" - CI_EXEC "python3 ${DIR_IWYU}/include-what-you-use/fix_includes.py --nosafe_headers < /tmp/iwyu_ci.out" - CI_EXEC "git --no-pager diff" + cd "${BASE_BUILD_DIR}/bitcoin-$HOST/src/" + ( run-clang-tidy-16 -quiet "${MAKEJOBS}" ) | grep -C5 "error" + cd "${BASE_BUILD_DIR}/bitcoin-$HOST/" + python3 "${DIR_IWYU}/include-what-you-use/iwyu_tool.py" \ + src/common/args.cpp \ + src/common/config.cpp \ + src/common/init.cpp \ + src/common/url.cpp \ + src/compat \ + src/dbwrapper.cpp \ + src/init \ + src/kernel \ + src/node/chainstate.cpp \ + src/node/chainstatemanager_args.cpp \ + src/node/mempool_args.cpp \ + src/node/minisketchwrapper.cpp \ + src/node/utxo_snapshot.cpp \ + src/node/validation_cache_args.cpp \ + src/policy/feerate.cpp \ + src/policy/packages.cpp \ + src/policy/settings.cpp \ + src/primitives/transaction.cpp \ + src/random.cpp \ + src/rpc/fees.cpp \ + src/rpc/signmessage.cpp \ + src/test/fuzz/string.cpp \ + src/test/fuzz/txorphan.cpp \ + src/test/fuzz/util \ + src/test/util/coins.cpp \ + src/uint256.cpp \ + src/util/bip32.cpp \ + src/util/bytevectorhash.cpp \ + src/util/check.cpp \ + src/util/error.cpp \ + src/util/exception.cpp \ + src/util/getuniquepath.cpp \ + src/util/hasher.cpp \ + src/util/message.cpp \ + src/util/moneystr.cpp \ + src/util/serfloat.cpp \ + src/util/spanparsing.cpp \ + src/util/strencodings.cpp \ + src/util/string.cpp \ + src/util/syserror.cpp \ + src/util/threadinterrupt.cpp \ + src/zmq \ + -p . "${MAKEJOBS}" \ + -- -Xiwyu --cxx17ns -Xiwyu --mapping_file="${BASE_BUILD_DIR}/bitcoin-$HOST/contrib/devtools/iwyu/bitcoin.core.imp" \ + 2>&1 | tee /tmp/iwyu_ci.out + cd "${BASE_ROOT_DIR}/src" + python3 "${DIR_IWYU}/include-what-you-use/fix_includes.py" --nosafe_headers < /tmp/iwyu_ci.out + git --no-pager diff fi if [ "$RUN_SECURITY_TESTS" = "true" ]; then - CI_EXEC make test-security-check + make test-security-check fi if [ "$RUN_FUZZ_TESTS" = "true" ]; then - CI_EXEC LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" test/fuzz/test_runner.py "${FUZZ_TESTS_CONFIG}" "$MAKEJOBS" -l DEBUG "${DIR_FUZZ_IN}" -fi - -if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then - echo "Stop and remove CI container by ID" - docker container kill "${CI_CONTAINER_ID}" + bash -c "LD_LIBRARY_PATH=${DEPENDS_DIR}/${HOST}/lib test/fuzz/test_runner.py ${FUZZ_TESTS_CONFIG} $MAKEJOBS -l DEBUG ${DIR_FUZZ_IN}" fi diff --git a/ci/test_run_all.sh b/ci/test_run_all.sh index 93b07aab1e..751a4056df 100755 --- a/ci/test_run_all.sh +++ b/ci/test_run_all.sh @@ -10,4 +10,10 @@ set -o errexit; source ./ci/test/00_setup_env.sh set -o errexit; source ./ci/test/04_install.sh set -o errexit; source ./ci/test/05_before_script.sh set -o errexit; source ./ci/test/06_script_a.sh -set -o errexit; source ./ci/test/06_script_b.sh +set -o errexit +CI_EXEC "${BASE_ROOT_DIR}/ci/test/06_script_b.sh" + +if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then + echo "Stop and remove CI container by ID" + docker container kill "${CI_CONTAINER_ID}" +fi diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys index 94daf28b15..f25486776f 100644 --- a/contrib/verify-commits/trusted-keys +++ b/contrib/verify-commits/trusted-keys @@ -2,3 +2,4 @@ E777299FC265DD04793070EB944D35F9AC3DB76A D1DBF2C4B96F2DEBF4C16654410108112E7EA81F 152812300785C96444D3334D17565732E08E5E41 6B002C6EA3F91B1B0DF0C9BC8F617F1200A6D25C +4D1B3D5ECBA1A7E05371EEBE46800E30FC748A66 diff --git a/doc/release-notes-26076.md b/doc/release-notes-26076.md new file mode 100644 index 0000000000..f95e4be0e3 --- /dev/null +++ b/doc/release-notes-26076.md @@ -0,0 +1,13 @@ +RPC +--- + +- The `listdescriptors`, `decodepsbt` and similar RPC methods now show `h` rather than apostrophe (`'`) to indicate + hardened derivation. This does not apply when using the `private` parameter, which + matches the marker used when descriptor was generated or imported. Newly created + wallets use `h`. This change makes it easier to handle descriptor strings manually. + E.g. the `importdescriptors` RPC call is easiest to use `h` as the marker: `'["desc": ".../0h/..."]'`. + With this change `listdescriptors` will use `h`, so you can copy-paste the result, + without having to add escape characters or switch `'` to 'h' manually. + Note that this changes the descriptor checksum. + For legacy wallets the `hdkeypath` field in `getaddressinfo` is unchanged, + nor is the serialization format of wallet dumps. (#26076) diff --git a/doc/release-process.md b/doc/release-process.md index 9c2e03d402..cf014c940b 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -60,6 +60,8 @@ Release Process - Create a pinned meta-issue for testing the release candidate (see [this issue](https://github.com/bitcoin/bitcoin/issues/17079) for an example) and provide a link to it in the release announcements where useful. - Translations on Transifex - Change the auto-update URL for the new major version's resource away from `master` and to the branch, e.g. `https://raw.githubusercontent.com/bitcoin/bitcoin/<branch>/src/qt/locale/bitcoin_en.xlf`. Do not forget this or it will keep tracking the translations on master instead, drifting away from the specific major release. +- Prune inputs from the qa-assets repo (See [pruning + inputs](https://github.com/bitcoin-core/qa-assets#pruning-inputs)). #### Before final release diff --git a/src/Makefile.am b/src/Makefile.am index d12edca64e..9e1fab869d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -280,6 +280,7 @@ BITCOIN_CORE_H = \ util/bip32.h \ util/bitdeque.h \ util/bytevectorhash.h \ + util/chaintype.h \ util/check.h \ util/epochguard.h \ util/error.h \ @@ -707,6 +708,7 @@ libbitcoin_util_a_SOURCES = \ util/asmap.cpp \ util/bip32.cpp \ util/bytevectorhash.cpp \ + util/chaintype.cpp \ util/check.cpp \ util/error.cpp \ util/exception.cpp \ @@ -956,6 +958,7 @@ libbitcoinkernel_la_SOURCES = \ txdb.cpp \ txmempool.cpp \ uint256.cpp \ + util/chaintype.cpp \ util/check.cpp \ util/exception.cpp \ util/fs.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 69965ed1b8..461022bbfc 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -344,6 +344,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/txorphan.cpp \ test/fuzz/txrequest.cpp \ test/fuzz/utxo_snapshot.cpp \ + test/fuzz/utxo_total_supply.cpp \ test/fuzz/validation_load_mempool.cpp \ test/fuzz/versionbits.cpp endif # ENABLE_FUZZ_BINARY diff --git a/src/attributes.h b/src/attributes.h index 9957bcd84b..a4603b0270 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -16,4 +16,12 @@ # define LIFETIMEBOUND #endif +#if defined(__GNUC__) +# define ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define ALWAYS_INLINE __forceinline +#else +# error No known always_inline attribute for this platform. +#endif + #endif // BITCOIN_ATTRIBUTES_H diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 8dd4117a3e..4d032cefc5 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -27,7 +27,7 @@ static void AssembleBlock(benchmark::Bench& bench) std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs; for (size_t b{0}; b < NUM_BLOCKS; ++b) { CMutableTransaction tx; - tx.vin.push_back(MineBlock(test_setup->m_node, P2WSH_OP_TRUE)); + tx.vin.push_back(CTxIn{MineBlock(test_setup->m_node, P2WSH_OP_TRUE)}); tx.vin.back().scriptWitness = witness; tx.vout.emplace_back(1337, P2WSH_OP_TRUE); if (NUM_BLOCKS - b >= COINBASE_MATURITY) diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 260c8991ce..269ac847a5 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -9,6 +9,7 @@ #include <common/args.h> #include <consensus/validation.h> #include <streams.h> +#include <util/chaintype.h> #include <validation.h> // These are the two major time-sinks which happen after we have fully received @@ -36,7 +37,7 @@ static void DeserializeAndCheckBlockTest(benchmark::Bench& bench) stream.write({&a, 1}); // Prevent compaction ArgsManager bench_args; - const auto chainParams = CreateChainParams(bench_args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(bench_args, ChainType::MAIN); bench.unit("block").run([&] { CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp index 0fd842c7c3..2ff72a3012 100644 --- a/src/bench/load_external.cpp +++ b/src/bench/load_external.cpp @@ -6,6 +6,7 @@ #include <bench/data.h> #include <chainparams.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <validation.h> /** @@ -22,7 +23,7 @@ */ static void LoadExternalBlockFile(benchmark::Bench& bench) { - const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN)}; + const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)}; // Create a single block as in the blocks files (magic bytes, block size, // block data) as a stream object. diff --git a/src/bench/logging.cpp b/src/bench/logging.cpp index 9aedb26236..c97c4e151b 100644 --- a/src/bench/logging.cpp +++ b/src/bench/logging.cpp @@ -5,6 +5,7 @@ #include <bench/bench.h> #include <logging.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> // All but 2 of the benchmarks should have roughly similar performance: // @@ -18,7 +19,7 @@ static void Logging(benchmark::Bench& bench, const std::vector<const char*>& ext LogInstance().DisableCategory(BCLog::LogFlags::ALL); TestingSetup test_setup{ - CBaseChainParams::REGTEST, + ChainType::REGTEST, extra_args, }; diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index 80c959cdfb..826da73800 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -7,6 +7,7 @@ #include <policy/policy.h> #include <test/util/setup_common.h> #include <txmempool.h> +#include <util/chaintype.h> #include <validation.h> #include <vector> @@ -88,7 +89,7 @@ static void ComplexMemPool(benchmark::Bench& bench) childTxs = static_cast<int>(bench.complexityN()); } std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /*min_ancestors=*/1); - const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN); + const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN); CTxMemPool& pool = *testing_setup.get()->m_node.mempool; LOCK2(cs_main, pool.cs); bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { @@ -103,7 +104,7 @@ static void ComplexMemPool(benchmark::Bench& bench) static void MempoolCheck(benchmark::Bench& bench) { FastRandomContext det_rand{true}; - auto testing_setup = MakeNoLogFileContext<TestChain100Setup>(CBaseChainParams::REGTEST, {"-checkmempool=1"}); + auto testing_setup = MakeNoLogFileContext<TestChain100Setup>(ChainType::REGTEST, {"-checkmempool=1"}); CTxMemPool& pool = *testing_setup.get()->m_node.mempool; LOCK2(cs_main, pool.cs); testing_setup->PopulateMempool(det_rand, 400, true); diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index f68b6acb5b..a9b197b190 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -8,6 +8,7 @@ #include <rpc/blockchain.h> #include <streams.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <validation.h> #include <univalue.h> @@ -15,7 +16,7 @@ namespace { struct TestBlockAndIndex { - const std::unique_ptr<const TestingSetup> testing_setup{MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN)}; + const std::unique_ptr<const TestingSetup> testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)}; CBlock block{}; uint256 blockHash{}; CBlockIndex blockindex{}; diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp index e3e1a07c83..7e274370e0 100644 --- a/src/bench/rpc_mempool.cpp +++ b/src/bench/rpc_mempool.cpp @@ -3,12 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <bench/bench.h> -#include <chainparamsbase.h> #include <kernel/cs_main.h> #include <kernel/mempool_entry.h> #include <rpc/mempool.h> #include <test/util/setup_common.h> #include <txmempool.h> +#include <util/chaintype.h> #include <univalue.h> @@ -21,7 +21,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& poo static void RpcMempool(benchmark::Bench& bench) { - const auto testing_setup = MakeNoLogFileContext<const ChainTestingSetup>(CBaseChainParams::MAIN); + const auto testing_setup = MakeNoLogFileContext<const ChainTestingSetup>(ChainType::MAIN); CTxMemPool& pool = *Assert(testing_setup->m_node.mempool); LOCK2(cs_main, pool.cs); diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp index 8dbbdec28c..4cbc0dfbbd 100644 --- a/src/bench/util_time.cpp +++ b/src/bench/util_time.cpp @@ -32,7 +32,7 @@ static void BenchTimeMillis(benchmark::Bench& bench) static void BenchTimeMillisSys(benchmark::Bench& bench) { bench.run([&] { - (void)GetTimeMillis(); + (void)TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now()); }); } diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 04f6aae4f7..52e697a78d 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -25,6 +25,7 @@ #include <node/chainstate.h> #include <scheduler.h> #include <script/sigcache.h> +#include <util/chaintype.h> #include <util/thread.h> #include <validation.h> #include <validationinterface.h> @@ -52,7 +53,7 @@ int main(int argc, char* argv[]) // SETUP: Misc Globals - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); auto chainparams = CChainParams::Main(); kernel::Context kernel_context{}; @@ -86,7 +87,10 @@ int main(int argc, char* argv[]) .datadir = gArgs.GetDataDirNet(), .adjusted_time_callback = NodeClock::now, }; - ChainstateManager chainman{chainman_opts, {}}; + const node::BlockManager::Options blockman_opts{ + .chainparams = chainman_opts.chainparams, + }; + ChainstateManager chainman{chainman_opts, blockman_opts}; node::CacheSizes cache_sizes; cache_sizes.block_tree_db = 2 << 20; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 63ba6a935d..48dc11b95a 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -20,6 +20,7 @@ #include <rpc/request.h> #include <tinyformat.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/exception.h> #include <util/strencodings.h> #include <util/system.h> @@ -73,10 +74,10 @@ static void SetupCliArgs(ArgsManager& argsman) { SetupHelpOptions(argsman); - const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); - const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); - const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET); - const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); + const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN); + const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); + const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); + const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -174,7 +175,7 @@ static int AppInitRPC(int argc, char* argv[]) } // Check for chain settings (BaseParams() calls are only valid after this clause) try { - SelectBaseParams(gArgs.GetChainName()); + SelectBaseParams(gArgs.GetChainType()); } catch (const std::exception& e) { tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; @@ -426,10 +427,17 @@ private: std::vector<Peer> m_peers; std::string ChainToString() const { - if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet"; - if (gArgs.GetChainName() == CBaseChainParams::SIGNET) return " signet"; - if (gArgs.GetChainName() == CBaseChainParams::REGTEST) return " regtest"; - return ""; + switch (gArgs.GetChainType()) { + case ChainType::TESTNET: + return " testnet"; + case ChainType::SIGNET: + return " signet"; + case ChainType::REGTEST: + return " regtest"; + case ChainType::MAIN: + return ""; + } + assert(false); } std::string PingTimeToString(double seconds) const { @@ -863,7 +871,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str try { response = CallRPC(rh, strMethod, args, rpcwallet); if (fWait) { - const UniValue& error = find_value(response, "error"); + const UniValue& error = response.find_value("error"); if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) { throw CConnectionFailed("server in warmup"); } @@ -891,8 +899,8 @@ static void ParseResult(const UniValue& result, std::string& strPrint) static void ParseError(const UniValue& error, std::string& strPrint, int& nRet) { if (error.isObject()) { - const UniValue& err_code = find_value(error, "code"); - const UniValue& err_msg = find_value(error, "message"); + const UniValue& err_code = error.find_value("code"); + const UniValue& err_msg = error.find_value("message"); if (!err_code.isNull()) { strPrint = "error code: " + err_code.getValStr() + "\n"; } @@ -918,15 +926,15 @@ static void GetWalletBalances(UniValue& result) { DefaultRequestHandler rh; const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{}); - if (!find_value(listwallets, "error").isNull()) return; - const UniValue& wallets = find_value(listwallets, "result"); + if (!listwallets.find_value("error").isNull()) return; + const UniValue& wallets = listwallets.find_value("result"); if (wallets.size() <= 1) return; UniValue balances(UniValue::VOBJ); for (const UniValue& wallet : wallets.getValues()) { const std::string& wallet_name = wallet.get_str(); const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name); - const UniValue& balance = find_value(getbalances, "result")["mine"]["trusted"]; + const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"]; balances.pushKV(wallet_name, balance); } result.pushKV("balances", balances); @@ -962,7 +970,7 @@ static void GetProgressBar(double progress, std::string& progress_bar) */ static void ParseGetInfoResult(UniValue& result) { - if (!find_value(result, "error").isNull()) return; + if (!result.find_value("error").isNull()) return; std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN; bool should_colorize = false; @@ -1174,9 +1182,9 @@ static int CommandLineRPC(int argc, char *argv[]) rh.reset(new NetinfoRequestHandler()); } else if (gArgs.GetBoolArg("-generate", false)) { const UniValue getnewaddress{GetNewAddress()}; - const UniValue& error{find_value(getnewaddress, "error")}; + const UniValue& error{getnewaddress.find_value("error")}; if (error.isNull()) { - SetGenerateToAddressArgs(find_value(getnewaddress, "result").get_str(), args); + SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args); rh.reset(new GenerateToAddressRequestHandler()); } else { ParseError(error, strPrint, nRet); @@ -1198,8 +1206,8 @@ static int CommandLineRPC(int argc, char *argv[]) const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name); // Parse reply - UniValue result = find_value(reply, "result"); - const UniValue& error = find_value(reply, "error"); + UniValue result = reply.find_value("result"); + const UniValue& error = reply.find_value("error"); if (error.isNull()) { if (gArgs.GetBoolArg("-getinfo", false)) { if (!gArgs.IsArgSet("-rpcwallet")) { diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index c35a111313..e291f20a11 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -6,6 +6,7 @@ #include <config/bitcoin-config.h> #endif +#include <chainparamsbase.h> #include <clientversion.h> #include <coins.h> #include <common/args.h> @@ -91,7 +92,7 @@ static int AppInitRawTx(int argc, char* argv[]) // Check for chain settings (Params() calls are only valid after this clause) try { - SelectParams(gArgs.GetChainName()); + SelectParams(gArgs.GetChainType()); } catch (const std::exception& e) { tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp index e3f70e1b76..f62a9f7bbf 100644 --- a/src/bitcoin-util.cpp +++ b/src/bitcoin-util.cpp @@ -75,7 +75,7 @@ static int AppInitUtil(ArgsManager& args, int argc, char* argv[]) // Check for chain settings (Params() calls are only valid after this clause) try { - SelectParams(args.GetChainName()); + SelectParams(args.GetChainType()); } catch (const std::exception& e) { tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 5dbf8a8616..1863173aa8 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -91,7 +91,7 @@ static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[ return EXIT_FAILURE; } // Check for chain settings (Params() calls are only valid after this clause) - SelectParams(args.GetChainName()); + SelectParams(args.GetChainType()); return std::nullopt; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d3d358a3f0..6f4453d1fe 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -5,6 +5,7 @@ #include <chainparams.h> +#include <chainparamsbase.h> #include <chainparamsseeds.h> #include <common/args.h> #include <consensus/merkle.h> @@ -12,6 +13,7 @@ #include <hash.h> // for signet block challenge hash #include <logging.h> #include <script/interpreter.h> +#include <util/chaintype.h> #include <util/string.h> #include <assert.h> @@ -97,26 +99,29 @@ const CChainParams &Params() { return *globalChainParams; } -std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain) +std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const ChainType chain) { - if (chain == CBaseChainParams::MAIN) { + switch (chain) { + case ChainType::MAIN: return CChainParams::Main(); - } else if (chain == CBaseChainParams::TESTNET) { + case ChainType::TESTNET: return CChainParams::TestNet(); - } else if (chain == CBaseChainParams::SIGNET) { + case ChainType::SIGNET: { auto opts = CChainParams::SigNetOptions{}; ReadSigNetArgs(args, opts); return CChainParams::SigNet(opts); - } else if (chain == CBaseChainParams::REGTEST) { + } + case ChainType::REGTEST: { auto opts = CChainParams::RegTestOptions{}; ReadRegTestArgs(args, opts); return CChainParams::RegTest(opts); } - throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); + } + assert(false); } -void SelectParams(const std::string& network) +void SelectParams(const ChainType chain) { - SelectBaseParams(network); - globalChainParams = CreateChainParams(gArgs, network); + SelectBaseParams(chain); + globalChainParams = CreateChainParams(gArgs, chain); } diff --git a/src/chainparams.h b/src/chainparams.h index cb34d068e1..1e8366dcf5 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -8,11 +8,11 @@ #include <kernel/chainparams.h> -#include <chainparamsbase.h> #include <consensus/params.h> #include <netaddress.h> #include <primitives/block.h> #include <protocol.h> +#include <util/chaintype.h> #include <util/hash_type.h> #include <cstdint> @@ -21,12 +21,12 @@ #include <unordered_map> #include <vector> +class ArgsManager; + /** * Creates and returns a std::unique_ptr<CChainParams> of the chosen chain. - * @returns a CChainParams* of the chosen chain. - * @throws a std::runtime_error if the chain is not supported. */ -std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain); +std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const ChainType chain); /** * Return the currently selected parameters. This won't change after app @@ -35,9 +35,8 @@ std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, c const CChainParams &Params(); /** - * Sets the params returned by Params() to those for the given chain name. - * @throws std::runtime_error when the chain is not supported. + * Sets the params returned by Params() to those for the given chain type. */ -void SelectParams(const std::string& chain); +void SelectParams(const ChainType chain); #endif // BITCOIN_CHAINPARAMS_H diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index eb7b31923d..8cbf9e85e0 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -7,14 +7,10 @@ #include <common/args.h> #include <tinyformat.h> +#include <util/chaintype.h> #include <assert.h> -const std::string CBaseChainParams::MAIN = "main"; -const std::string CBaseChainParams::TESTNET = "test"; -const std::string CBaseChainParams::SIGNET = "signet"; -const std::string CBaseChainParams::REGTEST = "regtest"; - void SetupChainParamsBaseOptions(ArgsManager& argsman) { argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); @@ -40,22 +36,23 @@ const CBaseChainParams& BaseParams() * Port numbers for incoming Tor connections (8334, 18334, 38334, 18445) have * been chosen arbitrarily to keep ranges of used ports tight. */ -std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain) +std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain) { - if (chain == CBaseChainParams::MAIN) { + switch (chain) { + case ChainType::MAIN: return std::make_unique<CBaseChainParams>("", 8332, 8334); - } else if (chain == CBaseChainParams::TESTNET) { + case ChainType::TESTNET: return std::make_unique<CBaseChainParams>("testnet3", 18332, 18334); - } else if (chain == CBaseChainParams::SIGNET) { + case ChainType::SIGNET: return std::make_unique<CBaseChainParams>("signet", 38332, 38334); - } else if (chain == CBaseChainParams::REGTEST) { + case ChainType::REGTEST: return std::make_unique<CBaseChainParams>("regtest", 18443, 18445); } - throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); + assert(false); } -void SelectBaseParams(const std::string& chain) +void SelectBaseParams(const ChainType chain) { globalChainBaseParams = CreateBaseChainParams(chain); - gArgs.SelectConfigNetwork(chain); + gArgs.SelectConfigNetwork(ChainTypeToString(chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index d593cff722..ea933d1ca8 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_CHAINPARAMSBASE_H #define BITCOIN_CHAINPARAMSBASE_H +#include <util/chaintype.h> + #include <memory> #include <string> @@ -17,14 +19,6 @@ class ArgsManager; class CBaseChainParams { public: - ///@{ - /** Chain name strings */ - static const std::string MAIN; - static const std::string TESTNET; - static const std::string SIGNET; - static const std::string REGTEST; - ///@} - const std::string& DataDir() const { return strDataDir; } uint16_t RPCPort() const { return m_rpc_port; } uint16_t OnionServiceTargetPort() const { return m_onion_service_target_port; } @@ -41,10 +35,8 @@ private: /** * Creates and returns a std::unique_ptr<CBaseChainParams> of the chosen chain. - * @returns a CBaseChainParams* of the chosen chain. - * @throws a std::runtime_error if the chain is not supported. */ -std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain); +std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain); /** *Set the arguments for chainparams @@ -57,7 +49,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman); */ const CBaseChainParams& BaseParams(); -/** Sets the params returned by Params() to those for the given network. */ -void SelectBaseParams(const std::string& chain); +/** Sets the params returned by Params() to those for the given chain. */ +void SelectBaseParams(const ChainType chain); #endif // BITCOIN_CHAINPARAMSBASE_H diff --git a/src/common/args.cpp b/src/common/args.cpp index d29b8648bf..93f2005931 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -10,6 +10,7 @@ #include <sync.h> #include <tinyformat.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/check.h> #include <util/fs.h> #include <util/fs_helpers.h> @@ -33,6 +34,7 @@ #include <stdexcept> #include <string> #include <utility> +#include <variant> const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_SETTINGS_FILENAME = "settings.json"; @@ -141,7 +143,7 @@ std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const if (m_network.empty()) return std::set<std::string> {}; // if it's okay to use the default section for this network, don't worry - if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {}; + if (m_network == ChainTypeToString(ChainType::MAIN)) return std::set<std::string> {}; for (const auto& arg : m_network_only_args) { if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) { @@ -155,10 +157,10 @@ std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const { // Section names to be recognized in the config file. static const std::set<std::string> available_sections{ - CBaseChainParams::REGTEST, - CBaseChainParams::SIGNET, - CBaseChainParams::TESTNET, - CBaseChainParams::MAIN + ChainTypeToString(ChainType::REGTEST), + ChainTypeToString(ChainType::SIGNET), + ChainTypeToString(ChainType::TESTNET), + ChainTypeToString(ChainType::MAIN), }; LOCK(cs_args); @@ -443,7 +445,7 @@ util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) c { LOCK(cs_args); return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name), - /*ignore_nonpersistent=*/true, /*get_chain_name=*/false); + /*ignore_nonpersistent=*/true, /*get_chain_type=*/false); } bool ArgsManager::IsArgNegated(const std::string& strArg) const @@ -717,39 +719,53 @@ fs::path ArgsManager::GetConfigFilePath() const return GetConfigFile(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME)); } -std::string ArgsManager::GetChainName() const +ChainType ArgsManager::GetChainType() const +{ + std::variant<ChainType, std::string> arg = GetChainArg(); + if (auto* parsed = std::get_if<ChainType>(&arg)) return *parsed; + throw std::runtime_error(strprintf("Unknown chain %s.", std::get<std::string>(arg))); +} + +std::string ArgsManager::GetChainTypeString() const +{ + auto arg = GetChainArg(); + if (auto* parsed = std::get_if<ChainType>(&arg)) return ChainTypeToString(*parsed); + return std::get<std::string>(arg); +} + +std::variant<ChainType, std::string> ArgsManager::GetChainArg() const { auto get_net = [&](const std::string& arg) { 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); + /* get_chain_type= */ true); return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); }; const bool fRegTest = get_net("-regtest"); const bool fSigNet = get_net("-signet"); const bool fTestNet = get_net("-testnet"); - const bool is_chain_arg_set = IsArgSet("-chain"); + const auto chain_arg = GetArg("-chain"); - if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) { + if ((int)chain_arg.has_value() + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) { throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one."); } - if (fRegTest) - return CBaseChainParams::REGTEST; - if (fSigNet) { - return CBaseChainParams::SIGNET; + if (chain_arg) { + if (auto parsed = ChainTypeFromString(*chain_arg)) return *parsed; + // Not a known string, so return original string + return *chain_arg; } - if (fTestNet) - return CBaseChainParams::TESTNET; - - return GetArg("-chain", CBaseChainParams::MAIN); + if (fRegTest) return ChainType::REGTEST; + if (fSigNet) return ChainType::SIGNET; + if (fTestNet) return ChainType::TESTNET; + return ChainType::MAIN; } bool ArgsManager::UseDefaultSection(const std::string& arg) const { - return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0; + return m_network == ChainTypeToString(ChainType::MAIN) || m_network_only_args.count(arg) == 0; } util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const @@ -757,7 +773,7 @@ util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const LOCK(cs_args); return util::GetSetting( m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), - /*ignore_nonpersistent=*/false, /*get_chain_name=*/false); + /*ignore_nonpersistent=*/false, /*get_chain_type=*/false); } std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const diff --git a/src/common/args.h b/src/common/args.h index 430c392e2b..537a64fcfd 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -7,6 +7,7 @@ #include <compat/compat.h> #include <sync.h> +#include <util/chaintype.h> #include <util/fs.h> #include <util/settings.h> @@ -17,6 +18,7 @@ #include <set> #include <stdint.h> #include <string> +#include <variant> #include <vector> class ArgsManager; @@ -323,10 +325,18 @@ protected: void ForceSetArg(const std::string& strArg, const std::string& strValue); /** - * Returns the appropriate chain name from the program arguments. - * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. + * Returns the appropriate chain type from the program arguments. + * @return ChainType::MAIN by default; raises runtime error if an invalid + * combination, or unknown chain is given. */ - std::string GetChainName() const; + ChainType GetChainType() const; + + /** + * Returns the appropriate chain type string from the program arguments. + * @return ChainType::MAIN string by default; raises runtime error if an + * invalid combination is given. + */ + std::string GetChainTypeString() const; /** * Add argument @@ -411,6 +421,14 @@ private: */ const fs::path& GetDataDir(bool net_specific) const; + /** + * Return -regtest/-signet/-testnet/-chain= setting as a ChainType enum if a + * recognized chain type was set, or as a string if an unrecognized chain + * name was set. Raise an exception if an invalid combination of flags was + * provided. + */ + std::variant<ChainType, std::string> GetChainArg() const; + // Helper function for LogArgs(). void logArgsPrefix( const std::string& prefix, diff --git a/src/common/config.cpp b/src/common/config.cpp index 747503ad2a..5efb5efb67 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -8,6 +8,7 @@ #include <sync.h> #include <tinyformat.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/fs.h> #include <util/settings.h> #include <util/string.h> @@ -152,7 +153,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } } if (use_conf_file) { - std::string chain_id = GetChainName(); + std::string chain_id = GetChainTypeString(); std::vector<std::string> conf_file_names; auto add_includes = [&](const std::string& network, size_t skip = 0) { @@ -191,7 +192,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) conf_file_names.clear(); add_includes(chain_id, /* skip= */ chain_includes); add_includes({}, /* skip= */ default_includes); - std::string chain_id_final = GetChainName(); + std::string chain_id_final = GetChainTypeString(); if (chain_id_final != chain_id) { // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs add_includes(chain_id_final); diff --git a/src/common/init.cpp b/src/common/init.cpp index 6ffa44847a..8933d59c27 100644 --- a/src/common/init.cpp +++ b/src/common/init.cpp @@ -26,7 +26,7 @@ std::optional<ConfigError> InitConfig(ArgsManager& args, SettingsAbortFn setting } // Check for chain settings (Params() calls are only valid after this clause) - SelectParams(args.GetChainName()); + SelectParams(args.GetChainType()); // Create datadir if it does not exist. const auto base_path{args.GetDataDirBase()}; diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp index 624bdb42e4..df8cb7a6c9 100644 --- a/src/crypto/sha256_avx2.cpp +++ b/src/crypto/sha256_avx2.cpp @@ -7,6 +7,7 @@ #include <stdint.h> #include <immintrin.h> +#include <attributes.h> #include <crypto/common.h> namespace sha256d64_avx2 { @@ -36,7 +37,7 @@ __m256i inline sigma0(__m256i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR( __m256i inline sigma1(__m256i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ -void inline __attribute__((always_inline)) Round(__m256i a, __m256i b, __m256i c, __m256i& d, __m256i e, __m256i f, __m256i g, __m256i& h, __m256i k) +void ALWAYS_INLINE Round(__m256i a, __m256i b, __m256i c, __m256i& d, __m256i e, __m256i f, __m256i g, __m256i& h, __m256i k) { __m256i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m256i t2 = Add(Sigma0(a), Maj(a, b, c)); diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp index 4eaf7d7b18..d041fdfefc 100644 --- a/src/crypto/sha256_sse41.cpp +++ b/src/crypto/sha256_sse41.cpp @@ -7,6 +7,7 @@ #include <stdint.h> #include <immintrin.h> +#include <attributes.h> #include <crypto/common.h> namespace sha256d64_sse41 { @@ -36,7 +37,7 @@ __m128i inline sigma0(__m128i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR( __m128i inline sigma1(__m128i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ -void inline __attribute__((always_inline)) Round(__m128i a, __m128i b, __m128i c, __m128i& d, __m128i e, __m128i f, __m128i g, __m128i& h, __m128i k) +void ALWAYS_INLINE Round(__m128i a, __m128i b, __m128i c, __m128i& d, __m128i e, __m128i f, __m128i g, __m128i& h, __m128i k) { __m128i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m128i t2 = Add(Sigma0(a), Maj(a, b, c)); diff --git a/src/crypto/sha256_x86_shani.cpp b/src/crypto/sha256_x86_shani.cpp index e3143a55c2..79871bfcc1 100644 --- a/src/crypto/sha256_x86_shani.cpp +++ b/src/crypto/sha256_x86_shani.cpp @@ -11,43 +11,45 @@ #include <stdint.h> #include <immintrin.h> +#include <attributes.h> + namespace { alignas(__m128i) const uint8_t MASK[16] = {0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c}; alignas(__m128i) const uint8_t INIT0[16] = {0x8c, 0x68, 0x05, 0x9b, 0x7f, 0x52, 0x0e, 0x51, 0x85, 0xae, 0x67, 0xbb, 0x67, 0xe6, 0x09, 0x6a}; alignas(__m128i) const uint8_t INIT1[16] = {0x19, 0xcd, 0xe0, 0x5b, 0xab, 0xd9, 0x83, 0x1f, 0x3a, 0xf5, 0x4f, 0xa5, 0x72, 0xf3, 0x6e, 0x3c}; -void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, uint64_t k1, uint64_t k0) +void ALWAYS_INLINE QuadRound(__m128i& state0, __m128i& state1, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_set_epi64x(k1, k0); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } -void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, __m128i m, uint64_t k1, uint64_t k0) +void ALWAYS_INLINE QuadRound(__m128i& state0, __m128i& state1, __m128i m, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_add_epi32(m, _mm_set_epi64x(k1, k0)); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } -void inline __attribute__((always_inline)) ShiftMessageA(__m128i& m0, __m128i m1) +void ALWAYS_INLINE ShiftMessageA(__m128i& m0, __m128i m1) { m0 = _mm_sha256msg1_epu32(m0, m1); } -void inline __attribute__((always_inline)) ShiftMessageC(__m128i& m0, __m128i m1, __m128i& m2) +void ALWAYS_INLINE ShiftMessageC(__m128i& m0, __m128i m1, __m128i& m2) { m2 = _mm_sha256msg2_epu32(_mm_add_epi32(m2, _mm_alignr_epi8(m1, m0, 4)), m1); } -void inline __attribute__((always_inline)) ShiftMessageB(__m128i& m0, __m128i m1, __m128i& m2) +void ALWAYS_INLINE ShiftMessageB(__m128i& m0, __m128i m1, __m128i& m2) { ShiftMessageC(m0, m1, m2); ShiftMessageA(m0, m1); } -void inline __attribute__((always_inline)) Shuffle(__m128i& s0, __m128i& s1) +void ALWAYS_INLINE Shuffle(__m128i& s0, __m128i& s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0xB1); const __m128i t2 = _mm_shuffle_epi32(s1, 0x1B); @@ -55,7 +57,7 @@ void inline __attribute__((always_inline)) Shuffle(__m128i& s0, __m128i& s1) s1 = _mm_blend_epi16(t2, t1, 0xF0); } -void inline __attribute__((always_inline)) Unshuffle(__m128i& s0, __m128i& s1) +void ALWAYS_INLINE Unshuffle(__m128i& s0, __m128i& s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0x1B); const __m128i t2 = _mm_shuffle_epi32(s1, 0xB1); @@ -63,12 +65,12 @@ void inline __attribute__((always_inline)) Unshuffle(__m128i& s0, __m128i& s1) s1 = _mm_alignr_epi8(t2, t1, 0x08); } -__m128i inline __attribute__((always_inline)) Load(const unsigned char* in) +__m128i ALWAYS_INLINE Load(const unsigned char* in) { return _mm_shuffle_epi8(_mm_loadu_si128((const __m128i*)in), _mm_load_si128((const __m128i*)MASK)); } -void inline __attribute__((always_inline)) Save(unsigned char* out, __m128i s) +void ALWAYS_INLINE Save(unsigned char* out, __m128i s) { _mm_storeu_si128((__m128i*)out, _mm_shuffle_epi8(s, _mm_load_si128((const __m128i*)MASK))); } diff --git a/src/external_signer.cpp b/src/external_signer.cpp index 5524b943f4..6b1e1f0241 100644 --- a/src/external_signer.cpp +++ b/src/external_signer.cpp @@ -30,7 +30,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS } for (const UniValue& signer : result.getValues()) { // Check for error - const UniValue& error = find_value(signer, "error"); + const UniValue& error = signer.find_value("error"); if (!error.isNull()) { if (!error.isStr()) { throw std::runtime_error(strprintf("'%s' error", command)); @@ -38,11 +38,11 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr())); } // Check if fingerprint is present - const UniValue& fingerprint = find_value(signer, "fingerprint"); + const UniValue& fingerprint = signer.find_value("fingerprint"); if (fingerprint.isNull()) { throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command)); } - const std::string fingerprintStr = fingerprint.get_str(); + const std::string& fingerprintStr{fingerprint.get_str()}; // Skip duplicate signer bool duplicate = false; for (const ExternalSigner& signer : signers) { @@ -50,7 +50,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS } if (duplicate) break; std::string name; - const UniValue& model_field = find_value(signer, "model"); + const UniValue& model_field = signer.find_value("model"); if (model_field.isStr() && model_field.getValStr() != "") { name += model_field.getValStr(); } @@ -97,19 +97,19 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str const UniValue signer_result = RunCommandParseJSON(command, stdinStr); - if (find_value(signer_result, "error").isStr()) { - error = find_value(signer_result, "error").get_str(); + if (signer_result.find_value("error").isStr()) { + error = signer_result.find_value("error").get_str(); return false; } - if (!find_value(signer_result, "psbt").isStr()) { + if (!signer_result.find_value("psbt").isStr()) { error = "Unexpected result from signer"; return false; } PartiallySignedTransaction signer_psbtx; std::string signer_psbt_error; - if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) { + if (!DecodeBase64PSBT(signer_psbtx, signer_result.find_value("psbt").get_str(), signer_psbt_error)) { error = strprintf("TX decode failed %s", signer_psbt_error); return false; } diff --git a/src/httprpc.cpp b/src/httprpc.cpp index bf3fa6298d..661406e122 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -76,7 +76,7 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni { // Send error reply from json-rpc error object int nStatus = HTTP_INTERNAL_SERVER_ERROR; - int code = find_value(objError, "code").getInt<int>(); + int code = objError.find_value("code").getInt<int>(); if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; @@ -213,7 +213,7 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req) } else { const UniValue& request = valRequest[reqIdx].get_obj(); // Parse method - std::string strMethod = find_value(request, "method").get_str(); + std::string strMethod = request.find_value("method").get_str(); if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); req->WriteReply(HTTP_FORBIDDEN); diff --git a/src/init.cpp b/src/init.cpp index 525648b812..802397b3cf 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -18,6 +18,7 @@ #include <blockfilter.h> #include <chain.h> #include <chainparams.h> +#include <chainparamsbase.h> #include <common/args.h> #include <consensus/amount.h> #include <deploymentstatus.h> @@ -69,6 +70,7 @@ #include <txdb.h> #include <txmempool.h> #include <util/asmap.h> +#include <util/chaintype.h> #include <util/check.h> #include <util/fs.h> #include <util/fs_helpers.h> @@ -408,14 +410,14 @@ void SetupServerArgs(ArgsManager& argsman) init::AddLoggingArgs(argsman); - const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); - const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); - const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET); - const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); - const auto defaultChainParams = CreateChainParams(argsman, CBaseChainParams::MAIN); - const auto testnetChainParams = CreateChainParams(argsman, CBaseChainParams::TESTNET); - const auto signetChainParams = CreateChainParams(argsman, CBaseChainParams::SIGNET); - const auto regtestChainParams = CreateChainParams(argsman, CBaseChainParams::REGTEST); + const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN); + const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); + const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); + const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); + const auto defaultChainParams = CreateChainParams(argsman, ChainType::MAIN); + const auto testnetChainParams = CreateChainParams(argsman, ChainType::TESTNET); + const auto signetChainParams = CreateChainParams(argsman, ChainType::SIGNET); + const auto regtestChainParams = CreateChainParams(argsman, ChainType::REGTEST); // Hidden Options std::vector<std::string> hidden_args = { @@ -844,14 +846,14 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb // Error if network-specific options (-addnode, -connect, etc) are // specified in default section of config file, but not overridden - // on the command line or in this network's section of the config file. - std::string network = args.GetChainName(); - if (network == CBaseChainParams::SIGNET) { + // on the command line or in this chain's section of the config file. + ChainType chain = args.GetChainType(); + if (chain == ChainType::SIGNET) { LogPrintf("Signet derived magic (message start): %s\n", HexStr(chainparams.MessageStart())); } bilingual_str errors; for (const auto& arg : args.GetUnsuitableSectionOnlyArgs()) { - errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network); + errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, ChainTypeToString(chain), ChainTypeToString(chain)); } if (!errors.empty()) { @@ -1036,7 +1038,9 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) { return InitError(*error); } - node::BlockManager::Options blockman_opts_dummy{}; + node::BlockManager::Options blockman_opts_dummy{ + .chainparams = chainman_opts_dummy.chainparams, + }; if (const auto error{ApplyArgsManOptions(args, blockman_opts_dummy)}) { return InitError(*error); } @@ -1439,7 +1443,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) }; Assert(!ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction - node::BlockManager::Options blockman_opts{}; + node::BlockManager::Options blockman_opts{ + .chainparams = chainman_opts.chainparams, + }; Assert(!ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction // cache size calculations diff --git a/src/kernel/blockmanager_opts.h b/src/kernel/blockmanager_opts.h index 9dc93b6dd2..5584a66e02 100644 --- a/src/kernel/blockmanager_opts.h +++ b/src/kernel/blockmanager_opts.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H #define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H +class CChainParams; + namespace kernel { /** @@ -12,6 +14,7 @@ namespace kernel { * `BlockManager::Options` due to the using-declaration in `BlockManager`. */ struct BlockManagerOpts { + const CChainParams& chainparams; uint64_t prune_target{0}; }; diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 2827737ee1..d9ed1547b3 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -10,13 +10,13 @@ #include <consensus/merkle.h> #include <consensus/params.h> #include <hash.h> -#include <chainparamsbase.h> #include <logging.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <script/interpreter.h> #include <script/script.h> #include <uint256.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <algorithm> @@ -70,7 +70,7 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits class CMainParams : public CChainParams { public: CMainParams() { - strNetworkID = CBaseChainParams::MAIN; + m_chain_type = ChainType::MAIN; consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; @@ -192,7 +192,7 @@ public: class CTestNetParams : public CChainParams { public: CTestNetParams() { - strNetworkID = CBaseChainParams::TESTNET; + m_chain_type = ChainType::TESTNET; consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; @@ -328,7 +328,7 @@ public: vSeeds = *options.seeds; } - strNetworkID = CBaseChainParams::SIGNET; + m_chain_type = ChainType::SIGNET; consensus.signet_blocks = true; consensus.signet_challenge.assign(bin.begin(), bin.end()); consensus.nSubsidyHalvingInterval = 210000; @@ -397,7 +397,7 @@ class CRegTestParams : public CChainParams public: explicit CRegTestParams(const RegTestOptions& opts) { - strNetworkID = CBaseChainParams::REGTEST; + m_chain_type = ChainType::REGTEST; consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 150; diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index 32fe618dbd..ad0b49a885 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -11,6 +11,7 @@ #include <primitives/block.h> #include <protocol.h> #include <uint256.h> +#include <util/chaintype.h> #include <util/hash_type.h> #include <cstdint> @@ -114,8 +115,10 @@ public: uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; } /** Whether it is possible to mine blocks on demand (no retargeting) */ bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; } - /** Return the network string */ - std::string NetworkIDString() const { return strNetworkID; } + /** Return the chain type string */ + std::string GetChainTypeString() const { return ChainTypeToString(m_chain_type); } + /** Return the chain type */ + ChainType GetChainType() const { return m_chain_type; } /** Return the list of hostnames to look up for DNS seeds */ const std::vector<std::string>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } @@ -172,7 +175,7 @@ protected: std::vector<std::string> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; std::string bech32_hrp; - std::string strNetworkID; + ChainType m_chain_type; CBlock genesis; std::vector<uint8_t> vFixedSeeds; bool fDefaultConsistencyChecks; diff --git a/src/kernel/coinstats.cpp b/src/kernel/coinstats.cpp index 4b75c387a6..527433f45e 100644 --- a/src/kernel/coinstats.cpp +++ b/src/kernel/coinstats.cpp @@ -123,7 +123,7 @@ static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, c uint256 prevkey; std::map<uint32_t, Coin> outputs; while (pcursor->Valid()) { - interruption_point(); + if (interruption_point) interruption_point(); COutPoint key; Coin coin; if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index f5552cfcd8..0f1fa0499c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -883,8 +883,11 @@ private: /** Remove this block from our tracked requested blocks. Called if: * - the block has been received from a peer * - the request for the block has timed out + * If "from_peer" is specified, then only remove the block if it is in + * flight from that peer (to avoid one peer's network traffic from + * affecting another's state). */ - void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void RemoveBlockRequest(const uint256& hash, std::optional<NodeId> from_peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /* Mark a block as in flight * Returns false, still setting pit, if the block was already in flight from the same peer @@ -1120,7 +1123,7 @@ bool PeerManagerImpl::IsBlockRequested(const uint256& hash) return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end(); } -void PeerManagerImpl::RemoveBlockRequest(const uint256& hash) +void PeerManagerImpl::RemoveBlockRequest(const uint256& hash, std::optional<NodeId> from_peer) { auto it = mapBlocksInFlight.find(hash); if (it == mapBlocksInFlight.end()) { @@ -1129,6 +1132,12 @@ void PeerManagerImpl::RemoveBlockRequest(const uint256& hash) } auto [node_id, list_it] = it->second; + + if (from_peer && node_id != *from_peer) { + // Block was requested by another peer + return; + } + CNodeState *state = State(node_id); assert(state != nullptr); @@ -1164,7 +1173,7 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st } // Make sure it's not listed somewhere already. - RemoveBlockRequest(hash); + RemoveBlockRequest(hash, std::nullopt); std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)}); @@ -3155,6 +3164,11 @@ void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlo m_chainman.ProcessNewBlock(block, force_processing, min_pow_checked, &new_block); if (new_block) { node.m_last_block_time = GetTime<std::chrono::seconds>(); + // In case this block came from a different peer than we requested + // from, we can erase the block request now anyway (as we just stored + // this block to disk). + LOCK(cs_main); + RemoveBlockRequest(block->GetHash(), std::nullopt); } else { LOCK(cs_main); mapBlockSource.erase(block->GetHash()); @@ -4305,7 +4319,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock; ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status == READ_STATUS_INVALID) { - RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect + RemoveBlockRequest(pindex->GetBlockHash(), pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect Misbehaving(*peer, 100, "invalid compact block"); return; } else if (status == READ_STATUS_FAILED) { @@ -4400,7 +4414,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // process from some other peer. We do this after calling // ProcessNewBlock so that a malleated cmpctblock announcement // can't be used to interfere with block relay. - RemoveBlockRequest(pblock->GetHash()); + RemoveBlockRequest(pblock->GetHash(), std::nullopt); } } return; @@ -4432,7 +4446,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock; ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); if (status == READ_STATUS_INVALID) { - RemoveBlockRequest(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect + RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect Misbehaving(*peer, 100, "invalid compact block/non-matching block transactions"); return; } else if (status == READ_STATUS_FAILED) { @@ -4458,7 +4472,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // though the block was successfully read, and rely on the // handling in ProcessNewBlock to ensure the block index is // updated, etc. - RemoveBlockRequest(resp.blockhash); // it is now an empty pointer + RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // it is now an empty pointer fBlockRead = true; // mapBlockSource is used for potentially punishing peers and // updating which peers send us compact blocks, so the race @@ -4547,7 +4561,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Always process the block if we requested it, since we may // need it even when it's not a candidate for a new best tip. forceProcessing = IsBlockRequested(hash); - RemoveBlockRequest(hash); + RemoveBlockRequest(hash, pfrom.GetId()); // mapBlockSource is only used for punishing peers and setting // which peers send us compact blocks, so the race between here and // cs_main in ProcessNewBlock is fine. diff --git a/src/netbase.cpp b/src/netbase.cpp index f39a3635f4..4f78d2e31a 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -36,8 +36,8 @@ static Proxy nameProxy GUARDED_BY(g_proxyinfo_mutex); int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; bool fNameLookup = DEFAULT_NAME_LOOKUP; -// Need ample time for negotiation for very slow proxies such as Tor (milliseconds) -int g_socks5_recv_timeout = 20 * 1000; +// Need ample time for negotiation for very slow proxies such as Tor +std::chrono::milliseconds g_socks5_recv_timeout = 20s; static std::atomic<bool> interruptSocks5Recv(false); std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup) @@ -296,7 +296,7 @@ enum class IntrRecvError { * * @param data The buffer where the read bytes should be stored. * @param len The number of bytes to read into the specified buffer. - * @param timeout The total timeout in milliseconds for this read. + * @param timeout The total timeout for this read. * @param sock The socket (has to be in non-blocking mode) from which to read bytes. * * @returns An IntrRecvError indicating the resulting status of this read. @@ -306,10 +306,10 @@ enum class IntrRecvError { * @see This function can be interrupted by calling InterruptSocks5(bool). * Sockets can be made non-blocking with Sock::SetNonBlocking(). */ -static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock) +static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, std::chrono::milliseconds timeout, const Sock& sock) { - int64_t curTime = GetTimeMillis(); - int64_t endTime = curTime + timeout; + auto curTime{Now<SteadyMilliseconds>()}; + const auto endTime{curTime + timeout}; while (len > 0 && curTime < endTime) { ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first if (ret > 0) { @@ -333,7 +333,7 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c } if (interruptSocks5Recv) return IntrRecvError::Interrupted; - curTime = GetTimeMillis(); + curTime = Now<SteadyMilliseconds>(); } return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index abd71ca50b..bafee4e237 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -253,9 +253,9 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash) return pindex; } -bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params) +bool BlockManager::LoadBlockIndex() { - if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) { + if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) { return false; } @@ -318,9 +318,9 @@ bool BlockManager::WriteBlockIndexDB() return true; } -bool BlockManager::LoadBlockIndexDB(const Consensus::Params& consensus_params) +bool BlockManager::LoadBlockIndexDB() { - if (!LoadBlockIndex(consensus_params)) { + if (!LoadBlockIndex()) { return false; } @@ -720,16 +720,16 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa return true; } -bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams) +bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block) { AssertLockHeld(::cs_main); // Write undo information to disk - if (pindex->GetUndoPos().IsNull()) { + if (block.GetUndoPos().IsNull()) { FlatFilePos _pos; - if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) { + if (!FindUndoPos(state, block.nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) { return error("ConnectBlock(): FindUndoPos failed"); } - if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) { + if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash(), GetParams().MessageStart())) { return AbortNode(state, "Failed to write undo data"); } // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order) @@ -737,14 +737,14 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid // in the block file info as below; note that this does not catch the case where the undo writes are keeping up // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in // the FindBlockPos function - if (_pos.nFile < m_last_blockfile && static_cast<uint32_t>(pindex->nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) { + if (_pos.nFile < m_last_blockfile && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) { FlushUndoFile(_pos.nFile, true); } // update nUndoPos in block index - pindex->nUndoPos = _pos.nPos; - pindex->nStatus |= BLOCK_HAVE_UNDO; - m_dirty_blockindex.insert(pindex); + block.nUndoPos = _pos.nPos; + block.nStatus |= BLOCK_HAVE_UNDO; + m_dirty_blockindex.insert(&block); } return true; @@ -829,7 +829,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c return true; } -FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) +FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const FlatFilePos* dbp) { unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION); FlatFilePos blockPos; @@ -847,7 +847,7 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CCha return FlatFilePos(); } if (!position_known) { - if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) { + if (!WriteBlockToDisk(block, blockPos, GetParams().MessageStart())) { AbortNode("Failed to write block"); return FlatFilePos(); } diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 3eb27cc72d..8f699f8e68 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -8,6 +8,7 @@ #include <attributes.h> #include <chain.h> #include <kernel/blockmanager_opts.h> +#include <kernel/chainparams.h> #include <kernel/cs_main.h> #include <protocol.h> #include <sync.h> @@ -81,12 +82,14 @@ class BlockManager friend ChainstateManager; private: + const CChainParams& GetParams() const { return m_opts.chainparams; } + const Consensus::Params& GetConsensus() const { return m_opts.chainparams.GetConsensus(); } /** * Load the blocktree off disk and into memory. Populate certain metadata * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral * collections like m_dirty_blockindex. */ - bool LoadBlockIndex(const Consensus::Params& consensus_params) + bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main); void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false); void FlushUndoFile(int block_file, bool finalize = false); @@ -162,7 +165,7 @@ public: std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main); bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - bool LoadBlockIndexDB(const Consensus::Params& consensus_params) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** * Remove any pruned block & undo files that are still on disk. @@ -184,11 +187,11 @@ public: /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); - bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams) + bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** Store block on disk. If dbp is not nullptr, then it provides the known position of the block within a block file on disk. */ - FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp); + FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const FlatFilePos* dbp); /** Whether running in -prune mode. */ [[nodiscard]] bool IsPruneMode() const { return m_prune_mode; } diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 5fcace833f..49b88c3596 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -239,7 +239,7 @@ public: std::vector<ExternalSigner> signers = {}; const std::string command = args().GetArg("-signer", ""); if (command == "") return {}; - ExternalSigner::Enumerate(command, signers, Params().NetworkIDString()); + ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString()); std::vector<std::unique_ptr<interfaces::ExternalSigner>> result; result.reserve(signers.size()); for (auto& signer : signers) { diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index f193469506..294111a58a 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -89,7 +89,7 @@ std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& argsman, con mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (!chainparams.IsTestChain() && !mempool_opts.require_standard) { - return strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()); + return strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.GetChainTypeString()); } mempool_opts.full_rbf = argsman.GetBoolArg("-mempoolfullrbf", mempool_opts.full_rbf); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 2f00009596..d602d2c1ac 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -601,7 +601,7 @@ int GuiMain(int argc, char* argv[]) PaymentServer::ipcParseCommandLine(argc, argv); #endif - QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().NetworkIDString())); + QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().GetChainType())); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets QApplication::setApplicationName(networkStyle->getAppName()); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 837e5f4fed..b22f1bc35c 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -28,8 +28,8 @@ #include <QThread> #include <QTimer> -static int64_t nLastHeaderTipUpdateNotification = 0; -static int64_t nLastBlockTipUpdateNotification = 0; +static SteadyClock::time_point g_last_header_tip_update_notification{}; +static SteadyClock::time_point g_last_block_tip_update_notification{}; ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), @@ -222,9 +222,9 @@ void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockT // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex. const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX; - const int64_t now = throttle ? GetTimeMillis() : 0; - int64_t& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; - if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) { + const auto now{throttle ? SteadyClock::now() : SteadyClock::time_point{}}; + auto& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? g_last_header_tip_update_notification : g_last_block_tip_update_notification; + if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) { return; } diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index dc7daed4bc..8d8328aad8 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -21,6 +21,7 @@ #include <protocol.h> #include <script/script.h> #include <script/standard.h> +#include <util/chaintype.h> #include <util/exception.h> #include <util/fs.h> #include <util/fs_helpers.h> @@ -503,12 +504,12 @@ bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event) #ifdef WIN32 fs::path static StartupShortcutPath() { - std::string chain = gArgs.GetChainName(); - if (chain == CBaseChainParams::MAIN) + ChainType chain = gArgs.GetChainType(); + if (chain == ChainType::MAIN) return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; - if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4" + if (chain == ChainType::TESTNET) // Remove this special case when testnet CBaseChainParams::DataDir() is incremented to "testnet4" return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk"; - return GetSpecialFolderPath(CSIDL_STARTUP) / fs::u8path(strprintf("Bitcoin (%s).lnk", chain)); + return GetSpecialFolderPath(CSIDL_STARTUP) / fs::u8path(strprintf("Bitcoin (%s).lnk", ChainTypeToString(chain))); } bool GetStartOnSystemStartup() @@ -541,7 +542,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Start client minimized QString strArgs = "-min"; // Set -testnet /-regtest options - strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainName())); + strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainTypeString())); // Set the path to the shortcut target psl->SetPath(pszExePath); @@ -586,10 +587,10 @@ fs::path static GetAutostartDir() fs::path static GetAutostartFilePath() { - std::string chain = gArgs.GetChainName(); - if (chain == CBaseChainParams::MAIN) + ChainType chain = gArgs.GetChainType(); + if (chain == ChainType::MAIN) return GetAutostartDir() / "bitcoin.desktop"; - return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", chain)); + return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", ChainTypeToString(chain))); } bool GetStartOnSystemStartup() @@ -629,15 +630,15 @@ bool SetStartOnSystemStartup(bool fAutoStart) std::ofstream optionFile{GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc}; if (!optionFile.good()) return false; - std::string chain = gArgs.GetChainName(); + ChainType chain = gArgs.GetChainType(); // Write a bitcoin.desktop file to the autostart directory: optionFile << "[Desktop Entry]\n"; optionFile << "Type=Application\n"; - if (chain == CBaseChainParams::MAIN) + if (chain == ChainType::MAIN) optionFile << "Name=Bitcoin\n"; else - optionFile << strprintf("Name=Bitcoin (%s)\n", chain); - optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", chain); + optionFile << strprintf("Name=Bitcoin (%s)\n", ChainTypeToString(chain)); + optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", ChainTypeToString(chain)); optionFile << "Terminal=false\n"; optionFile << "Hidden=false\n"; optionFile.close(); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index a54ba19354..f86b167076 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -9,6 +9,7 @@ #include <chainparams.h> #include <qt/intro.h> #include <qt/forms/ui_intro.h> +#include <util/chaintype.h> #include <util/fs.h> #include <qt/guiconstants.h> @@ -219,7 +220,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB) { /* Use selectParams here to guarantee Params() can be used by node interface */ try { - SelectParams(gArgs.GetChainName()); + SelectParams(gArgs.GetChainType()); } catch (const std::exception&) { return false; } diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index b789e6a958..b6314f5533 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -6,21 +6,21 @@ #include <qt/guiconstants.h> -#include <chainparamsbase.h> #include <tinyformat.h> +#include <util/chaintype.h> #include <QApplication> static const struct { - const char *networkId; + const ChainType networkId; const char *appName; const int iconColorHueShift; const int iconColorSaturationReduction; } network_styles[] = { - {"main", QAPP_APP_NAME_DEFAULT, 0, 0}, - {"test", QAPP_APP_NAME_TESTNET, 70, 30}, - {"signet", QAPP_APP_NAME_SIGNET, 35, 15}, - {"regtest", QAPP_APP_NAME_REGTEST, 160, 30}, + {ChainType::MAIN, QAPP_APP_NAME_DEFAULT, 0, 0}, + {ChainType::TESTNET, QAPP_APP_NAME_TESTNET, 70, 30}, + {ChainType::SIGNET, QAPP_APP_NAME_SIGNET, 35, 15}, + {ChainType::REGTEST, QAPP_APP_NAME_REGTEST, 160, 30}, }; // titleAddText needs to be const char* for tr() @@ -77,9 +77,9 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, trayAndWindowIcon = QIcon(pixmap.scaled(QSize(256,256))); } -const NetworkStyle* NetworkStyle::instantiate(const std::string& networkId) +const NetworkStyle* NetworkStyle::instantiate(const ChainType networkId) { - std::string titleAddText = networkId == CBaseChainParams::MAIN ? "" : strprintf("[%s]", networkId); + std::string titleAddText = networkId == ChainType::MAIN ? "" : strprintf("[%s]", ChainTypeToString(networkId)); for (const auto& network_style : network_styles) { if (networkId == network_style.networkId) { return new NetworkStyle( diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h index a73e3e2625..dd2aee3eb3 100644 --- a/src/qt/networkstyle.h +++ b/src/qt/networkstyle.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_QT_NETWORKSTYLE_H #define BITCOIN_QT_NETWORKSTYLE_H +#include <util/chaintype.h> + #include <QIcon> #include <QPixmap> #include <QString> @@ -14,7 +16,7 @@ class NetworkStyle { public: /** Get style associated with provided network id, or 0 if not known */ - static const NetworkStyle* instantiate(const std::string& networkId); + static const NetworkStyle* instantiate(const ChainType networkId); const QString &getAppName() const { return appName; } const QIcon &getAppIcon() const { return appIcon; } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 76500a4a36..bac64e3d5f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -249,7 +249,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes subelement = lastResult[parsed.value()]; } else if (lastResult.isObject()) - subelement = find_value(lastResult, curarg); + subelement = lastResult.find_value(curarg); else throw std::runtime_error("Invalid result query"); //no array or object: abort lastResult = subelement; @@ -448,8 +448,8 @@ void RPCExecutor::request(const QString &command, const WalletModel* wallet_mode { try // Nice formatting for standard-format error { - int code = find_value(objError, "code").getInt<int>(); - std::string message = find_value(objError, "message").get_str(); + int code = objError.find_value("code").getInt<int>(); + std::string message = objError.find_value("message").get_str(); Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")"); } catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message @@ -744,7 +744,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->dataDir->setText(model->dataDir()); ui->blocksDir->setText(model->blocksDir()); ui->startupTime->setText(model->formatClientStartupTime()); - ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); + ui->networkName->setText(QString::fromStdString(Params().GetChainTypeString())); //Setup autocomplete and attach it QStringList wordList; diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index 000bbe65be..e918e84184 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -73,7 +73,7 @@ void AppTests::appTests() qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo"); m_app.parameterSetup(); QVERIFY(m_app.createOptionsModel(/*resetSettings=*/true)); - QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().NetworkIDString())); + QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().GetChainType())); m_app.setupPlatformStyle(); m_app.createWindow(style.data()); connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests); diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 2d069f76a0..eaadbd7f7a 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -14,6 +14,7 @@ #include <qt/test/rpcnestedtests.h> #include <qt/test/uritests.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #ifdef ENABLE_WALLET #include <qt/test/addressbooktests.h> @@ -57,7 +58,7 @@ int main(int argc, char* argv[]) // // All tests must use their own testing setup (if needed). fs::create_directories([] { - BasicTestingSetup dummy{CBaseChainParams::REGTEST}; + BasicTestingSetup dummy{ChainType::REGTEST}; return gArgs.GetDataDirNet() / "blocks"; }()); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 11484a9d8d..e7a40f812c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1256,7 +1256,7 @@ RPCHelpMan getblockchaininfo() const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())}; const int height{tip.nHeight}; UniValue obj(UniValue::VOBJ); - obj.pushKV("chain", chainman.GetParams().NetworkIDString()); + obj.pushKV("chain", chainman.GetParams().GetChainTypeString()); obj.pushKV("blocks", height); obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1); obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex()); diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp index 1e139c9990..ac135ba216 100644 --- a/src/rpc/external_signer.cpp +++ b/src/rpc/external_signer.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 <chainparamsbase.h> #include <common/args.h> #include <external_signer.h> #include <rpc/protocol.h> @@ -43,7 +42,7 @@ static RPCHelpMan enumeratesigners() { const std::string command = gArgs.GetArg("-signer", ""); if (command == "") throw JSONRPCError(RPC_MISC_ERROR, "Error: restart bitcoind with -signer=<cmd>"); - const std::string chain = gArgs.GetChainName(); + const std::string chain = gArgs.GetChainTypeString(); UniValue signers_res = UniValue::VARR; try { std::vector<ExternalSigner> signers; diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 927b4ce1fc..89c403b6f5 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -638,7 +638,7 @@ static RPCHelpMan gettxspendingprevout() }, /*fAllowNull=*/false, /*fStrict=*/true); const uint256 txid(ParseHashO(o, "txid")); - const int nOutput{find_value(o, "vout").getInt<int>()}; + const int nOutput{o.find_value("vout").getInt<int>()}; if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index d55e20ba5e..68017e5af2 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -435,7 +435,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", chainman.GetParams().NetworkIDString()); + obj.pushKV("chain", chainman.GetParams().GetChainTypeString()); obj.pushKV("warnings", GetWarnings(false).original); return obj; }, @@ -612,7 +612,7 @@ static RPCHelpMan getblocktemplate() if (!request.params[0].isNull()) { const UniValue& oparam = request.params[0].get_obj(); - const UniValue& modeval = find_value(oparam, "mode"); + const UniValue& modeval = oparam.find_value("mode"); if (modeval.isStr()) strMode = modeval.get_str(); else if (modeval.isNull()) @@ -621,11 +621,11 @@ static RPCHelpMan getblocktemplate() } else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - lpval = find_value(oparam, "longpollid"); + lpval = oparam.find_value("longpollid"); if (strMode == "proposal") { - const UniValue& dataval = find_value(oparam, "data"); + const UniValue& dataval = oparam.find_value("data"); if (!dataval.isStr()) throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); @@ -652,7 +652,7 @@ static RPCHelpMan getblocktemplate() return BIP22ValidationResult(state); } - const UniValue& aClientRules = find_value(oparam, "rules"); + const UniValue& aClientRules = oparam.find_value("rules"); if (aClientRules.isArray()) { for (unsigned int i = 0; i < aClientRules.size(); ++i) { const UniValue& v = aClientRules[i]; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7ffa777ef4..f247e6ee91 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -21,6 +21,7 @@ #include <rpc/util.h> #include <sync.h> #include <timedata.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <util/string.h> #include <util/time.h> @@ -354,7 +355,7 @@ static RPCHelpMan addconnection() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - if (Params().NetworkIDString() != CBaseChainParams::REGTEST) { + if (Params().GetChainType() != ChainType::REGTEST) { throw std::runtime_error("addconnection is for regression testing (-regtest mode) only."); } @@ -512,15 +513,15 @@ static RPCHelpMan getaddednodeinfo() static RPCHelpMan getnettotals() { return RPCHelpMan{"getnettotals", - "\nReturns information about network traffic, including bytes in, bytes out,\n" - "and current time.\n", - {}, + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current system time.", + {}, RPCResult{ RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"}, {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"}, - {RPCResult::Type::NUM_TIME, "timemillis", "Current " + UNIX_EPOCH_TIME + " in milliseconds"}, + {RPCResult::Type::NUM_TIME, "timemillis", "Current system " + UNIX_EPOCH_TIME + " in milliseconds"}, {RPCResult::Type::OBJ, "uploadtarget", "", { {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"}, @@ -544,7 +545,7 @@ static RPCHelpMan getnettotals() UniValue obj(UniValue::VOBJ); obj.pushKV("totalbytesrecv", connman.GetTotalBytesRecv()); obj.pushKV("totalbytessent", connman.GetTotalBytesSent()); - obj.pushKV("timemillis", GetTimeMillis()); + obj.pushKV("timemillis", TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now())); UniValue outboundLimit(UniValue::VOBJ); outboundLimit.pushKV("timeframe", count_seconds(connman.GetMaxOutboundTimeframe())); diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 3ba930f84f..3a6fa39e4d 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -36,7 +36,7 @@ void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optio uint256 txid = ParseHashO(o, "txid"); - const UniValue& vout_v = find_value(o, "vout"); + const UniValue& vout_v = o.find_value("vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.getInt<int>(); @@ -54,7 +54,7 @@ void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optio } // set the sequence number if passed in the parameters object - const UniValue& sequenceObj = find_value(o, "sequence"); + const UniValue& sequenceObj = o.find_value("sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.getInt<int64_t>(); if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) { @@ -187,7 +187,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst uint256 txid = ParseHashO(prevOut, "txid"); - int nOut = find_value(prevOut, "vout").getInt<int>(); + int nOut = prevOut.find_value("vout").getInt<int>(); if (nOut < 0) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative"); } @@ -208,7 +208,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.nValue = MAX_MONEY; if (prevOut.exists("amount")) { - newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); + newcoin.out.nValue = AmountFromValue(prevOut.find_value("amount")); } newcoin.nHeight = 1; coins[out] = std::move(newcoin); @@ -223,8 +223,8 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst {"redeemScript", UniValueType(UniValue::VSTR)}, {"witnessScript", UniValueType(UniValue::VSTR)}, }, true); - UniValue rs = find_value(prevOut, "redeemScript"); - UniValue ws = find_value(prevOut, "witnessScript"); + const UniValue& rs{prevOut.find_value("redeemScript")}; + const UniValue& ws{prevOut.find_value("witnessScript")}; if (rs.isNull() && ws.isNull()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript"); } diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp index ad91ed0f23..cf1b6cd92b 100644 --- a/src/rpc/request.cpp +++ b/src/rpc/request.cpp @@ -165,10 +165,10 @@ void JSONRPCRequest::parse(const UniValue& valRequest) const UniValue& request = valRequest.get_obj(); // Parse id now so errors from here on will have the id - id = find_value(request, "id"); + id = request.find_value("id"); // Parse method - UniValue valMethod = find_value(request, "method"); + const UniValue& valMethod{request.find_value("method")}; if (valMethod.isNull()) throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); if (!valMethod.isStr()) @@ -181,7 +181,7 @@ void JSONRPCRequest::parse(const UniValue& valRequest) LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser); // Parse params - UniValue valParams = find_value(request, "params"); + const UniValue& valParams{request.find_value("params")}; if (valParams.isArray() || valParams.isObject()) params = valParams; else if (valParams.isNull()) diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 1f3f37d0a0..81489d7cec 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -37,7 +37,7 @@ void RPCTypeCheckObj(const UniValue& o, bool fStrict) { for (const auto& t : typesExpected) { - const UniValue& v = find_value(o, t.first); + const UniValue& v = o.find_value(t.first); if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); @@ -81,7 +81,7 @@ uint256 ParseHashV(const UniValue& v, std::string strName) } uint256 ParseHashO(const UniValue& o, std::string strKey) { - return ParseHashV(find_value(o, strKey), strKey); + return ParseHashV(o.find_value(strKey), strKey); } std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName) { @@ -94,7 +94,7 @@ std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName) } std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey) { - return ParseHexV(find_value(o, strKey), strKey); + return ParseHexV(o.find_value(strKey), strKey); } namespace { @@ -1133,10 +1133,10 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl if (scanobject.isStr()) { desc_str = scanobject.get_str(); } else if (scanobject.isObject()) { - UniValue desc_uni = find_value(scanobject, "desc"); + const UniValue& desc_uni{scanobject.find_value("desc")}; if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object"); desc_str = desc_uni.get_str(); - UniValue range_uni = find_value(scanobject, "range"); + const UniValue& range_uni{scanobject.find_value("range")}; if (!range_uni.isNull()) { range = ParseDescriptorRange(range_uni); } diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 0951504ee0..b8ade1684a 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -197,7 +197,9 @@ public: /** Get the descriptor string form including private data (if available in arg). */ virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0; - /** Get the descriptor string form with the xpub at the last hardened derivation */ + /** Get the descriptor string form with the xpub at the last hardened derivation, + * and always use h for hardened derivation. + */ virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const = 0; /** Derive a private key, if private data is available in arg. */ @@ -208,14 +210,15 @@ class OriginPubkeyProvider final : public PubkeyProvider { KeyOriginInfo m_origin; std::unique_ptr<PubkeyProvider> m_provider; + bool m_apostrophe; - std::string OriginString() const + std::string OriginString(bool normalized=false) const { - return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path); + return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, /*apostrophe=*/!normalized && m_apostrophe); } public: - OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)) {} + OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider, bool apostrophe) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)), m_apostrophe(apostrophe) {} bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override { if (!m_provider->GetPubKey(pos, arg, key, info, read_cache, write_cache)) return false; @@ -242,9 +245,9 @@ public: // and append that to our own origin string. if (sub[0] == '[') { sub = sub.substr(9); - ret = "[" + OriginString() + std::move(sub); + ret = "[" + OriginString(/*normalized=*/true) + std::move(sub); } else { - ret = "[" + OriginString() + "]" + std::move(sub); + ret = "[" + OriginString(/*normalized=*/true) + "]" + std::move(sub); } return true; } @@ -312,6 +315,8 @@ class BIP32PubkeyProvider final : public PubkeyProvider CExtPubKey m_root_extkey; KeyPath m_path; DeriveType m_derive; + // Whether ' or h is used in harded derivation + bool m_apostrophe; bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const { @@ -348,7 +353,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider } public: - BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive) {} + BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive, bool apostrophe) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive), m_apostrophe(apostrophe) {} bool IsRange() const override { return m_derive != DeriveType::NO; } size_t GetSize() const override { return 33; } bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override @@ -416,31 +421,36 @@ public: return true; } - std::string ToString() const override + std::string ToString(bool normalized) const { - std::string ret = EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path); + const bool use_apostrophe = !normalized && m_apostrophe; + std::string ret = EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path, /*apostrophe=*/use_apostrophe); if (IsRange()) { ret += "/*"; - if (m_derive == DeriveType::HARDENED) ret += '\''; + if (m_derive == DeriveType::HARDENED) ret += use_apostrophe ? '\'' : 'h'; } return ret; } + std::string ToString() const override + { + return ToString(/*normalized=*/false); + } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { CExtKey key; if (!GetExtKey(arg, key)) return false; - out = EncodeExtKey(key) + FormatHDKeypath(m_path); + out = EncodeExtKey(key) + FormatHDKeypath(m_path, /*apostrophe=*/m_apostrophe); if (IsRange()) { out += "/*"; - if (m_derive == DeriveType::HARDENED) out += '\''; + if (m_derive == DeriveType::HARDENED) out += m_apostrophe ? '\'' : 'h'; } return true; } bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override { - // For hardened derivation type, just return the typical string, nothing to normalize if (m_derive == DeriveType::HARDENED) { - out = ToString(); + out = ToString(/*normalized=*/true); + return true; } // Step backwards to find the last hardened step in the path @@ -1049,15 +1059,27 @@ enum class ParseScriptContext { P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf) }; -/** Parse a key path, being passed a split list of elements (the first element is ignored). */ -[[nodiscard]] bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error) +/** + * Parse a key path, being passed a split list of elements (the first element is ignored). + * + * @param[in] split BIP32 path string, using either ' or h for hardened derivation + * @param[out] out the key path + * @param[out] apostrophe only updated if hardened derivation is found + * @param[out] error parsing error message + * @returns false if parsing failed + **/ +[[nodiscard]] bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, bool& apostrophe, std::string& error) { for (size_t i = 1; i < split.size(); ++i) { Span<const char> elem = split[i]; bool hardened = false; - if (elem.size() > 0 && (elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) { - elem = elem.first(elem.size() - 1); - hardened = true; + if (elem.size() > 0) { + const char last = elem[elem.size() - 1]; + if (last == '\'' || last == 'h') { + elem = elem.first(elem.size() - 1); + hardened = true; + apostrophe = last == '\''; + } } uint32_t p; if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) { @@ -1073,7 +1095,7 @@ enum class ParseScriptContext { } /** Parse a public key that excludes origin information. */ -std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error) +std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, bool& apostrophe, std::string& error) { using namespace spanparsing; @@ -1130,15 +1152,16 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S split.pop_back(); type = DeriveType::UNHARDENED; } else if (split.back() == Span{"*'"}.first(2) || split.back() == Span{"*h"}.first(2)) { + apostrophe = split.back() == Span{"*'"}.first(2); split.pop_back(); type = DeriveType::HARDENED; } - if (!ParseKeyPath(split, path, error)) return nullptr; + if (!ParseKeyPath(split, path, apostrophe, error)) return nullptr; if (extkey.key.IsValid()) { extpubkey = extkey.Neuter(); out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key); } - return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type); + return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type, apostrophe); } /** Parse a public key including origin information (if enabled). */ @@ -1151,7 +1174,11 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c error = "Multiple ']' characters found for a single pubkey"; return nullptr; } - if (origin_split.size() == 1) return ParsePubkeyInner(key_exp_index, origin_split[0], ctx, out, error); + // This is set if either the origin or path suffix contains a hardened derivation. + bool apostrophe = false; + if (origin_split.size() == 1) { + return ParsePubkeyInner(key_exp_index, origin_split[0], ctx, out, apostrophe, error); + } if (origin_split[0].empty() || origin_split[0][0] != '[') { error = strprintf("Key origin start '[ character expected but not found, got '%c' instead", origin_split[0].empty() ? /** empty, implies split char */ ']' : origin_split[0][0]); @@ -1172,10 +1199,10 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes"); assert(fpr_bytes.size() == 4); std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint); - if (!ParseKeyPath(slash_split, info.path, error)) return nullptr; - auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, error); + if (!ParseKeyPath(slash_split, info.path, apostrophe, error)) return nullptr; + auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, apostrophe, error); if (!provider) return nullptr; - return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider)); + return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider), apostrophe); } std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider) @@ -1183,7 +1210,7 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, false); KeyOriginInfo info; if (provider.GetKeyOrigin(pubkey.GetID(), info)) { - return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider)); + return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider), /*apostrophe=*/false); } return key_provider; } @@ -1196,7 +1223,7 @@ std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseS std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true); KeyOriginInfo info; if (provider.GetKeyOriginByXOnly(xkey, info)) { - return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider)); + return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider), /*apostrophe=*/false); } return key_provider; } diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp index 6a0925f0bb..48bffc4ac9 100644 --- a/src/test/argsman_tests.cpp +++ b/src/test/argsman_tests.cpp @@ -8,6 +8,7 @@ #include <test/util/setup_common.h> #include <test/util/str.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/fs.h> #include <util/strencodings.h> @@ -254,7 +255,7 @@ BOOST_AUTO_TEST_CASE(util_ParseInvalidParameters) BOOST_CHECK(!test.ParseParameters(2, (char**)argv, error)); BOOST_CHECK_EQUAL(error, "Invalid parameter -unregistered"); - // Make sure registered parameters prefixed with a chain name trigger errors. + // Make sure registered parameters prefixed with a chain type trigger errors. // (Previously, they were accepted and ignored.) argv[1] = "-test.registered"; BOOST_CHECK(!test.ParseParameters(2, (char**)argv, error)); @@ -580,7 +581,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) test_args.SetNetworkOnlyArg("-ccc"); test_args.SetNetworkOnlyArg("-h"); - test_args.SelectConfigNetwork(CBaseChainParams::MAIN); + test_args.SelectConfigNetwork(ChainTypeToString(ChainType::MAIN)); BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); @@ -637,7 +638,7 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); } -BOOST_AUTO_TEST_CASE(util_GetChainName) +BOOST_AUTO_TEST_CASE(util_GetChainTypeString) { TestArgsManager test_args; const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY); @@ -655,39 +656,39 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) std::string error; BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "main"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "regtest"); BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + BOOST_CHECK_THROW(test_args.GetChainTypeString(), std::runtime_error); BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + BOOST_CHECK_THROW(test_args.GetChainTypeString(), std::runtime_error); BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + BOOST_CHECK_THROW(test_args.GetChainTypeString(), std::runtime_error); // check setting the network to test (and thus making // [test] regtest=1 potentially relevant) doesn't break things @@ -695,23 +696,23 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + BOOST_CHECK_THROW(test_args.GetChainTypeString(), std::runtime_error); BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_test_no_reg, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + BOOST_CHECK_THROW(test_args.GetChainTypeString(), std::runtime_error); } // Test different ways settings can be merged, and verify results. This test can @@ -755,8 +756,8 @@ struct ArgsMergeTestingSetup : public BasicTestingSetup { ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] { for (bool soft_set : {false, true}) { for (bool force_set : {false, true}) { - for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) { - for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) { + for (const std::string& section : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) { + for (const std::string& network : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) { for (bool net_specific : {false, true}) { fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific); } @@ -913,7 +914,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) BOOST_CHECK_EQUAL(out_sha_hex, "d1e436c1cd510d0ec44d5205d4b4e3bee6387d316e0075c58206cb16603f3d82"); } -// Similar test as above, but for ArgsManager::GetChainName function. +// Similar test as above, but for ArgsManager::GetChainTypeString function. struct ChainMergeTestingSetup : public BasicTestingSetup { static constexpr int MAX_ACTIONS = 2; @@ -982,7 +983,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) desc += " || "; try { - desc += parser.GetChainName(); + desc += parser.GetChainTypeString(); } catch (const std::runtime_error& e) { desc += "error: "; desc += e.what(); diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index a572bb02b9..d22dbcf671 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -141,10 +141,10 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) BOOST_REQUIRE(filter_index.Start()); // Allow filter index to catch up with the block index. - constexpr int64_t timeout_ms = 10 * 1000; - int64_t time_start = GetTimeMillis(); + constexpr auto timeout{10s}; + const auto time_start{SteadyClock::now()}; while (!filter_index.BlockUntilSyncedToCurrentChain()) { - BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis()); + BOOST_REQUIRE(time_start + timeout > SteadyClock::now()); UninterruptibleSleep(std::chrono::milliseconds{100}); } diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index 2118f476cd..fd33a21147 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -5,6 +5,7 @@ #include <chainparams.h> #include <node/blockstorage.h> #include <node/context.h> +#include <util/chaintype.h> #include <validation.h> #include <boost/test/unit_test.hpp> @@ -20,24 +21,27 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos) { - const auto params {CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)}; - BlockManager blockman{{}}; + const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)}; + node::BlockManager::Options blockman_opts{ + .chainparams = *params, + }; + BlockManager blockman{blockman_opts}; CChain chain {}; // simulate adding a genesis block normally - BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, *params, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); + BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); // simulate what happens during reindex // simulate a well-formed genesis block being found at offset 8 in the blk00000.dat file // the block is found at offset 8 because there is an 8 byte serialization header // consisting of 4 magic bytes + 4 length bytes before each block in a well-formed blk file. FlatFilePos pos{0, BLOCK_SERIALIZATION_HEADER_SIZE}; - BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, *params, &pos).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); + BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, &pos).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); // now simulate what happens after reindex for the first new block processed // the actual block contents don't matter, just that it's a block. // verify that the write position is at offset 0x12d. // this is a check to make sure that https://github.com/bitcoin/bitcoin/issues/21379 does not recur // 8 bytes (for serialization header) + 285 (for serialized genesis block) = 293 // add another 8 bytes for the second block's serialization header and we get 293 + 8 = 301 - FlatFilePos actual{blockman.SaveBlockToDisk(params->GenesisBlock(), 1, chain, *params, nullptr)}; + FlatFilePos actual{blockman.SaveBlockToDisk(params->GenesisBlock(), 1, chain, nullptr)}; BOOST_CHECK_EQUAL(actual.nPos, BLOCK_SERIALIZATION_HEADER_SIZE + ::GetSerializeSize(params->GenesisBlock(), CLIENT_VERSION) + BLOCK_SERIALIZATION_HEADER_SIZE); } diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 9011e703e8..cb3831071a 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -7,6 +7,7 @@ #include <sync.h> #include <test/util/random.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <util/time.h> #include <boost/test/unit_test.hpp> @@ -27,9 +28,9 @@ struct NoLockLoggingTestingSetup : public TestingSetup { NoLockLoggingTestingSetup() #ifdef DEBUG_LOCKCONTENTION - : TestingSetup{CBaseChainParams::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {} + : TestingSetup{ChainType::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {} #else - : TestingSetup{CBaseChainParams::MAIN} {} + : TestingSetup{ChainType::MAIN} {} #endif }; diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index c4b2b4c63b..7a7790e2ae 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -127,7 +127,7 @@ std::set<std::pair<CPubKey, KeyOriginInfo>> GetKeyOriginData(const FlatSigningPr return ret; } -void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, +void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false, uint32_t spender_nlocktime=0, uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL, @@ -141,16 +141,14 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string& std::unique_ptr<Descriptor> parse_pub; // Check that parsing succeeds. if (replace_apostrophe_with_h_in_prv) { - parse_priv = Parse(UseHInsteadOfApostrophe(prv), keys_priv, error); - } else { - parse_priv = Parse(prv, keys_priv, error); + prv = UseHInsteadOfApostrophe(prv); } + parse_priv = Parse(prv, keys_priv, error); BOOST_CHECK_MESSAGE(parse_priv, error); if (replace_apostrophe_with_h_in_pub) { - parse_pub = Parse(UseHInsteadOfApostrophe(pub), keys_pub, error); - } else { - parse_pub = Parse(pub, keys_pub, error); + pub = UseHInsteadOfApostrophe(pub); } + parse_pub = Parse(pub, keys_pub, error); BOOST_CHECK_MESSAGE(parse_pub, error); // Check that the correct OutputType is inferred @@ -171,19 +169,19 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string& if (!(flags & MISSING_PRIVKEYS)) { std::string prv1; BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1)); - BOOST_CHECK(EqualDescriptor(prv, prv1)); + BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv); BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1)); BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1)); - BOOST_CHECK(EqualDescriptor(prv, prv1)); + BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv); BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1)); } // Check that private can produce the normalized descriptors std::string norm1; BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1)); - BOOST_CHECK(EqualDescriptor(norm1, norm_pub)); + BOOST_CHECK_MESSAGE(EqualDescriptor(norm1, norm_pub), "priv->ToNormalizedString(): " + norm1 + " Norm. desc: " + norm_pub); BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1)); - BOOST_CHECK(EqualDescriptor(norm1, norm_pub)); + BOOST_CHECK_MESSAGE(EqualDescriptor(norm1, norm_pub), "pub->ToNormalizedString(): " + norm1 + " Norm. desc: " + norm_pub); // Check whether IsRange on both returns the expected result BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0); @@ -357,32 +355,13 @@ void Check(const std::string& prv, const std::string& pub, const std::string& no const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, uint32_t spender_nlocktime=0, uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL, std::map<std::vector<uint8_t>, std::vector<uint8_t>> preimages={}) { - bool found_apostrophes_in_prv = false; - bool found_apostrophes_in_pub = false; - // Do not replace apostrophes with 'h' in prv and pub DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/false, /*replace_apostrophe_with_h_in_pub=*/false, /*spender_nlocktime=*/spender_nlocktime, /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); - // Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv - if (prv.find('\'') != std::string::npos) { - found_apostrophes_in_prv = true; - DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/true, - /*replace_apostrophe_with_h_in_pub=*/false, /*spender_nlocktime=*/spender_nlocktime, - /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); - } - - // Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub - if (pub.find('\'') != std::string::npos) { - found_apostrophes_in_pub = true; - DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/false, - /*replace_apostrophe_with_h_in_pub=*/true, /*spender_nlocktime=*/spender_nlocktime, - /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); - } - // Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both - if (found_apostrophes_in_prv && found_apostrophes_in_pub) { + if (prv.find('\'') != std::string::npos && pub.find('\'') != std::string::npos) { DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/true, /*replace_apostrophe_with_h_in_pub=*/true, /*spender_nlocktime=*/spender_nlocktime, /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); @@ -398,12 +377,12 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Basic single-key compressed Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt); Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt); - Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}}); + Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}}); Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32); Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT); Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M); CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "wpkh(): Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey - CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin + CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin // Basic single-key uncompressed @@ -426,10 +405,10 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Versions with BIP32 derivations Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt); Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}}); - Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}}); + Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}}); - Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}}); - Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); + Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}}); + Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}}); Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}}); // Mixed xpubs and const pubkeys @@ -440,23 +419,23 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo(): Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "pkh(): Key path value 2147483648 is out of range"); // BIP 32 path element overflow CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "pkh(): Key path value '1aa' is not a valid uint32"); // Path is not valid uint - Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}}); + Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}}); // Multisig constructions Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}}); - Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); + Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT); Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M); CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript - CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor - CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint - CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: No key provided"); // No public key with origin - CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint - CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multi: Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint + CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor + CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint + CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: No key provided"); // No public key with origin + CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint + CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0 CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys @@ -474,8 +453,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // Cannot embed P2WSH inside P2WSH // Checksums - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#hgmsckna", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum @@ -491,13 +470,12 @@ BOOST_AUTO_TEST_CASE(descriptor_test) Check( "rawtr(xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/86'/1'/0'/1/*)#a5gn3t7k", "rawtr(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/86'/1'/0'/1/*)#4ur3xhft", - "rawtr([5a61ff8e/86'/1'/0']xpub6DtZpc9PRL2B6pwoNGysmHAaBofDmWv5S6KQEKKGPKhf5fV62ywDtSziSApYVK3JnYY5KUSgiCwiXW5wtd8z7LNBxT9Mu5sEro8itdGfTeA/1/*)#llheyd9x", + "rawtr([5a61ff8e/86h/1h/0h]xpub6DtZpc9PRL2B6pwoNGysmHAaBofDmWv5S6KQEKKGPKhf5fV62ywDtSziSApYVK3JnYY5KUSgiCwiXW5wtd8z7LNBxT9Mu5sEro8itdGfTeA/1/*)#vwgx7hj9", RANGE | HARDENED | XONLY_KEYS, {{"51205172af752f057d543ce8e4a6f8dcf15548ec6be44041bfa93b72e191cfc8c1ee"}, {"51201b66f20b86f700c945ecb9ad9b0ad1662b73084e2bfea48bee02126350b8a5b1"}, {"512063e70f66d815218abcc2306aa930aaca07c5cde73b75127eb27b5e8c16b58a25"}}, OutputType::BECH32M, {{0x80000056, 0x80000001, 0x80000000, 1, 0}, {0x80000056, 0x80000001, 0x80000000, 1, 1}, {0x80000056, 0x80000001, 0x80000000, 1, 2}}); - Check( "rawtr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "rawtr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 5ad7a25c53..7218bb82d1 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -16,6 +16,7 @@ #include <test/util/setup_common.h> #include <time.h> #include <util/asmap.h> +#include <util/chaintype.h> #include <cassert> #include <cstdint> @@ -34,7 +35,7 @@ int32_t GetCheckRatio() void initialize_addrman() { - static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::REGTEST); + static const auto testing_setup = MakeNoLogFileContext<>(ChainType::REGTEST); g_setup = testing_setup.get(); } diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp index c3e17724eb..e90dcc189a 100644 --- a/src/test/fuzz/block.cpp +++ b/src/test/fuzz/block.cpp @@ -11,6 +11,7 @@ #include <pubkey.h> #include <streams.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> #include <validation.h> #include <version.h> @@ -19,7 +20,7 @@ void initialize_block() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(block, initialize_block) diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 5843b80c0a..fc7e000dc7 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <chainparamsbase.h> #include <coins.h> #include <consensus/amount.h> #include <consensus/tx_check.h> diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 0f3c850e66..f81658b832 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -4,7 +4,6 @@ #include <addrman.h> #include <chainparams.h> -#include <chainparamsbase.h> #include <common/args.h> #include <net.h> #include <netaddress.h> diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 1f5601ca9f..12c22ef2ed 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -6,11 +6,12 @@ #include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> void initialize_descriptor_parse() { ECC_Start(); - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); } FUZZ_TARGET_INIT(descriptor_parse, initialize_descriptor_parse) diff --git a/src/test/fuzz/headerssync.cpp b/src/test/fuzz/headerssync.cpp index 521afadb51..c1a187038b 100644 --- a/src/test/fuzz/headerssync.cpp +++ b/src/test/fuzz/headerssync.cpp @@ -6,6 +6,7 @@ #include <test/fuzz/util.h> #include <test/util/setup_common.h> #include <uint256.h> +#include <util/chaintype.h> #include <util/time.h> #include <validation.h> @@ -15,7 +16,7 @@ static void initialize_headers_sync_state_fuzz() { static const auto testing_setup = MakeNoLogFileContext<>( - /*chain_name=*/CBaseChainParams::MAIN); + /*chain_type=*/ChainType::MAIN); } void MakeHeadersContinuous( diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index ead877fe05..392f3591f1 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -26,6 +26,7 @@ #include <test/fuzz/util.h> #include <uint256.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/check.h> #include <util/moneystr.h> #include <util/overflow.h> @@ -42,7 +43,7 @@ void initialize_integer() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(integer, initialize_integer) diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp index ea6883c08d..3eab2e20c0 100644 --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <chainparamsbase.h> #include <key.h> #include <key_io.h> #include <outputtype.h> @@ -17,6 +16,7 @@ #include <script/standard.h> #include <streams.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <cassert> @@ -28,7 +28,7 @@ void initialize_key() { ECC_Start(); - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(key, initialize_key) diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp index 29c6996365..a1c587a75b 100644 --- a/src/test/fuzz/key_io.cpp +++ b/src/test/fuzz/key_io.cpp @@ -5,6 +5,7 @@ #include <chainparams.h> #include <key_io.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> #include <cassert> #include <cstdint> @@ -14,7 +15,7 @@ void initialize_key_io() { ECC_Start(); - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); } FUZZ_TARGET_INIT(key_io, initialize_key_io) diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp index 63e24aacdd..8b7e3f11cc 100644 --- a/src/test/fuzz/message.cpp +++ b/src/test/fuzz/message.cpp @@ -7,6 +7,7 @@ #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <util/chaintype.h> #include <util/message.h> #include <util/strencodings.h> @@ -19,7 +20,7 @@ void initialize_message() { ECC_Start(); - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(message, initialize_message) diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index 13b4638688..e090f13061 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <chainparamsbase.h> #include <net.h> #include <net_permissions.h> #include <netaddress.h> @@ -16,6 +15,7 @@ #include <test/util/net.h> #include <test/util/setup_common.h> #include <util/asmap.h> +#include <util/chaintype.h> #include <cstdint> #include <optional> @@ -24,7 +24,7 @@ void initialize_net() { - static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN); + static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN); } FUZZ_TARGET_INIT(net, initialize_net) diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp index 96254aa222..ec3cdbff5a 100644 --- a/src/test/fuzz/p2p_transport_serialization.cpp +++ b/src/test/fuzz/p2p_transport_serialization.cpp @@ -9,6 +9,7 @@ #include <protocol.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> #include <cassert> #include <cstdint> @@ -18,7 +19,7 @@ void initialize_p2p_transport_serialization() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serialization) diff --git a/src/test/fuzz/parse_hd_keypath.cpp b/src/test/fuzz/parse_hd_keypath.cpp index 411b70230a..9a2d9a73da 100644 --- a/src/test/fuzz/parse_hd_keypath.cpp +++ b/src/test/fuzz/parse_hd_keypath.cpp @@ -18,6 +18,6 @@ FUZZ_TARGET(parse_hd_keypath) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::vector<uint32_t> random_keypath = ConsumeRandomLengthIntegralVector<uint32_t>(fuzzed_data_provider); - (void)FormatHDKeypath(random_keypath); + (void)FormatHDKeypath(random_keypath, /*apostrophe=*/true); // WriteHDKeypath calls this with false (void)WriteHDKeypath(random_keypath); } diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp index 16486f6b96..be15a38e92 100644 --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -7,13 +7,14 @@ #include <rpc/client.h> #include <rpc/util.h> #include <test/fuzz/fuzz.h> +#include <util/chaintype.h> #include <limits> #include <string> void initialize_parse_univalue() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(parse_univalue, initialize_parse_univalue) diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp index e5a3a6e68a..6d584c9f10 100644 --- a/src/test/fuzz/pow.cpp +++ b/src/test/fuzz/pow.cpp @@ -9,6 +9,7 @@ #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <util/chaintype.h> #include <util/check.h> #include <util/overflow.h> @@ -19,7 +20,7 @@ void initialize_pow() { - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); } FUZZ_TARGET_INIT(pow, initialize_pow) diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 0a7924f226..70dd1e17c5 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -19,6 +19,7 @@ #include <test/util/net.h> #include <test/util/setup_common.h> #include <test/util/validation.h> +#include <util/chaintype.h> #include <validationinterface.h> #include <version.h> @@ -57,7 +58,7 @@ void initialize_process_message() Assert(GetNumMsgTypes() == getAllNetMessageTypes().size()); // If this fails, add or remove the message type below static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>( - /*chain_name=*/CBaseChainParams::REGTEST, + /*chain_type=*/ChainType::REGTEST, /*extra_args=*/{"-txreconciliation"}); g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 96339743ba..68d4e02a26 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -24,7 +24,7 @@ const TestingSetup* g_setup; void initialize_process_messages() { static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>( - /*chain_name=*/CBaseChainParams::REGTEST, + /*chain_type=*/ChainType::REGTEST, /*extra_args=*/{"-txreconciliation"}); g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 1c6140c66a..33ffcc4cdd 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -23,6 +23,7 @@ #include <test/util/setup_common.h> #include <tinyformat.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <util/string.h> #include <util/time.h> @@ -37,7 +38,7 @@ namespace { struct RPCFuzzTestingSetup : public TestingSetup { - RPCFuzzTestingSetup(const std::string& chain_name, const std::vector<const char*>& extra_args) : TestingSetup{chain_name, extra_args} + RPCFuzzTestingSetup(const ChainType chain_type, const std::vector<const char*>& extra_args) : TestingSetup{chain_type, extra_args} { } @@ -363,7 +364,7 @@ FUZZ_TARGET_INIT(rpc, initialize_rpc) try { rpc_testing_setup->CallRPC(rpc_command, arguments); } catch (const UniValue& json_rpc_error) { - const std::string error_msg{find_value(json_rpc_error, "message").get_str()}; + const std::string error_msg{json_rpc_error.find_value("message").get_str()}; // Once c++20 is allowed, starts_with can be used. // if (error_msg.starts_with("Internal bug detected")) { if (0 == error_msg.rfind("Internal bug detected", 0)) { diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 1037dd934a..8a88c1107a 100644 --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -22,6 +22,7 @@ #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <univalue.h> +#include <util/chaintype.h> #include <algorithm> #include <cassert> @@ -32,7 +33,7 @@ void initialize_script() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(script, initialize_script) diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp index 9186746bcf..5aa0ea58ff 100644 --- a/src/test/fuzz/script_format.cpp +++ b/src/test/fuzz/script_format.cpp @@ -11,10 +11,11 @@ #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <univalue.h> +#include <util/chaintype.h> void initialize_script_format() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(script_format, initialize_script_format) diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp index de895cc69c..f332987987 100644 --- a/src/test/fuzz/script_sigcache.cpp +++ b/src/test/fuzz/script_sigcache.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <chainparamsbase.h> #include <key.h> #include <pubkey.h> #include <script/sigcache.h> diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp index c78c22e6cc..8b62daf162 100644 --- a/src/test/fuzz/script_sign.cpp +++ b/src/test/fuzz/script_sign.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <chainparamsbase.h> #include <key.h> #include <psbt.h> #include <pubkey.h> @@ -14,6 +13,7 @@ #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <util/chaintype.h> #include <util/translation.h> #include <cassert> @@ -27,7 +27,7 @@ void initialize_script_sign() { ECC_Start(); - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(script_sign, initialize_script_sign) diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp index 303dcf13e3..e9af93c639 100644 --- a/src/test/fuzz/signet.cpp +++ b/src/test/fuzz/signet.cpp @@ -11,6 +11,7 @@ #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <cstdint> #include <optional> @@ -18,7 +19,7 @@ void initialize_signet() { - static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::SIGNET); + static const auto testing_setup = MakeNoLogFileContext<>(ChainType::SIGNET); } FUZZ_TARGET_INIT(signet, initialize_signet) diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index 97f643db49..73235b7ced 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.cpp @@ -14,12 +14,12 @@ #include <string> #include <vector> +extern std::chrono::milliseconds g_socks5_recv_timeout; + namespace { -int default_socks5_recv_timeout; +decltype(g_socks5_recv_timeout) default_socks5_recv_timeout; }; -extern int g_socks5_recv_timeout; - void initialize_socks5() { static const auto testing_setup = MakeNoLogFileContext<const BasicTestingSetup>(); @@ -35,7 +35,7 @@ FUZZ_TARGET_INIT(socks5, initialize_socks5) InterruptSocks5(fuzzed_data_provider.ConsumeBool()); // Set FUZZED_SOCKET_FAKE_LATENCY=1 to exercise recv timeout code paths. This // will slow down fuzzing. - g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1 : default_socks5_recv_timeout; + g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1ms : default_socks5_recv_timeout; FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider); // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within // a few seconds of fuzzing. diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 935f0c21e1..04cbbe52cb 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -108,7 +108,7 @@ FUZZ_TARGET_INIT(system, initialize_system) (void)args_manager.GetArgs(s1); (void)args_manager.GetBoolArg(s1, b); try { - (void)args_manager.GetChainName(); + (void)args_manager.GetChainTypeString(); } catch (const std::runtime_error&) { } (void)args_manager.GetHelpMessage(); diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index bacb178b44..7035c53d13 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -15,6 +15,7 @@ #include <streams.h> #include <test/fuzz/fuzz.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/rbf.h> #include <validation.h> #include <version.h> @@ -23,7 +24,7 @@ void initialize_transaction() { - SelectParams(CBaseChainParams::REGTEST); + SelectParams(ChainType::REGTEST); } FUZZ_TARGET_INIT(transaction, initialize_transaction) diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index a055705575..b758c715ef 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -42,12 +42,12 @@ void initialize_tx_pool() g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { - CTxIn in = MineBlock(g_setup->m_node, P2WSH_OP_TRUE); + COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_OP_TRUE)}; // Remember the txids to avoid expensive disk access later on auto& outpoints = i < COINBASE_MATURITY ? g_outpoints_coinbase_init_mature : g_outpoints_coinbase_init_immature; - outpoints.push_back(in.prevout); + outpoints.push_back(prevout); } SyncWithValidationInterfaceQueue(); } diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index c14f633029..5f2aff08da 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 <chainparamsbase.h> #include <coins.h> #include <compat/compat.h> #include <consensus/amount.h> diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index be34906025..b4ef0c7a3e 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -10,6 +10,7 @@ #include <test/fuzz/util.h> #include <test/util/mining.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <util/fs.h> #include <validation.h> #include <validationinterface.h> @@ -22,7 +23,7 @@ const std::vector<std::shared_ptr<CBlock>>* g_chain; void initialize_chain() { - const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)}; + const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)}; static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)}; g_chain = &chain; } diff --git a/src/test/fuzz/utxo_total_supply.cpp b/src/test/fuzz/utxo_total_supply.cpp new file mode 100644 index 0000000000..19f41880f4 --- /dev/null +++ b/src/test/fuzz/utxo_total_supply.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chainparams.h> +#include <consensus/consensus.h> +#include <consensus/merkle.h> +#include <kernel/coinstats.h> +#include <node/miner.h> +#include <script/interpreter.h> +#include <streams.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/mining.h> +#include <test/util/setup_common.h> +#include <util/chaintype.h> +#include <validation.h> +#include <version.h> + +FUZZ_TARGET(utxo_total_supply) +{ + /** The testing setup that creates a chainman only (no chainstate) */ + ChainTestingSetup test_setup{ + ChainType::REGTEST, + { + "-testactivationheight=bip34@2", + }, + }; + // Create chainstate + test_setup.LoadVerifyActivateChainstate(); + auto& node{test_setup.m_node}; + auto& chainman{*Assert(test_setup.m_node.chainman)}; + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + const auto ActiveHeight = [&]() { + LOCK(chainman.GetMutex()); + return chainman.ActiveHeight(); + }; + const auto PrepareNextBlock = [&]() { + // Use OP_FALSE to avoid BIP30 check from hitting early + auto block = PrepareBlock(node, CScript{} << OP_FALSE); + // Replace OP_FALSE with OP_TRUE + { + CMutableTransaction tx{*block->vtx.back()}; + tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE; + block->vtx.back() = MakeTransactionRef(tx); + } + return block; + }; + + /** The block template this fuzzer is working on */ + auto current_block = PrepareNextBlock(); + /** Append-only set of tx outpoints, entries are not removed when spent */ + std::vector<std::pair<COutPoint, CTxOut>> txos; + /** The utxo stats at the chain tip */ + kernel::CCoinsStats utxo_stats; + /** The total amount of coins in the utxo set */ + CAmount circulation{0}; + + + // Store the tx out in the txo map + const auto StoreLastTxo = [&]() { + // get last tx + const CTransaction& tx = *current_block->vtx.back(); + // get last out + const uint32_t i = tx.vout.size() - 1; + // store it + txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i)); + if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) { + // also store coinbase + const uint32_t i = tx.vout.size() - 2; + txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i)); + } + }; + const auto AppendRandomTxo = [&](CMutableTransaction& tx) { + const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1)); + tx.vin.emplace_back(txo.first); + tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee + }; + const auto UpdateUtxoStats = [&]() { + LOCK(chainman.GetMutex()); + chainman.ActiveChainstate().ForceFlushStateToDisk(); + utxo_stats = std::move( + *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {}))); + // Check that miner can't print more money than they are allowed to + assert(circulation == utxo_stats.total_amount); + }; + + + // Update internal state to chain tip + StoreLastTxo(); + UpdateUtxoStats(); + assert(ActiveHeight() == 0); + // Get at which height we duplicate the coinbase + // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high. + // Up to 2000 seems reasonable. + int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 20 * COINBASE_MATURITY); + // Always pad with OP_0 at the end to avoid bad-cb-length error + const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0; + // Mine the first block with this duplicate + current_block = PrepareNextBlock(); + StoreLastTxo(); + + { + // Create duplicate (CScript should match exact format as in CreateNewBlock) + CMutableTransaction tx{*current_block->vtx.front()}; + tx.vin.at(0).scriptSig = duplicate_coinbase_script; + + // Mine block and create next block template + current_block->vtx.front() = MakeTransactionRef(tx); + } + current_block->hashMerkleRoot = BlockMerkleRoot(*current_block); + assert(!MineBlock(node, current_block).IsNull()); + circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); + + assert(ActiveHeight() == 1); + UpdateUtxoStats(); + current_block = PrepareNextBlock(); + StoreLastTxo(); + + LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 100'000) + { + CallOneOf( + fuzzed_data_provider, + [&] { + // Append an input-output pair to the last tx in the current block + CMutableTransaction tx{*current_block->vtx.back()}; + AppendRandomTxo(tx); + current_block->vtx.back() = MakeTransactionRef(tx); + StoreLastTxo(); + }, + [&] { + // Append a tx to the list of txs in the current block + CMutableTransaction tx{}; + AppendRandomTxo(tx); + current_block->vtx.push_back(MakeTransactionRef(tx)); + StoreLastTxo(); + }, + [&] { + // Append the current block to the active chain + node::RegenerateCommitments(*current_block, chainman); + const bool was_valid = !MineBlock(node, current_block).IsNull(); + + const auto prev_utxo_stats = utxo_stats; + if (was_valid) { + circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); + + if (duplicate_coinbase_height == ActiveHeight()) { + // we mined the duplicate coinbase + assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script); + } + } + + UpdateUtxoStats(); + + if (!was_valid) { + // utxo stats must not change + assert(prev_utxo_stats.hashSerialized == utxo_stats.hashSerialized); + } + + current_block = PrepareNextBlock(); + StoreLastTxo(); + }); + } +} diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp index 817593cf6e..4e2ca6d903 100644 --- a/src/test/fuzz/validation_load_mempool.cpp +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -4,7 +4,6 @@ #include <kernel/mempool_persist.h> -#include <chainparamsbase.h> #include <node/mempool_args.h> #include <node/mempool_persist_args.h> #include <test/fuzz/FuzzedDataProvider.h> diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp index e6a19d6e91..b3df4dadd2 100644 --- a/src/test/fuzz/versionbits.cpp +++ b/src/test/fuzz/versionbits.cpp @@ -7,6 +7,7 @@ #include <common/args.h> #include <consensus/params.h> #include <primitives/block.h> +#include <util/chaintype.h> #include <versionbits.h> #include <test/fuzz/FuzzedDataProvider.h> @@ -104,7 +105,7 @@ std::unique_ptr<const CChainParams> g_params; void initialize() { // this is actually comparatively slow, so only do it once - g_params = CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN); + g_params = CreateChainParams(ArgsManager{}, ChainType::MAIN); assert(g_params != nullptr); } diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index d37fe35a11..68377e197f 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findAncestorByHash) BOOST_AUTO_TEST_CASE(findCommonAncestor) { auto& chain = m_node.chain; - const CChain& active = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return Assert(m_node.chainman)->ActiveChain()); + const CChain& active{*WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return &Assert(m_node.chainman)->ActiveChain())}; auto* orig_tip = active.Tip(); for (int i = 0; i < 10; ++i) { BlockValidationState state; diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index a400afee71..92bdbae3c4 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -10,6 +10,7 @@ #include <script/script.h> #include <test/util/json.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <boost/test/unit_test.hpp> @@ -24,7 +25,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse) UniValue tests = read_json(std::string(json_tests::key_io_valid, json_tests::key_io_valid + sizeof(json_tests::key_io_valid))); CKey privkey; CTxDestination destination; - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); for (unsigned int idx = 0; idx < tests.size(); idx++) { const UniValue& test = tests[idx]; @@ -36,11 +37,11 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse) std::string exp_base58string = test[0].get_str(); const std::vector<std::byte> exp_payload{ParseHex<std::byte>(test[1].get_str())}; const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - SelectParams(find_value(metadata, "chain").get_str()); - bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); + bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); + SelectParams(ChainTypeFromString(metadata.find_value("chain").get_str()).value()); + bool try_case_flip = metadata.find_value("tryCaseFlip").isNull() ? false : metadata.find_value("tryCaseFlip").get_bool(); if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + bool isCompressed = metadata.find_value("isCompressed").get_bool(); // Must be valid private key privkey = DecodeSecret(exp_base58string); BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); @@ -95,10 +96,10 @@ BOOST_AUTO_TEST_CASE(key_io_valid_gen) std::string exp_base58string = test[0].get_str(); std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - SelectParams(find_value(metadata, "chain").get_str()); + bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); + SelectParams(ChainTypeFromString(metadata.find_value("chain").get_str()).value()); if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + bool isCompressed = metadata.find_value("isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); assert(key.IsValid()); @@ -113,7 +114,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_gen) } } - SelectParams(CBaseChainParams::MAIN); + SelectParams(ChainType::MAIN); } @@ -135,7 +136,7 @@ BOOST_AUTO_TEST_CASE(key_io_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST }) { + for (const auto& chain : {ChainType::MAIN, ChainType::TESTNET, ChainType::SIGNET, ChainType::REGTEST}) { SelectParams(chain); destination = DecodeDestination(exp_base58string); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index addc925bab..5bd14f22c6 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -7,6 +7,7 @@ #include <pow.h> #include <test/util/random.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <boost/test/unit_test.hpp> @@ -15,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup) /* Test calculation of next difficulty target with no constraints applying */ BOOST_AUTO_TEST_CASE(get_next_work) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); int64_t nLastRetargetTime = 1261130161; // Block #30240 CBlockIndex pindexLast; pindexLast.nHeight = 32255; @@ -34,7 +35,7 @@ BOOST_AUTO_TEST_CASE(get_next_work) /* Test the constraint on the upper bound for next work */ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); int64_t nLastRetargetTime = 1231006505; // Block #0 CBlockIndex pindexLast; pindexLast.nHeight = 2015; @@ -48,7 +49,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit) /* Test the constraint on the lower bound for actual time taken */ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); int64_t nLastRetargetTime = 1279008237; // Block #66528 CBlockIndex pindexLast; pindexLast.nHeight = 68543; @@ -65,7 +66,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual) /* Test the constraint on the upper bound for actual time taken */ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time CBlockIndex pindexLast; pindexLast.nHeight = 46367; @@ -81,7 +82,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual) BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target) { - const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus(); + const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus(); uint256 hash; unsigned int nBits; nBits = UintToArith256(consensus.powLimit).GetCompact(true); @@ -91,7 +92,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target) BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target) { - const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus(); + const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus(); uint256 hash; unsigned int nBits{~0x00800000U}; hash.SetHex("0x1"); @@ -100,7 +101,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target) BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target) { - const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus(); + const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus(); uint256 hash; unsigned int nBits; arith_uint256 nBits_arith = UintToArith256(consensus.powLimit); @@ -112,7 +113,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target) BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target) { - const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus(); + const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus(); uint256 hash; unsigned int nBits; arith_uint256 hash_arith = UintToArith256(consensus.powLimit); @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target) BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target) { - const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus(); + const auto consensus = CreateChainParams(*m_node.args, ChainType::MAIN)->GetConsensus(); uint256 hash; unsigned int nBits; arith_uint256 hash_arith{0}; @@ -135,7 +136,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target) BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); std::vector<CBlockIndex> blocks(10000); for (int i = 0; i < 10000; i++) { blocks[i].pprev = i ? &blocks[i - 1] : nullptr; @@ -155,9 +156,9 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) } } -void sanity_check_chainparams(const ArgsManager& args, std::string chainName) +void sanity_check_chainparams(const ArgsManager& args, ChainType chain_type) { - const auto chainParams = CreateChainParams(args, chainName); + const auto chainParams = CreateChainParams(args, chain_type); const auto consensus = chainParams->GetConsensus(); // hash genesis is correct @@ -184,22 +185,22 @@ void sanity_check_chainparams(const ArgsManager& args, std::string chainName) BOOST_AUTO_TEST_CASE(ChainParams_MAIN_sanity) { - sanity_check_chainparams(*m_node.args, CBaseChainParams::MAIN); + sanity_check_chainparams(*m_node.args, ChainType::MAIN); } BOOST_AUTO_TEST_CASE(ChainParams_REGTEST_sanity) { - sanity_check_chainparams(*m_node.args, CBaseChainParams::REGTEST); + sanity_check_chainparams(*m_node.args, ChainType::REGTEST); } BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity) { - sanity_check_chainparams(*m_node.args, CBaseChainParams::TESTNET); + sanity_check_chainparams(*m_node.args, ChainType::TESTNET); } BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity) { - sanity_check_chainparams(*m_node.args, CBaseChainParams::SIGNET); + sanity_check_chainparams(*m_node.args, ChainType::SIGNET); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 791c9ddf31..da0029c737 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -75,7 +75,7 @@ UniValue RPCTestingSetup::CallRPC(std::string args) return result; } catch (const UniValue& objError) { - throw std::runtime_error(find_value(objError, "message").get_str()); + throw std::runtime_error(objError.find_value("message").get_str()); } } @@ -130,9 +130,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), std::runtime_error); std::string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx)); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").getInt<int>(), 193); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").getInt<int>(), 1); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").getInt<int>(), 0); + BOOST_CHECK_EQUAL(r.get_obj().find_value("size").getInt<int>(), 193); + BOOST_CHECK_EQUAL(r.get_obj().find_value("version").getInt<int>(), 1); + BOOST_CHECK_EQUAL(r.get_obj().find_value("locktime").getInt<int>(), 0); BOOST_CHECK_THROW(CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false")); BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error); @@ -149,20 +149,20 @@ BOOST_AUTO_TEST_CASE(rpc_togglenetwork) UniValue r; r = CallRPC("getnetworkinfo"); - bool netState = find_value(r.get_obj(), "networkactive").get_bool(); + bool netState = r.get_obj().find_value("networkactive").get_bool(); BOOST_CHECK_EQUAL(netState, true); BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive false")); r = CallRPC("getnetworkinfo"); - int numConnection = find_value(r.get_obj(), "connections").getInt<int>(); + int numConnection = r.get_obj().find_value("connections").getInt<int>(); BOOST_CHECK_EQUAL(numConnection, 0); - netState = find_value(r.get_obj(), "networkactive").get_bool(); + netState = r.get_obj().find_value("networkactive").get_bool(); BOOST_CHECK_EQUAL(netState, false); BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive true")); r = CallRPC("getnetworkinfo"); - netState = find_value(r.get_obj(), "networkactive").get_bool(); + netState = r.get_obj().find_value("networkactive").get_bool(); BOOST_CHECK_EQUAL(netState, true); } @@ -180,9 +180,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout); - BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); + BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == false); r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout); - BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); + BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == true); } BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) @@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); UniValue ar = r.get_array(); UniValue o1 = ar[0].get_obj(); - UniValue adr = find_value(o1, "address"); + UniValue adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/32"); BOOST_CHECK_NO_THROW(CallRPC(std::string("setban 127.0.0.0 remove"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); @@ -336,8 +336,8 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); - int64_t banned_until{find_value(o1, "banned_until").getInt<int64_t>()}; + adr = o1.find_value("address"); + int64_t banned_until{o1.find_value("banned_until").getInt<int64_t>()}; BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); BOOST_CHECK_EQUAL(banned_until, 9907731200); // absolute time check @@ -351,11 +351,11 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); - banned_until = find_value(o1, "banned_until").getInt<int64_t>(); - const int64_t ban_created{find_value(o1, "ban_created").getInt<int64_t>()}; - const int64_t ban_duration{find_value(o1, "ban_duration").getInt<int64_t>()}; - const int64_t time_remaining{find_value(o1, "time_remaining").getInt<int64_t>()}; + adr = o1.find_value("address"); + banned_until = o1.find_value("banned_until").getInt<int64_t>(); + const int64_t ban_created{o1.find_value("ban_created").getInt<int64_t>()}; + const int64_t ban_duration{o1.find_value("ban_duration").getInt<int64_t>()}; + const int64_t time_remaining{o1.find_value("time_remaining").getInt<int64_t>()}; BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); BOOST_CHECK_EQUAL(banned_until, time_remaining_expected + now.count()); BOOST_CHECK_EQUAL(ban_duration, banned_until - ban_created); @@ -385,7 +385,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/128"); BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); @@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/30"); BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); @@ -401,7 +401,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128"); } diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index 604eb48df6..fff84d24f0 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -6,12 +6,12 @@ #include <test/util/setup_common.h> #include <test/util/str.h> -#include <util/fs.h> - #include <boost/test/unit_test.hpp> #include <common/args.h> #include <univalue.h> +#include <util/chaintype.h> +#include <util/fs.h> #include <util/strencodings.h> #include <util/string.h> @@ -190,7 +190,7 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup) if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed"); } - const std::string& network = CBaseChainParams::MAIN; + const std::string& network = ChainTypeToString(ChainType::MAIN); ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool force_set, bool ignore_default_section_config) { std::string desc; @@ -225,7 +225,7 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup) } desc += " || "; - desc += GetSetting(settings, network, name, ignore_default_section_config, /*ignore_nonpersistent=*/false, /*get_chain_name=*/false).write(); + desc += GetSetting(settings, network, name, ignore_default_section_config, /*ignore_nonpersistent=*/false, /*get_chain_type=*/false).write(); desc += " |"; for (const auto& s : GetSettingsList(settings, network, name, ignore_default_section_config)) { desc += " "; diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index c0a2566959..7ce350b84b 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(run_command) const UniValue result = RunCommandParseJSON("echo \"{\"success\": true}\""); #endif BOOST_CHECK(result.isObject()); - const UniValue& success = find_value(result, "success"); + const UniValue& success = result.find_value("success"); BOOST_CHECK(!success.isNull()); BOOST_CHECK_EQUAL(success.get_bool(), true); } @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(run_command) { const UniValue result = RunCommandParseJSON("cat", "{\"success\": true}"); BOOST_CHECK(result.isObject()); - const UniValue& success = find_value(result, "success"); + const UniValue& success = result.find_value("success"); BOOST_CHECK(!success.isNull()); BOOST_CHECK_EQUAL(success.get_bool(), true); } diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index b9bfa65c0a..b666517ae2 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -32,10 +32,10 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) BOOST_REQUIRE(txindex.Start()); // Allow tx index to catch up with the block index. - constexpr int64_t timeout_ms = 10 * 1000; - int64_t time_start = GetTimeMillis(); + constexpr auto timeout{10s}; + const auto time_start{SteadyClock::now()}; while (!txindex.BlockUntilSyncedToCurrentChain()) { - BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis()); + BOOST_REQUIRE(time_start + timeout > SteadyClock::now()); UninterruptibleSleep(std::chrono::milliseconds{100}); } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 8cdea3890e..8f8628169f 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -9,13 +9,14 @@ #include <script/standard.h> #include <test/util/setup_common.h> #include <txmempool.h> +#include <util/chaintype.h> #include <validation.h> #include <boost/test/unit_test.hpp> struct Dersig100Setup : public TestChain100Setup { Dersig100Setup() - : TestChain100Setup{CBaseChainParams::REGTEST, {"-testactivationheight=dersig@102"}} {} + : TestChain100Setup{ChainType::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 0df1db84c4..51f4b89512 100644 --- a/src/test/util/mining.cpp +++ b/src/test/util/mining.cpp @@ -6,19 +6,22 @@ #include <chainparams.h> #include <consensus/merkle.h> +#include <consensus/validation.h> #include <key_io.h> #include <node/context.h> #include <pow.h> +#include <primitives/transaction.h> #include <script/standard.h> #include <test/util/script.h> #include <util/check.h> #include <validation.h> +#include <validationinterface.h> #include <versionbits.h> using node::BlockAssembler; using node::NodeContext; -CTxIn generatetoaddress(const NodeContext& node, const std::string& address) +COutPoint generatetoaddress(const NodeContext& node, const std::string& address) { const auto dest = DecodeDestination(address); assert(IsValidDestination(dest)); @@ -58,19 +61,52 @@ std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const return ret; } -CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey) +COutPoint MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey) { auto block = PrepareBlock(node, coinbase_scriptPubKey); + auto valid = MineBlock(node, block); + assert(!valid.IsNull()); + return valid; +} + +struct BlockValidationStateCatcher : public CValidationInterface { + const uint256 m_hash; + std::optional<BlockValidationState> m_state; + BlockValidationStateCatcher(const uint256& hash) + : m_hash{hash}, + m_state{} {} + +protected: + void BlockChecked(const CBlock& block, const BlockValidationState& state) override + { + if (block.GetHash() != m_hash) return; + m_state = state; + } +}; + +COutPoint MineBlock(const NodeContext& node, std::shared_ptr<CBlock>& block) +{ while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) { ++block->nNonce; assert(block->nNonce); } - bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, true, nullptr)}; - assert(processed); - - return CTxIn{block->vtx[0]->GetHash(), 0}; + auto& chainman{*Assert(node.chainman)}; + const auto old_height = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()); + bool new_block; + BlockValidationStateCatcher bvsc{block->GetHash()}; + RegisterValidationInterface(&bvsc); + const bool processed{chainman.ProcessNewBlock(block, true, true, &new_block)}; + const bool duplicate{!new_block && processed}; + assert(!duplicate); + UnregisterValidationInterface(&bvsc); + SyncWithValidationInterfaceQueue(); + const bool was_valid{bvsc.m_state && bvsc.m_state->IsValid()}; + assert(old_height + was_valid == WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight())); + + if (was_valid) return {block->vtx[0]->GetHash(), 0}; + return {}; } std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey, diff --git a/src/test/util/mining.h b/src/test/util/mining.h index 70b1f7b3fb..3f071257f1 100644 --- a/src/test/util/mining.h +++ b/src/test/util/mining.h @@ -13,8 +13,8 @@ class CBlock; class CChainParams; +class COutPoint; class CScript; -class CTxIn; namespace node { struct NodeContext; } // namespace node @@ -23,7 +23,13 @@ struct NodeContext; std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params); /** Returns the generated coin */ -CTxIn MineBlock(const node::NodeContext&, const CScript& coinbase_scriptPubKey); +COutPoint MineBlock(const node::NodeContext&, const CScript& coinbase_scriptPubKey); + +/** + * Returns the generated coin (or Null if the block was invalid). + * It is recommended to call RegenerateCommitments before mining the block to avoid merkle tree mismatches. + **/ +COutPoint MineBlock(const node::NodeContext&, std::shared_ptr<CBlock>& block); /** Prepare a block to be mined */ std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext&, const CScript& coinbase_scriptPubKey); @@ -31,6 +37,6 @@ std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext& node, const CScrip const node::BlockAssembler::Options& assembler_options); /** RPC-like helper function, returns the generated coin */ -CTxIn generatetoaddress(const node::NodeContext&, const std::string& address); +COutPoint generatetoaddress(const node::NodeContext&, const std::string& address); #endif // BITCOIN_TEST_UTIL_MINING_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index ddc1e7ab37..642350b033 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -42,6 +42,7 @@ #include <timedata.h> #include <txdb.h> #include <txmempool.h> +#include <util/chaintype.h> #include <util/strencodings.h> #include <util/string.h> #include <util/system.h> @@ -98,7 +99,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num) return os; } -BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) +BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args) : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()}, m_args{} { @@ -132,7 +133,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve throw std::runtime_error{error}; } } - SelectParams(chainName); + SelectParams(chainType); SeedInsecureRand(); if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN); InitLogging(*m_node.args); @@ -163,8 +164,8 @@ BasicTestingSetup::~BasicTestingSetup() gArgs.ClearArgs(); } -ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) - : BasicTestingSetup(chainName, extra_args) +ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args) + : BasicTestingSetup(chainType, extra_args) { const CChainParams& chainparams = Params(); @@ -185,7 +186,10 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve .adjusted_time_callback = GetAdjustedTime, .check_block_index = true, }; - m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, node::BlockManager::Options{}); + node::BlockManager::Options blockman_opts{ + .chainparams = chainman_opts.chainparams, + }; + m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{ .path = m_args.GetDataDirNet() / "blocks" / "index", .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db), @@ -211,7 +215,7 @@ ChainTestingSetup::~ChainTestingSetup() m_node.chainman.reset(); } -void TestingSetup::LoadVerifyActivateChainstate() +void ChainTestingSetup::LoadVerifyActivateChainstate() { auto& chainman{*Assert(m_node.chainman)}; node::ChainstateLoadOptions options; @@ -237,14 +241,14 @@ void TestingSetup::LoadVerifyActivateChainstate() } TestingSetup::TestingSetup( - const std::string& chainName, + const ChainType chainType, const std::vector<const char*>& extra_args, const bool coins_db_in_memory, const bool block_tree_db_in_memory) - : ChainTestingSetup(chainName, extra_args), - m_coins_db_in_memory(coins_db_in_memory), - m_block_tree_db_in_memory(block_tree_db_in_memory) + : ChainTestingSetup(chainType, extra_args) { + m_coins_db_in_memory = coins_db_in_memory; + m_block_tree_db_in_memory = block_tree_db_in_memory; // 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); @@ -268,11 +272,11 @@ TestingSetup::TestingSetup( } TestChain100Setup::TestChain100Setup( - const std::string& chain_name, + const ChainType chain_type, const std::vector<const char*>& extra_args, const bool coins_db_in_memory, const bool block_tree_db_in_memory) - : TestingSetup{CBaseChainParams::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory} + : TestingSetup{ChainType::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory} { SetMockTime(1598887952); constexpr std::array<unsigned char, 32> vchKey = { diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 750f010fb0..bd5a81be45 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_TEST_UTIL_SETUP_COMMON_H #define BITCOIN_TEST_UTIL_SETUP_COMMON_H -#include <chainparamsbase.h> #include <common/args.h> #include <key.h> #include <node/caches.h> @@ -14,6 +13,7 @@ #include <pubkey.h> #include <random.h> #include <stdexcept> +#include <util/chaintype.h> #include <util/check.h> #include <util/fs.h> #include <util/string.h> @@ -80,7 +80,7 @@ static constexpr CAmount CENT{1000000}; struct BasicTestingSetup { 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 = {}); + explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {}); ~BasicTestingSetup(); const fs::path m_path_root; @@ -93,21 +93,21 @@ struct BasicTestingSetup { */ struct ChainTestingSetup : public BasicTestingSetup { node::CacheSizes m_cache_sizes{}; + bool m_coins_db_in_memory{true}; + bool m_block_tree_db_in_memory{true}; - explicit ChainTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {}); + explicit ChainTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {}); ~ChainTestingSetup(); + + // Supplies a chainstate, if one is needed + void LoadVerifyActivateChainstate(); }; /** Testing setup that configures a complete environment. */ struct TestingSetup : public ChainTestingSetup { - bool m_coins_db_in_memory{true}; - bool m_block_tree_db_in_memory{true}; - - void LoadVerifyActivateChainstate(); - explicit TestingSetup( - const std::string& chainName = CBaseChainParams::MAIN, + const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {}, const bool coins_db_in_memory = true, const bool block_tree_db_in_memory = true); @@ -116,7 +116,7 @@ struct TestingSetup : public ChainTestingSetup { /** Identical to TestingSetup, but chain set to regtest */ struct RegTestingSetup : public TestingSetup { RegTestingSetup() - : TestingSetup{CBaseChainParams::REGTEST} {} + : TestingSetup{ChainType::REGTEST} {} }; class CBlock; @@ -128,7 +128,7 @@ class CScript; */ struct TestChain100Setup : public TestingSetup { TestChain100Setup( - const std::string& chain_name = CBaseChainParams::REGTEST, + const ChainType chain_type = ChainType::REGTEST, const std::vector<const char*>& extra_args = {}, const bool coins_db_in_memory = true, const bool block_tree_db_in_memory = true); @@ -206,7 +206,7 @@ struct TestChain100Setup : public TestingSetup { * be used in "hot loops", for example fuzzing or benchmarking. */ template <class T = const BasicTestingSetup> -std::unique_ptr<T> MakeNoLogFileContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {}) +std::unique_ptr<T> MakeNoLogFileContext(const ChainType chain_type = ChainType::REGTEST, const std::vector<const char*>& extra_args = {}) { const std::vector<const char*> arguments = Cat( { @@ -215,7 +215,7 @@ std::unique_ptr<T> MakeNoLogFileContext(const std::string& chain_name = CBaseCha }, extra_args); - return std::make_unique<T>(chain_name, arguments); + return std::make_unique<T>(chain_type, arguments); } CBlock getBlock13b8a(); diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index edc7e4b70a..85fe065a38 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -381,10 +381,13 @@ struct SnapshotTestSetup : TestChain100Setup { .datadir = m_args.GetDataDirNet(), .adjusted_time_callback = GetAdjustedTime, }; + node::BlockManager::Options blockman_opts{ + .chainparams = chainman_opts.chainparams, + }; // For robustness, ensure the old manager is destroyed before creating a // new one. m_node.chainman.reset(); - m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, node::BlockManager::Options{}); + m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); } return *Assert(m_node.chainman); } diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index 0b4c491615..d00f2ff4d1 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -7,6 +7,7 @@ #include <net.h> #include <signet.h> #include <uint256.h> +#include <util/chaintype.h> #include <validation.h> #include <test/util/setup_common.h> @@ -41,7 +42,7 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval) BOOST_AUTO_TEST_CASE(block_subsidy_test) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main TestBlockSubsidyHalvings(150); // As in regtest TestBlockSubsidyHalvings(1000); // Just another interval @@ -49,7 +50,7 @@ BOOST_AUTO_TEST_CASE(block_subsidy_test) BOOST_AUTO_TEST_CASE(subsidy_limit_test) { - const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN); + const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); CAmount nSum = 0; for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) { CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus()); @@ -64,7 +65,7 @@ BOOST_AUTO_TEST_CASE(signet_parse_tests) { ArgsManager signet_argsman; signet_argsman.ForceSetArg("-signetchallenge", "51"); // set challenge to OP_TRUE - const auto signet_params = CreateChainParams(signet_argsman, CBaseChainParams::SIGNET); + const auto signet_params = CreateChainParams(signet_argsman, ChainType::SIGNET); CBlock block; BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector<uint8_t>{OP_TRUE}); CScript challenge{OP_TRUE}; @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(signet_parse_tests) //! Test retrieval of valid assumeutxo values. BOOST_AUTO_TEST_CASE(test_assumeutxo) { - const auto params = CreateChainParams(*m_node.args, CBaseChainParams::REGTEST); + const auto params = CreateChainParams(*m_node.args, ChainType::REGTEST); // These heights don't have assumeutxo configurations associated, per the contents // of kernel/chainparams.cpp. diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 80c00036e7..9e69992752 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -7,6 +7,7 @@ #include <consensus/params.h> #include <test/util/random.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> #include <versionbits.h> #include <boost/test/unit_test.hpp> @@ -418,8 +419,8 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // check that any deployment on any chain can conceivably reach both // ACTIVE and FAILED states in roughly the way we expect - for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST}) { - const auto chainParams = CreateChainParams(*m_node.args, chain_name); + for (const auto& chain_type: {ChainType::MAIN, ChainType::TESTNET, ChainType::SIGNET, ChainType::REGTEST}) { + const auto chainParams = CreateChainParams(*m_node.args, chain_type); uint32_t chain_all_vbits{0}; for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { const auto dep = static_cast<Consensus::DeploymentPos>(i); @@ -440,7 +441,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // deployment that's not always/never active ArgsManager args; args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999"); // January 1, 2008 - December 31, 2008 - const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST); + const auto chainParams = CreateChainParams(args, ChainType::REGTEST); check_computeblockversion(vbcache, chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY); } @@ -450,7 +451,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // live deployment ArgsManager args; args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200 - const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST); + const auto chainParams = CreateChainParams(args, ChainType::REGTEST); check_computeblockversion(vbcache, chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY); } } diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index d501c3fb69..004135ef97 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -123,7 +123,7 @@ public: const UniValue& get_array() const; enum VType type() const { return getType(); } - friend const UniValue& find_value( const UniValue& obj, const std::string& name); + const UniValue& find_value(std::string_view key) const; }; template <class It> @@ -201,6 +201,4 @@ static inline bool json_isspace(int ch) extern const UniValue NullUniValue; -const UniValue& find_value( const UniValue& obj, const std::string& name); - #endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 5aa39edb75..c3d19caae0 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -230,12 +230,13 @@ const char *uvTypeName(UniValue::VType t) return nullptr; } -const UniValue& find_value(const UniValue& obj, const std::string& name) +const UniValue& UniValue::find_value(std::string_view key) const { - for (unsigned int i = 0; i < obj.keys.size(); i++) - if (obj.keys[i] == name) - return obj.values.at(i); - + for (unsigned int i = 0; i < keys.size(); ++i) { + if (keys[i] == key) { + return values.at(i); + } + } return NullUniValue; } diff --git a/src/util/bip32.cpp b/src/util/bip32.cpp index c0ad9257ce..4ab14bd704 100644 --- a/src/util/bip32.cpp +++ b/src/util/bip32.cpp @@ -51,17 +51,17 @@ bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypa return true; } -std::string FormatHDKeypath(const std::vector<uint32_t>& path) +std::string FormatHDKeypath(const std::vector<uint32_t>& path, bool apostrophe) { std::string ret; for (auto i : path) { ret += strprintf("/%i", (i << 1) >> 1); - if (i >> 31) ret += '\''; + if (i >> 31) ret += apostrophe ? '\'' : 'h'; } return ret; } -std::string WriteHDKeypath(const std::vector<uint32_t>& keypath) +std::string WriteHDKeypath(const std::vector<uint32_t>& keypath, bool apostrophe) { - return "m" + FormatHDKeypath(keypath); + return "m" + FormatHDKeypath(keypath, apostrophe); } diff --git a/src/util/bip32.h b/src/util/bip32.h index b720cb5638..ea5f192c07 100644 --- a/src/util/bip32.h +++ b/src/util/bip32.h @@ -13,7 +13,7 @@ [[nodiscard]] bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); /** Write HD keypaths as strings */ -std::string WriteHDKeypath(const std::vector<uint32_t>& keypath); -std::string FormatHDKeypath(const std::vector<uint32_t>& path); +std::string WriteHDKeypath(const std::vector<uint32_t>& keypath, bool apostrophe = false); +std::string FormatHDKeypath(const std::vector<uint32_t>& path, bool apostrophe = false); #endif // BITCOIN_UTIL_BIP32_H diff --git a/src/util/chaintype.cpp b/src/util/chaintype.cpp new file mode 100644 index 0000000000..8a199e352a --- /dev/null +++ b/src/util/chaintype.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <util/chaintype.h> + +#include <cassert> +#include <optional> +#include <string> + +std::string ChainTypeToString(ChainType chain) +{ + switch (chain) { + case ChainType::MAIN: + return "main"; + case ChainType::TESTNET: + return "test"; + case ChainType::SIGNET: + return "signet"; + case ChainType::REGTEST: + return "regtest"; + } + assert(false); +} + +std::optional<ChainType> ChainTypeFromString(std::string_view chain) +{ + if (chain == "main") { + return ChainType::MAIN; + } else if (chain == "test") { + return ChainType::TESTNET; + } else if (chain == "signet") { + return ChainType::SIGNET; + } else if (chain == "regtest") { + return ChainType::REGTEST; + } else { + return std::nullopt; + } +} diff --git a/src/util/chaintype.h b/src/util/chaintype.h new file mode 100644 index 0000000000..c73985df57 --- /dev/null +++ b/src/util/chaintype.h @@ -0,0 +1,22 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_CHAINTYPE_H +#define BITCOIN_UTIL_CHAINTYPE_H + +#include <optional> +#include <string> + +enum class ChainType { + MAIN, + TESTNET, + SIGNET, + REGTEST, +}; + +std::string ChainTypeToString(ChainType chain); + +std::optional<ChainType> ChainTypeFromString(std::string_view chain); + +#endif // BITCOIN_UTIL_CHAINTYPE_H diff --git a/src/util/settings.cpp b/src/util/settings.cpp index bb257c0584..db3d60046e 100644 --- a/src/util/settings.cpp +++ b/src/util/settings.cpp @@ -130,7 +130,7 @@ SettingsValue GetSetting(const Settings& settings, const std::string& name, bool ignore_default_section_config, bool ignore_nonpersistent, - bool get_chain_name) + bool get_chain_type) { SettingsValue result; bool done = false; // Done merging any more settings sources. @@ -145,17 +145,17 @@ SettingsValue GetSetting(const Settings& settings, // assigned value instead of last. In general, later settings take // precedence over early settings, but for backwards compatibility in // the config file the precedence is reversed for all settings except - // chain name settings. + // chain type settings. const bool reverse_precedence = (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && - !get_chain_name; + !get_chain_type; // Weird behavior preserved for backwards compatibility: Negated // -regtest and -testnet arguments which you would expect to override // values set in the configuration file are currently accepted but // silently ignored. It would be better to apply these just like other // negated values, or at least warn they are ignored. - const bool skip_negated_command_line = get_chain_name; + const bool skip_negated_command_line = get_chain_type; if (done) return; diff --git a/src/util/settings.h b/src/util/settings.h index 27e87a4751..bb1fe585e1 100644 --- a/src/util/settings.h +++ b/src/util/settings.h @@ -60,14 +60,14 @@ bool WriteSettings(const fs::path& path, //! 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 +//! @param get_chain_type - enable special backwards compatible behavior +//! for GetChainType 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); + bool get_chain_type); //! Get combined setting value similar to GetSetting(), except if setting was //! specified multiple times, return a list of all the values specified. diff --git a/src/util/time.cpp b/src/util/time.cpp index fb9bc34931..5ca9d21f8d 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -78,14 +78,6 @@ NodeClock::time_point NodeClock::now() noexcept return time_point{ret}; }; -template <typename T> -static T GetSystemTime() -{ - const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch()); - assert(now.count() > 0); - return now; -} - void SetMockTime(int64_t nMockTimeIn) { Assert(nMockTimeIn >= 0); @@ -102,11 +94,6 @@ std::chrono::seconds GetMockTime() return std::chrono::seconds(nMockTime.load(std::memory_order_relaxed)); } -int64_t GetTimeMillis() -{ - return int64_t{GetSystemTime<std::chrono::milliseconds>().count()}; -} - int64_t GetTime() { return GetTime<std::chrono::seconds>().count(); } std::string FormatISO8601DateTime(int64_t nTime) { diff --git a/src/util/time.h b/src/util/time.h index 8c6baeb12a..b6aab615ba 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -71,9 +71,6 @@ using MillisecondsDouble = std::chrono::duration<double, std::chrono::millisecon */ int64_t GetTime(); -/** Returns the system time (not mockable) */ -int64_t GetTimeMillis(); - /** * DEPRECATED * Use SetMockTime with chrono type diff --git a/src/validation.cpp b/src/validation.cpp index 1873883c78..f929e77416 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2374,7 +2374,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, if (fJustCheck) return true; - if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, params)) { + if (!m_blockman.WriteUndoDataForBlock(blockundo, state, *pindex)) { return false; } @@ -4000,7 +4000,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV // Write block to history file if (fNewBlock) *fNewBlock = true; try { - FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, params, dbp)}; + FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, dbp)}; if (blockPos.IsNull()) { state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__)); return false; @@ -4418,7 +4418,7 @@ bool ChainstateManager::LoadBlockIndex() // Load block index from databases bool needs_init = fReindex; if (!fReindex) { - bool ret = m_blockman.LoadBlockIndexDB(GetConsensus()); + bool ret{m_blockman.LoadBlockIndexDB()}; if (!ret) return false; m_blockman.ScanAndUnlinkAlreadyPrunedFiles(); @@ -4522,7 +4522,7 @@ bool Chainstate::LoadGenesisBlock() try { const CBlock& block = params.GenesisBlock(); - FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, params, nullptr)}; + FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, nullptr)}; if (blockPos.IsNull()) { return error("%s: writing genesis block to disk failed", __func__); } diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp index 079e3baa4e..e2852c5d52 100644 --- a/src/wallet/external_signer_scriptpubkeyman.cpp +++ b/src/wallet/external_signer_scriptpubkeyman.cpp @@ -45,7 +45,7 @@ ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() { const std::string command = gArgs.GetArg("-signer", ""); if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>"); std::vector<ExternalSigner> signers; - ExternalSigner::Enumerate(command, signers, Params().NetworkIDString()); + ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString()); if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found"); // TODO: add fingerprint argument instead of failing in case of multiple signers. if (signers.size() > 1) throw std::runtime_error(std::string(__func__) + ": More than one external signer found. Please connect only one at a time."); diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index a5be0739a9..0bd6a9670c 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -608,7 +608,9 @@ RPCHelpMan getaddressinfo() if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) { ret.pushKV("timestamp", meta->nCreateTime); if (meta->has_key_origin) { - ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path)); + // In legacy wallets hdkeypath has always used an apostrophe for + // hardened derivation. Perhaps some external tool depends on that. + ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path, /*apostrophe=*/!desc_spk_man)); ret.pushKV("hdseedid", meta->hd_seed_id.GetHex()); ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint)); } diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index 553bbfb62f..43da15b36e 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -805,7 +805,7 @@ RPCHelpMan dumpwallet() } else { file << "change=1"; } - file << strprintf(" # addr=%s%s\n", strAddr, (metadata.has_key_origin ? " hdkeypath="+WriteHDKeypath(metadata.key_origin.path) : "")); + file << strprintf(" # addr=%s%s\n", strAddr, (metadata.has_key_origin ? " hdkeypath="+WriteHDKeypath(metadata.key_origin.path, /*apostrophe=*/true) : "")); } } file << "\n"; diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index 750ef69f6e..5bdd3c9e72 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -320,7 +320,7 @@ RPCHelpMan lockunspent() }); const uint256 txid(ParseHashO(o, "txid")); - const int nOutput = find_value(o, "vout").getInt<int>(); + const int nOutput = o.find_value("vout").getInt<int>(); if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); } diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 88ee6e96b0..a5b1f594bf 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -673,7 +673,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, for (const UniValue& input : options["input_weights"].get_array().getValues()) { uint256 txid = ParseHashO(input, "txid"); - const UniValue& vout_v = find_value(input, "vout"); + const UniValue& vout_v = input.find_value("vout"); if (!vout_v.isNum()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); } @@ -682,7 +682,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); } - const UniValue& weight_v = find_value(input, "weight"); + const UniValue& weight_v = input.find_value("weight"); if (!weight_v.isNum()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key"); } diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index d728b2fb96..a22862bfa9 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -122,7 +122,7 @@ static RPCHelpMan getwalletinfo() obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)); if (pwallet->IsScanning()) { UniValue scanning(UniValue::VOBJ); - scanning.pushKV("duration", pwallet->ScanningDuration() / 1000); + scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration())); scanning.pushKV("progress", pwallet->ScanningProgress()); obj.pushKV("scanning", scanning); } else { diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 62bd0c06cd..953fdb63b6 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1555,7 +1555,7 @@ bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path; mapKeyMetadata[pubkey.GetID()].has_key_origin = true; - mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path); + mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path, /*apostrophe=*/true); return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); } @@ -1821,7 +1821,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() // Make the combo descriptor std::string xpub = EncodeExtPubKey(master_key.Neuter()); - std::string desc_str = "combo(" + xpub + "/0'/" + ToString(i) + "'/*')"; + std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)"; FlatSigningProvider keys; std::string error; std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, error, false); @@ -2264,20 +2264,20 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_ std::string desc_suffix = "/*)"; switch (addr_type) { case OutputType::LEGACY: { - desc_prefix = "pkh(" + xpub + "/44'"; + desc_prefix = "pkh(" + xpub + "/44h"; break; } case OutputType::P2SH_SEGWIT: { - desc_prefix = "sh(wpkh(" + xpub + "/49'"; + desc_prefix = "sh(wpkh(" + xpub + "/49h"; desc_suffix += ")"; break; } case OutputType::BECH32: { - desc_prefix = "wpkh(" + xpub + "/84'"; + desc_prefix = "wpkh(" + xpub + "/84h"; break; } case OutputType::BECH32M: { - desc_prefix = "tr(" + xpub + "/86'"; + desc_prefix = "tr(" + xpub + "/86h"; break; } case OutputType::UNKNOWN: { @@ -2290,13 +2290,13 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_ // Mainnet derives at 0', testnet and regtest derive at 1' if (Params().IsTestChain()) { - desc_prefix += "/1'"; + desc_prefix += "/1h"; } else { - desc_prefix += "/0'"; + desc_prefix += "/0h"; } std::string internal_path = internal ? "/1" : "/0"; - std::string desc_str = desc_prefix + "/0'" + internal_path + desc_suffix; + std::string desc_str = desc_prefix + "/0h" + internal_path + desc_suffix; // Make the descriptor FlatSigningProvider keys; diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp index 0adc63876c..5bdf36ec19 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -4,6 +4,7 @@ #include <common/args.h> #include <univalue.h> +#include <util/chaintype.h> #include <util/check.h> #include <util/fs.h> @@ -13,7 +14,7 @@ #include <wallet/test/init_test_fixture.h> namespace wallet { -InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) +InitWalletDirTestingSetup::InitWalletDirTestingSetup(const ChainType chainType) : BasicTestingSetup(chainType) { m_wallet_loader = MakeWalletLoader(*m_node.chain, m_args); diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h index df5819fd1d..ac7bb8997c 100644 --- a/src/wallet/test/init_test_fixture.h +++ b/src/wallet/test/init_test_fixture.h @@ -9,11 +9,12 @@ #include <interfaces/wallet.h> #include <node/context.h> #include <test/util/setup_common.h> +#include <util/chaintype.h> namespace wallet { struct InitWalletDirTestingSetup: public BasicTestingSetup { - explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + explicit InitWalletDirTestingSetup(const ChainType chain_type = ChainType::MAIN); ~InitWalletDirTestingSetup(); void SetWalletDir(const fs::path& walletdir_path); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 2dd8f9ad33..369526d17f 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -5,10 +5,11 @@ #include <wallet/test/wallet_test_fixture.h> #include <scheduler.h> +#include <util/chaintype.h> namespace wallet { -WalletTestingSetup::WalletTestingSetup(const std::string& chainName) - : TestingSetup(chainName), +WalletTestingSetup::WalletTestingSetup(const ChainType chainType) + : TestingSetup(chainType), m_wallet_loader{interfaces::MakeWalletLoader(*m_node.chain, *Assert(m_node.args))}, m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase()) { diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index f1ef15a282..4aae02e075 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -10,6 +10,7 @@ #include <interfaces/chain.h> #include <interfaces/wallet.h> #include <node/context.h> +#include <util/chaintype.h> #include <util/check.h> #include <wallet/wallet.h> @@ -19,7 +20,7 @@ namespace wallet { /** Testing setup and teardown for wallet. */ struct WalletTestingSetup : public TestingSetup { - explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); + explicit WalletTestingSetup(const ChainType chainType = ChainType::MAIN); ~WalletTestingSetup(); std::unique_ptr<interfaces::WalletLoader> m_wallet_loader; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index caf95a3f03..b3eed8abc7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -559,13 +559,14 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (Unlock(_vMasterKey)) { - int64_t nStartTime = GetTimeMillis(); + constexpr MillisecondsDouble target{100}; + auto start{SteadyClock::now()}; crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)))); + pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start)); - nStartTime = GetTimeMillis(); + start = SteadyClock::now(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start))) / 2; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; @@ -762,13 +763,14 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) GetStrongRandBytes(kMasterKey.vchSalt); CCrypter crypter; - int64_t nStartTime = GetTimeMillis(); + constexpr MillisecondsDouble target{100}; + auto start{SteadyClock::now()}; crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = static_cast<unsigned int>(2500000 / ((double)(GetTimeMillis() - nStartTime))); + kMasterKey.nDeriveIterations = static_cast<unsigned int>(25000 * target / (SteadyClock::now() - start)); - nStartTime = GetTimeMillis(); + start = SteadyClock::now(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * target / (SteadyClock::now() - start))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; @@ -3587,7 +3589,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans() if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result"); for (bool internal : {false, true}) { - const UniValue& descriptor_vals = find_value(signer_res, internal ? "internal" : "receive"); + const UniValue& descriptor_vals = signer_res.find_value(internal ? "internal" : "receive"); if (!descriptor_vals.isArray()) throw std::runtime_error(std::string(__func__) + ": Unexpected result"); for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) { const std::string& desc_str = desc_val.getValStr(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 5252b46cdc..79f4c43456 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -284,7 +284,7 @@ private: std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver std::atomic<bool> m_attaching_chain{false}; std::atomic<bool> m_scanning_with_passphrase{false}; - std::atomic<int64_t> m_scanning_start{0}; + std::atomic<SteadyClock::time_point> m_scanning_start{SteadyClock::time_point{}}; std::atomic<double> m_scanning_progress{0}; friend class WalletRescanReserver; @@ -505,7 +505,7 @@ public: bool IsAbortingRescan() const { return fAbortRescan; } bool IsScanning() const { return fScanningWallet; } bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; } - int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; } + SteadyClock::duration ScanningDuration() const { return fScanningWallet ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; } double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; } //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo @@ -1014,7 +1014,7 @@ public: return false; } m_wallet.m_scanning_with_passphrase.exchange(with_passphrase); - m_wallet.m_scanning_start = GetTimeMillis(); + m_wallet.m_scanning_start = SteadyClock::now(); m_wallet.m_scanning_progress = 0; m_could_reserve = true; return true; diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py index 70a802dc58..7af67730bd 100755 --- a/test/functional/feature_init.py +++ b/test/functional/feature_init.py @@ -45,6 +45,13 @@ class InitStressTest(BitcoinTestFramework): node.process.terminate() node.process.wait() + def start_expecting_error(err_fragment): + node.assert_start_raises_init_error( + extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'], + expected_msg=err_fragment, + match=ErrorMatch.PARTIAL_REGEX, + ) + def check_clean_start(): """Ensure that node restarts successfully after various interrupts.""" node.start() @@ -87,36 +94,27 @@ class InitStressTest(BitcoinTestFramework): self.log.info("Test startup errors after removing certain essential files") - files_to_disturb = { + files_to_delete = { 'blocks/index/*.ldb': 'Error opening block database.', 'chainstate/*.ldb': 'Error opening block database.', 'blocks/blk*.dat': 'Error loading block database.', } - for file_patt, err_fragment in files_to_disturb.items(): + files_to_perturb = { + 'blocks/index/*.ldb': 'Error opening block database.', + 'chainstate/*.ldb': 'Error opening block database.', + 'blocks/blk*.dat': 'Error opening block database.', + } + + for file_patt, err_fragment in files_to_delete.items(): target_files = list(node.chain_path.glob(file_patt)) for target_file in target_files: - self.log.info(f"Tweaking file to ensure failure {target_file}") + self.log.info(f"Deleting file to ensure failure {target_file}") bak_path = str(target_file) + ".bak" target_file.rename(bak_path) - # TODO: at some point, we should test perturbing the files instead of removing - # them, e.g. - # - # contents = target_file.read_bytes() - # tweaked_contents = bytearray(contents) - # tweaked_contents[50:250] = b'1' * 200 - # target_file.write_bytes(bytes(tweaked_contents)) - # - # At the moment I can't get this to work (bitcoind loads successfully?) so - # investigate doing this later. - - node.assert_start_raises_init_error( - extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'], - expected_msg=err_fragment, - match=ErrorMatch.PARTIAL_REGEX, - ) + start_expecting_error(err_fragment) for target_file in target_files: bak_path = str(target_file) + ".bak" @@ -126,6 +124,18 @@ class InitStressTest(BitcoinTestFramework): check_clean_start() self.stop_node(0) + for file_patt, err_fragment in files_to_perturb.items(): + target_files = list(node.chain_path.glob(file_patt)) + + for target_file in target_files: + self.log.info(f"Perturbing file to ensure failure {target_file}") + with open(target_file, "rb") as tf_read, open(target_file, "wb") as tf_write: + contents = tf_read.read() + tweaked_contents = bytearray(contents) + tweaked_contents[50:250] = b'1' * 200 + tf_write.write(bytes(tweaked_contents)) + + start_expecting_error(err_fragment) if __name__ == '__main__': InitStressTest().main() diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py index 1b0f411e52..2eb36f260c 100755 --- a/test/functional/rpc_getdescriptorinfo.py +++ b/test/functional/rpc_getdescriptorinfo.py @@ -55,9 +55,9 @@ class DescriptorTest(BitcoinTestFramework): # A P2PK output with the public key of the specified xpub. self.test_desc('pk(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B)', isrange=False, issolvable=True, hasprivatekeys=False) # A P2PKH output with child key *1'/2* of the specified xpub. - self.test_desc("pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/2)", isrange=False, issolvable=True, hasprivatekeys=False) + self.test_desc("pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1h/2)", isrange=False, issolvable=True, hasprivatekeys=False) # A set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`. - self.test_desc("pkh([d34db33f/44'/0'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)", isrange=True, issolvable=True, hasprivatekeys=False) + self.test_desc("pkh([d34db33f/44h/0h/0h]tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)", isrange=True, issolvable=True, hasprivatekeys=False) # A set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). self.test_desc("wsh(multi(1,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/0/*,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/0/*))", isrange=True, issolvable=True, hasprivatekeys=False) diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index 9f87f18144..9f77f209ef 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -112,7 +112,7 @@ class ScantxoutsetTest(BitcoinTestFramework): assert_equal(self.nodes[0].scantxoutset("start", [{"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": [1500, 1500]}])['total_amount'], Decimal("16.384")) # Test the reported descriptors for a few matches - assert_equal(descriptors(self.nodes[0].scantxoutset("start", [{"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", "range": 1499}])), ["pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)#dzxw429x", "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)#43rvceed"]) + assert_equal(descriptors(self.nodes[0].scantxoutset("start", [{"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0h/*)", "range": 1499}])), ["pkh([0c5f9a1e/0h/0h/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)#rthll0rg", "pkh([0c5f9a1e/0h/0h/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)#mcjajulr"]) assert_equal(descriptors(self.nodes[0].scantxoutset("start", ["combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8"]) assert_equal(descriptors(self.nodes[0].scantxoutset("start", [{"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])), ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8', 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)#vchwd07g', 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)#z2t3ypsa']) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 66a23b443c..d4dc90a517 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -228,6 +228,23 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): PortSeed.n = self.options.port_seed + def set_binary_paths(self): + """Update self.options with the paths of all binaries from environment variables or their default values""" + + binaries = { + "bitcoind": ("bitcoind", "BITCOIND"), + "bitcoin-cli": ("bitcoincli", "BITCOINCLI"), + "bitcoin-util": ("bitcoinutil", "BITCOINUTIL"), + "bitcoin-wallet": ("bitcoinwallet", "BITCOINWALLET"), + } + for binary, [attribute_name, env_variable_name] in binaries.items(): + default_filename = os.path.join( + self.config["environment"]["BUILDDIR"], + "src", + binary + self.config["environment"]["EXEEXT"], + ) + setattr(self.options, attribute_name, os.getenv(env_variable_name, default=default_filename)) + def setup(self): """Call this method to start up the test framework object with options set.""" @@ -237,24 +254,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): config = self.config - fname_bitcoind = os.path.join( - config["environment"]["BUILDDIR"], - "src", - "bitcoind" + config["environment"]["EXEEXT"], - ) - fname_bitcoincli = os.path.join( - config["environment"]["BUILDDIR"], - "src", - "bitcoin-cli" + config["environment"]["EXEEXT"], - ) - fname_bitcoinutil = os.path.join( - config["environment"]["BUILDDIR"], - "src", - "bitcoin-util" + config["environment"]["EXEEXT"], - ) - self.options.bitcoind = os.getenv("BITCOIND", default=fname_bitcoind) - self.options.bitcoincli = os.getenv("BITCOINCLI", default=fname_bitcoincli) - self.options.bitcoinutil = os.getenv("BITCOINUTIL", default=fname_bitcoinutil) + self.set_binary_paths() os.environ['PATH'] = os.pathsep.join([ os.path.join(config['environment']['BUILDDIR'], 'src'), diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index a888f93b03..95999649b4 100755 --- a/test/functional/tool_wallet.py +++ b/test/functional/tool_wallet.py @@ -32,12 +32,11 @@ class ToolWalletTest(BitcoinTestFramework): self.skip_if_no_wallet_tool() def bitcoin_wallet_process(self, *args): - binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"] default_args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain] if not self.options.descriptors and 'create' in args: default_args.append('-legacy') - return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + return subprocess.Popen([self.options.bitcoinwallet] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) def assert_raises_tool_error(self, error, *args): p = self.bitcoin_wallet_process(*args) diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index ebeb5620e5..be5b3ebadb 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -176,7 +176,7 @@ class AddressTypeTest(BitcoinTestFramework): for deriv in decode['inputs'][0]['bip32_derivs']: assert_equal(len(deriv['master_fingerprint']), 8) assert_equal(deriv['path'][0], 'm') - key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + deriv['path'][1:] + ']' + deriv['pubkey'] + key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + deriv['path'][1:].replace("'","h") + ']' + deriv['pubkey'] # Verify the descriptor checksum against the Python implementation assert descsum_check(info['desc']) diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py index a6401a76c1..5088e11eda 100755 --- a/test/functional/wallet_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -253,7 +253,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): wallet = node_v17.get_wallet_rpc("u1_v17") address = wallet.getnewaddress("bech32") v17_info = wallet.getaddressinfo(address) - hdkeypath = v17_info["hdkeypath"] + hdkeypath = v17_info["hdkeypath"].replace("'", "h") pubkey = v17_info["pubkey"] if self.is_bdb_compiled(): diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index b0e93df36a..4673eb091c 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -60,43 +60,43 @@ class WalletDescriptorTest(BitcoinTestFramework): addr = self.nodes[0].getnewaddress("", "legacy") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('pkh(') - assert_equal(addr_info['hdkeypath'], 'm/44\'/1\'/0\'/0/0') + assert_equal(addr_info['hdkeypath'], 'm/44h/1h/0h/0/0') addr = self.nodes[0].getnewaddress("", "p2sh-segwit") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('sh(wpkh(') - assert_equal(addr_info['hdkeypath'], 'm/49\'/1\'/0\'/0/0') + assert_equal(addr_info['hdkeypath'], 'm/49h/1h/0h/0/0') addr = self.nodes[0].getnewaddress("", "bech32") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('wpkh(') - assert_equal(addr_info['hdkeypath'], 'm/84\'/1\'/0\'/0/0') + assert_equal(addr_info['hdkeypath'], 'm/84h/1h/0h/0/0') addr = self.nodes[0].getnewaddress("", "bech32m") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('tr(') - assert_equal(addr_info['hdkeypath'], 'm/86\'/1\'/0\'/0/0') + assert_equal(addr_info['hdkeypath'], 'm/86h/1h/0h/0/0') # Check that getrawchangeaddress works addr = self.nodes[0].getrawchangeaddress("legacy") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('pkh(') - assert_equal(addr_info['hdkeypath'], 'm/44\'/1\'/0\'/1/0') + assert_equal(addr_info['hdkeypath'], 'm/44h/1h/0h/1/0') addr = self.nodes[0].getrawchangeaddress("p2sh-segwit") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('sh(wpkh(') - assert_equal(addr_info['hdkeypath'], 'm/49\'/1\'/0\'/1/0') + assert_equal(addr_info['hdkeypath'], 'm/49h/1h/0h/1/0') addr = self.nodes[0].getrawchangeaddress("bech32") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('wpkh(') - assert_equal(addr_info['hdkeypath'], 'm/84\'/1\'/0\'/1/0') + assert_equal(addr_info['hdkeypath'], 'm/84h/1h/0h/1/0') addr = self.nodes[0].getrawchangeaddress("bech32m") addr_info = self.nodes[0].getaddressinfo(addr) assert addr_info['desc'].startswith('tr(') - assert_equal(addr_info['hdkeypath'], 'm/86\'/1\'/0\'/1/0') + assert_equal(addr_info['hdkeypath'], 'm/86h/1h/0h/1/0') # Make a wallet to receive coins at self.nodes[0].createwallet(wallet_name="desc2", descriptors=True) @@ -178,14 +178,14 @@ class WalletDescriptorTest(BitcoinTestFramework): self.nodes[0].createwallet(wallet_name='desc_import', disable_private_keys=True, descriptors=True) imp_rpc = self.nodes[0].get_wallet_rpc('desc_import') - addr_types = [('legacy', False, 'pkh(', '44\'/1\'/0\'', -13), - ('p2sh-segwit', False, 'sh(wpkh(', '49\'/1\'/0\'', -14), - ('bech32', False, 'wpkh(', '84\'/1\'/0\'', -13), - ('bech32m', False, 'tr(', '86\'/1\'/0\'', -13), - ('legacy', True, 'pkh(', '44\'/1\'/0\'', -13), - ('p2sh-segwit', True, 'sh(wpkh(', '49\'/1\'/0\'', -14), - ('bech32', True, 'wpkh(', '84\'/1\'/0\'', -13), - ('bech32m', True, 'tr(', '86\'/1\'/0\'', -13)] + addr_types = [('legacy', False, 'pkh(', '44h/1h/0h', -13), + ('p2sh-segwit', False, 'sh(wpkh(', '49h/1h/0h', -14), + ('bech32', False, 'wpkh(', '84h/1h/0h', -13), + ('bech32m', False, 'tr(', '86h/1h/0h', -13), + ('legacy', True, 'pkh(', '44h/1h/0h', -13), + ('p2sh-segwit', True, 'sh(wpkh(', '49h/1h/0h', -14), + ('bech32', True, 'wpkh(', '84h/1h/0h', -13), + ('bech32m', True, 'tr(', '86h/1h/0h', -13)] for addr_type, internal, desc_prefix, deriv_path, int_idx in addr_types: int_str = 'internal' if internal else 'external' diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 0f79df6e5d..8f84d8ed60 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -41,7 +41,7 @@ class WalletHDTest(BitcoinTestFramework): change_addr = self.nodes[1].getrawchangeaddress() change_addrV = self.nodes[1].getaddressinfo(change_addr) if self.options.descriptors: - assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/0") + assert_equal(change_addrV["hdkeypath"], "m/84h/1h/0h/1/0") else: assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key @@ -63,7 +63,7 @@ class WalletHDTest(BitcoinTestFramework): hd_add = self.nodes[1].getnewaddress() hd_info = self.nodes[1].getaddressinfo(hd_add) if self.options.descriptors: - assert_equal(hd_info["hdkeypath"], "m/84'/1'/0'/0/" + str(i)) + assert_equal(hd_info["hdkeypath"], "m/84h/1h/0h/0/" + str(i)) else: assert_equal(hd_info["hdkeypath"], "m/0'/0'/" + str(i) + "'") assert_equal(hd_info["hdmasterfingerprint"], hd_fingerprint) @@ -76,7 +76,7 @@ class WalletHDTest(BitcoinTestFramework): change_addr = self.nodes[1].getrawchangeaddress() change_addrV = self.nodes[1].getaddressinfo(change_addr) if self.options.descriptors: - assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/1") + assert_equal(change_addrV["hdkeypath"], "m/84h/1h/0h/1/1") else: assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key @@ -101,7 +101,7 @@ class WalletHDTest(BitcoinTestFramework): hd_add_2 = self.nodes[1].getnewaddress() hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) if self.options.descriptors: - assert_equal(hd_info_2["hdkeypath"], "m/84'/1'/0'/0/" + str(i)) + assert_equal(hd_info_2["hdkeypath"], "m/84h/1h/0h/0/" + str(i)) else: assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/" + str(i) + "'") assert_equal(hd_info_2["hdmasterfingerprint"], hd_fingerprint) @@ -143,7 +143,7 @@ class WalletHDTest(BitcoinTestFramework): keypath = self.nodes[1].getaddressinfo(out['scriptPubKey']['address'])['hdkeypath'] if self.options.descriptors: - assert_equal(keypath[0:14], "m/84'/1'/0'/1/") + assert_equal(keypath[0:14], "m/84h/1h/0h/1/") else: assert_equal(keypath[0:7], "m/0'/1'") diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py index 4f2db2018a..ad5ae111aa 100755 --- a/test/functional/wallet_importdescriptors.py +++ b/test/functional/wallet_importdescriptors.py @@ -332,15 +332,15 @@ class ImportDescriptorsTest(BitcoinTestFramework): assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'bech32') assert_equal(received_addr, expected_addr) bech32_addr_info = w1.getaddressinfo(received_addr) - assert_equal(bech32_addr_info['desc'][:23], 'wpkh([80002067/0\'/0\'/{}]'.format(i)) + assert_equal(bech32_addr_info['desc'][:23], 'wpkh([80002067/0h/0h/{}]'.format(i)) shwpkh_addr = w1.getnewaddress('', 'p2sh-segwit') shwpkh_addr_info = w1.getaddressinfo(shwpkh_addr) - assert_equal(shwpkh_addr_info['desc'][:26], 'sh(wpkh([abcdef12/0\'/0\'/{}]'.format(i)) + assert_equal(shwpkh_addr_info['desc'][:26], 'sh(wpkh([abcdef12/0h/0h/{}]'.format(i)) pkh_addr = w1.getnewaddress('', 'legacy') pkh_addr_info = w1.getaddressinfo(pkh_addr) - assert_equal(pkh_addr_info['desc'][:22], 'pkh([12345678/0\'/0\'/{}]'.format(i)) + assert_equal(pkh_addr_info['desc'][:22], 'pkh([12345678/0h/0h/{}]'.format(i)) assert_equal(w1.getwalletinfo()['keypoolsize'], 4 * 3) # After retrieving a key, we don't refill the keypool again, so it's one less for each address type w1.keypoolrefill() diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 18c3ae1f7c..f1458bb374 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -83,11 +83,11 @@ class KeypoolRestoreTest(BitcoinTestFramework): # Check that we have marked all keys up to the used keypool key as used if self.options.descriptors: if output_type == 'legacy': - assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/44'/1'/0'/0/110") + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/44h/1h/0h/0/110") elif output_type == 'p2sh-segwit': - assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/49'/1'/0'/0/110") + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/49h/1h/0h/0/110") elif output_type == 'bech32': - assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/84'/1'/0'/0/110") + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/84h/1h/0h/0/110") else: assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/0'/0'/110'") diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py index c5479089c6..712b881322 100755 --- a/test/functional/wallet_listdescriptors.py +++ b/test/functional/wallet_listdescriptors.py @@ -65,7 +65,7 @@ class ListDescriptorsTest(BitcoinTestFramework): self.log.info('Test descriptors with hardened derivations are listed in importable form.') xprv = 'tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg' xpub_acc = 'tpubDCMVLhErorrAGfApiJSJzEKwqeaf2z3NrkVMxgYQjZLzMjXMBeRw2muGNYbvaekAE8rUFLftyEar4LdrG2wXyyTJQZ26zptmeTEjPTaATts' - hardened_path = '/84\'/1\'/0\'' + hardened_path = '/84h/1h/0h' wallet = node.get_wallet_rpc('w2') wallet.importdescriptors([{ 'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'), diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index 7c2959bb89..7a04e11370 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -53,7 +53,7 @@ class WalletMigrationTest(BitcoinTestFramework): assert_equal(addr_info["address"], addr_info_old["address"]) assert_equal(addr_info["scriptPubKey"], addr_info_old["scriptPubKey"]) assert_equal(addr_info["ismine"], addr_info_old["ismine"]) - assert_equal(addr_info["hdkeypath"], addr_info_old["hdkeypath"]) + assert_equal(addr_info["hdkeypath"], addr_info_old["hdkeypath"].replace("'","h")) assert_equal(addr_info["solvable"], addr_info_old["solvable"]) assert_equal(addr_info["ischange"], addr_info_old["ischange"]) assert_equal(addr_info["hdmasterfingerprint"], addr_info_old["hdmasterfingerprint"]) @@ -91,11 +91,11 @@ class WalletMigrationTest(BitcoinTestFramework): self.assert_is_sqlite("basic0") # The wallet should create the following descriptors: - # * BIP32 descriptors in the form of "0'/0'/*" and "0'/1'/*" (2 descriptors) - # * BIP44 descriptors in the form of "44'/1'/0'/0/*" and "44'/1'/0'/1/*" (2 descriptors) - # * BIP49 descriptors, P2SH(P2WPKH), in the form of "86'/1'/0'/0/*" and "86'/1'/0'/1/*" (2 descriptors) - # * BIP84 descriptors, P2WPKH, in the form of "84'/1'/0'/1/*" and "84'/1'/0'/1/*" (2 descriptors) - # * BIP86 descriptors, P2TR, in the form of "86'/1'/0'/0/*" and "86'/1'/0'/1/*" (2 descriptors) + # * BIP32 descriptors in the form of "0h/0h/*" and "0h/1h/*" (2 descriptors) + # * BIP44 descriptors in the form of "44h/1h/0h/0/*" and "44h/1h/0h/1/*" (2 descriptors) + # * BIP49 descriptors, P2SH(P2WPKH), in the form of "86h/1h/0h/0/*" and "86h/1h/0h/1/*" (2 descriptors) + # * BIP84 descriptors, P2WPKH, in the form of "84h/1h/0h/1/*" and "84h/1h/0h/1/*" (2 descriptors) + # * BIP86 descriptors, P2TR, in the form of "86h/1h/0h/0/*" and "86h/1h/0h/1/*" (2 descriptors) # * A combo(PK) descriptor for the wallet master key. # So, should have a total of 11 descriptors on it. assert_equal(len(basic0.listdescriptors()["descriptors"]), 11) @@ -107,7 +107,7 @@ class WalletMigrationTest(BitcoinTestFramework): self.assert_addr_info_equal(change_addr_info, old_change_addr_info) addr_info = basic0.getaddressinfo(basic0.getnewaddress("", "bech32")) - assert_equal(addr_info["hdkeypath"], "m/84'/1'/0'/0/0") + assert_equal(addr_info["hdkeypath"], "m/84h/1h/0h/0/0") self.log.info("Test migration of a basic keys only wallet with a balance") basic1 = self.create_legacy_wallet("basic1") diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py index 8d25044e43..6aaec65b86 100755 --- a/test/functional/wallet_signer.py +++ b/test/functional/wallet_signer.py @@ -109,28 +109,28 @@ class WalletSignerTest(BitcoinTestFramework): address_info = hww.getaddressinfo(address1) assert_equal(address_info['solvable'], True) assert_equal(address_info['ismine'], True) - assert_equal(address_info['hdkeypath'], "m/84'/1'/0'/0/0") + assert_equal(address_info['hdkeypath'], "m/84h/1h/0h/0/0") address2 = hww.getnewaddress(address_type="p2sh-segwit") assert_equal(address2, "2N2gQKzjUe47gM8p1JZxaAkTcoHPXV6YyVp") address_info = hww.getaddressinfo(address2) assert_equal(address_info['solvable'], True) assert_equal(address_info['ismine'], True) - assert_equal(address_info['hdkeypath'], "m/49'/1'/0'/0/0") + assert_equal(address_info['hdkeypath'], "m/49h/1h/0h/0/0") address3 = hww.getnewaddress(address_type="legacy") assert_equal(address3, "n1LKejAadN6hg2FrBXoU1KrwX4uK16mco9") address_info = hww.getaddressinfo(address3) assert_equal(address_info['solvable'], True) assert_equal(address_info['ismine'], True) - assert_equal(address_info['hdkeypath'], "m/44'/1'/0'/0/0") + assert_equal(address_info['hdkeypath'], "m/44h/1h/0h/0/0") address4 = hww.getnewaddress(address_type="bech32m") assert_equal(address4, "bcrt1phw4cgpt6cd30kz9k4wkpwm872cdvhss29jga2xpmftelhqll62ms4e9sqj") address_info = hww.getaddressinfo(address4) assert_equal(address_info['solvable'], True) assert_equal(address_info['ismine'], True) - assert_equal(address_info['hdkeypath'], "m/86'/1'/0'/0/0") + assert_equal(address_info['hdkeypath'], "m/86h/1h/0h/0/0") self.log.info('Test walletdisplayaddress') result = hww.walletdisplayaddress(address1) @@ -153,14 +153,14 @@ class WalletSignerTest(BitcoinTestFramework): assert mock_wallet.getwalletinfo()['private_keys_enabled'] result = mock_wallet.importdescriptors([{ - "desc": "tr([00000001/86'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#0jtt2jc9", + "desc": "tr([00000001/86h/1h/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#7ew68cn8", "timestamp": 0, "range": [0,1], "internal": False, "active": True }, { - "desc": "tr([00000001/86'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#7xw2h8ga", + "desc": "tr([00000001/86h/1h/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#0dtm6drl", "timestamp": 0, "range": [0, 0], "internal": True, @@ -182,7 +182,7 @@ class WalletSignerTest(BitcoinTestFramework): # hww4 = self.nodes[1].get_wallet_rpc("hww4") # # descriptors = [{ - # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/*)#x30uthjs", + # "desc": "wpkh([00000001/84h/1h/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/*)#x30uthjs", # "timestamp": "now", # "range": [0, 1], # "internal": False, @@ -190,7 +190,7 @@ class WalletSignerTest(BitcoinTestFramework): # "active": True # }, # { - # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)#h92akzzg", + # "desc": "wpkh([00000001/84h/1h/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)#h92akzzg", # "timestamp": "now", # "range": [0, 0], # "internal": True, |