diff options
174 files changed, 5276 insertions, 3389 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 96357a103d..3f52adde8a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -43,7 +43,7 @@ env: # Global defaults # The following specific types should exist, with the following requirements: # - small: For an x86_64 machine, recommended to have 2 CPUs and 8 GB of memory. # - medium: For an x86_64 machine, recommended to have 4 CPUs and 16 GB of memory. -# - lunar: For a machine running the Linux kernel shipped with Ubuntu Lunar 23.04. The machine is recommended to have 4 CPUs and 16 GB of memory. +# - mantic: For a machine running the Linux kernel shipped with exaclty Ubuntu Mantic 23.10. The machine is recommended to have 4 CPUs and 16 GB of memory. # - arm64: For an aarch64 machine, recommended to have 2 CPUs and 8 GB of memory. # https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks @@ -165,7 +165,7 @@ task: << : *GLOBAL_TASK_TEMPLATE persistent_worker: labels: - type: lunar # Must use the lunar-specific worker (needed for USDT functional tests) + type: mantic # Must use this specific worker (needed for USDT functional tests) env: FILE_ENV: "./ci/test/00_setup_env_native_asan.sh" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a76a757129..c1e171c0e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,19 +31,41 @@ jobs: env: MAX_COUNT: 6 steps: - - run: echo "FETCH_DEPTH=$((${{ github.event.pull_request.commits }} + 2))" >> "$GITHUB_ENV" + - name: Determine fetch depth + run: echo "FETCH_DEPTH=$((${{ github.event.pull_request.commits }} + 2))" >> "$GITHUB_ENV" - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: ${{ env.FETCH_DEPTH }} - - run: | + - name: Determine commit range + run: | + # Checkout HEAD~ and find the test base commit + # Checkout HEAD~ because it would be wasteful to rerun tests on the PR + # head commit that are already run by other jobs. git checkout HEAD~ - echo "COMMIT_AFTER_LAST_MERGE=$(git log $(git log --merges -1 --format=%H)..HEAD --format=%H --max-count=${{ env.MAX_COUNT }} | tail -1)" >> "$GITHUB_ENV" + # Figure out test base commit by listing ancestors of HEAD, excluding + # ancestors of the most recent merge commit, limiting the list to the + # newest MAX_COUNT ancestors, ordering it from oldest to newest, and + # taking the first one. + # + # If the branch contains up to MAX_COUNT ancestor commits after the + # most recent merge commit, all of those commits will be tested. If it + # contains more, only the most recent MAX_COUNT commits will be + # tested. + # + # In the command below, the ^@ suffix is used to refer to all parents + # of the merge commit as described in: + # https://git-scm.com/docs/git-rev-parse#_other_rev_parent_shorthand_notations + # and the ^ prefix is used to exclude these parents and all their + # ancestors from the rev-list output as described in: + # https://git-scm.com/docs/git-rev-list + echo "TEST_BASE=$(git rev-list -n$((${{ env.MAX_COUNT }} + 1)) --reverse HEAD ^$(git rev-list -n1 --merges HEAD)^@ | head -1)" >> "$GITHUB_ENV" - run: sudo apt install clang ccache build-essential libtool autotools-dev automake pkg-config bsdmainutils python3-zmq libevent-dev libboost-dev libsqlite3-dev libdb++-dev systemtap-sdt-dev libminiupnpc-dev libnatpmp-dev libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools qtwayland5 libqrencode-dev -y - name: Compile and run tests run: | + # Run tests on commits after the last merge commit and before the PR head commit # Use clang++, because it is a bit faster and uses less memory than g++ - git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && ./autogen.sh && CC=clang CXX=clang++ ./configure && make clean && make -j $(nproc) check && ./test/functional/test_runner.py -j $(( $(nproc) * 2 ))" ${{ env.COMMIT_AFTER_LAST_MERGE }}~1 + git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && ./autogen.sh && CC=clang CXX=clang++ ./configure && make clean && make -j $(nproc) check && ./test/functional/test_runner.py -j $(( $(nproc) * 2 ))" ${{ env.TEST_BASE }} macos-native-x86_64: name: 'macOS 13 native, x86_64, no depends, sqlite only, gui' diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index 0f69ebdd9c..93d84bf17e 100755 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -16,11 +16,11 @@ else fi export CONTAINER_NAME=ci_native_asan -export PACKAGES="systemtap-sdt-dev clang-16 llvm-16 libclang-rt-16-dev python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev libboost-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev ${BPFCC_PACKAGE}" -export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.04" # This version will reach EOL in Jan 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version). +export PACKAGES="systemtap-sdt-dev clang-17 llvm-17 libclang-rt-17-dev python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev libboost-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev ${BPFCC_PACKAGE}" +export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version). export NO_DEPENDS=1 export GOAL="install" export BITCOIN_CONFIG="--enable-c++20 --enable-usdt --enable-zmq --with-incompatible-bdb --with-gui=qt5 \ CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' \ --with-sanitizers=address,float-divide-by-zero,integer,undefined \ -CC='clang-16 -ftrivial-auto-var-init=pattern' CXX='clang++-16 -ftrivial-auto-var-init=pattern'" +CC='clang-17 -ftrivial-auto-var-init=pattern' CXX='clang++-17 -ftrivial-auto-var-init=pattern'" diff --git a/ci/test/00_setup_env_native_tidy.sh b/ci/test/00_setup_env_native_tidy.sh index 456166d3be..6b0c708f19 100755 --- a/ci/test/00_setup_env_native_tidy.sh +++ b/ci/test/00_setup_env_native_tidy.sh @@ -8,12 +8,13 @@ export LC_ALL=C.UTF-8 export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version). export CONTAINER_NAME=ci_native_tidy -export PACKAGES="clang-16 libclang-16-dev llvm-16-dev libomp-16-dev clang-tidy-16 jq bear cmake libevent-dev libboost-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev systemtap-sdt-dev libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libqrencode-dev libsqlite3-dev libdb++-dev" +export TIDY_LLVM_V="17" +export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq bear cmake libevent-dev libboost-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev systemtap-sdt-dev libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libqrencode-dev libsqlite3-dev libdb++-dev" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false export RUN_FUZZ_TESTS=false export RUN_TIDY=true export GOAL="install" -export BITCOIN_CONFIG="CC=clang-16 CXX=clang++-16 --with-incompatible-bdb --disable-hardening CFLAGS='-O0 -g0' CXXFLAGS='-O0 -g0 -I/usr/lib/llvm-16/lib/clang/16/include'" +export BITCOIN_CONFIG="CC=clang-${TIDY_LLVM_V} CXX=clang++-${TIDY_LLVM_V} --with-incompatible-bdb --disable-hardening CFLAGS='-O0 -g0' CXXFLAGS='-O0 -g0 -I/usr/lib/llvm-${TIDY_LLVM_V}/lib/clang/${TIDY_LLVM_V}/include'" export CCACHE_MAXSIZE=200M diff --git a/ci/test/01_base_install.sh b/ci/test/01_base_install.sh index 424dca52dc..68b701f3ca 100755 --- a/ci/test/01_base_install.sh +++ b/ci/test/01_base_install.sh @@ -42,7 +42,7 @@ if [ -n "$PIP_PACKAGES" ]; then fi if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then - git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-17.0.0-rc4" /msan/llvm-project + ${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b llvmorg-17.0.2 /msan/llvm-project cmake -G Ninja -B /msan/clang_build/ \ -DLLVM_ENABLE_PROJECTS="clang" \ @@ -73,8 +73,9 @@ if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then fi if [[ "${RUN_TIDY}" == "true" ]]; then - git clone --depth=1 https://github.com/include-what-you-use/include-what-you-use -b clang_16 /include-what-you-use - cmake -B /iwyu-build/ -G 'Unix Makefiles' -DCMAKE_PREFIX_PATH=/usr/lib/llvm-16 -S /include-what-you-use + ${CI_RETRY_EXE} git clone https://github.com/include-what-you-use/include-what-you-use -b master /include-what-you-use + git -C /include-what-you-use checkout a138eaac254e5a472464e31d5ec418fe6e6f1fc7 + cmake -B /iwyu-build/ -G 'Unix Makefiles' -DCMAKE_PREFIX_PATH=/usr/lib/llvm-"${TIDY_LLVM_V}" -S /include-what-you-use make -C /iwyu-build/ install "-j$( nproc )" # Use nproc, because MAKEJOBS is the default in docker image builds fi @@ -98,7 +99,7 @@ if [ -n "$ANDROID_HOME" ] && [ ! -d "$ANDROID_HOME" ]; then fi mkdir -p "$ANDROID_HOME" unzip -o "$ANDROID_TOOLS_PATH" -d "$ANDROID_HOME" - yes | "${ANDROID_HOME}"/cmdline-tools/bin/sdkmanager --sdk_root="${ANDROID_HOME}" --install "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" "platform-tools" "platforms;android-${ANDROID_API_LEVEL}" "ndk;${ANDROID_NDK_VERSION}" + yes | "${ANDROID_HOME}"/cmdline-tools/bin/sdkmanager --sdk_root="${ANDROID_HOME}" --install "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" "platform-tools" "platforms;android-31" "platforms;android-${ANDROID_API_LEVEL}" "ndk;${ANDROID_NDK_VERSION}" fi git config --global ${CFG_DONE} "true" diff --git a/ci/test/04_install.sh b/ci/test/02_run_container.sh index 01faf9fff9..a74226b089 100755 --- a/ci/test/04_install.sh +++ b/ci/test/02_run_container.sh @@ -5,6 +5,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. export LC_ALL=C.UTF-8 +export CI_IMAGE_LABEL="bitcoin-ci-test" + +set -ex if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then # Export all env vars to avoid missing some. @@ -17,26 +20,34 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then --file "${BASE_READ_ONLY_DIR}/ci/test_imagefile" \ --build-arg "CI_IMAGE_NAME_TAG=${CI_IMAGE_NAME_TAG}" \ --build-arg "FILE_ENV=${FILE_ENV}" \ + --label="${CI_IMAGE_LABEL}" \ --tag="${CONTAINER_NAME}" \ "${BASE_READ_ONLY_DIR}" docker volume create "${CONTAINER_NAME}_ccache" || true docker volume create "${CONTAINER_NAME}_depends" || true + docker volume create "${CONTAINER_NAME}_depends_sources" || true + docker volume create "${CONTAINER_NAME}_depends_SDKs_android" || true docker volume create "${CONTAINER_NAME}_previous_releases" || true if [ -n "${RESTART_CI_DOCKER_BEFORE_RUN}" ] ; then echo "Restart docker before run to stop and clear all containers started with --rm" - podman container stop --all # Similar to "systemctl restart docker" + podman container rm --force --all # Similar to "systemctl restart docker" + + # Still prune everything in case the filtered pruning doesn't work, or if labels were not set + # on a previous run. Belt and suspenders approach, should be fine to remove in the future. echo "Prune all dangling images" docker image prune --force fi + echo "Prune all dangling $CI_IMAGE_LABEL images" + docker image prune --force --filter "label=$CI_IMAGE_LABEL" # shellcheck disable=SC2086 - - CI_CONTAINER_ID=$(docker run --cap-add LINUX_IMMUTABLE $CI_CONTAINER_CAP --rm --interactive --detach --tty \ --mount "type=bind,src=$BASE_READ_ONLY_DIR,dst=$BASE_READ_ONLY_DIR,readonly" \ --mount "type=volume,src=${CONTAINER_NAME}_ccache,dst=$CCACHE_DIR" \ - --mount "type=volume,src=${CONTAINER_NAME}_depends,dst=$DEPENDS_DIR" \ + --mount "type=volume,src=${CONTAINER_NAME}_depends,dst=$DEPENDS_DIR/built" \ + --mount "type=volume,src=${CONTAINER_NAME}_depends_sources,dst=$DEPENDS_DIR/sources" \ + --mount "type=volume,src=${CONTAINER_NAME}_depends_SDKs_android,dst=$DEPENDS_DIR/SDKs/android" \ --mount "type=volume,src=${CONTAINER_NAME}_previous_releases,dst=$PREVIOUS_RELEASES_DIR" \ --env-file /tmp/env \ --name "$CONTAINER_NAME" \ @@ -64,3 +75,10 @@ CI_EXEC "${BASE_ROOT_DIR}/ci/test/01_base_install.sh" CI_EXEC git config --global --add safe.directory \"*\" CI_EXEC mkdir -p "${BINS_SCRATCH_DIR}" + +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/ci/test/06_script_b.sh b/ci/test/06_script_b.sh index 89af61b87f..4d5f31b956 100755 --- a/ci/test/06_script_b.sh +++ b/ci/test/06_script_b.sh @@ -175,13 +175,13 @@ if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then fi if [ "${RUN_TIDY}" = "true" ]; then - cmake -B /tidy-build -DLLVM_DIR=/usr/lib/llvm-16/cmake -DCMAKE_BUILD_TYPE=Release -S "${BASE_ROOT_DIR}"/contrib/devtools/bitcoin-tidy + cmake -B /tidy-build -DLLVM_DIR=/usr/lib/llvm-"${TIDY_LLVM_V}"/cmake -DCMAKE_BUILD_TYPE=Release -S "${BASE_ROOT_DIR}"/contrib/devtools/bitcoin-tidy cmake --build /tidy-build "$MAKEJOBS" cmake --build /tidy-build --target bitcoin-tidy-tests "$MAKEJOBS" set -eo pipefail cd "${BASE_BUILD_DIR}/bitcoin-$HOST/src/" - ( run-clang-tidy-16 -quiet -load="/tidy-build/libbitcoin-tidy.so" "${MAKEJOBS}" ) | grep -C5 "error" + ( run-clang-tidy-"${TIDY_LLVM_V}" -quiet -load="/tidy-build/libbitcoin-tidy.so" "${MAKEJOBS}" ) | grep -C5 "error" # Filter out files by regex here, because regex may not be # accepted in src/.bear-tidy-config # Filter out: diff --git a/ci/test_run_all.sh b/ci/test_run_all.sh index 2284a2903b..3afc47b23e 100755 --- a/ci/test_run_all.sh +++ b/ci/test_run_all.sh @@ -1,17 +1,11 @@ #!/usr/bin/env bash # -# Copyright (c) 2019-2020 The Bitcoin Core developers +# Copyright (c) 2019-present The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. export LC_ALL=C.UTF-8 set -o errexit; source ./ci/test/00_setup_env.sh -set -o errexit; source ./ci/test/04_install.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 +"./ci/test/02_run_container.sh" diff --git a/configure.ac b/configure.ac index 9b4b9bd42b..0e8292f19a 100644 --- a/configure.ac +++ b/configure.ac @@ -471,6 +471,12 @@ fi dnl Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review. AX_CHECK_COMPILE_FLAG([-fno-extended-identifiers], [CORE_CXXFLAGS="$CORE_CXXFLAGS -fno-extended-identifiers"], [], [$CXXFLAG_WERROR]) +dnl Currently all versions of gcc are subject to a class of bugs, see the +dnl gccbug_90348 test case (only reproduces on GCC 11 and earlier) and +dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111843. To work around that, set +dnl -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) +AX_CHECK_COMPILE_FLAG([-fstack-reuse=none], [CORE_CXXFLAGS="$CORE_CXXFLAGS -fstack-reuse=none"]) + enable_arm_crc=no enable_arm_shani=no enable_sse42=no @@ -941,11 +947,6 @@ if test "$TARGET_OS" != "windows"; then AX_CHECK_COMPILE_FLAG([-fPIC], [PIC_FLAGS="-fPIC"]) fi -dnl Currently all versions of gcc are subject to a class of bugs, see the -dnl gccbug_90348 test case (only reproduces on GCC 11 and earlier) and the related bugs of -dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set -dnl -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) -AX_CHECK_COMPILE_FLAG([-fstack-reuse=none], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-reuse=none"]) if test "$use_hardening" != "no"; then use_hardening=yes AX_CHECK_COMPILE_FLAG([-Wstack-protector], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) @@ -964,6 +965,11 @@ if test "$use_hardening" != "no"; then ;; esac + case $host in + *aarch64*) + AX_CHECK_COMPILE_FLAG([-mbranch-protection=bti], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -mbranch-protection=bti"]) + ;; + esac dnl When enable_debug is yes, all optimizations are disabled. dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 8bbf39b67f..56eaeef815 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -83,7 +83,7 @@ A small script to automatically create manpages in ../../doc/man by running the This requires help2man which can be found at: https://www.gnu.org/software/help2man/ With in-tree builds this tool can be run from any directory within the -repostitory. To use this tool with out-of-tree builds set `BUILDDIR`. For +repository. To use this tool with out-of-tree builds set `BUILDDIR`. For example: ```bash diff --git a/contrib/devtools/headerssync-params.py b/contrib/devtools/headerssync-params.py index f0088d6cb9..0198f5db99 100644 --- a/contrib/devtools/headerssync-params.py +++ b/contrib/devtools/headerssync-params.py @@ -12,13 +12,13 @@ import random # Parameters: # Aim for still working fine at some point in the future. [datetime] -TIME = datetime(2026, 5, 25) +TIME = datetime(2026, 10, 5) # Expected block interval. [timedelta] BLOCK_INTERVAL = timedelta(seconds=600) # The number of headers corresponding to the minchainwork parameter. [headers] -MINCHAINWORK_HEADERS = 784000 +MINCHAINWORK_HEADERS = 804000 # Combined processing bandwidth from all attackers to one victim. [bit/s] # 6 Gbit/s is approximately the speed at which a single thread of a Ryzen 5950X CPU thread can hash diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index c5f0a761f1..a3d00bec95 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -240,6 +240,11 @@ def check_MACHO_sdk(binary) -> bool: return True return False +def check_MACHO_ld64(binary) -> bool: + if binary.build_version.tools[0].version == [711, 0, 0]: + return True + return False + def check_PE_libraries(binary) -> bool: ok: bool = True for dylib in binary.libraries: @@ -278,6 +283,7 @@ lief.EXE_FORMATS.MACHO: [ ('DYNAMIC_LIBRARIES', check_MACHO_libraries), ('MIN_OS', check_MACHO_min_os), ('SDK', check_MACHO_sdk), + ('LD64', check_MACHO_ld64), ], lief.EXE_FORMATS.PE: [ ('DYNAMIC_LIBRARIES', check_PE_libraries), diff --git a/contrib/devtools/test_utxo_snapshots.sh b/contrib/devtools/test_utxo_snapshots.sh index d4c49bf098..93a4cd1683 100755 --- a/contrib/devtools/test_utxo_snapshots.sh +++ b/contrib/devtools/test_utxo_snapshots.sh @@ -11,13 +11,16 @@ # loaded. We see the background validation chainstate removed after validation # completes. # +# The shellcheck rule SC2086 (quoted variables) disablements are necessary +# since this rule needs to be violated in order to get bitcoind to pick up on +# $EARLY_IBD_FLAGS for the script to work. export LC_ALL=C set -e BASE_HEIGHT=${1:-30000} INCREMENTAL_HEIGHT=20000 -FINAL_HEIGHT=$(($BASE_HEIGHT + $INCREMENTAL_HEIGHT)) +FINAL_HEIGHT=$((BASE_HEIGHT + INCREMENTAL_HEIGHT)) SERVER_DATADIR="$(pwd)/utxodemo-data-server-$BASE_HEIGHT" CLIENT_DATADIR="$(pwd)/utxodemo-data-client-$BASE_HEIGHT" @@ -72,6 +75,9 @@ server_sleep_til_boot() { client_sleep_til_boot() { while ! client_rpc ping >/dev/null 2>&1; do sleep 0.1; done } +server_sleep_til_shutdown() { + while server_rpc ping >/dev/null 2>&1; do sleep 0.1; done +} mkdir -p "$SERVER_DATADIR" "$CLIENT_DATADIR" @@ -104,12 +110,14 @@ read -p "Press [enter] to continue" _ echo echo "-- IBDing the blocks (height=$BASE_HEIGHT) required to the server node..." +# shellcheck disable=SC2086 ./src/bitcoind -logthreadnames=1 $SERVER_PORTS \ -datadir="$SERVER_DATADIR" $EARLY_IBD_FLAGS -stopatheight="$BASE_HEIGHT" >/dev/null echo echo "-- Creating snapshot at ~ height $BASE_HEIGHT ($UTXO_DAT_FILE)..." -sleep 2 +server_sleep_til_shutdown # wait for stopatheight to be hit +# shellcheck disable=SC2086 ./src/bitcoind -logthreadnames=1 $SERVER_PORTS \ -datadir="$SERVER_DATADIR" $EARLY_IBD_FLAGS -connect=0 -listen=0 >/dev/null & SERVER_PID="$!" @@ -124,8 +132,7 @@ RPC_AU=$(jq -r .txoutset_hash < "$DUMP_OUTPUT") RPC_NCHAINTX=$(jq -r .nchaintx < "$DUMP_OUTPUT") RPC_BLOCKHASH=$(jq -r .base_hash < "$DUMP_OUTPUT") -# Wait for server to shutdown... -while server_rpc ping >/dev/null 2>&1; do sleep 0.1; done +server_sleep_til_shutdown echo echo "-- Now: add the following to CMainParams::m_assumeutxo_data" @@ -135,11 +142,13 @@ echo " {${RPC_BASE_HEIGHT}, AssumeutxoHash{uint256S(\"0x${RPC_AU}\")}, ${RPC_N echo echo echo "-- IBDing more blocks to the server node (height=$FINAL_HEIGHT) so there is a diff between snapshot and tip..." +# shellcheck disable=SC2086 ./src/bitcoind $SERVER_PORTS -logthreadnames=1 -datadir="$SERVER_DATADIR" \ $EARLY_IBD_FLAGS -stopatheight="$FINAL_HEIGHT" >/dev/null echo echo "-- Starting the server node to provide blocks to the client node..." +# shellcheck disable=SC2086 ./src/bitcoind $SERVER_PORTS -logthreadnames=1 -debug=net -datadir="$SERVER_DATADIR" \ $EARLY_IBD_FLAGS -connect=0 -listen=1 >/dev/null & SERVER_PID="$!" @@ -163,6 +172,7 @@ read -p "When you're ready for all this, hit [enter]" _ echo echo "-- Starting the client node to get headers from the server, then load the snapshot..." +# shellcheck disable=SC2086 ./src/bitcoind $CLIENT_PORTS $ALL_INDEXES -logthreadnames=1 -datadir="$CLIENT_DATADIR" \ -connect=0 -addnode=127.0.0.1:$SERVER_PORT -debug=net $EARLY_IBD_FLAGS >/dev/null & CLIENT_PID="$!" @@ -186,9 +196,8 @@ echo " Press CTRL+C after you're satisfied to exit the demo" echo read -p "Press [enter] to continue" -while kill -0 "$CLIENT_PID"; do - sleep 1 -done +client_sleep_til_boot +# shellcheck disable=SC2086 ./src/bitcoind $CLIENT_PORTS $ALL_INDEXES -logthreadnames=1 -datadir="$CLIENT_DATADIR" -connect=0 \ -addnode=127.0.0.1:$SERVER_PORT "$EARLY_IBD_FLAGS" >/dev/null & CLIENT_PID="$!" diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index fa6d6d4b8b..ecd45540cf 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -2,7 +2,7 @@ OSX_MIN_VERSION=11.0 OSX_SDK_VERSION=11.0 XCODE_VERSION=12.2 XCODE_BUILD_ID=12B45b -LD64_VERSION=609 +LD64_VERSION=711 OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with-libcxx-headers diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 86df58f9d2..047d1d5aee 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -22,6 +22,7 @@ $(package)_patches += fast_fixed_dtoa_no_optimize.patch $(package)_patches += guix_cross_lib_path.patch $(package)_patches += fix-macos-linker.patch $(package)_patches += memory_resource.patch +$(package)_patches += windows_lto.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=38b942bc7e62794dd072945c8a92bb9dfffed24070aea300327a3bb42f855609 @@ -183,6 +184,9 @@ $(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'" $(package)_config_opts_mingw32 += "QMAKE_LIB = '$($(package)_ar) rc'" $(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_mingw32 += -pch +ifneq ($(LTO),) +$(package)_config_opts_mingw32 += -ltcg +endif $(package)_config_opts_android = -xplatform android-clang $(package)_config_opts_android += -android-sdk $(ANDROID_SDK) @@ -250,6 +254,7 @@ define $(package)_preprocess_cmds patch -p1 -i $($(package)_patch_dir)/duplicate_lcqpafonts.patch && \ patch -p1 -i $($(package)_patch_dir)/fast_fixed_dtoa_no_optimize.patch && \ patch -p1 -i $($(package)_patch_dir)/guix_cross_lib_path.patch && \ + patch -p1 -i $($(package)_patch_dir)/windows_lto.patch && \ mkdir -p qtbase/mkspecs/macx-clang-linux &&\ cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ diff --git a/depends/patches/qt/windows_lto.patch b/depends/patches/qt/windows_lto.patch new file mode 100644 index 0000000000..ea379a60f1 --- /dev/null +++ b/depends/patches/qt/windows_lto.patch @@ -0,0 +1,31 @@ +Qt (for Windows) fails to build under LTO, due to multiple definition issues, i.e + +multiple definition of `QAccessibleLineEdit::~QAccessibleLineEdit()'; + +Possibly related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94156. + +diff --git a/qtbase/src/widgets/accessible/simplewidgets.cpp b/qtbase/src/widgets/accessible/simplewidgets.cpp +index 107fd729fe..0e61878f39 100644 +--- a/qtbase/src/widgets/accessible/simplewidgets.cpp ++++ b/qtbase/src/widgets/accessible/simplewidgets.cpp +@@ -109,6 +109,8 @@ QString qt_accHotKey(const QString &text); + \ingroup accessibility + */ + ++QAccessibleLineEdit::~QAccessibleLineEdit(){}; ++ + /*! + Creates a QAccessibleButton object for \a w. + */ +diff --git a/qtbase/src/widgets/accessible/simplewidgets_p.h b/qtbase/src/widgets/accessible/simplewidgets_p.h +index 73572e3059..658da86143 100644 +--- a/qtbase/src/widgets/accessible/simplewidgets_p.h ++++ b/qtbase/src/widgets/accessible/simplewidgets_p.h +@@ -155,6 +155,7 @@ class QAccessibleLineEdit : public QAccessibleWidget, public QAccessibleTextInte + public: + explicit QAccessibleLineEdit(QWidget *o, const QString &name = QString()); + ++ ~QAccessibleLineEdit(); + QString text(QAccessible::Text t) const override; + void setText(QAccessible::Text t, const QString &text) override; + QAccessible::State state() const override; diff --git a/doc/design/assumeutxo.md b/doc/design/assumeutxo.md index 8068a93f27..75a7b6c866 100644 --- a/doc/design/assumeutxo.md +++ b/doc/design/assumeutxo.md @@ -1,7 +1,7 @@ # assumeutxo Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind -instance with a very similar security model to assumevalid. +instance. The RPC commands `dumptxoutset` and `loadtxoutset` are used to respectively generate and load UTXO snapshots. The utility script diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 3c3f612053..322fa987ae 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -488,7 +488,9 @@ To enable LCOV report generation during test runs: make make cov -# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`. +# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`, +# which covers unit tests, and `./total.coverage/index.html`, which covers +# unit and functional tests. ``` ### Performance profiling with perf diff --git a/doc/release-notes-123.md b/doc/release-notes-123.md new file mode 100644 index 0000000000..fffd211f37 --- /dev/null +++ b/doc/release-notes-123.md @@ -0,0 +1,5 @@ +Tools and Utilities +--- + +- A new `bitcoinconsensus_verify_script_with_spent_outputs` function is available in libconsensus which optionally accepts the spent outputs of the transaction being verified. +- A new `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT` flag is available in libconsensus that will verify scripts with the Taproot spending rules.
\ No newline at end of file diff --git a/doc/release-notes-27460.md b/doc/release-notes-27460.md new file mode 100644 index 0000000000..d663ec0baf --- /dev/null +++ b/doc/release-notes-27460.md @@ -0,0 +1,7 @@ +- A new `importmempool` RPC has been added. It loads a valid `mempool.dat` file and attempts to + add its contents to the mempool. This can be useful to import mempool data from another node + without having to modify the datadir contents and without having to restart the node. (#27460) + - Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over. + - If you want to apply fee deltas, it is recommended to use the `getprioritisedtransactions` and + `prioritisetransaction` RPCs instead of the `apply_fee_delta_priority` option to avoid + double-prioritising any already-prioritised transactions in the mempool. diff --git a/doc/release-notes-27596.md b/doc/release-notes-27596.md index 799b82643f..cbaf4b3a9e 100644 --- a/doc/release-notes-27596.md +++ b/doc/release-notes-27596.md @@ -12,7 +12,7 @@ RPC `loadtxoutset` has been added, which allows loading a UTXO snapshot of the format generated by `dumptxoutset`. Once this snapshot is loaded, its contents will be deserialized into a second chainstate data structure, which is then used to sync to -the network's tip under a security model very much like `assumevalid`. +the network's tip. Meanwhile, the original chainstate will complete the initial block download process in the background, eventually validating up to the block that the snapshot is based upon. diff --git a/doc/release-notes-27609.md b/doc/release-notes-27609.md new file mode 100644 index 0000000000..b8cecbd882 --- /dev/null +++ b/doc/release-notes-27609.md @@ -0,0 +1,14 @@ +- A new RPC, `submitpackage`, has been added. It can be used to submit a list of raw hex + transactions to the mempool to be evaluated as a package using consensus and mempool policy rules. +These policies include package CPFP, allowing a child with high fees to bump a parent below the +mempool minimum feerate (but not minimum relay feerate). + + - Warning: successful submission does not mean the transactions will propagate throughout the + network, as package relay is not supported. + + - Not all features are available. The package is limited to a child with all of its + unconfirmed parents, and no parent may spend the output of another parent. Also, package + RBF is not supported. Refer to doc/policy/packages.md for more details on package policies + and limitations. + + - This RPC is experimental. Its interface may change. diff --git a/doc/release-notes/release-notes-25.1.md b/doc/release-notes/release-notes-25.1.md new file mode 100644 index 0000000000..bfdbee4e76 --- /dev/null +++ b/doc/release-notes/release-notes-25.1.md @@ -0,0 +1,108 @@ +25.1 Release Notes +================== + +Bitcoin Core version 25.1 is now available from: + + <https://bitcoincore.org/bin/bitcoin-core-25.1/> + +This release includes various bug fixes and performance +improvements, as well as updated translations. + +Please report bugs using the issue tracker at GitHub: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes in some cases), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on macOS) +or `bitcoind`/`bitcoin-qt` (on Linux). + +Upgrading directly from a version of Bitcoin Core that has reached its EOL is +possible, but it might take some time if the data directory needs to be migrated. Old +wallet versions of Bitcoin Core are generally supported. + +Compatibility +============== + +Bitcoin Core is supported and extensively tested on operating systems +using the Linux kernel, macOS 10.15+, and Windows 7 and newer. Bitcoin +Core should also work on most other Unix-like systems but is not as +frequently tested on them. It is not recommended to use Bitcoin Core on +unsupported systems. + +Notable changes +=============== + +### P2P + +- #27626 Parallel compact block downloads, take 3 +- #27743 p2p: Unconditionally return when compact block status == READ_STATUS_FAILED + +### Fees + +- #27622 Fee estimation: avoid serving stale fee estimate + +### RPC + +- #27727 rpc: Fix invalid bech32 address handling + +### Rest + +- #27853 rest: fix crash error when calling /deploymentinfo +- #28551 http: bugfix: allow server shutdown in case of remote client disconnection + +### Wallet + +- #28038 wallet: address book migration bug fixes +- #28067 descriptors: do not return top-level only funcs as sub descriptors +- #28125 wallet: bugfix, disallow migration of invalid scripts +- #28542 wallet: Check for uninitialized last processed and conflicting heights in MarkConflicted + +### Build + +- #27724 build: disable boost multi index safe mode in debug mode +- #28097 depends: xcb-proto 1.15.2 +- #28543 build, macos: Fix qt package build with new Xcode 15 linker +- #28571 depends: fix unusable memory_resource in macos qt build + +### Gui + +- gui#751 macOS, do not process actions during shutdown + +### Miscellaneous + +- #28452 Do not use std::vector = {} to release memory + +### CI + +- #27777 ci: Prune dangling images on RESTART_CI_DOCKER_BEFORE_RUN +- #27834 ci: Nuke Android APK task, Use credits for tsan +- #27844 ci: Use podman stop over podman kill +- #27886 ci: Switch to amd64 container in "ARM" task + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Abubakar Sadiq Ismail +- Andrew Chow +- Bruno Garcia +- Gregory Sanders +- Hennadii Stepanov +- MacroFake +- Matias Furszyfer +- Michael Ford +- Pieter Wuille +- stickies-v +- Will Clark + +As well as to everyone that helped with translations on +[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
\ No newline at end of file diff --git a/doc/release-notes/release-notes-27511.md b/doc/release-notes/release-notes-27511.md new file mode 100644 index 0000000000..b5c0847e31 --- /dev/null +++ b/doc/release-notes/release-notes-27511.md @@ -0,0 +1,6 @@ +New RPCs +-------- + +- A new RPC `getaddrmaninfo` has been added to view the distribution of addresses in the new and tried table of the + node's address manager across different networks(ipv4, ipv6, onion, i2p, cjdns). The RPC returns count of addresses + in new and tried table as well as their sum for all networks. (#27511) diff --git a/doc/release-process.md b/doc/release-process.md index 468efeb7e1..c70b0194ab 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -28,7 +28,7 @@ Release Process #### Before branch-off -* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example. +* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/27488) for an example. * Update the following variables in [`src/kernel/chainparams.cpp`](/src/kernel/chainparams.cpp) for mainnet, testnet, and signet: - `m_assumed_blockchain_size` and `m_assumed_chain_state_size` with the current size plus some overhead (see [this](#how-to-calculate-assumed-blockchain-and-chain-state-size) for information on how to calculate them). @@ -36,7 +36,7 @@ Release Process that causes rejection of blocks in the past history. - `chainTxData` with statistics about the transaction count and rate. Use the output of the `getchaintxstats` RPC with an `nBlocks` of 4096 (28 days) and a `bestblockhash` of RPC `getbestblockhash`; see - [this pull request](https://github.com/bitcoin/bitcoin/pull/20263) for an example. Reviewers can verify the results by running + [this pull request](https://github.com/bitcoin/bitcoin/pull/28591) for an example. Reviewers can verify the results by running `getchaintxstats <window_block_count> <window_final_block_hash>` with the `window_block_count` and `window_final_block_hash` from your output. - `defaultAssumeValid` with the output of RPC `getblockhash` using the `height` of `window_final_block_height` above (and update the block height comment with that height), taking into account the following: @@ -45,7 +45,7 @@ Release Process - `nMinimumChainWork` with the "chainwork" value of RPC `getblockheader` using the same height as that selected for the previous step. * Consider updating the headers synchronization tuning parameters to account for the chainparams updates. The optimal values change very slowly, so this isn't strictly necessary every release, but doing so doesn't hurt. - - Update configuration variables in [`contrib/devtools/headerssync-params.py`](contrib/devtools/headerssync-params.py): + - Update configuration variables in [`contrib/devtools/headerssync-params.py`](/contrib/devtools/headerssync-params.py): - Set `TIME` to the software's expected supported lifetime -- after this time, its ability to defend against a high bandwidth timewarp attacker will begin to degrade. - Set `MINCHAINWORK_HEADERS` to the height used for the `nMinimumChainWork` calculation above. - Check that the other variables still look reasonable. diff --git a/doc/shared-libraries.md b/doc/shared-libraries.md index 147e223711..07aee46f0b 100644 --- a/doc/shared-libraries.md +++ b/doc/shared-libraries.md @@ -11,13 +11,14 @@ The interface is defined in the C header `bitcoinconsensus.h` located in `src/sc #### Version -`bitcoinconsensus_version` returns an `unsigned int` with the API version *(currently `1`)*. +`bitcoinconsensus_version` returns an `unsigned int` with the API version *(currently `2`)*. #### Script Validation -`bitcoinconsensus_verify_script` returns an `int` with the status of the verification. It will be `1` if the input script correctly spends the previous output `scriptPubKey`. +`bitcoinconsensus_verify_script`, `bitcoinconsensus_verify_script_with_amount` and `bitcoinconsensus_verify_script_with_spent_outputs` return an `int` with the status of the verification. It will be `1` if the input script correctly spends the previous output `scriptPubKey`. ##### Parameters +###### bitcoinconsensus_verify_script - `const unsigned char *scriptPubKey` - The previous output script that encumbers spending. - `unsigned int scriptPubKeyLen` - The number of bytes for the `scriptPubKey`. - `const unsigned char *txTo` - The transaction with the input that is spending the previous output. @@ -26,6 +27,28 @@ The interface is defined in the C header `bitcoinconsensus.h` located in `src/sc - `unsigned int flags` - The script validation flags *(see below)*. - `bitcoinconsensus_error* err` - Will have the error/success code for the operation *(see below)*. +###### bitcoinconsensus_verify_script_with_amount +- `const unsigned char *scriptPubKey` - The previous output script that encumbers spending. +- `unsigned int scriptPubKeyLen` - The number of bytes for the `scriptPubKey`. +- `int64_t amount` - The amount spent in the input +- `const unsigned char *txTo` - The transaction with the input that is spending the previous output. +- `unsigned int txToLen` - The number of bytes for the `txTo`. +- `unsigned int nIn` - The index of the input in `txTo` that spends the `scriptPubKey`. +- `unsigned int flags` - The script validation flags *(see below)*. +- `bitcoinconsensus_error* err` - Will have the error/success code for the operation *(see below)*. + +###### bitcoinconsensus_verify_script_with_spent_outputs +- `const unsigned char *scriptPubKey` - The previous output script that encumbers spending. +- `unsigned int scriptPubKeyLen` - The number of bytes for the `scriptPubKey`. +- `int64_t amount` - The amount spent in the input +- `const unsigned char *txTo` - The transaction with the input that is spending the previous output. +- `unsigned int txToLen` - The number of bytes for the `txTo`. +- `UTXO *spentOutputs` - Previous outputs spent in the transaction. `UTXO` is a struct composed by `const unsigned char *scriptPubKey`, `unsigned int scriptPubKeySize` (the number of bytes for the `scriptPubKey`) and `unsigned int value`. +- `unsigned int spentOutputsLen` - The number of bytes for the `spentOutputs`. +- `unsigned int nIn` - The index of the input in `txTo` that spends the `scriptPubKey`. +- `unsigned int flags` - The script validation flags *(see below)*. +- `bitcoinconsensus_error* err` - Will have the error/success code for the operation *(see below)*. + ##### Script Flags - `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE` - `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH` - Evaluate P2SH ([BIP16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki)) subscripts @@ -34,6 +57,7 @@ The interface is defined in the C header `bitcoinconsensus.h` located in `src/sc - `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY` - Enable CHECKLOCKTIMEVERIFY ([BIP65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)) - `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY` - Enable CHECKSEQUENCEVERIFY ([BIP112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki)) - `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS` - Enable WITNESS ([BIP141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)) +- `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT` - Enable TAPROOT ([BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki), [BIP342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki)) ##### Errors - `bitcoinconsensus_ERR_OK` - No errors with input parameters *(see the return value of `bitcoinconsensus_verify_script` for the verification status)* @@ -42,9 +66,12 @@ The interface is defined in the C header `bitcoinconsensus.h` located in `src/sc - `bitcoinconsensus_ERR_DESERIALIZE` - An error deserializing `txTo` - `bitcoinconsensus_ERR_AMOUNT_REQUIRED` - Input amount is required if WITNESS is used - `bitcoinconsensus_ERR_INVALID_FLAGS` - Script verification `flags` are invalid (i.e. not part of the libconsensus interface) +- `bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED` - Spent outputs are required if TAPROOT is used +- `bitcoinconsensus_ERR_SPENT_OUTPUTS_MISMATCH` - Spent outputs size doesn't match tx inputs size ### Example Implementations - [NBitcoin](https://github.com/MetacoSA/NBitcoin/blob/5e1055cd7c4186dee4227c344af8892aea54faec/NBitcoin/Script.cs#L979-#L1031) (.NET Bindings) - [node-libbitcoinconsensus](https://github.com/bitpay/node-libbitcoinconsensus) (Node.js Bindings) - [java-libbitcoinconsensus](https://github.com/dexX7/java-libbitcoinconsensus) (Java Bindings) - [bitcoinconsensus-php](https://github.com/Bit-Wasp/bitcoinconsensus-php) (PHP Bindings) +- [rust-bitcoinconsensus](https://github.com/rust-bitcoin/rust-bitcoinconsensus) (Rust Bindings)
\ No newline at end of file diff --git a/src/.bear-tidy-config b/src/.bear-tidy-config index 9b6b9d2cf7..0ab30a8d9d 100644 --- a/src/.bear-tidy-config +++ b/src/.bear-tidy-config @@ -8,6 +8,8 @@ "src/crypto/ctaes", "src/leveldb", "src/minisketch", + "src/bench/nanobench.cpp", + "src/bench/nanobench.h", "src/secp256k1" ] }, diff --git a/src/.clang-tidy b/src/.clang-tidy index 4deb5a85a5..0318bd01ce 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -5,9 +5,11 @@ bugprone-argument-comment, bugprone-use-after-move, misc-unused-using-decls, modernize-use-default-member-init, +modernize-use-emplace, modernize-use-noexcept, modernize-use-nullptr, performance-*, +-performance-avoid-endl, -performance-inefficient-string-concatenation, -performance-no-int-to-ptr, -performance-noexcept-move-constructor, diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 7852d1a2fa..c235c3c4da 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -358,11 +358,14 @@ SECONDARY: $(QT_QM) $(srcdir)/qt/bitcoinstrings.cpp: FORCE @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" - $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) + $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py \ + $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) \ + $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) \ + $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) # The resulted bitcoin_en.xlf source file should follow Transifex requirements. # See: https://docs.transifex.com/formats/xliff#how-to-distinguish-between-a-source-file-and-a-translation-file -translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) +translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/bitcoin_en.ts @test -n $(LCONVERT) || echo "lconvert is required for updating translations" diff --git a/src/addrman.cpp b/src/addrman.cpp index 6ce9c81c63..b001365ab3 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -854,7 +854,7 @@ std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries_(bool /*multiplicity_in=*/from_tried ? 1 : info.nRefCount, bucket, position); - infos.push_back(std::make_pair(info, location)); + infos.emplace_back(info, location); } } } diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 0737144b84..ba8ec16119 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -28,7 +28,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(CTxIn{MineBlock(test_setup->m_node, P2WSH_OP_TRUE)}); + tx.vin.emplace_back(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/disconnected_transactions.cpp b/src/bench/disconnected_transactions.cpp index 0a7344b248..264c0aa1e8 100644 --- a/src/bench/disconnected_transactions.cpp +++ b/src/bench/disconnected_transactions.cpp @@ -36,8 +36,8 @@ static BlockTxns CreateRandomTransactions(size_t num_txns) CScript spk = CScript() << OP_TRUE; for (uint32_t i = 0; i < num_txns; ++i) { CMutableTransaction tx; - tx.vin.emplace_back(CTxIn{COutPoint{prevout_hash, 0}}); - tx.vout.emplace_back(CTxOut{CENT, spk}); + tx.vin.emplace_back(COutPoint{prevout_hash, 0}); + tx.vout.emplace_back(CENT, spk); auto ptx{MakeTransactionRef(tx)}; txns.emplace_back(ptx); prevout_hash = ptx->GetHash(); diff --git a/src/bench/wallet_loading.cpp b/src/bench/wallet_loading.cpp index 5453238728..b17c7fe05a 100644 --- a/src/bench/wallet_loading.cpp +++ b/src/bench/wallet_loading.cpp @@ -20,8 +20,8 @@ namespace wallet{ static void AddTx(CWallet& wallet) { CMutableTransaction mtx; - mtx.vout.push_back({COIN, GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, "")))}); - mtx.vin.push_back(CTxIn()); + mtx.vout.emplace_back(COIN, GetScriptForDestination(*Assert(wallet.GetNewDestination(OutputType::BECH32, "")))); + mtx.vin.emplace_back(); wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}); } diff --git a/src/chain.h b/src/chain.h index 78b06719f4..4bf2001f74 100644 --- a/src/chain.h +++ b/src/chain.h @@ -280,10 +280,8 @@ public: * Note that this will be true for the snapshot base block, if one is loaded (and * all subsequent assumed-valid blocks) since its nChainTx value will have been set * manually based on the related AssumeutxoData entry. - * - * TODO: potentially change the name of this based on the fact above. */ - bool HaveTxsDownloaded() const { return nChainTx != 0; } + bool HaveNumChainTxs() const { return nChainTx != 0; } NodeSeconds Time() const { diff --git a/src/coins.cpp b/src/coins.cpp index 0fe642e46b..b44d920ee1 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -353,11 +353,13 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid) return coinEmpty; } -bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const { +template <typename Func> +static bool ExecuteBackedWrapper(Func func, const std::vector<std::function<void()>>& err_callbacks) +{ try { - return CCoinsViewBacked::GetCoin(outpoint, coin); + return func(); } catch(const std::runtime_error& e) { - for (const auto& f : m_err_callbacks) { + for (const auto& f : err_callbacks) { f(); } LogPrintf("Error reading from database: %s\n", e.what()); @@ -368,3 +370,11 @@ bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) cons std::abort(); } } + +bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const { + return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::GetCoin(outpoint, coin); }, m_err_callbacks); +} + +bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint &outpoint) const { + return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks); +} diff --git a/src/coins.h b/src/coins.h index 039a07054d..a6cbb03133 100644 --- a/src/coins.h +++ b/src/coins.h @@ -383,6 +383,7 @@ public: } bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; + bool HaveCoin(const COutPoint &outpoint) const override; private: /** A list of callbacks to execute upon leveldb read error. */ diff --git a/src/common/args.cpp b/src/common/args.cpp index 643838399f..ca04175696 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -216,7 +216,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin m_command.push_back(key); while (++i < argc) { // The remaining args are command args - m_command.push_back(argv[i]); + m_command.emplace_back(argv[i]); } break; } diff --git a/src/external_signer.cpp b/src/external_signer.cpp index 6b1e1f0241..102c58b56a 100644 --- a/src/external_signer.cpp +++ b/src/external_signer.cpp @@ -54,7 +54,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS if (model_field.isStr() && model_field.getValStr() != "") { name += model_field.getValStr(); } - signers.push_back(ExternalSigner(command, chain, fingerprintStr, name)); + signers.emplace_back(command, chain, fingerprintStr, name); } return true; } diff --git a/src/headerssync.cpp b/src/headerssync.cpp index 1b5d7305e8..234fc8da60 100644 --- a/src/headerssync.cpp +++ b/src/headerssync.cpp @@ -13,11 +13,11 @@ // contrib/devtools/headerssync-params.py. //! Store one header commitment per HEADER_COMMITMENT_PERIOD blocks. -constexpr size_t HEADER_COMMITMENT_PERIOD{600}; +constexpr size_t HEADER_COMMITMENT_PERIOD{606}; //! Only feed headers to validation once this many headers on top have been //! received and validated against commitments. -constexpr size_t REDOWNLOAD_BUFFER_SIZE{14308}; // 14308/600 = ~23.8 commitments +constexpr size_t REDOWNLOAD_BUFFER_SIZE{14441}; // 14441/606 = ~23.8 commitments // Our memory analysis assumes 48 bytes for a CompressedHeader (so we should // re-calculate parameters if we compress further) @@ -271,7 +271,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he } // Store this header for later processing. - m_redownloaded_headers.push_back(header); + m_redownloaded_headers.emplace_back(header); m_redownload_buffer_last_height = next_height; m_redownload_buffer_last_hash = header.GetHash(); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 069511563c..647e36adb3 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -221,8 +221,8 @@ static bool ClientAllowed(const CNetAddr& netaddr) static bool InitHTTPAllowList() { rpc_allow_subnets.clear(); - rpc_allow_subnets.push_back(CSubNet{LookupHost("127.0.0.1", false).value(), 8}); // always allow IPv4 local subnet - rpc_allow_subnets.push_back(CSubNet{LookupHost("::1", false).value()}); // always allow IPv6 localhost + rpc_allow_subnets.emplace_back(LookupHost("127.0.0.1", false).value(), 8); // always allow IPv4 local subnet + rpc_allow_subnets.emplace_back(LookupHost("::1", false).value()); // always allow IPv6 localhost for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) { CSubNet subnet; LookupSubNet(strAllow, subnet); @@ -364,8 +364,8 @@ static bool HTTPBindAddresses(struct evhttp* http) // Determine what addresses to bind to if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs - endpoints.push_back(std::make_pair("::1", http_port)); - endpoints.push_back(std::make_pair("127.0.0.1", http_port)); + endpoints.emplace_back("::1", http_port); + endpoints.emplace_back("127.0.0.1", http_port); if (gArgs.IsArgSet("-rpcallowip")) { LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n"); } @@ -377,7 +377,7 @@ static bool HTTPBindAddresses(struct evhttp* http) uint16_t port{http_port}; std::string host; SplitHostPort(strRPCBind, port, host); - endpoints.push_back(std::make_pair(host, port)); + endpoints.emplace_back(host, port); } } @@ -682,7 +682,7 @@ CService HTTPRequest::GetPeer() const evhttp_connection_get_peer(con, (char**)&address, &port); #endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR - peer = LookupNumeric(address, port); + peer = MaybeFlipIPv6toCJDNS(LookupNumeric(address, port)); } return peer; } @@ -746,7 +746,7 @@ void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPR { LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); LOCK(g_httppathhandlers_mutex); - pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler)); + pathHandlers.emplace_back(prefix, exactMatch, handler); } void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) diff --git a/src/init.cpp b/src/init.cpp index a0b4425898..42331d37e8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -462,8 +462,8 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk. This will also rebuild active optional indexes.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-reindex", "If enabled, wipe chain state and block index, and rebuild them from blk*.dat files on disk. Also wipe and rebuild other optional indexes that are active. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-reindex-chainstate", "If enabled, wipe chain state, and rebuild it from blk*.dat files on disk. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #if HAVE_SYSTEM argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -1138,6 +1138,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) RandAddPeriodic(); }, std::chrono::minutes{1}); + // Check disk space every 5 minutes to avoid db corruption. + node.scheduler->scheduleEvery([&args]{ + constexpr uint64_t min_disk_space = 50 << 20; // 50 MB + if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) { + LogPrintf("Shutting down due to lack of disk space!\n"); + StartShutdown(); + } + }, std::chrono::minutes{5}); + GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler); // Create client interfaces for wallets that are supposed to be loaded @@ -1305,30 +1314,24 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } if (args.IsArgSet("-onlynet")) { - std::set<enum Network> nets; + g_reachable_nets.RemoveAll(); for (const std::string& snet : args.GetArgs("-onlynet")) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); - nets.insert(net); - } - for (int n = 0; n < NET_MAX; n++) { - enum Network net = (enum Network)n; - assert(IsReachable(net)); - if (!nets.count(net)) - SetReachable(net, false); + g_reachable_nets.Add(net); } } if (!args.IsArgSet("-cjdnsreachable")) { - if (args.IsArgSet("-onlynet") && IsReachable(NET_CJDNS)) { + if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_CJDNS)) { return InitError( _("Outbound connections restricted to CJDNS (-onlynet=cjdns) but " "-cjdnsreachable is not provided")); } - SetReachable(NET_CJDNS, false); + g_reachable_nets.Remove(NET_CJDNS); } - // Now IsReachable(NET_CJDNS) is true if: + // Now g_reachable_nets.Contains(NET_CJDNS) is true if: // 1. -cjdnsreachable is given and // 2.1. -onlynet is not given or // 2.2. -onlynet=cjdns is given @@ -1336,7 +1339,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Requesting DNS seeds entails connecting to IPv4/IPv6, which -onlynet options may prohibit: // If -dnsseed=1 is explicitly specified, abort. If it's left unspecified by the user, we skip // the DNS seeds by adjusting -dnsseed in InitParameterInteraction. - if (args.GetBoolArg("-dnsseed") == true && !IsReachable(NET_IPV4) && !IsReachable(NET_IPV6)) { + if (args.GetBoolArg("-dnsseed") == true && !g_reachable_nets.Contains(NET_IPV4) && !g_reachable_nets.Contains(NET_IPV6)) { return InitError(strprintf(_("Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6"))); }; @@ -1366,7 +1369,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) onion_proxy = addrProxy; } - const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && IsReachable(NET_ONION)}; + const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_ONION)}; // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses // -noonion (or -onion=0) disables connecting to .onion entirely @@ -1401,7 +1404,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) "reaching the Tor network is not provided: none of -proxy, -onion or " "-listenonion is given")); } - SetReachable(NET_ONION, false); + g_reachable_nets.Remove(NET_ONION); } for (const std::string& strAddr : args.GetArgs("-externalip")) { @@ -1876,12 +1879,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } SetProxy(NET_I2P, Proxy{addr.value()}); } else { - if (args.IsArgSet("-onlynet") && IsReachable(NET_I2P)) { + if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_I2P)) { return InitError( _("Outbound connections restricted to i2p (-onlynet=i2p) but " "-i2psam is not provided")); } - SetReachable(NET_I2P, false); + g_reachable_nets.Remove(NET_I2P); } connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING); diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 5e893a3f58..3ac8756e41 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -104,8 +104,8 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000044a50fe819c39ad624021859"); - consensus.defaultAssumeValid = uint256S("0x000000000000000000035c3f0d31e71a5ee24c5aaf3354689f65bd7b07dee632"); // 784000 + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000052b2559353df4117b7348b64"); + consensus.defaultAssumeValid = uint256S("0x00000000000000000001a0a448d6cf2546b06801389cc030b2b18c6491266815"); // 804000 /** * The message start string is designed to be unlikely to occur in normal data. @@ -118,8 +118,8 @@ public: pchMessageStart[3] = 0xd9; nDefaultPort = 8333; nPruneAfterHeight = 100000; - m_assumed_blockchain_size = 540; - m_assumed_chain_state_size = 7; + m_assumed_blockchain_size = 590; + m_assumed_chain_state_size = 9; genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); @@ -177,10 +177,10 @@ public: }; chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 000000000000000000035c3f0d31e71a5ee24c5aaf3354689f65bd7b07dee632 - .nTime = 1680665245, - .nTxCount = 820876044, - .dTxRate = 3.672283614033389, + // Data from RPC: getchaintxstats 4096 00000000000000000001a0a448d6cf2546b06801389cc030b2b18c6491266815 + .nTime = 1692502494, + .nTxCount = 881818374, + .dTxRate = 5.521964628130412, }; } }; @@ -222,8 +222,8 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000977edb0244170858d07"); - consensus.defaultAssumeValid = uint256S("0x0000000000000021bc50a89cde4870d4a81ffe0153b3c8de77b435a2fd3f6761"); // 2429000 + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000b6a51f415a67c0da307"); + consensus.defaultAssumeValid = uint256S("0x0000000000000093bcb68c03a9a168ae252572d348a2eaeba2cdf9231d73206f"); // 2500000 pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; @@ -276,10 +276,10 @@ public: }; chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 0000000000000021bc50a89cde4870d4a81ffe0153b3c8de77b435a2fd3f6761 - .nTime = 1681542696, - .nTxCount = 65345929, - .dTxRate = 0.09855282814711661, + // Data from RPC: getchaintxstats 4096 0000000000000093bcb68c03a9a168ae252572d348a2eaeba2cdf9231d73206f + .nTime = 1694733634, + .nTxCount = 66484552, + .dTxRate = 0.1804908356632494, }; } }; @@ -302,15 +302,15 @@ public: vSeeds.emplace_back("178.128.221.177"); vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001899d8142b0"); - consensus.defaultAssumeValid = uint256S("0x0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec"); // 138000 + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001ad46be4862"); + consensus.defaultAssumeValid = uint256S("0x0000013d778ba3f914530f11f6b69869c9fab54acff85acd7b8201d111f19b7f"); // 150000 m_assumed_blockchain_size = 1; m_assumed_chain_state_size = 0; chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec - .nTime = 1681127428, - .nTxCount = 2226359, - .dTxRate = 0.006424463050600656, + // Data from RPC: getchaintxstats 4096 0000013d778ba3f914530f11f6b69869c9fab54acff85acd7b8201d111f19b7f + .nTime = 1688366339, + .nTxCount = 2262750, + .dTxRate = 0.003414084572046456, }; } else { bin = *options.challenge; @@ -495,7 +495,7 @@ public: { .height = 110, .hash_serialized = AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, - .nChainTx = 110, + .nChainTx = 111, .blockhash = uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c") }, { diff --git a/src/net.cpp b/src/net.cpp index 9048dd438e..09a3d8617a 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -115,7 +115,6 @@ bool fDiscover = true; bool fListen = true; GlobalMutex g_maplocalhost_mutex; std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex); -static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {}; std::string strSubVersion; size_t CSerializedNetMsg::GetMemoryUsage() const noexcept @@ -232,7 +231,7 @@ static int GetnScore(const CService& addr) { CService addrLocal = pnode->GetAddrLocal(); return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() && - IsReachable(addrLocal.GetNetwork()); + g_reachable_nets.Contains(addrLocal); } std::optional<CService> GetLocalAddrForPeer(CNode& node) @@ -270,22 +269,6 @@ std::optional<CService> GetLocalAddrForPeer(CNode& node) return std::nullopt; } -/** - * If an IPv6 address belongs to the address range used by the CJDNS network and - * the CJDNS network is reachable (-cjdnsreachable config is set), then change - * the type from NET_IPV6 to NET_CJDNS. - * @param[in] service Address to potentially convert. - * @return a copy of `service` either unmodified or changed to CJDNS. - */ -CService MaybeFlipIPv6toCJDNS(const CService& service) -{ - CService ret{service}; - if (ret.IsIPv6() && ret.HasCJDNSPrefix() && IsReachable(NET_CJDNS)) { - ret.m_net = NET_CJDNS; - } - return ret; -} - // learn a new local address bool AddLocal(const CService& addr_, int nScore) { @@ -297,7 +280,7 @@ bool AddLocal(const CService& addr_, int nScore) if (!fDiscover && nScore < LOCAL_MANUAL) return false; - if (!IsReachable(addr)) + if (!g_reachable_nets.Contains(addr)) return false; LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore); @@ -327,25 +310,6 @@ void RemoveLocal(const CService& addr) mapLocalHost.erase(addr); } -void SetReachable(enum Network net, bool reachable) -{ - if (net == NET_UNROUTABLE || net == NET_INTERNAL) - return; - LOCK(g_maplocalhost_mutex); - vfLimited[net] = !reachable; -} - -bool IsReachable(enum Network net) -{ - LOCK(g_maplocalhost_mutex); - return !vfLimited[net]; -} - -bool IsReachable(const CNetAddr &addr) -{ - return IsReachable(addr.GetNetwork()); -} - /** vote for a local address */ bool SeenLocal(const CService& addr) { @@ -375,17 +339,6 @@ CNode* CConnman::FindNode(const CNetAddr& ip) return nullptr; } -CNode* CConnman::FindNode(const CSubNet& subNet) -{ - LOCK(m_nodes_mutex); - for (CNode* pnode : m_nodes) { - if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) { - return pnode; - } - } - return nullptr; -} - CNode* CConnman::FindNode(const std::string& addrName) { LOCK(m_nodes_mutex); @@ -2433,7 +2386,7 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const for (int n = 0; n < NET_MAX; n++) { enum Network net = (enum Network)n; if (net == NET_UNROUTABLE || net == NET_INTERNAL) continue; - if (IsReachable(net) && addrman.Size(net, std::nullopt) == 0) { + if (g_reachable_nets.Contains(net) && addrman.Size(net, std::nullopt) == 0) { networks.insert(net); } } @@ -2453,7 +2406,7 @@ bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network) LOCK(m_nodes_mutex); for (const auto net : nets) { - if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) { + if (g_reachable_nets.Contains(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) { network = net; return true; } @@ -2683,7 +2636,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) if (anchor && !m_anchors.empty()) { const CAddress addr = m_anchors.back(); m_anchors.pop_back(); - if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) || + if (!addr.IsValid() || IsLocal(addr) || !g_reachable_nets.Contains(addr) || !HasAllDesirableServiceFlags(addr.nServices) || outbound_ipv46_peer_netgroups.count(m_netgroupman.GetGroup(addr))) continue; addrConnect = addr; @@ -2738,8 +2691,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) break; } - if (!IsReachable(addr)) + if (!g_reachable_nets.Contains(addr)) { continue; + } // only consider very recently tried nodes after 30 failed attempts if (current_time - addr_last_try < 10min && nTries < 30) { @@ -160,24 +160,12 @@ enum /** Returns a local address that we should advertise to this peer. */ std::optional<CService> GetLocalAddrForPeer(CNode& node); -/** - * Mark a network as reachable or unreachable (no automatic connects to it) - * @note Networks are reachable by default - */ -void SetReachable(enum Network net, bool reachable); -/** @returns true if the network is reachable, false otherwise */ -bool IsReachable(enum Network net); -/** @returns true if the address is in a reachable network, false otherwise */ -bool IsReachable(const CNetAddr& addr); - bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); void RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); CService GetLocalAddress(const CNode& peer); -CService MaybeFlipIPv6toCJDNS(const CService& service); - extern bool fDiscover; extern bool fListen; @@ -1341,7 +1329,6 @@ private: uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; CNode* FindNode(const CNetAddr& ip); - CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& addr); diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp index 23226bbb4f..cf6b58e08d 100644 --- a/src/net_permissions.cpp +++ b/src/net_permissions.cpp @@ -71,13 +71,13 @@ bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags) { std::vector<std::string> strings; - if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.push_back("bloomfilter"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.push_back("noban"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.push_back("forcerelay"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.push_back("relay"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.push_back("mempool"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.push_back("download"); - if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.push_back("addr"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.emplace_back("bloomfilter"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.emplace_back("noban"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.emplace_back("forcerelay"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.emplace_back("relay"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.emplace_back("mempool"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.emplace_back("download"); + if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.emplace_back("addr"); return strings; } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 06086d6804..3bfb606037 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -696,6 +696,10 @@ private: /** Send `feefilter` message. */ void MaybeSendFeefilter(CNode& node, Peer& peer, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex); + FastRandomContext m_rng GUARDED_BY(NetEventsInterface::g_msgproc_mutex); + + FeeFilterRounder m_fee_filter_rounder GUARDED_BY(NetEventsInterface::g_msgproc_mutex); + const CChainParams& m_chainparams; CConnman& m_connman; AddrMan& m_addrman; @@ -1053,7 +1057,7 @@ private: bool SetupAddressRelay(const CNode& node, Peer& peer) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex); void AddAddressKnown(Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex); - void PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex); + void PushAddress(Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex); }; const CNodeState* PeerManagerImpl::State(NodeId pnode) const EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -1085,7 +1089,7 @@ void PeerManagerImpl::AddAddressKnown(Peer& peer, const CAddress& addr) peer.m_addr_known->insert(addr.GetKey()); } -void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand) +void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr) { // Known checking here is only to save space from duplicates. // Before sending, we'll filter it again for known addresses that were @@ -1093,7 +1097,7 @@ void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomCo assert(peer.m_addr_known); if (addr.IsValid() && !peer.m_addr_known->contains(addr.GetKey()) && IsAddrCompatible(peer, addr)) { if (peer.m_addrs_to_send.size() >= MAX_ADDR_TO_SEND) { - peer.m_addrs_to_send[insecure_rand.randrange(peer.m_addrs_to_send.size())] = addr; + peer.m_addrs_to_send[m_rng.randrange(peer.m_addrs_to_send.size())] = addr; } else { peer.m_addrs_to_send.push_back(addr); } @@ -1444,7 +1448,7 @@ void PeerManagerImpl::FindNextBlocks(std::vector<const CBlockIndex*>& vBlocks, c return; } if (pindex->nStatus & BLOCK_HAVE_DATA || (activeChain && activeChain->Contains(pindex))) { - if (activeChain && pindex->HaveTxsDownloaded()) + if (activeChain && pindex->HaveNumChainTxs()) state->pindexLastCommonBlock = pindex; } else if (!IsBlockRequested(pindex->GetBlockHash())) { // The block is not already downloaded, and not yet in flight. @@ -1877,7 +1881,9 @@ std::unique_ptr<PeerManager> PeerManager::make(CConnman& connman, AddrMan& addrm PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, CTxMemPool& pool, Options opts) - : m_chainparams(chainman.GetParams()), + : m_rng{opts.deterministic_rng}, + m_fee_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}, m_rng}, + m_chainparams(chainman.GetParams()), m_connman(connman), m_addrman(addrman), m_banman(banman), @@ -1931,6 +1937,8 @@ void PeerManagerImpl::BlockConnected( } } + // The following task can be skipped since we don't maintain a mempool for + // the ibd/background chainstate. if (role == ChainstateRole::BACKGROUND) { return; } @@ -2183,7 +2191,6 @@ void PeerManagerImpl::RelayAddress(NodeId originator, const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY) .Write(hash_addr) .Write(time_addr)}; - FastRandomContext insecure_rand; // Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers. unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1; @@ -2207,7 +2214,7 @@ void PeerManagerImpl::RelayAddress(NodeId originator, }; for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) { - PushAddress(*best[i].second, addr, insecure_rand); + PushAddress(*best[i].second, addr); } } @@ -2226,7 +2233,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& LOCK(cs_main); const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { - if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && + if (pindex->HaveNumChainTxs() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && pindex->IsValid(BLOCK_VALID_TREE)) { // If we have the block and all of its parents, but have not yet validated it, // we might be in the middle of connecting it (ie in the unlock of cs_main @@ -2352,7 +2359,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& // and we want it right after the last block so they don't // wait for other stuff first. std::vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash())); + vInv.emplace_back(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()); m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); peer.m_continuation_block.SetNull(); } @@ -2754,7 +2761,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c break; } uint32_t nFetchFlags = GetFetchFlags(peer); - vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); + vGetData.emplace_back(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()); BlockRequested(pfrom.GetId(), *pindex); LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom.GetId()); @@ -3292,7 +3299,7 @@ void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const Bl if (first_in_flight) { // Might have collided, fall back to getdata now :( std::vector<CInv> invs; - invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(peer), block_transactions.blockhash)); + invs.emplace_back(MSG_BLOCK | GetFetchFlags(peer), block_transactions.blockhash); m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); @@ -3793,7 +3800,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr); uint64_t num_proc = 0; uint64_t num_rate_limit = 0; - Shuffle(vAddr.begin(), vAddr.end(), FastRandomContext()); + Shuffle(vAddr.begin(), vAddr.end(), m_rng); for (CAddress& addr : vAddr) { if (interruptMsgProc) @@ -3823,14 +3830,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, continue; } ++num_proc; - bool fReachable = IsReachable(addr); + const bool reachable{g_reachable_nets.Contains(addr)}; if (addr.nTime > current_a_time - 10min && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes - RelayAddress(pfrom.GetId(), addr, fReachable); + RelayAddress(pfrom.GetId(), addr, reachable); } // Do not store addresses outside our network - if (fReachable) + if (reachable) { vAddrOk.push_back(addr); + } } peer->m_addr_processed += num_proc; peer->m_addr_rate_limited += num_rate_limit; @@ -4142,7 +4150,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId()); for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex)) { - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.emplace_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } @@ -4735,9 +4743,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } else { vAddr = m_connman.GetAddresses(pfrom, MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); } - FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) { - PushAddress(*peer, addr, insecure_rand); + PushAddress(*peer, addr); } return; } @@ -5339,8 +5346,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros } if (std::optional<CService> local_service = GetLocalAddrForPeer(node)) { CAddress local_addr{*local_service, peer.m_our_services, Now<NodeSeconds>()}; - FastRandomContext insecure_rand; - PushAddress(peer, local_addr, insecure_rand); + PushAddress(peer, local_addr); } peer.m_next_local_addr_send = GetExponentialRand(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); } @@ -5419,14 +5425,13 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi if (pto.IsBlockOnlyConn()) return; CAmount currentFilter = m_mempool.GetMinFee().GetFeePerK(); - static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}}; if (m_chainman.IsInitialBlockDownload()) { // Received tx-inv messages are discarded when the active // chainstate is in IBD, so tell the peer to not send them. currentFilter = MAX_MONEY; } else { - static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)}; + static const CAmount MAX_FILTER{m_fee_filter_rounder.round(MAX_MONEY)}; if (peer.m_fee_filter_sent == MAX_FILTER) { // Send the current filter if we sent MAX_FILTER previously // and made it out of IBD. @@ -5434,7 +5439,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi } } if (current_time > peer.m_next_send_feefilter) { - CAmount filterToSend = g_filter_rounder.round(currentFilter); + CAmount filterToSend = m_fee_filter_rounder.round(currentFilter); // We always have a fee filter of at least the min relay fee filterToSend = std::max(filterToSend, m_mempool.m_min_relay_feerate.GetFeePerK()); if (filterToSend != peer.m_fee_filter_sent) { @@ -5645,14 +5650,14 @@ bool PeerManagerImpl::SendMessages(CNode* pto) pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.emplace_back(pindex->GetBlockHeader()); } else if (PeerHasHeader(&state, pindex)) { continue; // keep looking for the first new block } else if (pindex->pprev == nullptr || PeerHasHeader(&state, pindex->pprev)) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. fFoundStartingHeader = true; - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.emplace_back(pindex->GetBlockHeader()); } else { // Peer doesn't have this header or the prior one -- nothing will // connect, so bail out. @@ -5738,7 +5743,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // Add blocks for (const uint256& hash : peer->m_blocks_for_inv_relay) { - vInv.push_back(CInv(MSG_BLOCK, hash)); + vInv.emplace_back(MSG_BLOCK, hash); if (vInv.size() == MAX_INV_SZ) { m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); @@ -5944,7 +5949,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) } for (const CBlockIndex *pindex : vToDownload) { uint32_t nFetchFlags = GetFetchFlags(*peer); - vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); + vGetData.emplace_back(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()); BlockRequested(pto->GetId(), *pindex); LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->GetId()); diff --git a/src/net_processing.h b/src/net_processing.h index 837e308617..80d07648a4 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -58,6 +58,9 @@ public: uint32_t max_extra_txs{DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN}; //! Whether all P2P messages are captured to disk bool capture_messages{false}; + //! Whether or not the internal RNG behaves deterministically (this is + //! a test-only option). + bool deterministic_rng{false}; }; static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman, diff --git a/src/netaddress.h b/src/netaddress.h index ad09f16799..08dd77c0ff 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -81,6 +81,10 @@ static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{ 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5]. }; +/// All CJDNS addresses start with 0xFC. See +/// https://github.com/cjdelisle/cjdns/blob/master/doc/Whitepaper.md#pulling-it-all-together +static constexpr uint8_t CJDNS_PREFIX{0xFC}; + /// Size of IPv4 address (in bytes). static constexpr size_t ADDR_IPV4_SIZE = 4; @@ -174,7 +178,7 @@ public: [[nodiscard]] bool IsTor() const { return m_net == NET_ONION; } [[nodiscard]] bool IsI2P() const { return m_net == NET_I2P; } [[nodiscard]] bool IsCJDNS() const { return m_net == NET_CJDNS; } - [[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == 0xfc; } + [[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == CJDNS_PREFIX; } bool IsLocal() const; bool IsRoutable() const; bool IsInternal() const; diff --git a/src/netbase.cpp b/src/netbase.cpp index ca1a80d72f..5e1e121bfe 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -32,6 +32,8 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP; std::chrono::milliseconds g_socks5_recv_timeout = 20s; static std::atomic<bool> interruptSocks5Recv(false); +ReachableNets g_reachable_nets; + std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup) { addrinfo ai_hint{}; @@ -651,9 +653,10 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out) const size_t slash_pos{subnet_str.find_last_of('/')}; const std::string str_addr{subnet_str.substr(0, slash_pos)}; - const std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)}; + std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)}; if (addr.has_value()) { + addr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0})); if (slash_pos != subnet_str.npos) { const std::string netmask_str{subnet_str.substr(slash_pos + 1)}; uint8_t netmask; @@ -772,3 +775,12 @@ bool IsBadPort(uint16_t port) } return false; } + +CService MaybeFlipIPv6toCJDNS(const CService& service) +{ + CService ret{service}; + if (ret.IsIPv6() && ret.HasCJDNSPrefix() && g_reachable_nets.Contains(NET_CJDNS)) { + ret.m_net = NET_CJDNS; + } + return ret; +} diff --git a/src/netbase.h b/src/netbase.h index 1da4f5c51d..8b7da4109f 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -19,6 +19,7 @@ #include <stdint.h> #include <string> #include <type_traits> +#include <unordered_set> #include <vector> extern int nConnectTimeout; @@ -65,6 +66,61 @@ struct ProxyCredentials }; /** + * List of reachable networks. Everything is reachable by default. + */ +class ReachableNets { +public: + void Add(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + AssertLockNotHeld(m_mutex); + LOCK(m_mutex); + m_reachable.insert(net); + } + + void Remove(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + AssertLockNotHeld(m_mutex); + LOCK(m_mutex); + m_reachable.erase(net); + } + + void RemoveAll() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + AssertLockNotHeld(m_mutex); + LOCK(m_mutex); + m_reachable.clear(); + } + + [[nodiscard]] bool Contains(Network net) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + AssertLockNotHeld(m_mutex); + LOCK(m_mutex); + return m_reachable.count(net) > 0; + } + + [[nodiscard]] bool Contains(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + AssertLockNotHeld(m_mutex); + return Contains(addr.GetNetwork()); + } + +private: + mutable Mutex m_mutex; + + std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){ + NET_UNROUTABLE, + NET_IPV4, + NET_IPV6, + NET_ONION, + NET_I2P, + NET_CJDNS, + NET_INTERNAL + }; +}; + +extern ReachableNets g_reachable_nets; + +/** * Wrapper for getaddrinfo(3). Do not use directly: call Lookup/LookupHost/LookupNumeric/LookupSubNet. */ std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup); @@ -251,4 +307,13 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a */ bool IsBadPort(uint16_t port); +/** + * If an IPv6 address belongs to the address range used by the CJDNS network and + * the CJDNS network is reachable (-cjdnsreachable config is set), then change + * the type from NET_IPV6 to NET_CJDNS. + * @param[in] service Address to potentially convert. + * @return a copy of `service` either unmodified or changed to CJDNS. + */ +CService MaybeFlipIPv6toCJDNS(const CService& service); + #endif // BITCOIN_NETBASE_H diff --git a/src/netgroup.cpp b/src/netgroup.cpp index 2d4d231f2b..a03927b152 100644 --- a/src/netgroup.cpp +++ b/src/netgroup.cpp @@ -52,8 +52,8 @@ std::vector<unsigned char> NetGroupManager::GetGroup(const CNetAddr& address) co } else if (address.IsCJDNS()) { // Treat in the same way as Tor and I2P because the address in all of // them is "random" bytes (derived from a public key). However in CJDNS - // the first byte is a constant 0xfc, so the random bytes come after it. - // Thus skip the constant 8 bits at the start. + // the first byte is a constant (see CJDNS_PREFIX), so the random bytes + // come after it. Thus skip the constant 8 bits at the start. nBits = 12; } else if (address.IsHeNet()) { // for he.net, use /36 groups diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 706b62ea9b..f8f1aab551 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -458,7 +458,7 @@ bool BlockManager::WriteBlockIndexDB() std::vector<std::pair<int, const CBlockFileInfo*>> vFiles; vFiles.reserve(m_dirty_fileinfo.size()); for (std::set<int>::iterator it = m_dirty_fileinfo.begin(); it != m_dirty_fileinfo.end();) { - vFiles.push_back(std::make_pair(*it, &m_blockfile_info[*it])); + vFiles.emplace_back(*it, &m_blockfile_info[*it]); m_dirty_fileinfo.erase(it++); } std::vector<const CBlockIndex*> vBlocks; @@ -761,12 +761,14 @@ bool BlockManager::FlushChainstateBlockFile(int tip_height) { LOCK(cs_LastBlockFile); auto& cursor = m_blockfile_cursors[BlockfileTypeForHeight(tip_height)]; + // If the cursor does not exist, it means an assumeutxo snapshot is loaded, + // but no blocks past the snapshot height have been written yet, so there + // is no data associated with the chainstate, and it is safe not to flush. if (cursor) { - // The cursor may not exist after a snapshot has been loaded but before any - // blocks have been downloaded. return FlushBlockFile(cursor->file_num, /*fFinalize=*/false, /*finalize_undo=*/false); } - return false; + // No need to log warnings in this case. + return true; } uint64_t BlockManager::CalculateCurrentUsage() diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 16ca1d9156..eb1994177a 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -185,7 +185,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize chainman.InitializeChainstate(options.mempool); // Load a chain created from a UTXO snapshot, if any exist. - bool has_snapshot = chainman.DetectSnapshotChainstate(options.mempool); + bool has_snapshot = chainman.DetectSnapshotChainstate(); if (has_snapshot && (options.reindex || options.reindex_chainstate)) { LogPrintf("[snapshot] deleting snapshot chainstate due to reindexing\n"); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 4baa0da67c..f6dbe4f008 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -674,7 +674,7 @@ public: if (!m_node.mempool) { std::map<COutPoint, CAmount> bump_fees; for (const auto& outpoint : outpoints) { - bump_fees.emplace(std::make_pair(outpoint, 0)); + bump_fees.emplace(outpoint, 0); } return bump_fees; } diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h index a6dd3f3f13..1160bb55f0 100644 --- a/src/node/utxo_snapshot.h +++ b/src/node/utxo_snapshot.h @@ -35,8 +35,7 @@ public: SnapshotMetadata() { } SnapshotMetadata( const uint256& base_blockhash, - uint64_t coins_count, - unsigned int nchaintx) : + uint64_t coins_count) : m_base_blockhash(base_blockhash), m_coins_count(coins_count) { } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 553c88fddc..87bfa4cfc3 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -1054,8 +1054,9 @@ static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee, return fee_set; } -FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) - : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)} +FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee, FastRandomContext& rng) + : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)}, + insecure_rand{rng} { } diff --git a/src/policy/fees.h b/src/policy/fees.h index 8ed13482e9..69bda195be 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -320,7 +320,7 @@ private: public: /** Create new FeeFilterRounder */ - explicit FeeFilterRounder(const CFeeRate& min_incremental_fee); + explicit FeeFilterRounder(const CFeeRate& min_incremental_fee, FastRandomContext& rng); /** Quantize a minimum fee for privacy purpose before broadcast. */ CAmount round(CAmount currentMinFee) EXCLUSIVE_LOCKS_REQUIRED(!m_insecure_rand_mutex); @@ -328,7 +328,7 @@ public: private: const std::set<double> m_fee_set; Mutex m_insecure_rand_mutex; - FastRandomContext insecure_rand GUARDED_BY(m_insecure_rand_mutex); + FastRandomContext& insecure_rand GUARDED_BY(m_insecure_rand_mutex); }; #endif // BITCOIN_POLICY_FEES_H diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp index fd272a2642..47a9274a31 100644 --- a/src/policy/packages.cpp +++ b/src/policy/packages.cpp @@ -88,3 +88,18 @@ bool IsChildWithParents(const Package& package) return std::all_of(package.cbegin(), package.cend() - 1, [&input_txids](const auto& ptx) { return input_txids.count(ptx->GetHash()) > 0; }); } + +bool IsChildWithParentsTree(const Package& package) +{ + if (!IsChildWithParents(package)) return false; + std::unordered_set<uint256, SaltedTxidHasher> parent_txids; + std::transform(package.cbegin(), package.cend() - 1, std::inserter(parent_txids, parent_txids.end()), + [](const auto& ptx) { return ptx->GetHash(); }); + // Each parent must not have an input who is one of the other parents. + return std::all_of(package.cbegin(), package.cend() - 1, [&](const auto& ptx) { + for (const auto& input : ptx->vin) { + if (parent_txids.count(input.prevout.hash) > 0) return false; + } + return true; + }); +} diff --git a/src/policy/packages.h b/src/policy/packages.h index 702667b8ad..cf37857e4b 100644 --- a/src/policy/packages.h +++ b/src/policy/packages.h @@ -63,4 +63,8 @@ bool CheckPackage(const Package& txns, PackageValidationState& state); */ bool IsChildWithParents(const Package& package); +/** Context-free check that a package IsChildWithParents() and none of the parents depend on each + * other (the package is a "tree"). + */ +bool IsChildWithParentsTree(const Package& package); #endif // BITCOIN_POLICY_PACKAGES_H diff --git a/src/psbt.cpp b/src/psbt.cpp index 7ec9b9c136..76a2fd8241 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -132,6 +132,7 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const } for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) { sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin); + sigdata.tap_pubkeys.emplace(Hash160(pubkey), pubkey); } for (const auto& [hash, preimage] : ripemd160_preimages) { sigdata.ripemd160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage); @@ -246,6 +247,7 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const } for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) { sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin); + sigdata.tap_pubkeys.emplace(Hash160(pubkey), pubkey); } } diff --git a/src/psbt.h b/src/psbt.h index 48e0453084..5b4daafed5 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -874,7 +874,7 @@ struct PSBTOutput if ((leaf_ver & ~TAPROOT_LEAF_MASK) != 0) { throw std::ios_base::failure("Output Taproot tree has a leaf with an invalid leaf version"); } - m_tap_tree.push_back(std::make_tuple(depth, leaf_ver, script)); + m_tap_tree.emplace_back(depth, leaf_ver, script); builder.Add((int)depth, script, (int)leaf_ver, /*track=*/true); } if (!builder.IsComplete()) { diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 05808e4c22..11e1b4abb5 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -204,6 +204,13 @@ std::vector<CKeyID> XOnlyPubKey::GetKeyIDs() const return out; } +CPubKey XOnlyPubKey::GetEvenCorrespondingCPubKey() const +{ + unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02}; + std::copy(begin(), end(), full_key + 1); + return CPubKey{full_key}; +} + bool XOnlyPubKey::IsFullyValid() const { secp256k1_xonly_pubkey pubkey; diff --git a/src/pubkey.h b/src/pubkey.h index 274779f9a4..2b655c3f73 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -191,6 +191,12 @@ public: return size() > 0; } + /** Check if a public key is a syntactically valid compressed or uncompressed key. */ + bool IsValidNonHybrid() const noexcept + { + return size() > 0 && (vch[0] == 0x02 || vch[0] == 0x03 || vch[0] == 0x04); + } + //! fully validate whether this is a valid public key (more expensive than IsValid()) bool IsFullyValid() const; @@ -276,6 +282,8 @@ public: */ std::vector<CKeyID> GetKeyIDs() const; + CPubKey GetEvenCorrespondingCPubKey() const; + const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } const unsigned char* data() const { return m_keydata.begin(); } static constexpr size_t size() { return decltype(m_keydata)::size(); } diff --git a/src/qt/android/gradle.properties b/src/qt/android/gradle.properties index 838870f62d..468f4757c0 100644 --- a/src/qt/android/gradle.properties +++ b/src/qt/android/gradle.properties @@ -1,4 +1,4 @@ -androidBuildToolsVersion=28.0.3 -androidCompileSdkVersion=28 +androidBuildToolsVersion=30.0.3 +androidCompileSdkVersion=30 qt5AndroidDir=new File(".").absolutePath org.gradle.jvmargs=-Xmx4608M diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 8a46d46437..720f5584d6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -109,10 +109,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty { /** Create wallet frame and make it the central widget */ walletFrame = new WalletFrame(_platformStyle, this); - connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] { - auto activity = new CreateWalletActivity(getWalletController(), this); - activity->create(); - }); + connect(walletFrame, &WalletFrame::createWalletButtonClicked, this, &BitcoinGUI::createWallet); connect(walletFrame, &WalletFrame::message, [this](const QString& title, const QString& message, unsigned int style) { this->message(title, message, style); }); @@ -395,7 +392,7 @@ void BitcoinGUI::createActions() connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses); connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses); connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked); - connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] { + connect(m_open_wallet_menu, &QMenu::aboutToShow, m_wallet_controller, [this] { m_open_wallet_menu->clear(); for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) { const std::string& path = i.first; @@ -412,7 +409,7 @@ void BitcoinGUI::createActions() continue; } - connect(action, &QAction::triggered, [this, path] { + connect(action, &QAction::triggered, m_wallet_controller, [this, path] { auto activity = new OpenWalletActivity(m_wallet_controller, this); connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection); connect(activity, &OpenWalletActivity::opened, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection); @@ -424,7 +421,7 @@ void BitcoinGUI::createActions() action->setEnabled(false); } }); - connect(m_restore_wallet_action, &QAction::triggered, [this] { + connect(m_restore_wallet_action, &QAction::triggered, m_wallet_controller, [this] { //: Name of the wallet data file format. QString name_data_file = tr("Wallet Data"); @@ -450,19 +447,14 @@ void BitcoinGUI::createActions() auto backup_file_path = fs::PathFromString(backup_file.toStdString()); activity->restore(backup_file_path, wallet_name.toStdString()); }); - connect(m_close_wallet_action, &QAction::triggered, [this] { + connect(m_close_wallet_action, &QAction::triggered, m_wallet_controller, [this] { m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this); }); - connect(m_create_wallet_action, &QAction::triggered, [this] { - auto activity = new CreateWalletActivity(m_wallet_controller, this); - connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet); - connect(activity, &CreateWalletActivity::created, rpcConsole, &RPCConsole::setCurrentWallet); - activity->create(); - }); - connect(m_close_all_wallets_action, &QAction::triggered, [this] { + connect(m_create_wallet_action, &QAction::triggered, this, &BitcoinGUI::createWallet); + connect(m_close_all_wallets_action, &QAction::triggered, m_wallet_controller, [this] { m_wallet_controller->closeAllWallets(this); }); - connect(m_migrate_wallet_action, &QAction::triggered, [this] { + connect(m_migrate_wallet_action, &QAction::triggered, m_wallet_controller, [this] { auto activity = new MigrateWalletActivity(m_wallet_controller, this); connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet); activity->migrate(walletFrame->currentWalletModel()); @@ -658,7 +650,8 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH m_mask_values_action->setChecked(_clientModel->getOptionsModel()->getOption(OptionsModel::OptionID::MaskValues).toBool()); } else { - if(trayIconMenu) + // Shutdown requested, disable menus + if (trayIconMenu) { // Disable context menu on tray icon trayIconMenu->clear(); @@ -672,6 +665,8 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(nullptr); + // Disable top bar menu actions + appMenuBar->clear(); } } @@ -1191,6 +1186,21 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer progressBar->setToolTip(tooltip); } +void BitcoinGUI::createWallet() +{ +#ifdef ENABLE_WALLET +#ifndef USE_SQLITE + // Compiled without sqlite support (required for descriptor wallets) + message(tr("Error creating wallet"), tr("Cannot create new wallet, the software was compiled without sqlite support (required for descriptor wallets)"), CClientUIInterface::MSG_ERROR); + return; +#endif // USE_SQLITE + auto activity = new CreateWalletActivity(getWalletController(), this); + connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet); + connect(activity, &CreateWalletActivity::created, rpcConsole, &RPCConsole::setCurrentWallet); + activity->create(); +#endif // ENABLE_WALLET +} + void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret, const QString& detailed_message) { // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines). diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 510561454b..6fdc4c60d8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -230,6 +230,8 @@ public Q_SLOTS: void setNetworkActive(bool network_active); /** Set number of blocks and last block date shown in the UI */ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state); + /** Launch the wallet creation modal (no-op if wallet is not compiled) **/ + void createWallet(); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 92fb3912d1..d7b1c70c59 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -84,6 +84,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: Unable to produce descriptors for this legacy wallet. Make sure to " "provide the wallet's passphrase if it is encrypted."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Failed to calculate bump fees, because unconfirmed UTXOs depend on enormous " +"cluster of unconfirmed transactions."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Failed to rename invalid peers.dat file. Please move or delete it and try " "again."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -148,18 +151,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is " "supported"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"The -txindex upgrade started by a previous version cannot be completed. " -"Restart with the previous version or run a full -reindex."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "The block database contains a block which appears to be from the future. " "This may be due to your computer's date and time being set incorrectly. Only " "rebuild the block database if you are sure that your computer's date and " "time are correct"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"The block index db contains a legacy 'txindex'. To clear the occupied disk " -"space, run a full -reindex, otherwise ignore this error. This error message " -"will not be displayed again."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "The inputs size exceeds the maximum weight. Please try sending a smaller " "amount or manually consolidating your wallet's UTXOs"), QT_TRANSLATE_NOOP("bitcoin-core", "" diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 38c7656e00..70aa9bc8da 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -421,7 +421,19 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * std::vector<unsigned char> witnessprogram; if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); + // add input skeleton bytes (outpoint, scriptSig size, nSequence) + nBytesInputs += (32 + 4 + 1 + 4); + + if (witnessversion == 0) { // P2WPKH + // 1 WU (witness item count) + 72 WU (ECDSA signature with len byte) + 34 WU (pubkey with len byte) + nBytesInputs += 107 / WITNESS_SCALE_FACTOR; + } else if (witnessversion == 1) { // P2TR key-path spend + // 1 WU (witness item count) + 65 WU (Schnorr signature with len byte) + nBytesInputs += 66 / WITNESS_SCALE_FACTOR; + } else { + // not supported, should be unreachable + throw std::runtime_error("Trying to spend future segwit version script"); + } fWitness = true; } else if(ExtractDestination(out.txout.scriptPubKey, address)) diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp index 3e8be3e675..6557280d89 100644 --- a/src/qt/createwalletdialog.cpp +++ b/src/qt/createwalletdialog.cpp @@ -50,12 +50,10 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) : ui->encrypt_wallet_checkbox->setEnabled(!checked); ui->blank_wallet_checkbox->setEnabled(!checked); ui->disable_privkeys_checkbox->setEnabled(!checked); - ui->descriptor_checkbox->setEnabled(!checked); // The external signer checkbox is only enabled when a device is detected. // In that case it is checked by default. Toggling it restores the other // options to their default. - ui->descriptor_checkbox->setChecked(checked); ui->encrypt_wallet_checkbox->setChecked(false); ui->disable_privkeys_checkbox->setChecked(checked); ui->blank_wallet_checkbox->setChecked(false); @@ -87,19 +85,6 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) : } }); -#ifndef USE_SQLITE - ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)")); - ui->descriptor_checkbox->setEnabled(false); - ui->descriptor_checkbox->setChecked(false); - ui->external_signer_checkbox->setEnabled(false); - ui->external_signer_checkbox->setChecked(false); -#endif - -#ifndef USE_BDB - ui->descriptor_checkbox->setEnabled(false); - ui->descriptor_checkbox->setChecked(true); -#endif - #ifndef ENABLE_EXTERNAL_SIGNER //: "External signing" means using devices such as hardware wallets. ui->external_signer_checkbox->setToolTip(tr("Compiled without external signing support (required for external signing)")); @@ -155,11 +140,6 @@ bool CreateWalletDialog::isMakeBlankWalletChecked() const return ui->blank_wallet_checkbox->isChecked(); } -bool CreateWalletDialog::isDescriptorWalletChecked() const -{ - return ui->descriptor_checkbox->isChecked(); -} - bool CreateWalletDialog::isExternalSignerChecked() const { return ui->external_signer_checkbox->isChecked(); diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h index 939b82ff78..24ee97385b 100644 --- a/src/qt/createwalletdialog.h +++ b/src/qt/createwalletdialog.h @@ -35,7 +35,6 @@ public: bool isEncryptWalletChecked() const; bool isDisablePrivateKeysChecked() const; bool isMakeBlankWalletChecked() const; - bool isDescriptorWalletChecked() const; bool isExternalSignerChecked() const; private: diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui index 56adbe17a5..1d6f0ed530 100644 --- a/src/qt/forms/createwalletdialog.ui +++ b/src/qt/forms/createwalletdialog.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>364</width> - <height>249</height> + <width>371</width> + <height>298</height> </rect> </property> <property name="windowTitle"> @@ -18,6 +18,48 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> + <widget class="QLabel" name="label_description"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>You are one step away from creating your new wallet!</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_subdescription"> + <property name="text"> + <string>Please provide a name and, if desired, enable any advanced options</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>3</height> + </size> + </property> + </spacer> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="wallet_name_label"> @@ -75,7 +117,19 @@ <property name="title"> <string>Advanced Options</string> </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> <layout class="QVBoxLayout" name="verticalLayout_groupbox"> + <property name="spacing"> + <number>9</number> + </property> <item> <widget class="QCheckBox" name="disable_privkeys_checkbox"> <property name="enabled"> @@ -100,19 +154,6 @@ </widget> </item> <item> - <widget class="QCheckBox" name="descriptor_checkbox"> - <property name="toolTip"> - <string>Use descriptors for scriptPubKey management</string> - </property> - <property name="text"> - <string>Descriptor Wallet</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="external_signer_checkbox"> <property name="toolTip"> <string>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</string> @@ -155,7 +196,6 @@ <tabstop>encrypt_wallet_checkbox</tabstop> <tabstop>disable_privkeys_checkbox</tabstop> <tabstop>blank_wallet_checkbox</tabstop> - <tabstop>descriptor_checkbox</tabstop> <tabstop>external_signer_checkbox</tabstop> </tabstops> <resources/> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 8cb5ffbc65..fcba44f809 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -55,12 +55,12 @@ </message> <message> <location line="-30"/> - <location filename="../addressbookpage.cpp" line="+127"/> + <location filename="../addressbookpage.cpp" line="+117"/> <source>&Delete</source> <translation>&Delete</translation> </message> <message> - <location filename="../addressbookpage.cpp" line="-38"/> + <location filename="../addressbookpage.cpp" line="-30"/> <source>Choose the address to send coins to</source> <translation type="unfinished"></translation> </message> @@ -76,16 +76,6 @@ </message> <message> <location line="+6"/> - <source>Sending addresses</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>Receiving addresses</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+7"/> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> <translation type="unfinished"></translation> </message> @@ -111,7 +101,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+164"/> + <location line="+165"/> <source>Export Address List</source> <translation type="unfinished"></translation> </message> @@ -128,7 +118,17 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-3"/> + <location line="+32"/> + <source>Sending addresses - %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Receiving addresses - %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-36"/> <source>Exporting Failed</source> <translation type="unfinished"></translation> </message> @@ -215,12 +215,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> </message> <message> <location line="+18"/> - <location line="+55"/> + <location line="+58"/> <source>Wallet encrypted</source> <translation type="unfinished"></translation> </message> <message> - <location line="-135"/> + <location line="-138"/> <source>Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> <translation type="unfinished"></translation> </message> @@ -257,41 +257,41 @@ Signing is only possible with addresses of the type 'legacy'.</source> <message> <location line="+6"/> <location line="+8"/> - <location line="+59"/> + <location line="+62"/> <source>Wallet encryption failed</source> <translation type="unfinished"></translation> </message> <message> - <location line="-66"/> + <location line="-69"/> <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source> <translation type="unfinished"></translation> </message> <message> <location line="+8"/> - <location line="+59"/> + <location line="+62"/> <source>The supplied passphrases do not match.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-46"/> + <location line="-49"/> <location line="+3"/> - <location line="+12"/> + <location line="+15"/> <source>Wallet unlock failed</source> <translation type="unfinished"></translation> </message> <message> - <location line="-14"/> - <location line="+31"/> + <location line="-17"/> + <location line="+34"/> <source>The passphrase entered for the wallet decryption was incorrect.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-28"/> + <location line="-31"/> <source>The passphrase entered for the wallet decryption is incorrect. It contains a null character (ie - a zero byte). If the passphrase was set with a version of this software prior to 25.0, please try again with only the characters up to — but not including — the first null character. If this is successful, please set a new passphrase to avoid this issue in the future.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+20"/> + <location line="+23"/> <source>Wallet passphrase was successfully changed.</source> <translation type="unfinished"></translation> </message> @@ -357,7 +357,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+254"/> + <location filename="../bitcoingui.cpp" line="+250"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -417,7 +417,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+164"/> + <location line="+163"/> <source>&Minimize</source> <translation type="unfinished"></translation> </message> @@ -427,18 +427,18 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+407"/> + <location line="+412"/> <source>Network activity disabled.</source> <extracomment>A substring of the tooltip.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+436"/> + <location line="+451"/> <source>Proxy is <b>enabled</b>: %1</source> <translation type="unfinished"></translation> </message> <message> - <location line="-1175"/> + <location line="-1194"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -533,12 +533,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+123"/> + <location line="+121"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+20"/> + <location line="+21"/> <source>&Settings</source> <translation>&Settings</translation> </message> @@ -553,7 +553,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation>Tabs toolbar</translation> </message> <message> - <location line="+471"/> + <location line="+476"/> <source>Syncing Headers (%1%)…</source> <translation type="unfinished"></translation> </message> @@ -578,7 +578,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-848"/> + <location line="-852"/> <source>Request payments (generates QR codes and bitcoin: URIs)</source> <translation type="unfinished"></translation> </message> @@ -593,7 +593,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+26"/> + <location line="+30"/> <source>&Command-line options</source> <translation type="unfinished"></translation> </message> @@ -626,7 +626,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation>Transactions after this will not yet be visible.</translation> </message> <message> - <location line="+25"/> + <location line="+40"/> <source>Error</source> <translation>Error</translation> </message> @@ -641,12 +641,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation>Information</translation> </message> <message> - <location line="-78"/> + <location line="-93"/> <source>Up to date</source> <translation>Up to date</translation> </message> <message> - <location line="-833"/> + <location line="-837"/> <source>Ctrl+Q</source> <translation type="unfinished"></translation> </message> @@ -723,6 +723,16 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> + <location line="+2"/> + <source>Migrate Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Migrate a wallet</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+4"/> <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source> <translation type="unfinished"></translation> @@ -772,7 +782,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+74"/> + <location line="+69"/> <source>&Window</source> <translation type="unfinished">&Window</translation> </message> @@ -792,12 +802,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+272"/> + <location line="+274"/> <source>%1 client</source> <translation type="unfinished"></translation> </message> <message> - <location line="+65"/> + <location line="+68"/> <source>&Hide</source> <translation type="unfinished"></translation> </message> @@ -845,7 +855,17 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+149"/> + <location line="+139"/> + <source>Error creating wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>Cannot create new wallet, the software was compiled without sqlite support (required for descriptor wallets)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+25"/> <source>Error: %1</source> <translation type="unfinished"></translation> </message> @@ -1115,7 +1135,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+33"/> + <location line="+32"/> <source>Create wallet failed</source> <translation type="unfinished"></translation> </message> @@ -1143,7 +1163,17 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+15"/> + <source>You are one step away from creating your new wallet!</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Please provide a name and, if desired, enable any advanced options</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+25"/> <source>Wallet Name</source> <translation type="unfinished"></translation> </message> @@ -1168,7 +1198,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+21"/> <source>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source> <translation type="unfinished"></translation> </message> @@ -1189,16 +1219,6 @@ Signing is only possible with addresses of the type 'legacy'.</source> </message> <message> <location line="+7"/> - <source>Use descriptors for scriptPubKey management</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> - <source>Descriptor Wallet</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+10"/> <source>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</source> <translation type="unfinished"></translation> </message> @@ -1214,11 +1234,6 @@ Signing is only possible with addresses of the type 'legacy'.</source> </message> <message> <location line="+68"/> - <source>Compiled without sqlite support (required for descriptor wallets)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+14"/> <source>Compiled without external signing support (required for external signing)</source> <extracomment>"External signing" means using devices such as hardware wallets.</extracomment> <translation type="unfinished"></translation> @@ -1485,6 +1500,63 @@ Signing is only possible with addresses of the type 'legacy'.</source> </message> </context> <context> + <name>MigrateWalletActivity</name> + <message> + <location line="+63"/> + <source>Migrate wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Are you sure you wish to migrate the wallet <i>%1</i>?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Migrating the wallet will convert this wallet to one or more descriptor wallets. A new wallet backup will need to be made. +If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts. +If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts. + +The migration process will create a backup of the wallet before migrating. This backup file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of an incorrect migration, the backup can be restored with the "Restore Wallet" functionality.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+23"/> + <source>Migrate Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>Migrating Wallet <b>%1</b>…</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>The wallet '%1' was migrated successfully.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source> Watchonly scripts have been migrated to a new wallet named '%1'.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source> Solvable but not watched scripts have been migrated to a new wallet named '%1'.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+14"/> + <source>Migration failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Migration Successful</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>ModalOverlay</name> <message> <location filename="../forms/modaloverlay.ui" line="+14"/> @@ -1587,7 +1659,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context> <name>OpenWalletActivity</name> <message> - <location filename="../walletcontroller.cpp" line="-46"/> + <location filename="../walletcontroller.cpp" line="-161"/> <source>Open wallet failed</source> <translation type="unfinished"></translation> </message> @@ -2675,7 +2747,7 @@ If you are receiving this error you should request the merchant provide a BIP21 </message> <message> <location line="+2"/> - <location filename="../rpcconsole.cpp" line="+994"/> + <location filename="../rpcconsole.cpp" line="+1004"/> <source>%1 kB</source> <translation type="unfinished"></translation> </message> @@ -2774,6 +2846,8 @@ If you are receiving this error you should request the merchant provide a BIP21 <location line="+692"/> <location line="+26"/> <location line="+26"/> + <location line="+26"/> + <location line="+26"/> <location line="+23"/> <location line="+23"/> <location line="+23"/> @@ -2802,7 +2876,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation>N/A</translation> </message> <message> - <location line="-1597"/> + <location line="-1649"/> <source>Client version</source> <translation>Client version</translation> </message> @@ -2843,12 +2917,12 @@ If you are receiving this error you should request the merchant provide a BIP21 </message> <message> <location line="+29"/> - <location line="+892"/> + <location line="+944"/> <source>Network</source> <translation>Network</translation> </message> <message> - <location line="-885"/> + <location line="-937"/> <source>Name</source> <translation type="unfinished"></translation> </message> @@ -2894,18 +2968,18 @@ If you are receiving this error you should request the merchant provide a BIP21 </message> <message> <location line="+80"/> - <location line="+708"/> + <location line="+760"/> <source>Received</source> <translation type="unfinished"></translation> </message> <message> - <location line="-628"/> - <location line="+605"/> + <location line="-680"/> + <location line="+657"/> <source>Sent</source> <translation type="unfinished"></translation> </message> <message> - <location line="-564"/> + <location line="-616"/> <source>&Peers</source> <translation type="unfinished"></translation> </message> @@ -2921,7 +2995,27 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+106"/> + <location line="+80"/> + <source>The transport layer version: %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Transport</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+23"/> + <source>The BIP324 session ID string in hex, if any.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Session ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+49"/> <source>Version</source> <translation type="unfinished"></translation> </message> @@ -3002,13 +3096,13 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="-1564"/> - <location line="+1051"/> + <location line="-1616"/> + <location line="+1103"/> <source>User Agent</source> <translation type="unfinished"></translation> </message> <message> - <location line="-1125"/> + <location line="-1177"/> <source>Node window</source> <translation type="unfinished"></translation> </message> @@ -3048,7 +3142,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> + <location line="+75"/> <source>The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source> <translation type="unfinished"></translation> </message> @@ -3124,7 +3218,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="-1258"/> + <location line="-1310"/> <source>Last block time</source> <translation>Last block time</translation> </message> @@ -3199,7 +3293,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-464"/> + <location filename="../rpcconsole.cpp" line="-473"/> <source>Inbound: initiated by peer</source> <extracomment>Explanatory text for an inbound peer connection.</extracomment> <translation type="unfinished"></translation> @@ -3235,6 +3329,24 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> + <location line="+5"/> + <source>detecting: peer could be v1 or v2</source> + <extracomment>Explanatory text for "detecting" transport type.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>v1: unencrypted, plaintext transport protocol</source> + <extracomment>Explanatory text for v1 transport type.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>v2: BIP324 encrypted transport protocol</source> + <extracomment>Explanatory text for v2 transport type.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> <location line="+4"/> <source>we selected the peer for high bandwidth relay</source> <translation type="unfinished"></translation> @@ -3326,7 +3438,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+316"/> + <location line="+325"/> <source>Ctrl+I</source> <translation type="unfinished"></translation> </message> @@ -3346,7 +3458,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="-321"/> + <location line="-330"/> <source>Executing command using "%1" wallet</source> <translation type="unfinished"></translation> </message> @@ -4721,7 +4833,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+257"/> + <location filename="../transactiontablemodel.cpp" line="+258"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -4787,11 +4899,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </message> <message> <location line="+2"/> - <source>Payment to yourself</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+2"/> <source>Mined</source> <translation type="unfinished"></translation> </message> @@ -4801,12 +4908,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+16"/> + <location line="+14"/> <source>(n/a)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+207"/> + <location line="+205"/> <source>(no label)</source> <translation type="unfinished"></translation> </message> @@ -4886,11 +4993,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </message> <message> <location line="+2"/> - <source>To yourself</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>Mined</source> <translation type="unfinished"></translation> </message> @@ -4910,12 +5012,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="-26"/> + <location line="-25"/> <source>Range…</source> <translation type="unfinished"></translation> </message> <message> - <location line="+90"/> + <location line="+89"/> <source>&Copy address</source> <translation type="unfinished"></translation> </message> @@ -5058,7 +5160,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <context> <name>WalletController</name> <message> - <location filename="../walletcontroller.cpp" line="-347"/> + <location filename="../walletcontroller.cpp" line="-346"/> <source>Close wallet</source> <translation type="unfinished"></translation> </message> @@ -5351,7 +5453,7 @@ Go to File > Open Wallet to load a wallet. <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+12"/> <source>File %s already exists. If you are sure this is what you want, move it out of the way first.</source> <translation type="unfinished"></translation> </message> @@ -5416,17 +5518,12 @@ Go to File > Open Wallet to load a wallet. <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+3"/> <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> - <source>The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+11"/> + <location line="+12"/> <source>The transaction amount is too small to send after the fee has been deducted</source> <translation type="unfinished"></translation> </message> @@ -5551,12 +5648,7 @@ Go to File > Open Wallet to load a wallet. <translation type="unfinished"></translation> </message> <message> - <location line="-105"/> - <source>The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-124"/> + <location line="-225"/> <source>%s is set very high! Fees this large could be paid on a single transaction.</source> <translation type="unfinished"></translation> </message> @@ -5592,6 +5684,11 @@ Go to File > Open Wallet to load a wallet. </message> <message> <location line="+6"/> + <source>Failed to calculate bump fees, because unconfirmed UTXOs depend on enormous cluster of unconfirmed transactions.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Failed to rename invalid peers.dat file. Please move or delete it and try again.</source> <translation type="unfinished"></translation> </message> @@ -5631,7 +5728,7 @@ Go to File > Open Wallet to load a wallet. <translation type="unfinished"></translation> </message> <message> - <location line="+36"/> + <location line="+29"/> <source>The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs</source> <translation type="unfinished"></translation> </message> diff --git a/src/qt/locale/bitcoin_en.xlf b/src/qt/locale/bitcoin_en.xlf index 9d4a989cf0..b1a01cb4a1 100644 --- a/src/qt/locale/bitcoin_en.xlf +++ b/src/qt/locale/bitcoin_en.xlf @@ -45,7 +45,7 @@ <trans-unit id="_msg11"> <source xml:space="preserve">&Delete</source> <context-group purpose="location"><context context-type="linenumber">101</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../addressbookpage.cpp</context><context context-type="linenumber">127</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../addressbookpage.cpp</context><context context-type="linenumber">117</context></context-group> </trans-unit> </group> </body></file> @@ -53,62 +53,62 @@ <group restype="x-trolltech-linguist-context" resname="AddressBookPage"> <trans-unit id="_msg12"> <source xml:space="preserve">Choose the address to send coins to</source> - <context-group purpose="location"><context context-type="linenumber">89</context></context-group> + <context-group purpose="location"><context context-type="linenumber">87</context></context-group> </trans-unit> <trans-unit id="_msg13"> <source xml:space="preserve">Choose the address to receive coins with</source> - <context-group purpose="location"><context context-type="linenumber">90</context></context-group> + <context-group purpose="location"><context context-type="linenumber">88</context></context-group> </trans-unit> <trans-unit id="_msg14"> <source xml:space="preserve">C&hoose</source> - <context-group purpose="location"><context context-type="linenumber">95</context></context-group> + <context-group purpose="location"><context context-type="linenumber">93</context></context-group> </trans-unit> <trans-unit id="_msg15"> - <source xml:space="preserve">Sending addresses</source> - <context-group purpose="location"><context context-type="linenumber">101</context></context-group> - </trans-unit> - <trans-unit id="_msg16"> - <source xml:space="preserve">Receiving addresses</source> - <context-group purpose="location"><context context-type="linenumber">102</context></context-group> - </trans-unit> - <trans-unit id="_msg17"> <source xml:space="preserve">These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> - <context-group purpose="location"><context context-type="linenumber">109</context></context-group> + <context-group purpose="location"><context context-type="linenumber">99</context></context-group> </trans-unit> - <trans-unit id="_msg18"> + <trans-unit id="_msg16"> <source xml:space="preserve">These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses. Signing is only possible with addresses of the type 'legacy'.</source> - <context-group purpose="location"><context context-type="linenumber">114</context></context-group> + <context-group purpose="location"><context context-type="linenumber">104</context></context-group> </trans-unit> - <trans-unit id="_msg19"> + <trans-unit id="_msg17"> <source xml:space="preserve">&Copy Address</source> - <context-group purpose="location"><context context-type="linenumber">122</context></context-group> + <context-group purpose="location"><context context-type="linenumber">112</context></context-group> </trans-unit> - <trans-unit id="_msg20"> + <trans-unit id="_msg18"> <source xml:space="preserve">Copy &Label</source> - <context-group purpose="location"><context context-type="linenumber">123</context></context-group> + <context-group purpose="location"><context context-type="linenumber">113</context></context-group> </trans-unit> - <trans-unit id="_msg21"> + <trans-unit id="_msg19"> <source xml:space="preserve">&Edit</source> - <context-group purpose="location"><context context-type="linenumber">124</context></context-group> + <context-group purpose="location"><context context-type="linenumber">114</context></context-group> </trans-unit> - <trans-unit id="_msg22"> + <trans-unit id="_msg20"> <source xml:space="preserve">Export Address List</source> - <context-group purpose="location"><context context-type="linenumber">288</context></context-group> + <context-group purpose="location"><context context-type="linenumber">279</context></context-group> </trans-unit> - <trans-unit id="_msg23"> + <trans-unit id="_msg21"> <source xml:space="preserve">Comma separated file</source> - <context-group purpose="location"><context context-type="linenumber">291</context></context-group> + <context-group purpose="location"><context context-type="linenumber">282</context></context-group> <note annotates="source" from="developer">Expanded name of the CSV file format. See: https://en.wikipedia.org/wiki/Comma-separated_values.</note> </trans-unit> - <trans-unit id="_msg24"> + <trans-unit id="_msg22"> <source xml:space="preserve">There was an error trying to save the address list to %1. Please try again.</source> - <context-group purpose="location"><context context-type="linenumber">307</context></context-group> + <context-group purpose="location"><context context-type="linenumber">298</context></context-group> <note annotates="source" from="developer">An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</note> </trans-unit> + <trans-unit id="_msg23"> + <source xml:space="preserve">Sending addresses - %1</source> + <context-group purpose="location"><context context-type="linenumber">330</context></context-group> + </trans-unit> + <trans-unit id="_msg24"> + <source xml:space="preserve">Receiving addresses - %1</source> + <context-group purpose="location"><context context-type="linenumber">331</context></context-group> + </trans-unit> <trans-unit id="_msg25"> <source xml:space="preserve">Exporting Failed</source> - <context-group purpose="location"><context context-type="linenumber">304</context></context-group> + <context-group purpose="location"><context context-type="linenumber">295</context></context-group> </trans-unit> </group> </body></file> @@ -185,7 +185,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <trans-unit id="_msg41"> <source xml:space="preserve">Wallet encrypted</source> <context-group purpose="location"><context context-type="linenumber">126</context></context-group> - <context-group purpose="location"><context context-type="linenumber">181</context></context-group> + <context-group purpose="location"><context context-type="linenumber">184</context></context-group> </trans-unit> <trans-unit id="_msg42"> <source xml:space="preserve">Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> @@ -219,7 +219,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <source xml:space="preserve">Wallet encryption failed</source> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> <context-group purpose="location"><context context-type="linenumber">144</context></context-group> - <context-group purpose="location"><context context-type="linenumber">203</context></context-group> + <context-group purpose="location"><context context-type="linenumber">206</context></context-group> </trans-unit> <trans-unit id="_msg50"> <source xml:space="preserve">Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source> @@ -228,18 +228,18 @@ Signing is only possible with addresses of the type 'legacy'.</source> <trans-unit id="_msg51"> <source xml:space="preserve">The supplied passphrases do not match.</source> <context-group purpose="location"><context context-type="linenumber">145</context></context-group> - <context-group purpose="location"><context context-type="linenumber">204</context></context-group> + <context-group purpose="location"><context context-type="linenumber">207</context></context-group> </trans-unit> <trans-unit id="_msg52"> <source xml:space="preserve">Wallet unlock failed</source> <context-group purpose="location"><context context-type="linenumber">158</context></context-group> <context-group purpose="location"><context context-type="linenumber">161</context></context-group> - <context-group purpose="location"><context context-type="linenumber">173</context></context-group> + <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> <trans-unit id="_msg53"> <source xml:space="preserve">The passphrase entered for the wallet decryption was incorrect.</source> <context-group purpose="location"><context context-type="linenumber">159</context></context-group> - <context-group purpose="location"><context context-type="linenumber">190</context></context-group> + <context-group purpose="location"><context context-type="linenumber">193</context></context-group> </trans-unit> <trans-unit id="_msg54"> <source xml:space="preserve">The passphrase entered for the wallet decryption is incorrect. It contains a null character (ie - a zero byte). If the passphrase was set with a version of this software prior to 25.0, please try again with only the characters up to — but not including — the first null character. If this is successful, please set a new passphrase to avoid this issue in the future.</source> @@ -247,21 +247,21 @@ Signing is only possible with addresses of the type 'legacy'.</source> </trans-unit> <trans-unit id="_msg55"> <source xml:space="preserve">Wallet passphrase was successfully changed.</source> - <context-group purpose="location"><context context-type="linenumber">182</context></context-group> + <context-group purpose="location"><context context-type="linenumber">185</context></context-group> </trans-unit> <trans-unit id="_msg56"> <source xml:space="preserve">Passphrase change failed</source> - <context-group purpose="location"><context context-type="linenumber">189</context></context-group> <context-group purpose="location"><context context-type="linenumber">192</context></context-group> + <context-group purpose="location"><context context-type="linenumber">195</context></context-group> </trans-unit> <trans-unit id="_msg57"> <source xml:space="preserve">The old passphrase entered for the wallet decryption is incorrect. It contains a null character (ie - a zero byte). If the passphrase was set with a version of this software prior to 25.0, please try again with only the characters up to — but not including — the first null character.</source> - <context-group purpose="location"><context context-type="linenumber">193</context></context-group> + <context-group purpose="location"><context context-type="linenumber">196</context></context-group> </trans-unit> <trans-unit id="_msg58"> <source xml:space="preserve">Warning: The Caps Lock key is on!</source> - <context-group purpose="location"><context context-type="linenumber">238</context></context-group> - <context-group purpose="location"><context context-type="linenumber">271</context></context-group> + <context-group purpose="location"><context context-type="linenumber">241</context></context-group> + <context-group purpose="location"><context context-type="linenumber">274</context></context-group> </trans-unit> </group> </body></file> @@ -325,59 +325,59 @@ Signing is only possible with addresses of the type 'legacy'.</source> <group restype="x-trolltech-linguist-context" resname="BitcoinGUI"> <trans-unit id="_msg70"> <source xml:space="preserve">&Overview</source> - <context-group purpose="location"><context context-type="linenumber">254</context></context-group> + <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> <trans-unit id="_msg71"> <source xml:space="preserve">Show general overview of wallet</source> - <context-group purpose="location"><context context-type="linenumber">255</context></context-group> + <context-group purpose="location"><context context-type="linenumber">251</context></context-group> </trans-unit> <trans-unit id="_msg72"> <source xml:space="preserve">&Transactions</source> - <context-group purpose="location"><context context-type="linenumber">275</context></context-group> + <context-group purpose="location"><context context-type="linenumber">271</context></context-group> </trans-unit> <trans-unit id="_msg73"> <source xml:space="preserve">Browse transaction history</source> - <context-group purpose="location"><context context-type="linenumber">276</context></context-group> + <context-group purpose="location"><context context-type="linenumber">272</context></context-group> </trans-unit> <trans-unit id="_msg74"> <source xml:space="preserve">E&xit</source> - <context-group purpose="location"><context context-type="linenumber">295</context></context-group> + <context-group purpose="location"><context context-type="linenumber">291</context></context-group> </trans-unit> <trans-unit id="_msg75"> <source xml:space="preserve">Quit application</source> - <context-group purpose="location"><context context-type="linenumber">296</context></context-group> + <context-group purpose="location"><context context-type="linenumber">292</context></context-group> </trans-unit> <trans-unit id="_msg76"> <source xml:space="preserve">&About %1</source> - <context-group purpose="location"><context context-type="linenumber">299</context></context-group> + <context-group purpose="location"><context context-type="linenumber">295</context></context-group> </trans-unit> <trans-unit id="_msg77"> <source xml:space="preserve">Show information about %1</source> - <context-group purpose="location"><context context-type="linenumber">300</context></context-group> + <context-group purpose="location"><context context-type="linenumber">296</context></context-group> </trans-unit> <trans-unit id="_msg78"> <source xml:space="preserve">About &Qt</source> - <context-group purpose="location"><context context-type="linenumber">303</context></context-group> + <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> <trans-unit id="_msg79"> <source xml:space="preserve">Show information about Qt</source> - <context-group purpose="location"><context context-type="linenumber">304</context></context-group> + <context-group purpose="location"><context context-type="linenumber">300</context></context-group> </trans-unit> <trans-unit id="_msg80"> <source xml:space="preserve">Modify configuration options for %1</source> - <context-group purpose="location"><context context-type="linenumber">307</context></context-group> + <context-group purpose="location"><context context-type="linenumber">303</context></context-group> </trans-unit> <trans-unit id="_msg81"> <source xml:space="preserve">Create a new wallet</source> - <context-group purpose="location"><context context-type="linenumber">351</context></context-group> + <context-group purpose="location"><context context-type="linenumber">347</context></context-group> </trans-unit> <trans-unit id="_msg82"> <source xml:space="preserve">&Minimize</source> - <context-group purpose="location"><context context-type="linenumber">515</context></context-group> + <context-group purpose="location"><context context-type="linenumber">510</context></context-group> </trans-unit> <trans-unit id="_msg83"> <source xml:space="preserve">Wallet:</source> - <context-group purpose="location"><context context-type="linenumber">594</context></context-group> + <context-group purpose="location"><context context-type="linenumber">589</context></context-group> </trans-unit> <trans-unit id="_msg84"> <source xml:space="preserve">Network activity disabled.</source> @@ -386,99 +386,99 @@ Signing is only possible with addresses of the type 'legacy'.</source> </trans-unit> <trans-unit id="_msg85"> <source xml:space="preserve">Proxy is <b>enabled</b>: %1</source> - <context-group purpose="location"><context context-type="linenumber">1437</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1452</context></context-group> </trans-unit> <trans-unit id="_msg86"> <source xml:space="preserve">Send coins to a Bitcoin address</source> - <context-group purpose="location"><context context-type="linenumber">262</context></context-group> + <context-group purpose="location"><context context-type="linenumber">258</context></context-group> </trans-unit> <trans-unit id="_msg87"> <source xml:space="preserve">Backup wallet to another location</source> - <context-group purpose="location"><context context-type="linenumber">315</context></context-group> + <context-group purpose="location"><context context-type="linenumber">311</context></context-group> </trans-unit> <trans-unit id="_msg88"> <source xml:space="preserve">Change the passphrase used for wallet encryption</source> - <context-group purpose="location"><context context-type="linenumber">317</context></context-group> + <context-group purpose="location"><context context-type="linenumber">313</context></context-group> </trans-unit> <trans-unit id="_msg89"> <source xml:space="preserve">&Send</source> - <context-group purpose="location"><context context-type="linenumber">261</context></context-group> + <context-group purpose="location"><context context-type="linenumber">257</context></context-group> </trans-unit> <trans-unit id="_msg90"> <source xml:space="preserve">&Receive</source> - <context-group purpose="location"><context context-type="linenumber">268</context></context-group> + <context-group purpose="location"><context context-type="linenumber">264</context></context-group> </trans-unit> <trans-unit id="_msg91"> <source xml:space="preserve">&Options…</source> - <context-group purpose="location"><context context-type="linenumber">306</context></context-group> + <context-group purpose="location"><context context-type="linenumber">302</context></context-group> </trans-unit> <trans-unit id="_msg92"> <source xml:space="preserve">&Encrypt Wallet…</source> - <context-group purpose="location"><context context-type="linenumber">311</context></context-group> + <context-group purpose="location"><context context-type="linenumber">307</context></context-group> </trans-unit> <trans-unit id="_msg93"> <source xml:space="preserve">Encrypt the private keys that belong to your wallet</source> - <context-group purpose="location"><context context-type="linenumber">312</context></context-group> + <context-group purpose="location"><context context-type="linenumber">308</context></context-group> </trans-unit> <trans-unit id="_msg94"> <source xml:space="preserve">&Backup Wallet…</source> - <context-group purpose="location"><context context-type="linenumber">314</context></context-group> + <context-group purpose="location"><context context-type="linenumber">310</context></context-group> </trans-unit> <trans-unit id="_msg95"> <source xml:space="preserve">&Change Passphrase…</source> - <context-group purpose="location"><context context-type="linenumber">316</context></context-group> + <context-group purpose="location"><context context-type="linenumber">312</context></context-group> </trans-unit> <trans-unit id="_msg96"> <source xml:space="preserve">Sign &message…</source> - <context-group purpose="location"><context context-type="linenumber">318</context></context-group> + <context-group purpose="location"><context context-type="linenumber">314</context></context-group> </trans-unit> <trans-unit id="_msg97"> <source xml:space="preserve">Sign messages with your Bitcoin addresses to prove you own them</source> - <context-group purpose="location"><context context-type="linenumber">319</context></context-group> + <context-group purpose="location"><context context-type="linenumber">315</context></context-group> </trans-unit> <trans-unit id="_msg98"> <source xml:space="preserve">&Verify message…</source> - <context-group purpose="location"><context context-type="linenumber">320</context></context-group> + <context-group purpose="location"><context context-type="linenumber">316</context></context-group> </trans-unit> <trans-unit id="_msg99"> <source xml:space="preserve">Verify messages to ensure they were signed with specified Bitcoin addresses</source> - <context-group purpose="location"><context context-type="linenumber">321</context></context-group> + <context-group purpose="location"><context context-type="linenumber">317</context></context-group> </trans-unit> <trans-unit id="_msg100"> <source xml:space="preserve">&Load PSBT from file…</source> - <context-group purpose="location"><context context-type="linenumber">322</context></context-group> + <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> <trans-unit id="_msg101"> <source xml:space="preserve">Open &URI…</source> - <context-group purpose="location"><context context-type="linenumber">338</context></context-group> + <context-group purpose="location"><context context-type="linenumber">334</context></context-group> </trans-unit> <trans-unit id="_msg102"> <source xml:space="preserve">Close Wallet…</source> - <context-group purpose="location"><context context-type="linenumber">346</context></context-group> + <context-group purpose="location"><context context-type="linenumber">342</context></context-group> </trans-unit> <trans-unit id="_msg103"> <source xml:space="preserve">Create Wallet…</source> - <context-group purpose="location"><context context-type="linenumber">349</context></context-group> + <context-group purpose="location"><context context-type="linenumber">345</context></context-group> </trans-unit> <trans-unit id="_msg104"> <source xml:space="preserve">Close All Wallets…</source> - <context-group purpose="location"><context context-type="linenumber">359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">355</context></context-group> </trans-unit> <trans-unit id="_msg105"> <source xml:space="preserve">&File</source> - <context-group purpose="location"><context context-type="linenumber">482</context></context-group> + <context-group purpose="location"><context context-type="linenumber">476</context></context-group> </trans-unit> <trans-unit id="_msg106"> <source xml:space="preserve">&Settings</source> - <context-group purpose="location"><context context-type="linenumber">502</context></context-group> + <context-group purpose="location"><context context-type="linenumber">497</context></context-group> </trans-unit> <trans-unit id="_msg107"> <source xml:space="preserve">&Help</source> - <context-group purpose="location"><context context-type="linenumber">563</context></context-group> + <context-group purpose="location"><context context-type="linenumber">558</context></context-group> </trans-unit> <trans-unit id="_msg108"> <source xml:space="preserve">Tabs toolbar</source> - <context-group purpose="location"><context context-type="linenumber">574</context></context-group> + <context-group purpose="location"><context context-type="linenumber">569</context></context-group> </trans-unit> <trans-unit id="_msg109"> <source xml:space="preserve">Syncing Headers (%1%)…</source> @@ -502,15 +502,15 @@ Signing is only possible with addresses of the type 'legacy'.</source> </trans-unit> <trans-unit id="_msg114"> <source xml:space="preserve">Request payments (generates QR codes and bitcoin: URIs)</source> - <context-group purpose="location"><context context-type="linenumber">269</context></context-group> + <context-group purpose="location"><context context-type="linenumber">265</context></context-group> </trans-unit> <trans-unit id="_msg115"> <source xml:space="preserve">Show the list of used sending addresses and labels</source> - <context-group purpose="location"><context context-type="linenumber">334</context></context-group> + <context-group purpose="location"><context context-type="linenumber">330</context></context-group> </trans-unit> <trans-unit id="_msg116"> <source xml:space="preserve">Show the list of used receiving addresses and labels</source> - <context-group purpose="location"><context context-type="linenumber">336</context></context-group> + <context-group purpose="location"><context context-type="linenumber">332</context></context-group> </trans-unit> <trans-unit id="_msg117"> <source xml:space="preserve">&Command-line options</source> @@ -543,15 +543,15 @@ Signing is only possible with addresses of the type 'legacy'.</source> </trans-unit> <trans-unit id="_msg123"> <source xml:space="preserve">Error</source> - <context-group purpose="location"><context context-type="linenumber">1200</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1215</context></context-group> </trans-unit> <trans-unit id="_msg124"> <source xml:space="preserve">Warning</source> - <context-group purpose="location"><context context-type="linenumber">1204</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1219</context></context-group> </trans-unit> <trans-unit id="_msg125"> <source xml:space="preserve">Information</source> - <context-group purpose="location"><context context-type="linenumber">1208</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1223</context></context-group> </trans-unit> <trans-unit id="_msg126"> <source xml:space="preserve">Up to date</source> @@ -559,309 +559,325 @@ Signing is only possible with addresses of the type 'legacy'.</source> </trans-unit> <trans-unit id="_msg127"> <source xml:space="preserve">Ctrl+Q</source> - <context-group purpose="location"><context context-type="linenumber">297</context></context-group> + <context-group purpose="location"><context context-type="linenumber">293</context></context-group> </trans-unit> <trans-unit id="_msg128"> <source xml:space="preserve">Load Partially Signed Bitcoin Transaction</source> - <context-group purpose="location"><context context-type="linenumber">323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">319</context></context-group> </trans-unit> <trans-unit id="_msg129"> <source xml:space="preserve">Load PSBT from &clipboard…</source> - <context-group purpose="location"><context context-type="linenumber">324</context></context-group> + <context-group purpose="location"><context context-type="linenumber">320</context></context-group> </trans-unit> <trans-unit id="_msg130"> <source xml:space="preserve">Load Partially Signed Bitcoin Transaction from clipboard</source> - <context-group purpose="location"><context context-type="linenumber">325</context></context-group> + <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> <trans-unit id="_msg131"> <source xml:space="preserve">Node window</source> - <context-group purpose="location"><context context-type="linenumber">327</context></context-group> + <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> <trans-unit id="_msg132"> <source xml:space="preserve">Open node debugging and diagnostic console</source> - <context-group purpose="location"><context context-type="linenumber">328</context></context-group> + <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> <trans-unit id="_msg133"> <source xml:space="preserve">&Sending addresses</source> - <context-group purpose="location"><context context-type="linenumber">333</context></context-group> + <context-group purpose="location"><context context-type="linenumber">329</context></context-group> </trans-unit> <trans-unit id="_msg134"> <source xml:space="preserve">&Receiving addresses</source> - <context-group purpose="location"><context context-type="linenumber">335</context></context-group> + <context-group purpose="location"><context context-type="linenumber">331</context></context-group> </trans-unit> <trans-unit id="_msg135"> <source xml:space="preserve">Open a bitcoin: URI</source> - <context-group purpose="location"><context context-type="linenumber">339</context></context-group> + <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> <trans-unit id="_msg136"> <source xml:space="preserve">Open Wallet</source> - <context-group purpose="location"><context context-type="linenumber">341</context></context-group> + <context-group purpose="location"><context context-type="linenumber">337</context></context-group> </trans-unit> <trans-unit id="_msg137"> <source xml:space="preserve">Open a wallet</source> - <context-group purpose="location"><context context-type="linenumber">343</context></context-group> + <context-group purpose="location"><context context-type="linenumber">339</context></context-group> </trans-unit> <trans-unit id="_msg138"> <source xml:space="preserve">Close wallet</source> - <context-group purpose="location"><context context-type="linenumber">347</context></context-group> + <context-group purpose="location"><context context-type="linenumber">343</context></context-group> </trans-unit> <trans-unit id="_msg139"> <source xml:space="preserve">Restore Wallet…</source> - <context-group purpose="location"><context context-type="linenumber">354</context></context-group> + <context-group purpose="location"><context context-type="linenumber">350</context></context-group> <note annotates="source" from="developer">Name of the menu item that restores wallet from a backup file.</note> </trans-unit> <trans-unit id="_msg140"> <source xml:space="preserve">Restore a wallet from a backup file</source> - <context-group purpose="location"><context context-type="linenumber">357</context></context-group> + <context-group purpose="location"><context context-type="linenumber">353</context></context-group> <note annotates="source" from="developer">Status tip for Restore Wallet menu item</note> </trans-unit> <trans-unit id="_msg141"> <source xml:space="preserve">Close all wallets</source> - <context-group purpose="location"><context context-type="linenumber">360</context></context-group> + <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> <trans-unit id="_msg142"> + <source xml:space="preserve">Migrate Wallet</source> + <context-group purpose="location"><context context-type="linenumber">358</context></context-group> + </trans-unit> + <trans-unit id="_msg143"> + <source xml:space="preserve">Migrate a wallet</source> + <context-group purpose="location"><context context-type="linenumber">360</context></context-group> + </trans-unit> + <trans-unit id="_msg144"> <source xml:space="preserve">Show the %1 help message to get a list with possible Bitcoin command-line options</source> <context-group purpose="location"><context context-type="linenumber">364</context></context-group> </trans-unit> - <trans-unit id="_msg143"> + <trans-unit id="_msg145"> <source xml:space="preserve">&Mask values</source> <context-group purpose="location"><context context-type="linenumber">366</context></context-group> </trans-unit> - <trans-unit id="_msg144"> + <trans-unit id="_msg146"> <source xml:space="preserve">Mask the values in the Overview tab</source> <context-group purpose="location"><context context-type="linenumber">368</context></context-group> </trans-unit> - <trans-unit id="_msg145"> + <trans-unit id="_msg147"> <source xml:space="preserve">default wallet</source> <context-group purpose="location"><context context-type="linenumber">399</context></context-group> </trans-unit> - <trans-unit id="_msg146"> + <trans-unit id="_msg148"> <source xml:space="preserve">No wallets available</source> <context-group purpose="location"><context context-type="linenumber">420</context></context-group> </trans-unit> - <trans-unit id="_msg147"> + <trans-unit id="_msg149"> <source xml:space="preserve">Wallet Data</source> <context-group purpose="location"><context context-type="linenumber">426</context></context-group> <note annotates="source" from="developer">Name of the wallet data file format.</note> </trans-unit> - <trans-unit id="_msg148"> + <trans-unit id="_msg150"> <source xml:space="preserve">Load Wallet Backup</source> <context-group purpose="location"><context context-type="linenumber">429</context></context-group> <note annotates="source" from="developer">The title for Restore Wallet File Windows</note> </trans-unit> - <trans-unit id="_msg149"> + <trans-unit id="_msg151"> <source xml:space="preserve">Restore Wallet</source> <context-group purpose="location"><context context-type="linenumber">437</context></context-group> <note annotates="source" from="developer">Title of pop-up window shown when the user is attempting to restore a wallet.</note> </trans-unit> - <trans-unit id="_msg150"> + <trans-unit id="_msg152"> <source xml:space="preserve">Wallet Name</source> <context-group purpose="location"><context context-type="linenumber">439</context></context-group> <note annotates="source" from="developer">Label of the input field where the name of the wallet is entered.</note> </trans-unit> - <trans-unit id="_msg151"> + <trans-unit id="_msg153"> <source xml:space="preserve">&Window</source> - <context-group purpose="location"><context context-type="linenumber">513</context></context-group> + <context-group purpose="location"><context context-type="linenumber">508</context></context-group> </trans-unit> - <trans-unit id="_msg152"> + <trans-unit id="_msg154"> <source xml:space="preserve">Ctrl+M</source> - <context-group purpose="location"><context context-type="linenumber">516</context></context-group> + <context-group purpose="location"><context context-type="linenumber">511</context></context-group> </trans-unit> - <trans-unit id="_msg153"> + <trans-unit id="_msg155"> <source xml:space="preserve">Zoom</source> - <context-group purpose="location"><context context-type="linenumber">525</context></context-group> + <context-group purpose="location"><context context-type="linenumber">520</context></context-group> </trans-unit> - <trans-unit id="_msg154"> + <trans-unit id="_msg156"> <source xml:space="preserve">Main Window</source> - <context-group purpose="location"><context context-type="linenumber">543</context></context-group> + <context-group purpose="location"><context context-type="linenumber">538</context></context-group> </trans-unit> - <trans-unit id="_msg155"> + <trans-unit id="_msg157"> <source xml:space="preserve">%1 client</source> - <context-group purpose="location"><context context-type="linenumber">815</context></context-group> + <context-group purpose="location"><context context-type="linenumber">812</context></context-group> </trans-unit> - <trans-unit id="_msg156"> + <trans-unit id="_msg158"> <source xml:space="preserve">&Hide</source> <context-group purpose="location"><context context-type="linenumber">880</context></context-group> </trans-unit> - <trans-unit id="_msg157"> + <trans-unit id="_msg159"> <source xml:space="preserve">S&how</source> <context-group purpose="location"><context context-type="linenumber">881</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">998</context></context-group> <note annotates="source" from="developer">A substring of the tooltip.</note> - <trans-unit id="_msg158[0]"> + <trans-unit id="_msg160[0]"> <source xml:space="preserve">%n active connection(s) to Bitcoin network.</source> </trans-unit> - <trans-unit id="_msg158[1]"> + <trans-unit id="_msg160[1]"> <source xml:space="preserve">%n active connection(s) to Bitcoin network.</source> </trans-unit> </group> - <trans-unit id="_msg159"> + <trans-unit id="_msg161"> <source xml:space="preserve">Click for more actions.</source> <context-group purpose="location"><context context-type="linenumber">1008</context></context-group> <note annotates="source" from="developer">A substring of the tooltip. "More actions" are available via the context menu.</note> </trans-unit> - <trans-unit id="_msg160"> + <trans-unit id="_msg162"> <source xml:space="preserve">Show Peers tab</source> <context-group purpose="location"><context context-type="linenumber">1025</context></context-group> <note annotates="source" from="developer">A context menu item. The "Peers tab" is an element of the "Node window".</note> </trans-unit> - <trans-unit id="_msg161"> + <trans-unit id="_msg163"> <source xml:space="preserve">Disable network activity</source> <context-group purpose="location"><context context-type="linenumber">1033</context></context-group> <note annotates="source" from="developer">A context menu item.</note> </trans-unit> - <trans-unit id="_msg162"> + <trans-unit id="_msg164"> <source xml:space="preserve">Enable network activity</source> <context-group purpose="location"><context context-type="linenumber">1035</context></context-group> <note annotates="source" from="developer">A context menu item. The network activity was disabled previously.</note> </trans-unit> - <trans-unit id="_msg163"> + <trans-unit id="_msg165"> <source xml:space="preserve">Pre-syncing Headers (%1%)…</source> <context-group purpose="location"><context context-type="linenumber">1052</context></context-group> </trans-unit> - <trans-unit id="_msg164"> + <trans-unit id="_msg166"> + <source xml:space="preserve">Error creating wallet</source> + <context-group purpose="location"><context context-type="linenumber">1191</context></context-group> + </trans-unit> + <trans-unit id="_msg167"> + <source xml:space="preserve">Cannot create new wallet, the software was compiled without sqlite support (required for descriptor wallets)</source> + <context-group purpose="location"><context context-type="linenumber">1191</context></context-group> + </trans-unit> + <trans-unit id="_msg168"> <source xml:space="preserve">Error: %1</source> - <context-group purpose="location"><context context-type="linenumber">1201</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1216</context></context-group> </trans-unit> - <trans-unit id="_msg165"> + <trans-unit id="_msg169"> <source xml:space="preserve">Warning: %1</source> - <context-group purpose="location"><context context-type="linenumber">1205</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1220</context></context-group> </trans-unit> - <trans-unit id="_msg166"> + <trans-unit id="_msg170"> <source xml:space="preserve">Date: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1313</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1328</context></context-group> </trans-unit> - <trans-unit id="_msg167"> + <trans-unit id="_msg171"> <source xml:space="preserve">Amount: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1314</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1329</context></context-group> </trans-unit> - <trans-unit id="_msg168"> + <trans-unit id="_msg172"> <source xml:space="preserve">Wallet: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1316</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1331</context></context-group> </trans-unit> - <trans-unit id="_msg169"> + <trans-unit id="_msg173"> <source xml:space="preserve">Type: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1318</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1333</context></context-group> </trans-unit> - <trans-unit id="_msg170"> + <trans-unit id="_msg174"> <source xml:space="preserve">Label: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1320</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1335</context></context-group> </trans-unit> - <trans-unit id="_msg171"> + <trans-unit id="_msg175"> <source xml:space="preserve">Address: %1 </source> - <context-group purpose="location"><context context-type="linenumber">1322</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1337</context></context-group> </trans-unit> - <trans-unit id="_msg172"> + <trans-unit id="_msg176"> <source xml:space="preserve">Sent transaction</source> - <context-group purpose="location"><context context-type="linenumber">1323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1338</context></context-group> </trans-unit> - <trans-unit id="_msg173"> + <trans-unit id="_msg177"> <source xml:space="preserve">Incoming transaction</source> - <context-group purpose="location"><context context-type="linenumber">1323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1338</context></context-group> </trans-unit> - <trans-unit id="_msg174"> + <trans-unit id="_msg178"> <source xml:space="preserve">HD key generation is <b>enabled</b></source> - <context-group purpose="location"><context context-type="linenumber">1375</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1390</context></context-group> </trans-unit> - <trans-unit id="_msg175"> + <trans-unit id="_msg179"> <source xml:space="preserve">HD key generation is <b>disabled</b></source> - <context-group purpose="location"><context context-type="linenumber">1375</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1390</context></context-group> </trans-unit> - <trans-unit id="_msg176"> + <trans-unit id="_msg180"> <source xml:space="preserve">Private key <b>disabled</b></source> - <context-group purpose="location"><context context-type="linenumber">1375</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1390</context></context-group> </trans-unit> - <trans-unit id="_msg177"> + <trans-unit id="_msg181"> <source xml:space="preserve">Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> - <context-group purpose="location"><context context-type="linenumber">1398</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1413</context></context-group> </trans-unit> - <trans-unit id="_msg178"> + <trans-unit id="_msg182"> <source xml:space="preserve">Wallet is <b>encrypted</b> and currently <b>locked</b></source> - <context-group purpose="location"><context context-type="linenumber">1406</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1421</context></context-group> </trans-unit> - <trans-unit id="_msg179"> + <trans-unit id="_msg183"> <source xml:space="preserve">Original message:</source> - <context-group purpose="location"><context context-type="linenumber">1525</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1540</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="UnitDisplayStatusBarControl"> - <trans-unit id="_msg180"> + <trans-unit id="_msg184"> <source xml:space="preserve">Unit to show amounts in. Click to select another unit.</source> - <context-group purpose="location"><context context-type="linenumber">1564</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1579</context></context-group> </trans-unit> </group> </body></file> <file original="../forms/coincontroldialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="CoinControlDialog"> - <trans-unit id="_msg181"> + <trans-unit id="_msg185"> <source xml:space="preserve">Coin Selection</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg182"> + <trans-unit id="_msg186"> <source xml:space="preserve">Quantity:</source> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg183"> + <trans-unit id="_msg187"> <source xml:space="preserve">Bytes:</source> <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg184"> + <trans-unit id="_msg188"> <source xml:space="preserve">Amount:</source> <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> - <trans-unit id="_msg185"> + <trans-unit id="_msg189"> <source xml:space="preserve">Fee:</source> <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> - <trans-unit id="_msg186"> + <trans-unit id="_msg190"> <source xml:space="preserve">After Fee:</source> <context-group purpose="location"><context context-type="linenumber">218</context></context-group> </trans-unit> - <trans-unit id="_msg187"> + <trans-unit id="_msg191"> <source xml:space="preserve">Change:</source> <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg188"> + <trans-unit id="_msg192"> <source xml:space="preserve">(un)select all</source> <context-group purpose="location"><context context-type="linenumber">306</context></context-group> </trans-unit> - <trans-unit id="_msg189"> + <trans-unit id="_msg193"> <source xml:space="preserve">Tree mode</source> <context-group purpose="location"><context context-type="linenumber">322</context></context-group> </trans-unit> - <trans-unit id="_msg190"> + <trans-unit id="_msg194"> <source xml:space="preserve">List mode</source> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg191"> + <trans-unit id="_msg195"> <source xml:space="preserve">Amount</source> <context-group purpose="location"><context context-type="linenumber">391</context></context-group> </trans-unit> - <trans-unit id="_msg192"> + <trans-unit id="_msg196"> <source xml:space="preserve">Received with label</source> <context-group purpose="location"><context context-type="linenumber">396</context></context-group> </trans-unit> - <trans-unit id="_msg193"> + <trans-unit id="_msg197"> <source xml:space="preserve">Received with address</source> <context-group purpose="location"><context context-type="linenumber">401</context></context-group> </trans-unit> - <trans-unit id="_msg194"> + <trans-unit id="_msg198"> <source xml:space="preserve">Date</source> <context-group purpose="location"><context context-type="linenumber">406</context></context-group> </trans-unit> - <trans-unit id="_msg195"> + <trans-unit id="_msg199"> <source xml:space="preserve">Confirmations</source> <context-group purpose="location"><context context-type="linenumber">411</context></context-group> </trans-unit> - <trans-unit id="_msg196"> + <trans-unit id="_msg200"> <source xml:space="preserve">Confirmed</source> <context-group purpose="location"><context context-type="linenumber">414</context></context-group> </trans-unit> @@ -869,72 +885,72 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../coincontroldialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="CoinControlDialog"> - <trans-unit id="_msg197"> + <trans-unit id="_msg201"> <source xml:space="preserve">Copy amount</source> <context-group purpose="location"><context context-type="linenumber">69</context></context-group> </trans-unit> - <trans-unit id="_msg198"> + <trans-unit id="_msg202"> <source xml:space="preserve">&Copy address</source> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg199"> + <trans-unit id="_msg203"> <source xml:space="preserve">Copy &label</source> <context-group purpose="location"><context context-type="linenumber">59</context></context-group> </trans-unit> - <trans-unit id="_msg200"> + <trans-unit id="_msg204"> <source xml:space="preserve">Copy &amount</source> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> - <trans-unit id="_msg201"> + <trans-unit id="_msg205"> <source xml:space="preserve">Copy transaction &ID and output index</source> <context-group purpose="location"><context context-type="linenumber">61</context></context-group> </trans-unit> - <trans-unit id="_msg202"> + <trans-unit id="_msg206"> <source xml:space="preserve">L&ock unspent</source> <context-group purpose="location"><context context-type="linenumber">63</context></context-group> </trans-unit> - <trans-unit id="_msg203"> + <trans-unit id="_msg207"> <source xml:space="preserve">&Unlock unspent</source> <context-group purpose="location"><context context-type="linenumber">64</context></context-group> </trans-unit> - <trans-unit id="_msg204"> + <trans-unit id="_msg208"> <source xml:space="preserve">Copy quantity</source> <context-group purpose="location"><context context-type="linenumber">68</context></context-group> </trans-unit> - <trans-unit id="_msg205"> + <trans-unit id="_msg209"> <source xml:space="preserve">Copy fee</source> <context-group purpose="location"><context context-type="linenumber">70</context></context-group> </trans-unit> - <trans-unit id="_msg206"> + <trans-unit id="_msg210"> <source xml:space="preserve">Copy after fee</source> <context-group purpose="location"><context context-type="linenumber">71</context></context-group> </trans-unit> - <trans-unit id="_msg207"> + <trans-unit id="_msg211"> <source xml:space="preserve">Copy bytes</source> <context-group purpose="location"><context context-type="linenumber">72</context></context-group> </trans-unit> - <trans-unit id="_msg208"> + <trans-unit id="_msg212"> <source xml:space="preserve">Copy change</source> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> - <trans-unit id="_msg209"> + <trans-unit id="_msg213"> <source xml:space="preserve">(%1 locked)</source> <context-group purpose="location"><context context-type="linenumber">371</context></context-group> </trans-unit> - <trans-unit id="_msg210"> + <trans-unit id="_msg214"> <source xml:space="preserve">Can vary +/- %1 satoshi(s) per input.</source> <context-group purpose="location"><context context-type="linenumber">524</context></context-group> </trans-unit> - <trans-unit id="_msg211"> + <trans-unit id="_msg215"> <source xml:space="preserve">(no label)</source> <context-group purpose="location"><context context-type="linenumber">569</context></context-group> <context-group purpose="location"><context context-type="linenumber">623</context></context-group> </trans-unit> - <trans-unit id="_msg212"> + <trans-unit id="_msg216"> <source xml:space="preserve">change from %1 (%2)</source> <context-group purpose="location"><context context-type="linenumber">616</context></context-group> </trans-unit> - <trans-unit id="_msg213"> + <trans-unit id="_msg217"> <source xml:space="preserve">(change)</source> <context-group purpose="location"><context context-type="linenumber">617</context></context-group> </trans-unit> @@ -942,114 +958,160 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../walletcontroller.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="CreateWalletActivity"> - <trans-unit id="_msg214"> + <trans-unit id="_msg218"> <source xml:space="preserve">Create Wallet</source> <context-group purpose="location"><context context-type="linenumber">246</context></context-group> <note annotates="source" from="developer">Title of window indicating the progress of creation of a new wallet.</note> </trans-unit> - <trans-unit id="_msg215"> + <trans-unit id="_msg219"> <source xml:space="preserve">Creating Wallet <b>%1</b>…</source> <context-group purpose="location"><context context-type="linenumber">249</context></context-group> <note annotates="source" from="developer">Descriptive text of the create wallet progress window which indicates to the user which wallet is currently being created.</note> </trans-unit> - <trans-unit id="_msg216"> + <trans-unit id="_msg220"> <source xml:space="preserve">Create wallet failed</source> - <context-group purpose="location"><context context-type="linenumber">282</context></context-group> + <context-group purpose="location"><context context-type="linenumber">281</context></context-group> </trans-unit> - <trans-unit id="_msg217"> + <trans-unit id="_msg221"> <source xml:space="preserve">Create wallet warning</source> - <context-group purpose="location"><context context-type="linenumber">284</context></context-group> + <context-group purpose="location"><context context-type="linenumber">283</context></context-group> </trans-unit> - <trans-unit id="_msg218"> + <trans-unit id="_msg222"> <source xml:space="preserve">Can't list signers</source> - <context-group purpose="location"><context context-type="linenumber">300</context></context-group> + <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> - <trans-unit id="_msg219"> + <trans-unit id="_msg223"> <source xml:space="preserve">Too many external signers found</source> - <context-group purpose="location"><context context-type="linenumber">303</context></context-group> + <context-group purpose="location"><context context-type="linenumber">302</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="LoadWalletsActivity"> - <trans-unit id="_msg220"> + <trans-unit id="_msg224"> <source xml:space="preserve">Load Wallets</source> - <context-group purpose="location"><context context-type="linenumber">377</context></context-group> + <context-group purpose="location"><context context-type="linenumber">376</context></context-group> <note annotates="source" from="developer">Title of progress window which is displayed when wallets are being loaded.</note> </trans-unit> - <trans-unit id="_msg221"> + <trans-unit id="_msg225"> <source xml:space="preserve">Loading wallets…</source> - <context-group purpose="location"><context context-type="linenumber">380</context></context-group> + <context-group purpose="location"><context context-type="linenumber">379</context></context-group> <note annotates="source" from="developer">Descriptive text of the load wallets progress window which indicates to the user that wallets are currently being loaded.</note> </trans-unit> </group> + <group restype="x-trolltech-linguist-context" resname="MigrateWalletActivity"> + <trans-unit id="_msg226"> + <source xml:space="preserve">Migrate wallet</source> + <context-group purpose="location"><context context-type="linenumber">442</context></context-group> + </trans-unit> + <trans-unit id="_msg227"> + <source xml:space="preserve">Are you sure you wish to migrate the wallet <i>%1</i>?</source> + <context-group purpose="location"><context context-type="linenumber">443</context></context-group> + </trans-unit> + <trans-unit id="_msg228"> + <source xml:space="preserve">Migrating the wallet will convert this wallet to one or more descriptor wallets. A new wallet backup will need to be made. +If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts. +If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts. + +The migration process will create a backup of the wallet before migrating. This backup file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of an incorrect migration, the backup can be restored with the "Restore Wallet" functionality.</source> + <context-group purpose="location"><context context-type="linenumber">444</context></context-group> + </trans-unit> + <trans-unit id="_msg229"> + <source xml:space="preserve">Migrate Wallet</source> + <context-group purpose="location"><context context-type="linenumber">467</context></context-group> + </trans-unit> + <trans-unit id="_msg230"> + <source xml:space="preserve">Migrating Wallet <b>%1</b>…</source> + <context-group purpose="location"><context context-type="linenumber">467</context></context-group> + </trans-unit> + <trans-unit id="_msg231"> + <source xml:space="preserve">The wallet '%1' was migrated successfully.</source> + <context-group purpose="location"><context context-type="linenumber">473</context></context-group> + </trans-unit> + <trans-unit id="_msg232"> + <source xml:space="preserve"> Watchonly scripts have been migrated to a new wallet named '%1'.</source> + <context-group purpose="location"><context context-type="linenumber">475</context></context-group> + </trans-unit> + <trans-unit id="_msg233"> + <source xml:space="preserve"> Solvable but not watched scripts have been migrated to a new wallet named '%1'.</source> + <context-group purpose="location"><context context-type="linenumber">478</context></context-group> + </trans-unit> + <trans-unit id="_msg234"> + <source xml:space="preserve">Migration failed</source> + <context-group purpose="location"><context context-type="linenumber">492</context></context-group> + </trans-unit> + <trans-unit id="_msg235"> + <source xml:space="preserve">Migration Successful</source> + <context-group purpose="location"><context context-type="linenumber">494</context></context-group> + </trans-unit> + </group> <group restype="x-trolltech-linguist-context" resname="OpenWalletActivity"> - <trans-unit id="_msg222"> + <trans-unit id="_msg236"> <source xml:space="preserve">Open wallet failed</source> - <context-group purpose="location"><context context-type="linenumber">334</context></context-group> + <context-group purpose="location"><context context-type="linenumber">333</context></context-group> </trans-unit> - <trans-unit id="_msg223"> + <trans-unit id="_msg237"> <source xml:space="preserve">Open wallet warning</source> - <context-group purpose="location"><context context-type="linenumber">336</context></context-group> + <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg224"> + <trans-unit id="_msg238"> <source xml:space="preserve">default wallet</source> - <context-group purpose="location"><context context-type="linenumber">346</context></context-group> + <context-group purpose="location"><context context-type="linenumber">345</context></context-group> </trans-unit> - <trans-unit id="_msg225"> + <trans-unit id="_msg239"> <source xml:space="preserve">Open Wallet</source> - <context-group purpose="location"><context context-type="linenumber">350</context></context-group> + <context-group purpose="location"><context context-type="linenumber">349</context></context-group> <note annotates="source" from="developer">Title of window indicating the progress of opening of a wallet.</note> </trans-unit> - <trans-unit id="_msg226"> + <trans-unit id="_msg240"> <source xml:space="preserve">Opening Wallet <b>%1</b>…</source> - <context-group purpose="location"><context context-type="linenumber">353</context></context-group> + <context-group purpose="location"><context context-type="linenumber">352</context></context-group> <note annotates="source" from="developer">Descriptive text of the open wallet progress window which indicates to the user which wallet is currently being opened.</note> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="RestoreWalletActivity"> - <trans-unit id="_msg227"> + <trans-unit id="_msg241"> <source xml:space="preserve">Restore Wallet</source> - <context-group purpose="location"><context context-type="linenumber">403</context></context-group> + <context-group purpose="location"><context context-type="linenumber">402</context></context-group> <note annotates="source" from="developer">Title of progress window which is displayed when wallets are being restored.</note> </trans-unit> - <trans-unit id="_msg228"> + <trans-unit id="_msg242"> <source xml:space="preserve">Restoring Wallet <b>%1</b>…</source> - <context-group purpose="location"><context context-type="linenumber">406</context></context-group> + <context-group purpose="location"><context context-type="linenumber">405</context></context-group> <note annotates="source" from="developer">Descriptive text of the restore wallets progress window which indicates to the user that wallets are currently being restored.</note> </trans-unit> - <trans-unit id="_msg229"> + <trans-unit id="_msg243"> <source xml:space="preserve">Restore wallet failed</source> - <context-group purpose="location"><context context-type="linenumber">425</context></context-group> + <context-group purpose="location"><context context-type="linenumber">424</context></context-group> <note annotates="source" from="developer">Title of message box which is displayed when the wallet could not be restored.</note> </trans-unit> - <trans-unit id="_msg230"> + <trans-unit id="_msg244"> <source xml:space="preserve">Restore wallet warning</source> - <context-group purpose="location"><context context-type="linenumber">428</context></context-group> + <context-group purpose="location"><context context-type="linenumber">427</context></context-group> <note annotates="source" from="developer">Title of message box which is displayed when the wallet is restored with some warning.</note> </trans-unit> - <trans-unit id="_msg231"> + <trans-unit id="_msg245"> <source xml:space="preserve">Restore wallet message</source> - <context-group purpose="location"><context context-type="linenumber">431</context></context-group> + <context-group purpose="location"><context context-type="linenumber">430</context></context-group> <note annotates="source" from="developer">Title of message box which is displayed when the wallet is successfully restored.</note> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="WalletController"> - <trans-unit id="_msg232"> + <trans-unit id="_msg246"> <source xml:space="preserve">Close wallet</source> <context-group purpose="location"><context context-type="linenumber">84</context></context-group> </trans-unit> - <trans-unit id="_msg233"> + <trans-unit id="_msg247"> <source xml:space="preserve">Are you sure you wish to close the wallet <i>%1</i>?</source> <context-group purpose="location"><context context-type="linenumber">85</context></context-group> </trans-unit> - <trans-unit id="_msg234"> + <trans-unit id="_msg248"> <source xml:space="preserve">Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg235"> + <trans-unit id="_msg249"> <source xml:space="preserve">Close all wallets</source> <context-group purpose="location"><context context-type="linenumber">99</context></context-group> </trans-unit> - <trans-unit id="_msg236"> + <trans-unit id="_msg250"> <source xml:space="preserve">Are you sure you wish to close all wallets?</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> @@ -1057,100 +1119,96 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/createwalletdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="CreateWalletDialog"> - <trans-unit id="_msg237"> + <trans-unit id="_msg251"> <source xml:space="preserve">Create Wallet</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg238"> + <trans-unit id="_msg252"> + <source xml:space="preserve">You are one step away from creating your new wallet!</source> + <context-group purpose="location"><context context-type="linenumber">29</context></context-group> + </trans-unit> + <trans-unit id="_msg253"> + <source xml:space="preserve">Please provide a name and, if desired, enable any advanced options</source> + <context-group purpose="location"><context context-type="linenumber">42</context></context-group> + </trans-unit> + <trans-unit id="_msg254"> <source xml:space="preserve">Wallet Name</source> - <context-group purpose="location"><context context-type="linenumber">25</context></context-group> + <context-group purpose="location"><context context-type="linenumber">67</context></context-group> </trans-unit> - <trans-unit id="_msg239"> + <trans-unit id="_msg255"> <source xml:space="preserve">Wallet</source> - <context-group purpose="location"><context context-type="linenumber">38</context></context-group> + <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg240"> + <trans-unit id="_msg256"> <source xml:space="preserve">Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source> - <context-group purpose="location"><context context-type="linenumber">47</context></context-group> + <context-group purpose="location"><context context-type="linenumber">89</context></context-group> </trans-unit> - <trans-unit id="_msg241"> + <trans-unit id="_msg257"> <source xml:space="preserve">Encrypt Wallet</source> - <context-group purpose="location"><context context-type="linenumber">50</context></context-group> + <context-group purpose="location"><context context-type="linenumber">92</context></context-group> </trans-unit> - <trans-unit id="_msg242"> + <trans-unit id="_msg258"> <source xml:space="preserve">Advanced Options</source> - <context-group purpose="location"><context context-type="linenumber">76</context></context-group> + <context-group purpose="location"><context context-type="linenumber">118</context></context-group> </trans-unit> - <trans-unit id="_msg243"> + <trans-unit id="_msg259"> <source xml:space="preserve">Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source> - <context-group purpose="location"><context context-type="linenumber">85</context></context-group> + <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> - <trans-unit id="_msg244"> + <trans-unit id="_msg260"> <source xml:space="preserve">Disable Private Keys</source> - <context-group purpose="location"><context context-type="linenumber">88</context></context-group> + <context-group purpose="location"><context context-type="linenumber">142</context></context-group> </trans-unit> - <trans-unit id="_msg245"> + <trans-unit id="_msg261"> <source xml:space="preserve">Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source> - <context-group purpose="location"><context context-type="linenumber">95</context></context-group> + <context-group purpose="location"><context context-type="linenumber">149</context></context-group> </trans-unit> - <trans-unit id="_msg246"> + <trans-unit id="_msg262"> <source xml:space="preserve">Make Blank Wallet</source> - <context-group purpose="location"><context context-type="linenumber">98</context></context-group> - </trans-unit> - <trans-unit id="_msg247"> - <source xml:space="preserve">Use descriptors for scriptPubKey management</source> - <context-group purpose="location"><context context-type="linenumber">105</context></context-group> - </trans-unit> - <trans-unit id="_msg248"> - <source xml:space="preserve">Descriptor Wallet</source> - <context-group purpose="location"><context context-type="linenumber">108</context></context-group> + <context-group purpose="location"><context context-type="linenumber">152</context></context-group> </trans-unit> - <trans-unit id="_msg249"> + <trans-unit id="_msg263"> <source xml:space="preserve">Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</source> - <context-group purpose="location"><context context-type="linenumber">118</context></context-group> + <context-group purpose="location"><context context-type="linenumber">159</context></context-group> </trans-unit> - <trans-unit id="_msg250"> + <trans-unit id="_msg264"> <source xml:space="preserve">External signer</source> - <context-group purpose="location"><context context-type="linenumber">121</context></context-group> + <context-group purpose="location"><context context-type="linenumber">162</context></context-group> </trans-unit> </group> </body></file> <file original="../createwalletdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="CreateWalletDialog"> - <trans-unit id="_msg251"> + <trans-unit id="_msg265"> <source xml:space="preserve">Create</source> <context-group purpose="location"><context context-type="linenumber">22</context></context-group> </trans-unit> - <trans-unit id="_msg252"> - <source xml:space="preserve">Compiled without sqlite support (required for descriptor wallets)</source> - <context-group purpose="location"><context context-type="linenumber">90</context></context-group> - </trans-unit> - <trans-unit id="_msg253"> + <trans-unit id="_msg266"> <source xml:space="preserve">Compiled without external signing support (required for external signing)</source> - <context-group purpose="location"><context context-type="linenumber">104</context></context-group> + <context-group purpose="location"><context context-type="linenumber">90</context></context-group> <note annotates="source" from="developer">"External signing" means using devices such as hardware wallets.</note> </trans-unit> </group> </body></file> <file original="../forms/editaddressdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="EditAddressDialog"> - <trans-unit id="_msg254"> + <trans-unit id="_msg267"> <source xml:space="preserve">Edit Address</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg255"> + <trans-unit id="_msg268"> <source xml:space="preserve">&Label</source> <context-group purpose="location"><context context-type="linenumber">25</context></context-group> </trans-unit> - <trans-unit id="_msg256"> + <trans-unit id="_msg269"> <source xml:space="preserve">The label associated with this address list entry</source> <context-group purpose="location"><context context-type="linenumber">35</context></context-group> </trans-unit> - <trans-unit id="_msg257"> + <trans-unit id="_msg270"> <source xml:space="preserve">The address associated with this address list entry. This can only be modified for sending addresses.</source> <context-group purpose="location"><context context-type="linenumber">52</context></context-group> </trans-unit> - <trans-unit id="_msg258"> + <trans-unit id="_msg271"> <source xml:space="preserve">&Address</source> <context-group purpose="location"><context context-type="linenumber">42</context></context-group> </trans-unit> @@ -1158,35 +1216,35 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../editaddressdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="EditAddressDialog"> - <trans-unit id="_msg259"> + <trans-unit id="_msg272"> <source xml:space="preserve">New sending address</source> <context-group purpose="location"><context context-type="linenumber">29</context></context-group> </trans-unit> - <trans-unit id="_msg260"> + <trans-unit id="_msg273"> <source xml:space="preserve">Edit receiving address</source> <context-group purpose="location"><context context-type="linenumber">32</context></context-group> </trans-unit> - <trans-unit id="_msg261"> + <trans-unit id="_msg274"> <source xml:space="preserve">Edit sending address</source> <context-group purpose="location"><context context-type="linenumber">36</context></context-group> </trans-unit> - <trans-unit id="_msg262"> + <trans-unit id="_msg275"> <source xml:space="preserve">The entered address "%1" is not a valid Bitcoin address.</source> <context-group purpose="location"><context context-type="linenumber">113</context></context-group> </trans-unit> - <trans-unit id="_msg263"> + <trans-unit id="_msg276"> <source xml:space="preserve">Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg264"> + <trans-unit id="_msg277"> <source xml:space="preserve">The entered address "%1" is already in the address book with label "%2".</source> <context-group purpose="location"><context context-type="linenumber">151</context></context-group> </trans-unit> - <trans-unit id="_msg265"> + <trans-unit id="_msg278"> <source xml:space="preserve">Could not unlock wallet.</source> <context-group purpose="location"><context context-type="linenumber">123</context></context-group> </trans-unit> - <trans-unit id="_msg266"> + <trans-unit id="_msg279"> <source xml:space="preserve">New key generation failed.</source> <context-group purpose="location"><context context-type="linenumber">128</context></context-group> </trans-unit> @@ -1194,94 +1252,94 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../intro.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="FreespaceChecker"> - <trans-unit id="_msg267"> + <trans-unit id="_msg280"> <source xml:space="preserve">A new data directory will be created.</source> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> </trans-unit> - <trans-unit id="_msg268"> + <trans-unit id="_msg281"> <source xml:space="preserve">name</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg269"> + <trans-unit id="_msg282"> <source xml:space="preserve">Directory already exists. Add %1 if you intend to create a new directory here.</source> <context-group purpose="location"><context context-type="linenumber">99</context></context-group> </trans-unit> - <trans-unit id="_msg270"> + <trans-unit id="_msg283"> <source xml:space="preserve">Path already exists, and is not a directory.</source> <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> - <trans-unit id="_msg271"> + <trans-unit id="_msg284"> <source xml:space="preserve">Cannot create data directory here.</source> <context-group purpose="location"><context context-type="linenumber">109</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="Intro"> - <trans-unit id="_msg272"> + <trans-unit id="_msg285"> <source xml:space="preserve">Bitcoin</source> <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">301</context></context-group> - <trans-unit id="_msg273[0]"> + <trans-unit id="_msg286[0]"> <source xml:space="preserve">%n GB of space available</source> </trans-unit> - <trans-unit id="_msg273[1]"> + <trans-unit id="_msg286[1]"> <source xml:space="preserve">%n GB of space available</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">303</context></context-group> - <trans-unit id="_msg274[0]"> + <trans-unit id="_msg287[0]"> <source xml:space="preserve">(of %n GB needed)</source> </trans-unit> - <trans-unit id="_msg274[1]"> + <trans-unit id="_msg287[1]"> <source xml:space="preserve">(of %n GB needed)</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">306</context></context-group> - <trans-unit id="_msg275[0]"> + <trans-unit id="_msg288[0]"> <source xml:space="preserve">(%n GB needed for full chain)</source> </trans-unit> - <trans-unit id="_msg275[1]"> + <trans-unit id="_msg288[1]"> <source xml:space="preserve">(%n GB needed for full chain)</source> </trans-unit> </group> - <trans-unit id="_msg276"> + <trans-unit id="_msg289"> <source xml:space="preserve">Choose data directory</source> <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> - <trans-unit id="_msg277"> + <trans-unit id="_msg290"> <source xml:space="preserve">At least %1 GB of data will be stored in this directory, and it will grow over time.</source> <context-group purpose="location"><context context-type="linenumber">378</context></context-group> </trans-unit> - <trans-unit id="_msg278"> + <trans-unit id="_msg291"> <source xml:space="preserve">Approximately %1 GB of data will be stored in this directory.</source> <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">390</context></context-group> <note annotates="source" from="developer">Explanatory text on the capability of the current prune target.</note> - <trans-unit id="_msg279[0]"> + <trans-unit id="_msg292[0]"> <source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source> </trans-unit> - <trans-unit id="_msg279[1]"> + <trans-unit id="_msg292[1]"> <source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source> </trans-unit> </group> - <trans-unit id="_msg280"> + <trans-unit id="_msg293"> <source xml:space="preserve">%1 will download and store a copy of the Bitcoin block chain.</source> <context-group purpose="location"><context context-type="linenumber">392</context></context-group> </trans-unit> - <trans-unit id="_msg281"> + <trans-unit id="_msg294"> <source xml:space="preserve">The wallet will also be stored in this directory.</source> <context-group purpose="location"><context context-type="linenumber">394</context></context-group> </trans-unit> - <trans-unit id="_msg282"> + <trans-unit id="_msg295"> <source xml:space="preserve">Error: Specified data directory "%1" cannot be created.</source> <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg283"> + <trans-unit id="_msg296"> <source xml:space="preserve">Error</source> <context-group purpose="location"><context context-type="linenumber">280</context></context-group> </trans-unit> @@ -1289,25 +1347,25 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../utilitydialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="HelpMessageDialog"> - <trans-unit id="_msg284"> + <trans-unit id="_msg297"> <source xml:space="preserve">version</source> <context-group purpose="location"><context context-type="linenumber">38</context></context-group> </trans-unit> - <trans-unit id="_msg285"> + <trans-unit id="_msg298"> <source xml:space="preserve">About %1</source> <context-group purpose="location"><context context-type="linenumber">42</context></context-group> </trans-unit> - <trans-unit id="_msg286"> + <trans-unit id="_msg299"> <source xml:space="preserve">Command-line options</source> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="ShutdownWindow"> - <trans-unit id="_msg287"> + <trans-unit id="_msg300"> <source xml:space="preserve">%1 is shutting down…</source> <context-group purpose="location"><context context-type="linenumber">145</context></context-group> </trans-unit> - <trans-unit id="_msg288"> + <trans-unit id="_msg301"> <source xml:space="preserve">Do not shut down the computer until this window disappears.</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> @@ -1315,47 +1373,47 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/intro.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="Intro"> - <trans-unit id="_msg289"> + <trans-unit id="_msg302"> <source xml:space="preserve">Welcome</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg290"> + <trans-unit id="_msg303"> <source xml:space="preserve">Welcome to %1.</source> <context-group purpose="location"><context context-type="linenumber">23</context></context-group> </trans-unit> - <trans-unit id="_msg291"> + <trans-unit id="_msg304"> <source xml:space="preserve">As this is the first time the program is launched, you can choose where %1 will store its data.</source> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg292"> + <trans-unit id="_msg305"> <source xml:space="preserve">Limit block chain storage to</source> <context-group purpose="location"><context context-type="linenumber">238</context></context-group> </trans-unit> - <trans-unit id="_msg293"> + <trans-unit id="_msg306"> <source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source> <context-group purpose="location"><context context-type="linenumber">241</context></context-group> </trans-unit> - <trans-unit id="_msg294"> + <trans-unit id="_msg307"> <source xml:space="preserve"> GB</source> <context-group purpose="location"><context context-type="linenumber">248</context></context-group> </trans-unit> - <trans-unit id="_msg295"> + <trans-unit id="_msg308"> <source xml:space="preserve">This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source> <context-group purpose="location"><context context-type="linenumber">216</context></context-group> </trans-unit> - <trans-unit id="_msg296"> + <trans-unit id="_msg309"> <source xml:space="preserve">When you click OK, %1 will begin to download and process the full %4 block chain (%2 GB) starting with the earliest transactions in %3 when %4 initially launched.</source> <context-group purpose="location"><context context-type="linenumber">206</context></context-group> </trans-unit> - <trans-unit id="_msg297"> + <trans-unit id="_msg310"> <source xml:space="preserve">If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg298"> + <trans-unit id="_msg311"> <source xml:space="preserve">Use the default data directory</source> <context-group purpose="location"><context context-type="linenumber">66</context></context-group> </trans-unit> - <trans-unit id="_msg299"> + <trans-unit id="_msg312"> <source xml:space="preserve">Use a custom data directory:</source> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> @@ -1363,54 +1421,54 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/modaloverlay.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ModalOverlay"> - <trans-unit id="_msg300"> + <trans-unit id="_msg313"> <source xml:space="preserve">Form</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg301"> + <trans-unit id="_msg314"> <source xml:space="preserve">Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source> <context-group purpose="location"><context context-type="linenumber">133</context></context-group> </trans-unit> - <trans-unit id="_msg302"> + <trans-unit id="_msg315"> <source xml:space="preserve">Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source> <context-group purpose="location"><context context-type="linenumber">152</context></context-group> </trans-unit> - <trans-unit id="_msg303"> + <trans-unit id="_msg316"> <source xml:space="preserve">Number of blocks left</source> <context-group purpose="location"><context context-type="linenumber">215</context></context-group> </trans-unit> - <trans-unit id="_msg304"> + <trans-unit id="_msg317"> <source xml:space="preserve">Unknown…</source> <context-group purpose="location"><context context-type="linenumber">222</context></context-group> <context-group purpose="location"><context context-type="linenumber">248</context></context-group> <context-group purpose="location"><context context-type="sourcefile">../modaloverlay.cpp</context><context context-type="linenumber">152</context></context-group> </trans-unit> - <trans-unit id="_msg305"> + <trans-unit id="_msg318"> <source xml:space="preserve">calculating…</source> <context-group purpose="location"><context context-type="linenumber">292</context></context-group> <context-group purpose="location"><context context-type="linenumber">312</context></context-group> </trans-unit> - <trans-unit id="_msg306"> + <trans-unit id="_msg319"> <source xml:space="preserve">Last block time</source> <context-group purpose="location"><context context-type="linenumber">235</context></context-group> </trans-unit> - <trans-unit id="_msg307"> + <trans-unit id="_msg320"> <source xml:space="preserve">Progress</source> <context-group purpose="location"><context context-type="linenumber">261</context></context-group> </trans-unit> - <trans-unit id="_msg308"> + <trans-unit id="_msg321"> <source xml:space="preserve">Progress increase per hour</source> <context-group purpose="location"><context context-type="linenumber">285</context></context-group> </trans-unit> - <trans-unit id="_msg309"> + <trans-unit id="_msg322"> <source xml:space="preserve">Estimated time left until synced</source> <context-group purpose="location"><context context-type="linenumber">305</context></context-group> </trans-unit> - <trans-unit id="_msg310"> + <trans-unit id="_msg323"> <source xml:space="preserve">Hide</source> <context-group purpose="location"><context context-type="linenumber">342</context></context-group> </trans-unit> - <trans-unit id="_msg311"> + <trans-unit id="_msg324"> <source xml:space="preserve">Esc</source> <context-group purpose="location"><context context-type="linenumber">345</context></context-group> </trans-unit> @@ -1418,21 +1476,21 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../modaloverlay.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ModalOverlay"> - <trans-unit id="_msg312"> + <trans-unit id="_msg325"> <source xml:space="preserve">%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</source> <context-group purpose="location"><context context-type="linenumber">31</context></context-group> </trans-unit> - <trans-unit id="_msg313"> + <trans-unit id="_msg326"> <source xml:space="preserve">Unknown. Syncing Headers (%1, %2%)…</source> <context-group purpose="location"><context context-type="linenumber">158</context></context-group> </trans-unit> - <trans-unit id="_msg314"> + <trans-unit id="_msg327"> <source xml:space="preserve">Unknown. Pre-syncing Headers (%1, %2%)…</source> <context-group purpose="location"><context context-type="linenumber">163</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="QObject"> - <trans-unit id="_msg315"> + <trans-unit id="_msg328"> <source xml:space="preserve">unknown</source> <context-group purpose="location"><context context-type="linenumber">123</context></context-group> </trans-unit> @@ -1440,15 +1498,15 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/openuridialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OpenURIDialog"> - <trans-unit id="_msg316"> + <trans-unit id="_msg329"> <source xml:space="preserve">Open bitcoin URI</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg317"> + <trans-unit id="_msg330"> <source xml:space="preserve">URI:</source> <context-group purpose="location"><context context-type="linenumber">22</context></context-group> </trans-unit> - <trans-unit id="_msg318"> + <trans-unit id="_msg331"> <source xml:space="preserve">Paste address from clipboard</source> <context-group purpose="location"><context context-type="linenumber">36</context></context-group> <note annotates="source" from="developer">Tooltip text for button that allows you to paste an address that is in your clipboard.</note> @@ -1457,310 +1515,310 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/optionsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OptionsDialog"> - <trans-unit id="_msg319"> + <trans-unit id="_msg332"> <source xml:space="preserve">Options</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg320"> + <trans-unit id="_msg333"> <source xml:space="preserve">&Main</source> <context-group purpose="location"><context context-type="linenumber">27</context></context-group> </trans-unit> - <trans-unit id="_msg321"> + <trans-unit id="_msg334"> <source xml:space="preserve">Automatically start %1 after logging in to the system.</source> <context-group purpose="location"><context context-type="linenumber">33</context></context-group> </trans-unit> - <trans-unit id="_msg322"> + <trans-unit id="_msg335"> <source xml:space="preserve">&Start %1 on system login</source> <context-group purpose="location"><context context-type="linenumber">36</context></context-group> </trans-unit> - <trans-unit id="_msg323"> + <trans-unit id="_msg336"> <source xml:space="preserve">Enabling pruning significantly reduces the disk space required to store transactions. All blocks are still fully validated. Reverting this setting requires re-downloading the entire blockchain.</source> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg324"> + <trans-unit id="_msg337"> <source xml:space="preserve">Size of &database cache</source> <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> - <trans-unit id="_msg325"> + <trans-unit id="_msg338"> <source xml:space="preserve">Number of script &verification threads</source> <context-group purpose="location"><context context-type="linenumber">157</context></context-group> </trans-unit> - <trans-unit id="_msg326"> + <trans-unit id="_msg339"> <source xml:space="preserve">Full path to a %1 compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</source> <context-group purpose="location"><context context-type="linenumber">289</context></context-group> </trans-unit> - <trans-unit id="_msg327"> + <trans-unit id="_msg340"> <source xml:space="preserve">IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source> <context-group purpose="location"><context context-type="linenumber">388</context></context-group> <context-group purpose="location"><context context-type="linenumber">575</context></context-group> </trans-unit> - <trans-unit id="_msg328"> + <trans-unit id="_msg341"> <source xml:space="preserve">Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source> <context-group purpose="location"><context context-type="linenumber">457</context></context-group> <context-group purpose="location"><context context-type="linenumber">480</context></context-group> <context-group purpose="location"><context context-type="linenumber">503</context></context-group> </trans-unit> - <trans-unit id="_msg329"> + <trans-unit id="_msg342"> <source xml:space="preserve">Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> <context-group purpose="location"><context context-type="linenumber">672</context></context-group> </trans-unit> - <trans-unit id="_msg330"> + <trans-unit id="_msg343"> <source xml:space="preserve">Options set in this dialog are overridden by the command line:</source> <context-group purpose="location"><context context-type="linenumber">899</context></context-group> </trans-unit> - <trans-unit id="_msg331"> + <trans-unit id="_msg344"> <source xml:space="preserve">Open the %1 configuration file from the working directory.</source> <context-group purpose="location"><context context-type="linenumber">944</context></context-group> </trans-unit> - <trans-unit id="_msg332"> + <trans-unit id="_msg345"> <source xml:space="preserve">Open Configuration File</source> <context-group purpose="location"><context context-type="linenumber">947</context></context-group> </trans-unit> - <trans-unit id="_msg333"> + <trans-unit id="_msg346"> <source xml:space="preserve">Reset all client options to default.</source> <context-group purpose="location"><context context-type="linenumber">957</context></context-group> </trans-unit> - <trans-unit id="_msg334"> + <trans-unit id="_msg347"> <source xml:space="preserve">&Reset Options</source> <context-group purpose="location"><context context-type="linenumber">960</context></context-group> </trans-unit> - <trans-unit id="_msg335"> + <trans-unit id="_msg348"> <source xml:space="preserve">&Network</source> <context-group purpose="location"><context context-type="linenumber">315</context></context-group> </trans-unit> - <trans-unit id="_msg336"> + <trans-unit id="_msg349"> <source xml:space="preserve">Prune &block storage to</source> <context-group purpose="location"><context context-type="linenumber">61</context></context-group> </trans-unit> - <trans-unit id="_msg337"> + <trans-unit id="_msg350"> <source xml:space="preserve">GB</source> <context-group purpose="location"><context context-type="linenumber">71</context></context-group> </trans-unit> - <trans-unit id="_msg338"> + <trans-unit id="_msg351"> <source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain.</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg339"> + <trans-unit id="_msg352"> <source xml:space="preserve">Maximum database cache size. A larger cache can contribute to faster sync, after which the benefit is less pronounced for most use cases. Lowering the cache size will reduce memory usage. Unused mempool memory is shared for this cache.</source> <context-group purpose="location"><context context-type="linenumber">108</context></context-group> <note annotates="source" from="developer">Tooltip text for Options window setting that sets the size of the database cache. Explains the corresponding effects of increasing/decreasing this value.</note> </trans-unit> - <trans-unit id="_msg340"> + <trans-unit id="_msg353"> <source xml:space="preserve">MiB</source> <context-group purpose="location"><context context-type="linenumber">127</context></context-group> </trans-unit> - <trans-unit id="_msg341"> + <trans-unit id="_msg354"> <source xml:space="preserve">Set the number of script verification threads. Negative values correspond to the number of cores you want to leave free to the system.</source> <context-group purpose="location"><context context-type="linenumber">154</context></context-group> <note annotates="source" from="developer">Tooltip text for Options window setting that sets the number of script verification threads. Explains that negative values mean to leave these many cores free to the system.</note> </trans-unit> - <trans-unit id="_msg342"> + <trans-unit id="_msg355"> <source xml:space="preserve">(0 = auto, <0 = leave that many cores free)</source> <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> - <trans-unit id="_msg343"> + <trans-unit id="_msg356"> <source xml:space="preserve">This allows you or a third party tool to communicate with the node through command-line and JSON-RPC commands.</source> <context-group purpose="location"><context context-type="linenumber">192</context></context-group> <note annotates="source" from="developer">Tooltip text for Options window setting that enables the RPC server.</note> </trans-unit> - <trans-unit id="_msg344"> + <trans-unit id="_msg357"> <source xml:space="preserve">Enable R&PC server</source> <context-group purpose="location"><context context-type="linenumber">195</context></context-group> <note annotates="source" from="developer">An Options window setting to enable the RPC server.</note> </trans-unit> - <trans-unit id="_msg345"> + <trans-unit id="_msg358"> <source xml:space="preserve">W&allet</source> <context-group purpose="location"><context context-type="linenumber">216</context></context-group> </trans-unit> - <trans-unit id="_msg346"> + <trans-unit id="_msg359"> <source xml:space="preserve">Whether to set subtract fee from amount as default or not.</source> <context-group purpose="location"><context context-type="linenumber">222</context></context-group> <note annotates="source" from="developer">Tooltip text for Options window setting that sets subtracting the fee from a sending amount as default.</note> </trans-unit> - <trans-unit id="_msg347"> + <trans-unit id="_msg360"> <source xml:space="preserve">Subtract &fee from amount by default</source> <context-group purpose="location"><context context-type="linenumber">225</context></context-group> <note annotates="source" from="developer">An Options window setting to set subtracting the fee from a sending amount as default.</note> </trans-unit> - <trans-unit id="_msg348"> + <trans-unit id="_msg361"> <source xml:space="preserve">Expert</source> <context-group purpose="location"><context context-type="linenumber">232</context></context-group> </trans-unit> - <trans-unit id="_msg349"> + <trans-unit id="_msg362"> <source xml:space="preserve">Enable coin &control features</source> <context-group purpose="location"><context context-type="linenumber">241</context></context-group> </trans-unit> - <trans-unit id="_msg350"> + <trans-unit id="_msg363"> <source xml:space="preserve">If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source> <context-group purpose="location"><context context-type="linenumber">248</context></context-group> </trans-unit> - <trans-unit id="_msg351"> + <trans-unit id="_msg364"> <source xml:space="preserve">&Spend unconfirmed change</source> <context-group purpose="location"><context context-type="linenumber">251</context></context-group> </trans-unit> - <trans-unit id="_msg352"> + <trans-unit id="_msg365"> <source xml:space="preserve">Enable &PSBT controls</source> <context-group purpose="location"><context context-type="linenumber">258</context></context-group> <note annotates="source" from="developer">An options window setting to enable PSBT controls.</note> </trans-unit> - <trans-unit id="_msg353"> + <trans-unit id="_msg366"> <source xml:space="preserve">Whether to show PSBT controls.</source> <context-group purpose="location"><context context-type="linenumber">261</context></context-group> <note annotates="source" from="developer">Tooltip text for options window setting that enables PSBT controls.</note> </trans-unit> - <trans-unit id="_msg354"> + <trans-unit id="_msg367"> <source xml:space="preserve">External Signer (e.g. hardware wallet)</source> <context-group purpose="location"><context context-type="linenumber">271</context></context-group> </trans-unit> - <trans-unit id="_msg355"> + <trans-unit id="_msg368"> <source xml:space="preserve">&External signer script path</source> <context-group purpose="location"><context context-type="linenumber">279</context></context-group> </trans-unit> - <trans-unit id="_msg356"> + <trans-unit id="_msg369"> <source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source> <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg357"> + <trans-unit id="_msg370"> <source xml:space="preserve">Map port using &UPnP</source> <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> - <trans-unit id="_msg358"> + <trans-unit id="_msg371"> <source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</source> <context-group purpose="location"><context context-type="linenumber">331</context></context-group> </trans-unit> - <trans-unit id="_msg359"> + <trans-unit id="_msg372"> <source xml:space="preserve">Map port using NA&T-PMP</source> <context-group purpose="location"><context context-type="linenumber">334</context></context-group> </trans-unit> - <trans-unit id="_msg360"> + <trans-unit id="_msg373"> <source xml:space="preserve">Accept connections from outside.</source> <context-group purpose="location"><context context-type="linenumber">341</context></context-group> </trans-unit> - <trans-unit id="_msg361"> + <trans-unit id="_msg374"> <source xml:space="preserve">Allow incomin&g connections</source> <context-group purpose="location"><context context-type="linenumber">344</context></context-group> </trans-unit> - <trans-unit id="_msg362"> + <trans-unit id="_msg375"> <source xml:space="preserve">Connect to the Bitcoin network through a SOCKS5 proxy.</source> <context-group purpose="location"><context context-type="linenumber">351</context></context-group> </trans-unit> - <trans-unit id="_msg363"> + <trans-unit id="_msg376"> <source xml:space="preserve">&Connect through SOCKS5 proxy (default proxy):</source> <context-group purpose="location"><context context-type="linenumber">354</context></context-group> </trans-unit> - <trans-unit id="_msg364"> + <trans-unit id="_msg377"> <source xml:space="preserve">Proxy &IP:</source> <context-group purpose="location"><context context-type="linenumber">363</context></context-group> <context-group purpose="location"><context context-type="linenumber">550</context></context-group> </trans-unit> - <trans-unit id="_msg365"> + <trans-unit id="_msg378"> <source xml:space="preserve">&Port:</source> <context-group purpose="location"><context context-type="linenumber">395</context></context-group> <context-group purpose="location"><context context-type="linenumber">582</context></context-group> </trans-unit> - <trans-unit id="_msg366"> + <trans-unit id="_msg379"> <source xml:space="preserve">Port of the proxy (e.g. 9050)</source> <context-group purpose="location"><context context-type="linenumber">420</context></context-group> <context-group purpose="location"><context context-type="linenumber">607</context></context-group> </trans-unit> - <trans-unit id="_msg367"> + <trans-unit id="_msg380"> <source xml:space="preserve">Used for reaching peers via:</source> <context-group purpose="location"><context context-type="linenumber">444</context></context-group> </trans-unit> - <trans-unit id="_msg368"> + <trans-unit id="_msg381"> <source xml:space="preserve">IPv4</source> <context-group purpose="location"><context context-type="linenumber">467</context></context-group> </trans-unit> - <trans-unit id="_msg369"> + <trans-unit id="_msg382"> <source xml:space="preserve">IPv6</source> <context-group purpose="location"><context context-type="linenumber">490</context></context-group> </trans-unit> - <trans-unit id="_msg370"> + <trans-unit id="_msg383"> <source xml:space="preserve">Tor</source> <context-group purpose="location"><context context-type="linenumber">513</context></context-group> </trans-unit> - <trans-unit id="_msg371"> + <trans-unit id="_msg384"> <source xml:space="preserve">&Window</source> <context-group purpose="location"><context context-type="linenumber">643</context></context-group> </trans-unit> - <trans-unit id="_msg372"> + <trans-unit id="_msg385"> <source xml:space="preserve">Show the icon in the system tray.</source> <context-group purpose="location"><context context-type="linenumber">649</context></context-group> </trans-unit> - <trans-unit id="_msg373"> + <trans-unit id="_msg386"> <source xml:space="preserve">&Show tray icon</source> <context-group purpose="location"><context context-type="linenumber">652</context></context-group> </trans-unit> - <trans-unit id="_msg374"> + <trans-unit id="_msg387"> <source xml:space="preserve">Show only a tray icon after minimizing the window.</source> <context-group purpose="location"><context context-type="linenumber">662</context></context-group> </trans-unit> - <trans-unit id="_msg375"> + <trans-unit id="_msg388"> <source xml:space="preserve">&Minimize to the tray instead of the taskbar</source> <context-group purpose="location"><context context-type="linenumber">665</context></context-group> </trans-unit> - <trans-unit id="_msg376"> + <trans-unit id="_msg389"> <source xml:space="preserve">M&inimize on close</source> <context-group purpose="location"><context context-type="linenumber">675</context></context-group> </trans-unit> - <trans-unit id="_msg377"> + <trans-unit id="_msg390"> <source xml:space="preserve">&Display</source> <context-group purpose="location"><context context-type="linenumber">696</context></context-group> </trans-unit> - <trans-unit id="_msg378"> + <trans-unit id="_msg391"> <source xml:space="preserve">User Interface &language:</source> <context-group purpose="location"><context context-type="linenumber">704</context></context-group> </trans-unit> - <trans-unit id="_msg379"> + <trans-unit id="_msg392"> <source xml:space="preserve">The user interface language can be set here. This setting will take effect after restarting %1.</source> <context-group purpose="location"><context context-type="linenumber">717</context></context-group> </trans-unit> - <trans-unit id="_msg380"> + <trans-unit id="_msg393"> <source xml:space="preserve">&Unit to show amounts in:</source> <context-group purpose="location"><context context-type="linenumber">728</context></context-group> </trans-unit> - <trans-unit id="_msg381"> + <trans-unit id="_msg394"> <source xml:space="preserve">Choose the default subdivision unit to show in the interface and when sending coins.</source> <context-group purpose="location"><context context-type="linenumber">741</context></context-group> </trans-unit> - <trans-unit id="_msg382"> + <trans-unit id="_msg395"> <source xml:space="preserve">Third-party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <context-group purpose="location"><context context-type="linenumber">752</context></context-group> <context-group purpose="location"><context context-type="linenumber">765</context></context-group> </trans-unit> - <trans-unit id="_msg383"> + <trans-unit id="_msg396"> <source xml:space="preserve">&Third-party transaction URLs</source> <context-group purpose="location"><context context-type="linenumber">755</context></context-group> </trans-unit> - <trans-unit id="_msg384"> + <trans-unit id="_msg397"> <source xml:space="preserve">Whether to show coin control features or not.</source> <context-group purpose="location"><context context-type="linenumber">238</context></context-group> </trans-unit> - <trans-unit id="_msg385"> + <trans-unit id="_msg398"> <source xml:space="preserve">Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source> <context-group purpose="location"><context context-type="linenumber">538</context></context-group> </trans-unit> - <trans-unit id="_msg386"> + <trans-unit id="_msg399"> <source xml:space="preserve">Use separate SOCKS&5 proxy to reach peers via Tor onion services:</source> <context-group purpose="location"><context context-type="linenumber">541</context></context-group> </trans-unit> - <trans-unit id="_msg387"> + <trans-unit id="_msg400"> <source xml:space="preserve">Monospaced font in the Overview tab:</source> <context-group purpose="location"><context context-type="linenumber">777</context></context-group> </trans-unit> - <trans-unit id="_msg388"> + <trans-unit id="_msg401"> <source xml:space="preserve">embedded "%1"</source> <context-group purpose="location"><context context-type="linenumber">785</context></context-group> </trans-unit> - <trans-unit id="_msg389"> + <trans-unit id="_msg402"> <source xml:space="preserve">closest matching "%1"</source> <context-group purpose="location"><context context-type="linenumber">834</context></context-group> </trans-unit> - <trans-unit id="_msg390"> + <trans-unit id="_msg403"> <source xml:space="preserve">&OK</source> <context-group purpose="location"><context context-type="linenumber">1040</context></context-group> </trans-unit> - <trans-unit id="_msg391"> + <trans-unit id="_msg404"> <source xml:space="preserve">&Cancel</source> <context-group purpose="location"><context context-type="linenumber">1053</context></context-group> </trans-unit> @@ -1768,71 +1826,71 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../optionsdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OptionsDialog"> - <trans-unit id="_msg392"> + <trans-unit id="_msg405"> <source xml:space="preserve">Compiled without external signing support (required for external signing)</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> <note annotates="source" from="developer">"External signing" means using devices such as hardware wallets.</note> </trans-unit> - <trans-unit id="_msg393"> + <trans-unit id="_msg406"> <source xml:space="preserve">default</source> <context-group purpose="location"><context context-type="linenumber">108</context></context-group> </trans-unit> - <trans-unit id="_msg394"> + <trans-unit id="_msg407"> <source xml:space="preserve">none</source> <context-group purpose="location"><context context-type="linenumber">194</context></context-group> </trans-unit> - <trans-unit id="_msg395"> + <trans-unit id="_msg408"> <source xml:space="preserve">Confirm options reset</source> <context-group purpose="location"><context context-type="linenumber">301</context></context-group> <note annotates="source" from="developer">Window title text of pop-up window shown when the user has chosen to reset options.</note> </trans-unit> - <trans-unit id="_msg396"> + <trans-unit id="_msg409"> <source xml:space="preserve">Client restart required to activate changes.</source> <context-group purpose="location"><context context-type="linenumber">292</context></context-group> <context-group purpose="location"><context context-type="linenumber">371</context></context-group> <note annotates="source" from="developer">Text explaining that the settings changed will not come into effect until the client is restarted.</note> </trans-unit> - <trans-unit id="_msg397"> + <trans-unit id="_msg410"> <source xml:space="preserve">Current settings will be backed up at "%1".</source> <context-group purpose="location"><context context-type="linenumber">296</context></context-group> <note annotates="source" from="developer">Text explaining to the user that the client's current settings will be backed up at a specific location. %1 is a stand-in argument for the backup location's path.</note> </trans-unit> - <trans-unit id="_msg398"> + <trans-unit id="_msg411"> <source xml:space="preserve">Client will be shut down. Do you want to proceed?</source> <context-group purpose="location"><context context-type="linenumber">299</context></context-group> <note annotates="source" from="developer">Text asking the user to confirm if they would like to proceed with a client shutdown.</note> </trans-unit> - <trans-unit id="_msg399"> + <trans-unit id="_msg412"> <source xml:space="preserve">Configuration options</source> <context-group purpose="location"><context context-type="linenumber">319</context></context-group> <note annotates="source" from="developer">Window title text of pop-up box that allows opening up of configuration file.</note> </trans-unit> - <trans-unit id="_msg400"> + <trans-unit id="_msg413"> <source xml:space="preserve">The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source> <context-group purpose="location"><context context-type="linenumber">322</context></context-group> <note annotates="source" from="developer">Explanatory text about the priority order of instructions considered by client. The order from high to low being: command-line, configuration file, GUI settings.</note> </trans-unit> - <trans-unit id="_msg401"> + <trans-unit id="_msg414"> <source xml:space="preserve">Continue</source> <context-group purpose="location"><context context-type="linenumber">325</context></context-group> </trans-unit> - <trans-unit id="_msg402"> + <trans-unit id="_msg415"> <source xml:space="preserve">Cancel</source> <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg403"> + <trans-unit id="_msg416"> <source xml:space="preserve">Error</source> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg404"> + <trans-unit id="_msg417"> <source xml:space="preserve">The configuration file could not be opened.</source> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg405"> + <trans-unit id="_msg418"> <source xml:space="preserve">This change would require a client restart.</source> <context-group purpose="location"><context context-type="linenumber">375</context></context-group> </trans-unit> - <trans-unit id="_msg406"> + <trans-unit id="_msg419"> <source xml:space="preserve">The supplied proxy address is invalid.</source> <context-group purpose="location"><context context-type="linenumber">403</context></context-group> </trans-unit> @@ -1840,7 +1898,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../optionsmodel.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OptionsModel"> - <trans-unit id="_msg407"> + <trans-unit id="_msg420"> <source xml:space="preserve">Could not read setting "%1", %2.</source> <context-group purpose="location"><context context-type="linenumber">198</context></context-group> </trans-unit> @@ -1848,76 +1906,76 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/overviewpage.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OverviewPage"> - <trans-unit id="_msg408"> + <trans-unit id="_msg421"> <source xml:space="preserve">Form</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg409"> + <trans-unit id="_msg422"> <source xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <context-group purpose="location"><context context-type="linenumber">76</context></context-group> <context-group purpose="location"><context context-type="linenumber">411</context></context-group> </trans-unit> - <trans-unit id="_msg410"> + <trans-unit id="_msg423"> <source xml:space="preserve">Watch-only:</source> <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg411"> + <trans-unit id="_msg424"> <source xml:space="preserve">Available:</source> <context-group purpose="location"><context context-type="linenumber">294</context></context-group> </trans-unit> - <trans-unit id="_msg412"> + <trans-unit id="_msg425"> <source xml:space="preserve">Your current spendable balance</source> <context-group purpose="location"><context context-type="linenumber">304</context></context-group> </trans-unit> - <trans-unit id="_msg413"> + <trans-unit id="_msg426"> <source xml:space="preserve">Pending:</source> <context-group purpose="location"><context context-type="linenumber">339</context></context-group> </trans-unit> - <trans-unit id="_msg414"> + <trans-unit id="_msg427"> <source xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source> <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> - <trans-unit id="_msg415"> + <trans-unit id="_msg428"> <source xml:space="preserve">Immature:</source> <context-group purpose="location"><context context-type="linenumber">239</context></context-group> </trans-unit> - <trans-unit id="_msg416"> + <trans-unit id="_msg429"> <source xml:space="preserve">Mined balance that has not yet matured</source> <context-group purpose="location"><context context-type="linenumber">210</context></context-group> </trans-unit> - <trans-unit id="_msg417"> + <trans-unit id="_msg430"> <source xml:space="preserve">Balances</source> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> - <trans-unit id="_msg418"> + <trans-unit id="_msg431"> <source xml:space="preserve">Total:</source> <context-group purpose="location"><context context-type="linenumber">200</context></context-group> </trans-unit> - <trans-unit id="_msg419"> + <trans-unit id="_msg432"> <source xml:space="preserve">Your current total balance</source> <context-group purpose="location"><context context-type="linenumber">249</context></context-group> </trans-unit> - <trans-unit id="_msg420"> + <trans-unit id="_msg433"> <source xml:space="preserve">Your current balance in watch-only addresses</source> <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> - <trans-unit id="_msg421"> + <trans-unit id="_msg434"> <source xml:space="preserve">Spendable:</source> <context-group purpose="location"><context context-type="linenumber">346</context></context-group> </trans-unit> - <trans-unit id="_msg422"> + <trans-unit id="_msg435"> <source xml:space="preserve">Recent transactions</source> <context-group purpose="location"><context context-type="linenumber">395</context></context-group> </trans-unit> - <trans-unit id="_msg423"> + <trans-unit id="_msg436"> <source xml:space="preserve">Unconfirmed transactions to watch-only addresses</source> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg424"> + <trans-unit id="_msg437"> <source xml:space="preserve">Mined balance in watch-only addresses that has not yet matured</source> <context-group purpose="location"><context context-type="linenumber">158</context></context-group> </trans-unit> - <trans-unit id="_msg425"> + <trans-unit id="_msg438"> <source xml:space="preserve">Current total balance in watch-only addresses</source> <context-group purpose="location"><context context-type="linenumber">268</context></context-group> </trans-unit> @@ -1925,7 +1983,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../overviewpage.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OverviewPage"> - <trans-unit id="_msg426"> + <trans-unit id="_msg439"> <source xml:space="preserve">Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings->Mask values.</source> <context-group purpose="location"><context context-type="linenumber">184</context></context-group> </trans-unit> @@ -1933,27 +1991,27 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/psbtoperationsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog"> - <trans-unit id="_msg427"> + <trans-unit id="_msg440"> <source xml:space="preserve">PSBT Operations</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg428"> + <trans-unit id="_msg441"> <source xml:space="preserve">Sign Tx</source> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg429"> + <trans-unit id="_msg442"> <source xml:space="preserve">Broadcast Tx</source> <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> - <trans-unit id="_msg430"> + <trans-unit id="_msg443"> <source xml:space="preserve">Copy to Clipboard</source> <context-group purpose="location"><context context-type="linenumber">122</context></context-group> </trans-unit> - <trans-unit id="_msg431"> + <trans-unit id="_msg444"> <source xml:space="preserve">Save…</source> <context-group purpose="location"><context context-type="linenumber">129</context></context-group> </trans-unit> - <trans-unit id="_msg432"> + <trans-unit id="_msg445"> <source xml:space="preserve">Close</source> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> @@ -1961,112 +2019,112 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../psbtoperationsdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog"> - <trans-unit id="_msg433"> + <trans-unit id="_msg446"> <source xml:space="preserve">Failed to load transaction: %1</source> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> - <trans-unit id="_msg434"> + <trans-unit id="_msg447"> <source xml:space="preserve">Failed to sign transaction: %1</source> <context-group purpose="location"><context context-type="linenumber">85</context></context-group> </trans-unit> - <trans-unit id="_msg435"> + <trans-unit id="_msg448"> <source xml:space="preserve">Cannot sign inputs while wallet is locked.</source> <context-group purpose="location"><context context-type="linenumber">93</context></context-group> </trans-unit> - <trans-unit id="_msg436"> + <trans-unit id="_msg449"> <source xml:space="preserve">Could not sign any more inputs.</source> <context-group purpose="location"><context context-type="linenumber">95</context></context-group> </trans-unit> - <trans-unit id="_msg437"> + <trans-unit id="_msg450"> <source xml:space="preserve">Signed %1 inputs, but more signatures are still required.</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg438"> + <trans-unit id="_msg451"> <source xml:space="preserve">Signed transaction successfully. Transaction is ready to broadcast.</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> - <trans-unit id="_msg439"> + <trans-unit id="_msg452"> <source xml:space="preserve">Unknown error processing transaction.</source> <context-group purpose="location"><context context-type="linenumber">112</context></context-group> </trans-unit> - <trans-unit id="_msg440"> + <trans-unit id="_msg453"> <source xml:space="preserve">Transaction broadcast successfully! Transaction ID: %1</source> <context-group purpose="location"><context context-type="linenumber">122</context></context-group> </trans-unit> - <trans-unit id="_msg441"> + <trans-unit id="_msg454"> <source xml:space="preserve">Transaction broadcast failed: %1</source> <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> - <trans-unit id="_msg442"> + <trans-unit id="_msg455"> <source xml:space="preserve">PSBT copied to clipboard.</source> <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> - <trans-unit id="_msg443"> + <trans-unit id="_msg456"> <source xml:space="preserve">Save Transaction Data</source> <context-group purpose="location"><context context-type="linenumber">157</context></context-group> </trans-unit> - <trans-unit id="_msg444"> + <trans-unit id="_msg457"> <source xml:space="preserve">Partially Signed Transaction (Binary)</source> <context-group purpose="location"><context context-type="linenumber">159</context></context-group> <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note> </trans-unit> - <trans-unit id="_msg445"> + <trans-unit id="_msg458"> <source xml:space="preserve">PSBT saved to disk.</source> <context-group purpose="location"><context context-type="linenumber">166</context></context-group> </trans-unit> - <trans-unit id="_msg446"> + <trans-unit id="_msg459"> <source xml:space="preserve"> * Sends %1 to %2</source> <context-group purpose="location"><context context-type="linenumber">182</context></context-group> </trans-unit> - <trans-unit id="_msg447"> + <trans-unit id="_msg460"> <source xml:space="preserve">own address</source> <context-group purpose="location"><context context-type="linenumber">186</context></context-group> </trans-unit> - <trans-unit id="_msg448"> + <trans-unit id="_msg461"> <source xml:space="preserve">Unable to calculate transaction fee or total transaction amount.</source> <context-group purpose="location"><context context-type="linenumber">194</context></context-group> </trans-unit> - <trans-unit id="_msg449"> + <trans-unit id="_msg462"> <source xml:space="preserve">Pays transaction fee: </source> <context-group purpose="location"><context context-type="linenumber">196</context></context-group> </trans-unit> - <trans-unit id="_msg450"> + <trans-unit id="_msg463"> <source xml:space="preserve">Total Amount</source> <context-group purpose="location"><context context-type="linenumber">208</context></context-group> </trans-unit> - <trans-unit id="_msg451"> + <trans-unit id="_msg464"> <source xml:space="preserve">or</source> <context-group purpose="location"><context context-type="linenumber">211</context></context-group> </trans-unit> - <trans-unit id="_msg452"> + <trans-unit id="_msg465"> <source xml:space="preserve">Transaction has %1 unsigned inputs.</source> <context-group purpose="location"><context context-type="linenumber">217</context></context-group> </trans-unit> - <trans-unit id="_msg453"> + <trans-unit id="_msg466"> <source xml:space="preserve">Transaction is missing some information about inputs.</source> <context-group purpose="location"><context context-type="linenumber">263</context></context-group> </trans-unit> - <trans-unit id="_msg454"> + <trans-unit id="_msg467"> <source xml:space="preserve">Transaction still needs signature(s).</source> <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg455"> + <trans-unit id="_msg468"> <source xml:space="preserve">(But no wallet is loaded.)</source> <context-group purpose="location"><context context-type="linenumber">270</context></context-group> </trans-unit> - <trans-unit id="_msg456"> + <trans-unit id="_msg469"> <source xml:space="preserve">(But this wallet cannot sign transactions.)</source> <context-group purpose="location"><context context-type="linenumber">273</context></context-group> </trans-unit> - <trans-unit id="_msg457"> + <trans-unit id="_msg470"> <source xml:space="preserve">(But this wallet does not have the right keys.)</source> <context-group purpose="location"><context context-type="linenumber">276</context></context-group> </trans-unit> - <trans-unit id="_msg458"> + <trans-unit id="_msg471"> <source xml:space="preserve">Transaction is fully signed and ready for broadcast.</source> <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg459"> + <trans-unit id="_msg472"> <source xml:space="preserve">Transaction status is unknown.</source> <context-group purpose="location"><context context-type="linenumber">288</context></context-group> </trans-unit> @@ -2074,37 +2132,37 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../paymentserver.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PaymentServer"> - <trans-unit id="_msg460"> + <trans-unit id="_msg473"> <source xml:space="preserve">Payment request error</source> <context-group purpose="location"><context context-type="linenumber">149</context></context-group> </trans-unit> - <trans-unit id="_msg461"> + <trans-unit id="_msg474"> <source xml:space="preserve">Cannot start bitcoin: click-to-pay handler</source> <context-group purpose="location"><context context-type="linenumber">150</context></context-group> </trans-unit> - <trans-unit id="_msg462"> + <trans-unit id="_msg475"> <source xml:space="preserve">URI handling</source> <context-group purpose="location"><context context-type="linenumber">198</context></context-group> <context-group purpose="location"><context context-type="linenumber">214</context></context-group> <context-group purpose="location"><context context-type="linenumber">220</context></context-group> <context-group purpose="location"><context context-type="linenumber">227</context></context-group> </trans-unit> - <trans-unit id="_msg463"> + <trans-unit id="_msg476"> <source xml:space="preserve">'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source> <context-group purpose="location"><context context-type="linenumber">198</context></context-group> </trans-unit> - <trans-unit id="_msg464"> + <trans-unit id="_msg477"> <source xml:space="preserve">Cannot process payment request because BIP70 is not supported. Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored. If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source> <context-group purpose="location"><context context-type="linenumber">215</context></context-group> <context-group purpose="location"><context context-type="linenumber">238</context></context-group> </trans-unit> - <trans-unit id="_msg465"> + <trans-unit id="_msg478"> <source xml:space="preserve">URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> <context-group purpose="location"><context context-type="linenumber">228</context></context-group> </trans-unit> - <trans-unit id="_msg466"> + <trans-unit id="_msg479"> <source xml:space="preserve">Payment request file handling</source> <context-group purpose="location"><context context-type="linenumber">237</context></context-group> </trans-unit> @@ -2112,52 +2170,52 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../peertablemodel.h" datatype="c" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PeerTableModel"> - <trans-unit id="_msg467"> + <trans-unit id="_msg480"> <source xml:space="preserve">User Agent</source> <context-group purpose="location"><context context-type="linenumber">112</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which contains the peer's User Agent string.</note> </trans-unit> - <trans-unit id="_msg468"> + <trans-unit id="_msg481"> <source xml:space="preserve">Ping</source> <context-group purpose="location"><context context-type="linenumber">103</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which indicates the current latency of the connection with the peer.</note> </trans-unit> - <trans-unit id="_msg469"> + <trans-unit id="_msg482"> <source xml:space="preserve">Peer</source> <context-group purpose="location"><context context-type="linenumber">85</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which contains a unique number used to identify a connection.</note> </trans-unit> - <trans-unit id="_msg470"> + <trans-unit id="_msg483"> <source xml:space="preserve">Age</source> <context-group purpose="location"><context context-type="linenumber">88</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which indicates the duration (length of time) since the peer connection started.</note> </trans-unit> - <trans-unit id="_msg471"> + <trans-unit id="_msg484"> <source xml:space="preserve">Direction</source> <context-group purpose="location"><context context-type="linenumber">94</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which indicates the direction the peer connection was initiated from.</note> </trans-unit> - <trans-unit id="_msg472"> + <trans-unit id="_msg485"> <source xml:space="preserve">Sent</source> <context-group purpose="location"><context context-type="linenumber">106</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</note> </trans-unit> - <trans-unit id="_msg473"> + <trans-unit id="_msg486"> <source xml:space="preserve">Received</source> <context-group purpose="location"><context context-type="linenumber">109</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have received from the peer.</note> </trans-unit> - <trans-unit id="_msg474"> + <trans-unit id="_msg487"> <source xml:space="preserve">Address</source> <context-group purpose="location"><context context-type="linenumber">91</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</note> </trans-unit> - <trans-unit id="_msg475"> + <trans-unit id="_msg488"> <source xml:space="preserve">Type</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which describes the type of peer connection. The "type" describes why the connection exists.</note> </trans-unit> - <trans-unit id="_msg476"> + <trans-unit id="_msg489"> <source xml:space="preserve">Network</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> <note annotates="source" from="developer">Title of Peers Table column which states the network the peer connected through.</note> @@ -2166,12 +2224,12 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../peertablemodel.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PeerTableModel"> - <trans-unit id="_msg477"> + <trans-unit id="_msg490"> <source xml:space="preserve">Inbound</source> <context-group purpose="location"><context context-type="linenumber">77</context></context-group> <note annotates="source" from="developer">An Inbound Connection from a Peer.</note> </trans-unit> - <trans-unit id="_msg478"> + <trans-unit id="_msg491"> <source xml:space="preserve">Outbound</source> <context-group purpose="location"><context context-type="linenumber">79</context></context-group> <note annotates="source" from="developer">An Outbound Connection to a Peer.</note> @@ -2180,7 +2238,7 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../bitcoinunits.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QObject"> - <trans-unit id="_msg479"> + <trans-unit id="_msg492"> <source xml:space="preserve">Amount</source> <context-group purpose="location"><context context-type="linenumber">197</context></context-group> </trans-unit> @@ -2188,190 +2246,190 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../guiutil.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QObject"> - <trans-unit id="_msg480"> + <trans-unit id="_msg493"> <source xml:space="preserve">Enter a Bitcoin address (e.g. %1)</source> <context-group purpose="location"><context context-type="linenumber">133</context></context-group> </trans-unit> - <trans-unit id="_msg481"> + <trans-unit id="_msg494"> <source xml:space="preserve">Ctrl+W</source> <context-group purpose="location"><context context-type="linenumber">421</context></context-group> </trans-unit> - <trans-unit id="_msg482"> + <trans-unit id="_msg495"> <source xml:space="preserve">Unroutable</source> <context-group purpose="location"><context context-type="linenumber">678</context></context-group> </trans-unit> - <trans-unit id="_msg483"> + <trans-unit id="_msg496"> <source xml:space="preserve">IPv4</source> <context-group purpose="location"><context context-type="linenumber">680</context></context-group> <context-group><context context-type="x-gettext-msgctxt">network name</context></context-group> <note annotates="source" from="developer">Name of IPv4 network in peer info</note> </trans-unit> - <trans-unit id="_msg484"> + <trans-unit id="_msg497"> <source xml:space="preserve">IPv6</source> <context-group purpose="location"><context context-type="linenumber">682</context></context-group> <context-group><context context-type="x-gettext-msgctxt">network name</context></context-group> <note annotates="source" from="developer">Name of IPv6 network in peer info</note> </trans-unit> - <trans-unit id="_msg485"> + <trans-unit id="_msg498"> <source xml:space="preserve">Onion</source> <context-group purpose="location"><context context-type="linenumber">684</context></context-group> <context-group><context context-type="x-gettext-msgctxt">network name</context></context-group> <note annotates="source" from="developer">Name of Tor network in peer info</note> </trans-unit> - <trans-unit id="_msg486"> + <trans-unit id="_msg499"> <source xml:space="preserve">I2P</source> <context-group purpose="location"><context context-type="linenumber">686</context></context-group> <context-group><context context-type="x-gettext-msgctxt">network name</context></context-group> <note annotates="source" from="developer">Name of I2P network in peer info</note> </trans-unit> - <trans-unit id="_msg487"> + <trans-unit id="_msg500"> <source xml:space="preserve">CJDNS</source> <context-group purpose="location"><context context-type="linenumber">688</context></context-group> <context-group><context context-type="x-gettext-msgctxt">network name</context></context-group> <note annotates="source" from="developer">Name of CJDNS network in peer info</note> </trans-unit> - <trans-unit id="_msg488"> + <trans-unit id="_msg501"> <source xml:space="preserve">Inbound</source> <context-group purpose="location"><context context-type="linenumber">702</context></context-group> <note annotates="source" from="developer">An inbound connection from a peer. An inbound connection is a connection initiated by a peer.</note> </trans-unit> - <trans-unit id="_msg489"> + <trans-unit id="_msg502"> <source xml:space="preserve">Outbound</source> <context-group purpose="location"><context context-type="linenumber">705</context></context-group> <note annotates="source" from="developer">An outbound connection to a peer. An outbound connection is a connection initiated by us.</note> </trans-unit> - <trans-unit id="_msg490"> + <trans-unit id="_msg503"> <source xml:space="preserve">Full Relay</source> <context-group purpose="location"><context context-type="linenumber">710</context></context-group> <note annotates="source" from="developer">Peer connection type that relays all network information.</note> </trans-unit> - <trans-unit id="_msg491"> + <trans-unit id="_msg504"> <source xml:space="preserve">Block Relay</source> <context-group purpose="location"><context context-type="linenumber">713</context></context-group> <note annotates="source" from="developer">Peer connection type that relays network information about blocks and not transactions or addresses.</note> </trans-unit> - <trans-unit id="_msg492"> + <trans-unit id="_msg505"> <source xml:space="preserve">Manual</source> <context-group purpose="location"><context context-type="linenumber">715</context></context-group> <note annotates="source" from="developer">Peer connection type established manually through one of several methods.</note> </trans-unit> - <trans-unit id="_msg493"> + <trans-unit id="_msg506"> <source xml:space="preserve">Feeler</source> <context-group purpose="location"><context context-type="linenumber">717</context></context-group> <note annotates="source" from="developer">Short-lived peer connection type that tests the aliveness of known addresses.</note> </trans-unit> - <trans-unit id="_msg494"> + <trans-unit id="_msg507"> <source xml:space="preserve">Address Fetch</source> <context-group purpose="location"><context context-type="linenumber">719</context></context-group> <note annotates="source" from="developer">Short-lived peer connection type that solicits known addresses from a peer.</note> </trans-unit> - <trans-unit id="_msg495"> + <trans-unit id="_msg508"> <source xml:space="preserve">%1 d</source> <context-group purpose="location"><context context-type="linenumber">732</context></context-group> <context-group purpose="location"><context context-type="linenumber">744</context></context-group> </trans-unit> - <trans-unit id="_msg496"> + <trans-unit id="_msg509"> <source xml:space="preserve">%1 h</source> <context-group purpose="location"><context context-type="linenumber">733</context></context-group> <context-group purpose="location"><context context-type="linenumber">745</context></context-group> </trans-unit> - <trans-unit id="_msg497"> + <trans-unit id="_msg510"> <source xml:space="preserve">%1 m</source> <context-group purpose="location"><context context-type="linenumber">734</context></context-group> <context-group purpose="location"><context context-type="linenumber">746</context></context-group> </trans-unit> - <trans-unit id="_msg498"> + <trans-unit id="_msg511"> <source xml:space="preserve">%1 s</source> <context-group purpose="location"><context context-type="linenumber">736</context></context-group> <context-group purpose="location"><context context-type="linenumber">747</context></context-group> <context-group purpose="location"><context context-type="linenumber">773</context></context-group> </trans-unit> - <trans-unit id="_msg499"> + <trans-unit id="_msg512"> <source xml:space="preserve">None</source> <context-group purpose="location"><context context-type="linenumber">761</context></context-group> </trans-unit> - <trans-unit id="_msg500"> + <trans-unit id="_msg513"> <source xml:space="preserve">N/A</source> <context-group purpose="location"><context context-type="linenumber">767</context></context-group> </trans-unit> - <trans-unit id="_msg501"> + <trans-unit id="_msg514"> <source xml:space="preserve">%1 ms</source> <context-group purpose="location"><context context-type="linenumber">768</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">786</context></context-group> - <trans-unit id="_msg502[0]"> + <trans-unit id="_msg515[0]"> <source xml:space="preserve">%n second(s)</source> </trans-unit> - <trans-unit id="_msg502[1]"> + <trans-unit id="_msg515[1]"> <source xml:space="preserve">%n second(s)</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">790</context></context-group> - <trans-unit id="_msg503[0]"> + <trans-unit id="_msg516[0]"> <source xml:space="preserve">%n minute(s)</source> </trans-unit> - <trans-unit id="_msg503[1]"> + <trans-unit id="_msg516[1]"> <source xml:space="preserve">%n minute(s)</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">794</context></context-group> - <trans-unit id="_msg504[0]"> + <trans-unit id="_msg517[0]"> <source xml:space="preserve">%n hour(s)</source> </trans-unit> - <trans-unit id="_msg504[1]"> + <trans-unit id="_msg517[1]"> <source xml:space="preserve">%n hour(s)</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">798</context></context-group> - <trans-unit id="_msg505[0]"> + <trans-unit id="_msg518[0]"> <source xml:space="preserve">%n day(s)</source> </trans-unit> - <trans-unit id="_msg505[1]"> + <trans-unit id="_msg518[1]"> <source xml:space="preserve">%n day(s)</source> </trans-unit> </group> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">802</context></context-group> <context-group purpose="location"><context context-type="linenumber">808</context></context-group> - <trans-unit id="_msg506[0]"> + <trans-unit id="_msg519[0]"> <source xml:space="preserve">%n week(s)</source> </trans-unit> - <trans-unit id="_msg506[1]"> + <trans-unit id="_msg519[1]"> <source xml:space="preserve">%n week(s)</source> </trans-unit> </group> - <trans-unit id="_msg507"> + <trans-unit id="_msg520"> <source xml:space="preserve">%1 and %2</source> <context-group purpose="location"><context context-type="linenumber">808</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">808</context></context-group> - <trans-unit id="_msg508[0]"> + <trans-unit id="_msg521[0]"> <source xml:space="preserve">%n year(s)</source> </trans-unit> - <trans-unit id="_msg508[1]"> + <trans-unit id="_msg521[1]"> <source xml:space="preserve">%n year(s)</source> </trans-unit> </group> - <trans-unit id="_msg509"> + <trans-unit id="_msg522"> <source xml:space="preserve">%1 B</source> <context-group purpose="location"><context context-type="linenumber">816</context></context-group> </trans-unit> - <trans-unit id="_msg510"> + <trans-unit id="_msg523"> <source xml:space="preserve">%1 kB</source> <context-group purpose="location"><context context-type="linenumber">818</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">994</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1004</context></context-group> </trans-unit> - <trans-unit id="_msg511"> + <trans-unit id="_msg524"> <source xml:space="preserve">%1 MB</source> <context-group purpose="location"><context context-type="linenumber">820</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">996</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1006</context></context-group> </trans-unit> - <trans-unit id="_msg512"> + <trans-unit id="_msg525"> <source xml:space="preserve">%1 GB</source> <context-group purpose="location"><context context-type="linenumber">822</context></context-group> </trans-unit> @@ -2379,31 +2437,31 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../qrimagewidget.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QRImageWidget"> - <trans-unit id="_msg513"> + <trans-unit id="_msg526"> <source xml:space="preserve">&Save Image…</source> <context-group purpose="location"><context context-type="linenumber">30</context></context-group> </trans-unit> - <trans-unit id="_msg514"> + <trans-unit id="_msg527"> <source xml:space="preserve">&Copy Image</source> <context-group purpose="location"><context context-type="linenumber">31</context></context-group> </trans-unit> - <trans-unit id="_msg515"> + <trans-unit id="_msg528"> <source xml:space="preserve">Resulting URI too long, try to reduce the text for label / message.</source> <context-group purpose="location"><context context-type="linenumber">42</context></context-group> </trans-unit> - <trans-unit id="_msg516"> + <trans-unit id="_msg529"> <source xml:space="preserve">Error encoding URI into QR Code.</source> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg517"> + <trans-unit id="_msg530"> <source xml:space="preserve">QR code support not available.</source> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg518"> + <trans-unit id="_msg531"> <source xml:space="preserve">Save QR Code</source> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg519"> + <trans-unit id="_msg532"> <source xml:space="preserve">PNG Image</source> <context-group purpose="location"><context context-type="linenumber">123</context></context-group> <note annotates="source" from="developer">Expanded name of the PNG file format. See: https://en.wikipedia.org/wiki/Portable_Network_Graphics.</note> @@ -2412,7 +2470,7 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../forms/debugwindow.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg520"> + <trans-unit id="_msg533"> <source xml:space="preserve">N/A</source> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> <context-group purpose="location"><context context-type="linenumber">101</context></context-group> @@ -2428,316 +2486,334 @@ If you are receiving this error you should request the merchant provide a BIP21 <context-group purpose="location"><context context-type="linenumber">1051</context></context-group> <context-group purpose="location"><context context-type="linenumber">1077</context></context-group> <context-group purpose="location"><context context-type="linenumber">1103</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1126</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1149</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1172</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1129</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1155</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1178</context></context-group> <context-group purpose="location"><context context-type="linenumber">1201</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1227</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1250</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1273</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1296</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1319</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1345</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1224</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1253</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1279</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1302</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1325</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1348</context></context-group> <context-group purpose="location"><context context-type="linenumber">1371</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1394</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1417</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1440</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1463</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1486</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1512</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1535</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1558</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1584</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1397</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1423</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1446</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1469</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1492</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1515</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1538</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1564</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1587</context></context-group> <context-group purpose="location"><context context-type="linenumber">1610</context></context-group> <context-group purpose="location"><context context-type="linenumber">1636</context></context-group> <context-group purpose="location"><context context-type="linenumber">1662</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1688</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1714</context></context-group> <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.h</context><context context-type="linenumber">147</context></context-group> </trans-unit> - <trans-unit id="_msg521"> + <trans-unit id="_msg534"> <source xml:space="preserve">Client version</source> <context-group purpose="location"><context context-type="linenumber">65</context></context-group> </trans-unit> - <trans-unit id="_msg522"> + <trans-unit id="_msg535"> <source xml:space="preserve">&Information</source> <context-group purpose="location"><context context-type="linenumber">43</context></context-group> </trans-unit> - <trans-unit id="_msg523"> + <trans-unit id="_msg536"> <source xml:space="preserve">General</source> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg524"> + <trans-unit id="_msg537"> <source xml:space="preserve">Datadir</source> <context-group purpose="location"><context context-type="linenumber">114</context></context-group> </trans-unit> - <trans-unit id="_msg525"> + <trans-unit id="_msg538"> <source xml:space="preserve">To specify a non-default location of the data directory use the '%1' option.</source> <context-group purpose="location"><context context-type="linenumber">124</context></context-group> </trans-unit> - <trans-unit id="_msg526"> + <trans-unit id="_msg539"> <source xml:space="preserve">Blocksdir</source> <context-group purpose="location"><context context-type="linenumber">143</context></context-group> </trans-unit> - <trans-unit id="_msg527"> + <trans-unit id="_msg540"> <source xml:space="preserve">To specify a non-default location of the blocks directory use the '%1' option.</source> <context-group purpose="location"><context context-type="linenumber">153</context></context-group> </trans-unit> - <trans-unit id="_msg528"> + <trans-unit id="_msg541"> <source xml:space="preserve">Startup time</source> <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> - <trans-unit id="_msg529"> + <trans-unit id="_msg542"> <source xml:space="preserve">Network</source> <context-group purpose="location"><context context-type="linenumber">201</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1093</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1145</context></context-group> </trans-unit> - <trans-unit id="_msg530"> + <trans-unit id="_msg543"> <source xml:space="preserve">Name</source> <context-group purpose="location"><context context-type="linenumber">208</context></context-group> </trans-unit> - <trans-unit id="_msg531"> + <trans-unit id="_msg544"> <source xml:space="preserve">Number of connections</source> <context-group purpose="location"><context context-type="linenumber">231</context></context-group> </trans-unit> - <trans-unit id="_msg532"> + <trans-unit id="_msg545"> <source xml:space="preserve">Block chain</source> <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg533"> + <trans-unit id="_msg546"> <source xml:space="preserve">Memory Pool</source> <context-group purpose="location"><context context-type="linenumber">319</context></context-group> </trans-unit> - <trans-unit id="_msg534"> + <trans-unit id="_msg547"> <source xml:space="preserve">Current number of transactions</source> <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg535"> + <trans-unit id="_msg548"> <source xml:space="preserve">Memory usage</source> <context-group purpose="location"><context context-type="linenumber">349</context></context-group> </trans-unit> - <trans-unit id="_msg536"> + <trans-unit id="_msg549"> <source xml:space="preserve">Wallet: </source> <context-group purpose="location"><context context-type="linenumber">443</context></context-group> </trans-unit> - <trans-unit id="_msg537"> + <trans-unit id="_msg550"> <source xml:space="preserve">(none)</source> <context-group purpose="location"><context context-type="linenumber">454</context></context-group> </trans-unit> - <trans-unit id="_msg538"> + <trans-unit id="_msg551"> <source xml:space="preserve">&Reset</source> <context-group purpose="location"><context context-type="linenumber">665</context></context-group> </trans-unit> - <trans-unit id="_msg539"> + <trans-unit id="_msg552"> <source xml:space="preserve">Received</source> <context-group purpose="location"><context context-type="linenumber">745</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1453</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1505</context></context-group> </trans-unit> - <trans-unit id="_msg540"> + <trans-unit id="_msg553"> <source xml:space="preserve">Sent</source> <context-group purpose="location"><context context-type="linenumber">825</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1430</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1482</context></context-group> </trans-unit> - <trans-unit id="_msg541"> + <trans-unit id="_msg554"> <source xml:space="preserve">&Peers</source> <context-group purpose="location"><context context-type="linenumber">866</context></context-group> </trans-unit> - <trans-unit id="_msg542"> + <trans-unit id="_msg555"> <source xml:space="preserve">Banned peers</source> <context-group purpose="location"><context context-type="linenumber">942</context></context-group> </trans-unit> - <trans-unit id="_msg543"> + <trans-unit id="_msg556"> <source xml:space="preserve">Select a peer to view detailed information.</source> <context-group purpose="location"><context context-type="linenumber">1010</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1161</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1171</context></context-group> </trans-unit> - <trans-unit id="_msg544"> - <source xml:space="preserve">Version</source> + <trans-unit id="_msg557"> + <source xml:space="preserve">The transport layer version: %1</source> + <context-group purpose="location"><context context-type="linenumber">1090</context></context-group> + </trans-unit> + <trans-unit id="_msg558"> + <source xml:space="preserve">Transport</source> + <context-group purpose="location"><context context-type="linenumber">1093</context></context-group> + </trans-unit> + <trans-unit id="_msg559"> + <source xml:space="preserve">The BIP324 session ID string in hex, if any.</source> <context-group purpose="location"><context context-type="linenumber">1116</context></context-group> </trans-unit> - <trans-unit id="_msg545"> + <trans-unit id="_msg560"> + <source xml:space="preserve">Session ID</source> + <context-group purpose="location"><context context-type="linenumber">1119</context></context-group> + </trans-unit> + <trans-unit id="_msg561"> + <source xml:space="preserve">Version</source> + <context-group purpose="location"><context context-type="linenumber">1168</context></context-group> + </trans-unit> + <trans-unit id="_msg562"> <source xml:space="preserve">Whether we relay transactions to this peer.</source> - <context-group purpose="location"><context context-type="linenumber">1188</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1240</context></context-group> </trans-unit> - <trans-unit id="_msg546"> + <trans-unit id="_msg563"> <source xml:space="preserve">Transaction Relay</source> - <context-group purpose="location"><context context-type="linenumber">1191</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1243</context></context-group> </trans-unit> - <trans-unit id="_msg547"> + <trans-unit id="_msg564"> <source xml:space="preserve">Starting Block</source> - <context-group purpose="location"><context context-type="linenumber">1240</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1292</context></context-group> </trans-unit> - <trans-unit id="_msg548"> + <trans-unit id="_msg565"> <source xml:space="preserve">Synced Headers</source> - <context-group purpose="location"><context context-type="linenumber">1263</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1315</context></context-group> </trans-unit> - <trans-unit id="_msg549"> + <trans-unit id="_msg566"> <source xml:space="preserve">Synced Blocks</source> - <context-group purpose="location"><context context-type="linenumber">1286</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1338</context></context-group> </trans-unit> - <trans-unit id="_msg550"> + <trans-unit id="_msg567"> <source xml:space="preserve">Last Transaction</source> - <context-group purpose="location"><context context-type="linenumber">1361</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1413</context></context-group> </trans-unit> - <trans-unit id="_msg551"> + <trans-unit id="_msg568"> <source xml:space="preserve">The mapped Autonomous System used for diversifying peer selection.</source> - <context-group purpose="location"><context context-type="linenumber">1571</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1623</context></context-group> </trans-unit> - <trans-unit id="_msg552"> + <trans-unit id="_msg569"> <source xml:space="preserve">Mapped AS</source> - <context-group purpose="location"><context context-type="linenumber">1574</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1626</context></context-group> </trans-unit> - <trans-unit id="_msg553"> + <trans-unit id="_msg570"> <source xml:space="preserve">Whether we relay addresses to this peer.</source> - <context-group purpose="location"><context context-type="linenumber">1597</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1649</context></context-group> <note annotates="source" from="developer">Tooltip text for the Address Relay field in the peer details area, which displays whether we relay addresses to this peer (Yes/No).</note> </trans-unit> - <trans-unit id="_msg554"> + <trans-unit id="_msg571"> <source xml:space="preserve">Address Relay</source> - <context-group purpose="location"><context context-type="linenumber">1600</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1652</context></context-group> <note annotates="source" from="developer">Text title for the Address Relay field in the peer details area, which displays whether we relay addresses to this peer (Yes/No).</note> </trans-unit> - <trans-unit id="_msg555"> + <trans-unit id="_msg572"> <source xml:space="preserve">The total number of addresses received from this peer that were processed (excludes addresses that were dropped due to rate-limiting).</source> - <context-group purpose="location"><context context-type="linenumber">1623</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1675</context></context-group> <note annotates="source" from="developer">Tooltip text for the Addresses Processed field in the peer details area, which displays the total number of addresses received from this peer that were processed (excludes addresses that were dropped due to rate-limiting).</note> </trans-unit> - <trans-unit id="_msg556"> + <trans-unit id="_msg573"> <source xml:space="preserve">The total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.</source> - <context-group purpose="location"><context context-type="linenumber">1649</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1701</context></context-group> <note annotates="source" from="developer">Tooltip text for the Addresses Rate-Limited field in the peer details area, which displays the total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.</note> </trans-unit> - <trans-unit id="_msg557"> + <trans-unit id="_msg574"> <source xml:space="preserve">Addresses Processed</source> - <context-group purpose="location"><context context-type="linenumber">1626</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1678</context></context-group> <note annotates="source" from="developer">Text title for the Addresses Processed field in the peer details area, which displays the total number of addresses received from this peer that were processed (excludes addresses that were dropped due to rate-limiting).</note> </trans-unit> - <trans-unit id="_msg558"> + <trans-unit id="_msg575"> <source xml:space="preserve">Addresses Rate-Limited</source> - <context-group purpose="location"><context context-type="linenumber">1652</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1704</context></context-group> <note annotates="source" from="developer">Text title for the Addresses Rate-Limited field in the peer details area, which displays the total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.</note> </trans-unit> - <trans-unit id="_msg559"> + <trans-unit id="_msg576"> <source xml:space="preserve">User Agent</source> <context-group purpose="location"><context context-type="linenumber">88</context></context-group> - <context-group purpose="location"><context context-type="linenumber">1139</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1191</context></context-group> </trans-unit> - <trans-unit id="_msg560"> + <trans-unit id="_msg577"> <source xml:space="preserve">Node window</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg561"> + <trans-unit id="_msg578"> <source xml:space="preserve">Current block height</source> <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg562"> + <trans-unit id="_msg579"> <source xml:space="preserve">Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source> <context-group purpose="location"><context context-type="linenumber">397</context></context-group> </trans-unit> - <trans-unit id="_msg563"> + <trans-unit id="_msg580"> <source xml:space="preserve">Decrease font size</source> <context-group purpose="location"><context context-type="linenumber">475</context></context-group> </trans-unit> - <trans-unit id="_msg564"> + <trans-unit id="_msg581"> <source xml:space="preserve">Increase font size</source> <context-group purpose="location"><context context-type="linenumber">495</context></context-group> </trans-unit> - <trans-unit id="_msg565"> + <trans-unit id="_msg582"> <source xml:space="preserve">Permissions</source> <context-group purpose="location"><context context-type="linenumber">1041</context></context-group> </trans-unit> - <trans-unit id="_msg566"> + <trans-unit id="_msg583"> <source xml:space="preserve">The direction and type of peer connection: %1</source> <context-group purpose="location"><context context-type="linenumber">1064</context></context-group> </trans-unit> - <trans-unit id="_msg567"> + <trans-unit id="_msg584"> <source xml:space="preserve">Direction/Type</source> <context-group purpose="location"><context context-type="linenumber">1067</context></context-group> </trans-unit> - <trans-unit id="_msg568"> + <trans-unit id="_msg585"> <source xml:space="preserve">The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source> - <context-group purpose="location"><context context-type="linenumber">1090</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1142</context></context-group> </trans-unit> - <trans-unit id="_msg569"> + <trans-unit id="_msg586"> <source xml:space="preserve">Services</source> - <context-group purpose="location"><context context-type="linenumber">1162</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1214</context></context-group> </trans-unit> - <trans-unit id="_msg570"> + <trans-unit id="_msg587"> <source xml:space="preserve">High bandwidth BIP152 compact block relay: %1</source> - <context-group purpose="location"><context context-type="linenumber">1214</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1266</context></context-group> </trans-unit> - <trans-unit id="_msg571"> + <trans-unit id="_msg588"> <source xml:space="preserve">High Bandwidth</source> - <context-group purpose="location"><context context-type="linenumber">1217</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1269</context></context-group> </trans-unit> - <trans-unit id="_msg572"> + <trans-unit id="_msg589"> <source xml:space="preserve">Connection Time</source> - <context-group purpose="location"><context context-type="linenumber">1309</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1361</context></context-group> </trans-unit> - <trans-unit id="_msg573"> + <trans-unit id="_msg590"> <source xml:space="preserve">Elapsed time since a novel block passing initial validity checks was received from this peer.</source> - <context-group purpose="location"><context context-type="linenumber">1332</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1384</context></context-group> </trans-unit> - <trans-unit id="_msg574"> + <trans-unit id="_msg591"> <source xml:space="preserve">Last Block</source> - <context-group purpose="location"><context context-type="linenumber">1335</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1387</context></context-group> </trans-unit> - <trans-unit id="_msg575"> + <trans-unit id="_msg592"> <source xml:space="preserve">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</source> - <context-group purpose="location"><context context-type="linenumber">1358</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1410</context></context-group> <note annotates="source" from="developer">Tooltip text for the Last Transaction field in the peer details area.</note> </trans-unit> - <trans-unit id="_msg576"> + <trans-unit id="_msg593"> <source xml:space="preserve">Last Send</source> - <context-group purpose="location"><context context-type="linenumber">1384</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1436</context></context-group> </trans-unit> - <trans-unit id="_msg577"> + <trans-unit id="_msg594"> <source xml:space="preserve">Last Receive</source> - <context-group purpose="location"><context context-type="linenumber">1407</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1459</context></context-group> </trans-unit> - <trans-unit id="_msg578"> + <trans-unit id="_msg595"> <source xml:space="preserve">Ping Time</source> - <context-group purpose="location"><context context-type="linenumber">1476</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1528</context></context-group> </trans-unit> - <trans-unit id="_msg579"> + <trans-unit id="_msg596"> <source xml:space="preserve">The duration of a currently outstanding ping.</source> - <context-group purpose="location"><context context-type="linenumber">1499</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1551</context></context-group> </trans-unit> - <trans-unit id="_msg580"> + <trans-unit id="_msg597"> <source xml:space="preserve">Ping Wait</source> - <context-group purpose="location"><context context-type="linenumber">1502</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1554</context></context-group> </trans-unit> - <trans-unit id="_msg581"> + <trans-unit id="_msg598"> <source xml:space="preserve">Min Ping</source> - <context-group purpose="location"><context context-type="linenumber">1525</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1577</context></context-group> </trans-unit> - <trans-unit id="_msg582"> + <trans-unit id="_msg599"> <source xml:space="preserve">Time Offset</source> - <context-group purpose="location"><context context-type="linenumber">1548</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1600</context></context-group> </trans-unit> - <trans-unit id="_msg583"> + <trans-unit id="_msg600"> <source xml:space="preserve">Last block time</source> <context-group purpose="location"><context context-type="linenumber">290</context></context-group> </trans-unit> - <trans-unit id="_msg584"> + <trans-unit id="_msg601"> <source xml:space="preserve">&Open</source> <context-group purpose="location"><context context-type="linenumber">400</context></context-group> </trans-unit> - <trans-unit id="_msg585"> + <trans-unit id="_msg602"> <source xml:space="preserve">&Console</source> <context-group purpose="location"><context context-type="linenumber">426</context></context-group> </trans-unit> - <trans-unit id="_msg586"> + <trans-unit id="_msg603"> <source xml:space="preserve">&Network Traffic</source> <context-group purpose="location"><context context-type="linenumber">613</context></context-group> </trans-unit> - <trans-unit id="_msg587"> + <trans-unit id="_msg604"> <source xml:space="preserve">Totals</source> <context-group purpose="location"><context context-type="linenumber">681</context></context-group> </trans-unit> - <trans-unit id="_msg588"> + <trans-unit id="_msg605"> <source xml:space="preserve">Debug log file</source> <context-group purpose="location"><context context-type="linenumber">390</context></context-group> </trans-unit> - <trans-unit id="_msg589"> + <trans-unit id="_msg606"> <source xml:space="preserve">Clear console</source> <context-group purpose="location"><context context-type="linenumber">515</context></context-group> </trans-unit> @@ -2745,139 +2821,154 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../rpcconsole.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg590"> + <trans-unit id="_msg607"> <source xml:space="preserve">In:</source> - <context-group purpose="location"><context context-type="linenumber">958</context></context-group> + <context-group purpose="location"><context context-type="linenumber">968</context></context-group> </trans-unit> - <trans-unit id="_msg591"> + <trans-unit id="_msg608"> <source xml:space="preserve">Out:</source> - <context-group purpose="location"><context context-type="linenumber">959</context></context-group> + <context-group purpose="location"><context context-type="linenumber">969</context></context-group> </trans-unit> - <trans-unit id="_msg592"> + <trans-unit id="_msg609"> <source xml:space="preserve">Inbound: initiated by peer</source> - <context-group purpose="location"><context context-type="linenumber">495</context></context-group> + <context-group purpose="location"><context context-type="linenumber">496</context></context-group> <note annotates="source" from="developer">Explanatory text for an inbound peer connection.</note> </trans-unit> - <trans-unit id="_msg593"> + <trans-unit id="_msg610"> <source xml:space="preserve">Outbound Full Relay: default</source> - <context-group purpose="location"><context context-type="linenumber">499</context></context-group> + <context-group purpose="location"><context context-type="linenumber">500</context></context-group> <note annotates="source" from="developer">Explanatory text for an outbound peer connection that relays all network information. This is the default behavior for outbound connections.</note> </trans-unit> - <trans-unit id="_msg594"> + <trans-unit id="_msg611"> <source xml:space="preserve">Outbound Block Relay: does not relay transactions or addresses</source> - <context-group purpose="location"><context context-type="linenumber">502</context></context-group> + <context-group purpose="location"><context context-type="linenumber">503</context></context-group> <note annotates="source" from="developer">Explanatory text for an outbound peer connection that relays network information about blocks and not transactions or addresses.</note> </trans-unit> - <trans-unit id="_msg595"> + <trans-unit id="_msg612"> <source xml:space="preserve">Outbound Manual: added using RPC %1 or %2/%3 configuration options</source> - <context-group purpose="location"><context context-type="linenumber">507</context></context-group> + <context-group purpose="location"><context context-type="linenumber">508</context></context-group> <note annotates="source" from="developer">Explanatory text for an outbound peer connection that was established manually through one of several methods. The numbered arguments are stand-ins for the methods available to establish manual connections.</note> </trans-unit> - <trans-unit id="_msg596"> + <trans-unit id="_msg613"> <source xml:space="preserve">Outbound Feeler: short-lived, for testing addresses</source> - <context-group purpose="location"><context context-type="linenumber">513</context></context-group> + <context-group purpose="location"><context context-type="linenumber">514</context></context-group> <note annotates="source" from="developer">Explanatory text for a short-lived outbound peer connection that is used to test the aliveness of known addresses.</note> </trans-unit> - <trans-unit id="_msg597"> + <trans-unit id="_msg614"> <source xml:space="preserve">Outbound Address Fetch: short-lived, for soliciting addresses</source> - <context-group purpose="location"><context context-type="linenumber">516</context></context-group> + <context-group purpose="location"><context context-type="linenumber">517</context></context-group> <note annotates="source" from="developer">Explanatory text for a short-lived outbound peer connection that is used to request addresses from a peer.</note> </trans-unit> - <trans-unit id="_msg598"> + <trans-unit id="_msg615"> + <source xml:space="preserve">detecting: peer could be v1 or v2</source> + <context-group purpose="location"><context context-type="linenumber">522</context></context-group> + <note annotates="source" from="developer">Explanatory text for "detecting" transport type.</note> + </trans-unit> + <trans-unit id="_msg616"> + <source xml:space="preserve">v1: unencrypted, plaintext transport protocol</source> + <context-group purpose="location"><context context-type="linenumber">524</context></context-group> + <note annotates="source" from="developer">Explanatory text for v1 transport type.</note> + </trans-unit> + <trans-unit id="_msg617"> + <source xml:space="preserve">v2: BIP324 encrypted transport protocol</source> + <context-group purpose="location"><context context-type="linenumber">526</context></context-group> + <note annotates="source" from="developer">Explanatory text for v2 transport type.</note> + </trans-unit> + <trans-unit id="_msg618"> <source xml:space="preserve">we selected the peer for high bandwidth relay</source> - <context-group purpose="location"><context context-type="linenumber">520</context></context-group> + <context-group purpose="location"><context context-type="linenumber">530</context></context-group> </trans-unit> - <trans-unit id="_msg599"> + <trans-unit id="_msg619"> <source xml:space="preserve">the peer selected us for high bandwidth relay</source> - <context-group purpose="location"><context context-type="linenumber">521</context></context-group> + <context-group purpose="location"><context context-type="linenumber">531</context></context-group> </trans-unit> - <trans-unit id="_msg600"> + <trans-unit id="_msg620"> <source xml:space="preserve">no high bandwidth relay selected</source> - <context-group purpose="location"><context context-type="linenumber">522</context></context-group> + <context-group purpose="location"><context context-type="linenumber">532</context></context-group> </trans-unit> - <trans-unit id="_msg601"> + <trans-unit id="_msg621"> <source xml:space="preserve">Ctrl++</source> - <context-group purpose="location"><context context-type="linenumber">535</context></context-group> + <context-group purpose="location"><context context-type="linenumber">545</context></context-group> <note annotates="source" from="developer">Main shortcut to increase the RPC console font size.</note> </trans-unit> - <trans-unit id="_msg602"> + <trans-unit id="_msg622"> <source xml:space="preserve">Ctrl+=</source> - <context-group purpose="location"><context context-type="linenumber">537</context></context-group> + <context-group purpose="location"><context context-type="linenumber">547</context></context-group> <note annotates="source" from="developer">Secondary shortcut to increase the RPC console font size.</note> </trans-unit> - <trans-unit id="_msg603"> + <trans-unit id="_msg623"> <source xml:space="preserve">Ctrl+-</source> - <context-group purpose="location"><context context-type="linenumber">541</context></context-group> + <context-group purpose="location"><context context-type="linenumber">551</context></context-group> <note annotates="source" from="developer">Main shortcut to decrease the RPC console font size.</note> </trans-unit> - <trans-unit id="_msg604"> + <trans-unit id="_msg624"> <source xml:space="preserve">Ctrl+_</source> - <context-group purpose="location"><context context-type="linenumber">543</context></context-group> + <context-group purpose="location"><context context-type="linenumber">553</context></context-group> <note annotates="source" from="developer">Secondary shortcut to decrease the RPC console font size.</note> </trans-unit> - <trans-unit id="_msg605"> + <trans-unit id="_msg625"> <source xml:space="preserve">&Copy address</source> - <context-group purpose="location"><context context-type="linenumber">694</context></context-group> + <context-group purpose="location"><context context-type="linenumber">704</context></context-group> <note annotates="source" from="developer">Context menu action to copy the address of a peer.</note> </trans-unit> - <trans-unit id="_msg606"> + <trans-unit id="_msg626"> <source xml:space="preserve">&Disconnect</source> - <context-group purpose="location"><context context-type="linenumber">698</context></context-group> + <context-group purpose="location"><context context-type="linenumber">708</context></context-group> </trans-unit> - <trans-unit id="_msg607"> + <trans-unit id="_msg627"> <source xml:space="preserve">1 &hour</source> - <context-group purpose="location"><context context-type="linenumber">699</context></context-group> + <context-group purpose="location"><context context-type="linenumber">709</context></context-group> </trans-unit> - <trans-unit id="_msg608"> + <trans-unit id="_msg628"> <source xml:space="preserve">1 d&ay</source> - <context-group purpose="location"><context context-type="linenumber">700</context></context-group> + <context-group purpose="location"><context context-type="linenumber">710</context></context-group> </trans-unit> - <trans-unit id="_msg609"> + <trans-unit id="_msg629"> <source xml:space="preserve">1 &week</source> - <context-group purpose="location"><context context-type="linenumber">701</context></context-group> + <context-group purpose="location"><context context-type="linenumber">711</context></context-group> </trans-unit> - <trans-unit id="_msg610"> + <trans-unit id="_msg630"> <source xml:space="preserve">1 &year</source> - <context-group purpose="location"><context context-type="linenumber">702</context></context-group> + <context-group purpose="location"><context context-type="linenumber">712</context></context-group> </trans-unit> - <trans-unit id="_msg611"> + <trans-unit id="_msg631"> <source xml:space="preserve">&Copy IP/Netmask</source> - <context-group purpose="location"><context context-type="linenumber">728</context></context-group> + <context-group purpose="location"><context context-type="linenumber">738</context></context-group> <note annotates="source" from="developer">Context menu action to copy the IP/Netmask of a banned peer. IP/Netmask is the combination of a peer's IP address and its Netmask. For IP address, see: https://en.wikipedia.org/wiki/IP_address.</note> </trans-unit> - <trans-unit id="_msg612"> + <trans-unit id="_msg632"> <source xml:space="preserve">&Unban</source> - <context-group purpose="location"><context context-type="linenumber">732</context></context-group> + <context-group purpose="location"><context context-type="linenumber">742</context></context-group> </trans-unit> - <trans-unit id="_msg613"> + <trans-unit id="_msg633"> <source xml:space="preserve">Network activity disabled</source> - <context-group purpose="location"><context context-type="linenumber">962</context></context-group> + <context-group purpose="location"><context context-type="linenumber">972</context></context-group> </trans-unit> - <trans-unit id="_msg614"> + <trans-unit id="_msg634"> <source xml:space="preserve">Executing command without any wallet</source> - <context-group purpose="location"><context context-type="linenumber">1041</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1051</context></context-group> </trans-unit> - <trans-unit id="_msg615"> + <trans-unit id="_msg635"> <source xml:space="preserve">Ctrl+I</source> - <context-group purpose="location"><context context-type="linenumber">1357</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1376</context></context-group> </trans-unit> - <trans-unit id="_msg616"> + <trans-unit id="_msg636"> <source xml:space="preserve">Ctrl+T</source> - <context-group purpose="location"><context context-type="linenumber">1358</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1377</context></context-group> </trans-unit> - <trans-unit id="_msg617"> + <trans-unit id="_msg637"> <source xml:space="preserve">Ctrl+N</source> - <context-group purpose="location"><context context-type="linenumber">1359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1378</context></context-group> </trans-unit> - <trans-unit id="_msg618"> + <trans-unit id="_msg638"> <source xml:space="preserve">Ctrl+P</source> - <context-group purpose="location"><context context-type="linenumber">1360</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1379</context></context-group> </trans-unit> - <trans-unit id="_msg619"> + <trans-unit id="_msg639"> <source xml:space="preserve">Executing command using "%1" wallet</source> - <context-group purpose="location"><context context-type="linenumber">1039</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1049</context></context-group> </trans-unit> - <trans-unit id="_msg620"> + <trans-unit id="_msg640"> <source xml:space="preserve">Welcome to the %1 RPC console. Use up and down arrows to navigate history, and %2 to clear screen. Use %3 and %4 to increase or decrease the font size. @@ -2885,51 +2976,51 @@ Type %5 for an overview of available commands. For more information on using this console, type %6. %7WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.%8</source> - <context-group purpose="location"><context context-type="linenumber">892</context></context-group> + <context-group purpose="location"><context context-type="linenumber">902</context></context-group> <note annotates="source" from="developer">RPC console welcome message. Placeholders %7 and %8 are style tags for the warning content, and they are not space separated from the rest of the text intentionally.</note> </trans-unit> - <trans-unit id="_msg621"> + <trans-unit id="_msg641"> <source xml:space="preserve">Executing…</source> - <context-group purpose="location"><context context-type="linenumber">1049</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1059</context></context-group> <note annotates="source" from="developer">A console message indicating an entered command is currently being executed.</note> </trans-unit> - <trans-unit id="_msg622"> + <trans-unit id="_msg642"> <source xml:space="preserve">(peer: %1)</source> - <context-group purpose="location"><context context-type="linenumber">1167</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1177</context></context-group> </trans-unit> - <trans-unit id="_msg623"> + <trans-unit id="_msg643"> <source xml:space="preserve">via %1</source> - <context-group purpose="location"><context context-type="linenumber">1169</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1179</context></context-group> </trans-unit> </group> </body></file> <file original="../rpcconsole.h" datatype="c" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg624"> + <trans-unit id="_msg644"> <source xml:space="preserve">Yes</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg625"> + <trans-unit id="_msg645"> <source xml:space="preserve">No</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg626"> + <trans-unit id="_msg646"> <source xml:space="preserve">To</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg627"> + <trans-unit id="_msg647"> <source xml:space="preserve">From</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg628"> + <trans-unit id="_msg648"> <source xml:space="preserve">Ban for</source> <context-group purpose="location"><context context-type="linenumber">147</context></context-group> </trans-unit> - <trans-unit id="_msg629"> + <trans-unit id="_msg649"> <source xml:space="preserve">Never</source> <context-group purpose="location"><context context-type="linenumber">189</context></context-group> </trans-unit> - <trans-unit id="_msg630"> + <trans-unit id="_msg650"> <source xml:space="preserve">Unknown</source> <context-group purpose="location"><context context-type="linenumber">147</context></context-group> </trans-unit> @@ -2937,72 +3028,72 @@ For more information on using this console, type %6. </body></file> <file original="../forms/receivecoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog"> - <trans-unit id="_msg631"> + <trans-unit id="_msg651"> <source xml:space="preserve">&Amount:</source> <context-group purpose="location"><context context-type="linenumber">37</context></context-group> </trans-unit> - <trans-unit id="_msg632"> + <trans-unit id="_msg652"> <source xml:space="preserve">&Label:</source> <context-group purpose="location"><context context-type="linenumber">83</context></context-group> </trans-unit> - <trans-unit id="_msg633"> + <trans-unit id="_msg653"> <source xml:space="preserve">&Message:</source> <context-group purpose="location"><context context-type="linenumber">53</context></context-group> </trans-unit> - <trans-unit id="_msg634"> + <trans-unit id="_msg654"> <source xml:space="preserve">An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg635"> + <trans-unit id="_msg655"> <source xml:space="preserve">An optional label to associate with the new receiving address.</source> <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg636"> + <trans-unit id="_msg656"> <source xml:space="preserve">Use this form to request payments. All fields are <b>optional</b>.</source> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> - <trans-unit id="_msg637"> + <trans-unit id="_msg657"> <source xml:space="preserve">An optional amount to request. Leave this empty or zero to not request a specific amount.</source> <context-group purpose="location"><context context-type="linenumber">34</context></context-group> <context-group purpose="location"><context context-type="linenumber">193</context></context-group> </trans-unit> - <trans-unit id="_msg638"> + <trans-unit id="_msg658"> <source xml:space="preserve">An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</source> <context-group purpose="location"><context context-type="linenumber">66</context></context-group> </trans-unit> - <trans-unit id="_msg639"> + <trans-unit id="_msg659"> <source xml:space="preserve">An optional message that is attached to the payment request and may be displayed to the sender.</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg640"> + <trans-unit id="_msg660"> <source xml:space="preserve">&Create new receiving address</source> <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> - <trans-unit id="_msg641"> + <trans-unit id="_msg661"> <source xml:space="preserve">Clear all fields of the form.</source> <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> - <trans-unit id="_msg642"> + <trans-unit id="_msg662"> <source xml:space="preserve">Clear</source> <context-group purpose="location"><context context-type="linenumber">137</context></context-group> </trans-unit> - <trans-unit id="_msg643"> + <trans-unit id="_msg663"> <source xml:space="preserve">Requested payments history</source> <context-group purpose="location"><context context-type="linenumber">273</context></context-group> </trans-unit> - <trans-unit id="_msg644"> + <trans-unit id="_msg664"> <source xml:space="preserve">Show the selected request (does the same as double clicking an entry)</source> <context-group purpose="location"><context context-type="linenumber">298</context></context-group> </trans-unit> - <trans-unit id="_msg645"> + <trans-unit id="_msg665"> <source xml:space="preserve">Show</source> <context-group purpose="location"><context context-type="linenumber">301</context></context-group> </trans-unit> - <trans-unit id="_msg646"> + <trans-unit id="_msg666"> <source xml:space="preserve">Remove the selected entries from the list</source> <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> - <trans-unit id="_msg647"> + <trans-unit id="_msg667"> <source xml:space="preserve">Remove</source> <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> @@ -3010,63 +3101,63 @@ For more information on using this console, type %6. </body></file> <file original="../receivecoinsdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog"> - <trans-unit id="_msg648"> + <trans-unit id="_msg668"> <source xml:space="preserve">Copy &URI</source> <context-group purpose="location"><context context-type="linenumber">46</context></context-group> </trans-unit> - <trans-unit id="_msg649"> + <trans-unit id="_msg669"> <source xml:space="preserve">&Copy address</source> <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg650"> + <trans-unit id="_msg670"> <source xml:space="preserve">Copy &label</source> <context-group purpose="location"><context context-type="linenumber">48</context></context-group> </trans-unit> - <trans-unit id="_msg651"> + <trans-unit id="_msg671"> <source xml:space="preserve">Copy &message</source> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg652"> + <trans-unit id="_msg672"> <source xml:space="preserve">Copy &amount</source> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg653"> + <trans-unit id="_msg673"> <source xml:space="preserve">Base58 (Legacy)</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg654"> + <trans-unit id="_msg674"> <source xml:space="preserve">Not recommended due to higher fees and less protection against typos.</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg655"> + <trans-unit id="_msg675"> <source xml:space="preserve">Base58 (P2SH-SegWit)</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg656"> + <trans-unit id="_msg676"> <source xml:space="preserve">Generates an address compatible with older wallets.</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg657"> + <trans-unit id="_msg677"> <source xml:space="preserve">Bech32 (SegWit)</source> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg658"> + <trans-unit id="_msg678"> <source xml:space="preserve">Generates a native segwit address (BIP-173). Some old wallets don't support it.</source> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg659"> + <trans-unit id="_msg679"> <source xml:space="preserve">Bech32m (Taproot)</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> - <trans-unit id="_msg660"> + <trans-unit id="_msg680"> <source xml:space="preserve">Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited.</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> - <trans-unit id="_msg661"> + <trans-unit id="_msg681"> <source xml:space="preserve">Could not unlock wallet.</source> <context-group purpose="location"><context context-type="linenumber">175</context></context-group> </trans-unit> - <trans-unit id="_msg662"> + <trans-unit id="_msg682"> <source xml:space="preserve">Could not generate new %1 address</source> <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> @@ -3074,51 +3165,51 @@ For more information on using this console, type %6. </body></file> <file original="../forms/receiverequestdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog"> - <trans-unit id="_msg663"> + <trans-unit id="_msg683"> <source xml:space="preserve">Request payment to …</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg664"> + <trans-unit id="_msg684"> <source xml:space="preserve">Address:</source> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg665"> + <trans-unit id="_msg685"> <source xml:space="preserve">Amount:</source> <context-group purpose="location"><context context-type="linenumber">119</context></context-group> </trans-unit> - <trans-unit id="_msg666"> + <trans-unit id="_msg686"> <source xml:space="preserve">Label:</source> <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> - <trans-unit id="_msg667"> + <trans-unit id="_msg687"> <source xml:space="preserve">Message:</source> <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg668"> + <trans-unit id="_msg688"> <source xml:space="preserve">Wallet:</source> <context-group purpose="location"><context context-type="linenumber">212</context></context-group> </trans-unit> - <trans-unit id="_msg669"> + <trans-unit id="_msg689"> <source xml:space="preserve">Copy &URI</source> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg670"> + <trans-unit id="_msg690"> <source xml:space="preserve">Copy &Address</source> <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg671"> + <trans-unit id="_msg691"> <source xml:space="preserve">&Verify</source> <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg672"> + <trans-unit id="_msg692"> <source xml:space="preserve">Verify this address on e.g. a hardware wallet screen</source> <context-group purpose="location"><context context-type="linenumber">263</context></context-group> </trans-unit> - <trans-unit id="_msg673"> + <trans-unit id="_msg693"> <source xml:space="preserve">&Save Image…</source> <context-group purpose="location"><context context-type="linenumber">273</context></context-group> </trans-unit> - <trans-unit id="_msg674"> + <trans-unit id="_msg694"> <source xml:space="preserve">Payment information</source> <context-group purpose="location"><context context-type="linenumber">39</context></context-group> </trans-unit> @@ -3126,7 +3217,7 @@ For more information on using this console, type %6. </body></file> <file original="../receiverequestdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog"> - <trans-unit id="_msg675"> + <trans-unit id="_msg695"> <source xml:space="preserve">Request payment to %1</source> <context-group purpose="location"><context context-type="linenumber">48</context></context-group> </trans-unit> @@ -3134,31 +3225,31 @@ For more information on using this console, type %6. </body></file> <file original="../recentrequeststablemodel.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RecentRequestsTableModel"> - <trans-unit id="_msg676"> + <trans-unit id="_msg696"> <source xml:space="preserve">Date</source> <context-group purpose="location"><context context-type="linenumber">32</context></context-group> </trans-unit> - <trans-unit id="_msg677"> + <trans-unit id="_msg697"> <source xml:space="preserve">Label</source> <context-group purpose="location"><context context-type="linenumber">32</context></context-group> </trans-unit> - <trans-unit id="_msg678"> + <trans-unit id="_msg698"> <source xml:space="preserve">Message</source> <context-group purpose="location"><context context-type="linenumber">32</context></context-group> </trans-unit> - <trans-unit id="_msg679"> + <trans-unit id="_msg699"> <source xml:space="preserve">(no label)</source> <context-group purpose="location"><context context-type="linenumber">70</context></context-group> </trans-unit> - <trans-unit id="_msg680"> + <trans-unit id="_msg700"> <source xml:space="preserve">(no message)</source> <context-group purpose="location"><context context-type="linenumber">79</context></context-group> </trans-unit> - <trans-unit id="_msg681"> + <trans-unit id="_msg701"> <source xml:space="preserve">(no amount requested)</source> <context-group purpose="location"><context context-type="linenumber">87</context></context-group> </trans-unit> - <trans-unit id="_msg682"> + <trans-unit id="_msg702"> <source xml:space="preserve">Requested</source> <context-group purpose="location"><context context-type="linenumber">130</context></context-group> </trans-unit> @@ -3166,150 +3257,150 @@ For more information on using this console, type %6. </body></file> <file original="../forms/sendcoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog"> - <trans-unit id="_msg683"> + <trans-unit id="_msg703"> <source xml:space="preserve">Send Coins</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">762</context></context-group> </trans-unit> - <trans-unit id="_msg684"> + <trans-unit id="_msg704"> <source xml:space="preserve">Coin Control Features</source> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg685"> + <trans-unit id="_msg705"> <source xml:space="preserve">automatically selected</source> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg686"> + <trans-unit id="_msg706"> <source xml:space="preserve">Insufficient funds!</source> <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> - <trans-unit id="_msg687"> + <trans-unit id="_msg707"> <source xml:space="preserve">Quantity:</source> <context-group purpose="location"><context context-type="linenumber">231</context></context-group> </trans-unit> - <trans-unit id="_msg688"> + <trans-unit id="_msg708"> <source xml:space="preserve">Bytes:</source> <context-group purpose="location"><context context-type="linenumber">266</context></context-group> </trans-unit> - <trans-unit id="_msg689"> + <trans-unit id="_msg709"> <source xml:space="preserve">Amount:</source> <context-group purpose="location"><context context-type="linenumber">314</context></context-group> </trans-unit> - <trans-unit id="_msg690"> + <trans-unit id="_msg710"> <source xml:space="preserve">Fee:</source> <context-group purpose="location"><context context-type="linenumber">365</context></context-group> </trans-unit> - <trans-unit id="_msg691"> + <trans-unit id="_msg711"> <source xml:space="preserve">After Fee:</source> <context-group purpose="location"><context context-type="linenumber">419</context></context-group> </trans-unit> - <trans-unit id="_msg692"> + <trans-unit id="_msg712"> <source xml:space="preserve">Change:</source> <context-group purpose="location"><context context-type="linenumber">451</context></context-group> </trans-unit> - <trans-unit id="_msg693"> + <trans-unit id="_msg713"> <source xml:space="preserve">If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> <context-group purpose="location"><context context-type="linenumber">495</context></context-group> </trans-unit> - <trans-unit id="_msg694"> + <trans-unit id="_msg714"> <source xml:space="preserve">Custom change address</source> <context-group purpose="location"><context context-type="linenumber">498</context></context-group> </trans-unit> - <trans-unit id="_msg695"> + <trans-unit id="_msg715"> <source xml:space="preserve">Transaction Fee:</source> <context-group purpose="location"><context context-type="linenumber">704</context></context-group> </trans-unit> - <trans-unit id="_msg696"> + <trans-unit id="_msg716"> <source xml:space="preserve">Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source> <context-group purpose="location"><context context-type="linenumber">742</context></context-group> </trans-unit> - <trans-unit id="_msg697"> + <trans-unit id="_msg717"> <source xml:space="preserve">Warning: Fee estimation is currently not possible.</source> <context-group purpose="location"><context context-type="linenumber">751</context></context-group> </trans-unit> - <trans-unit id="_msg698"> + <trans-unit id="_msg718"> <source xml:space="preserve">per kilobyte</source> <context-group purpose="location"><context context-type="linenumber">833</context></context-group> </trans-unit> - <trans-unit id="_msg699"> + <trans-unit id="_msg719"> <source xml:space="preserve">Hide</source> <context-group purpose="location"><context context-type="linenumber">780</context></context-group> </trans-unit> - <trans-unit id="_msg700"> + <trans-unit id="_msg720"> <source xml:space="preserve">Recommended:</source> <context-group purpose="location"><context context-type="linenumber">892</context></context-group> </trans-unit> - <trans-unit id="_msg701"> + <trans-unit id="_msg721"> <source xml:space="preserve">Custom:</source> <context-group purpose="location"><context context-type="linenumber">922</context></context-group> </trans-unit> - <trans-unit id="_msg702"> + <trans-unit id="_msg722"> <source xml:space="preserve">Send to multiple recipients at once</source> <context-group purpose="location"><context context-type="linenumber">1137</context></context-group> </trans-unit> - <trans-unit id="_msg703"> + <trans-unit id="_msg723"> <source xml:space="preserve">Add &Recipient</source> <context-group purpose="location"><context context-type="linenumber">1140</context></context-group> </trans-unit> - <trans-unit id="_msg704"> + <trans-unit id="_msg724"> <source xml:space="preserve">Clear all fields of the form.</source> <context-group purpose="location"><context context-type="linenumber">1120</context></context-group> </trans-unit> - <trans-unit id="_msg705"> + <trans-unit id="_msg725"> <source xml:space="preserve">Inputs…</source> <context-group purpose="location"><context context-type="linenumber">110</context></context-group> </trans-unit> - <trans-unit id="_msg706"> + <trans-unit id="_msg726"> <source xml:space="preserve">Choose…</source> <context-group purpose="location"><context context-type="linenumber">718</context></context-group> </trans-unit> - <trans-unit id="_msg707"> + <trans-unit id="_msg727"> <source xml:space="preserve">Hide transaction fee settings</source> <context-group purpose="location"><context context-type="linenumber">777</context></context-group> </trans-unit> - <trans-unit id="_msg708"> + <trans-unit id="_msg728"> <source xml:space="preserve">Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size. Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satoshis per kvB" for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</source> <context-group purpose="location"><context context-type="linenumber">828</context></context-group> </trans-unit> - <trans-unit id="_msg709"> + <trans-unit id="_msg729"> <source xml:space="preserve">When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source> <context-group purpose="location"><context context-type="linenumber">863</context></context-group> </trans-unit> - <trans-unit id="_msg710"> + <trans-unit id="_msg730"> <source xml:space="preserve">A too low fee might result in a never confirming transaction (read the tooltip)</source> <context-group purpose="location"><context context-type="linenumber">866</context></context-group> </trans-unit> - <trans-unit id="_msg711"> + <trans-unit id="_msg731"> <source xml:space="preserve">(Smart fee not initialized yet. This usually takes a few blocks…)</source> <context-group purpose="location"><context context-type="linenumber">971</context></context-group> </trans-unit> - <trans-unit id="_msg712"> + <trans-unit id="_msg732"> <source xml:space="preserve">Confirmation time target:</source> <context-group purpose="location"><context context-type="linenumber">997</context></context-group> </trans-unit> - <trans-unit id="_msg713"> + <trans-unit id="_msg733"> <source xml:space="preserve">Enable Replace-By-Fee</source> <context-group purpose="location"><context context-type="linenumber">1055</context></context-group> </trans-unit> - <trans-unit id="_msg714"> + <trans-unit id="_msg734"> <source xml:space="preserve">With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source> <context-group purpose="location"><context context-type="linenumber">1058</context></context-group> </trans-unit> - <trans-unit id="_msg715"> + <trans-unit id="_msg735"> <source xml:space="preserve">Clear &All</source> <context-group purpose="location"><context context-type="linenumber">1123</context></context-group> </trans-unit> - <trans-unit id="_msg716"> + <trans-unit id="_msg736"> <source xml:space="preserve">Balance:</source> <context-group purpose="location"><context context-type="linenumber">1178</context></context-group> </trans-unit> - <trans-unit id="_msg717"> + <trans-unit id="_msg737"> <source xml:space="preserve">Confirm the send action</source> <context-group purpose="location"><context context-type="linenumber">1094</context></context-group> </trans-unit> - <trans-unit id="_msg718"> + <trans-unit id="_msg738"> <source xml:space="preserve">S&end</source> <context-group purpose="location"><context context-type="linenumber">1097</context></context-group> </trans-unit> @@ -3317,231 +3408,231 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../sendcoinsdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog"> - <trans-unit id="_msg719"> + <trans-unit id="_msg739"> <source xml:space="preserve">Copy quantity</source> <context-group purpose="location"><context context-type="linenumber">95</context></context-group> </trans-unit> - <trans-unit id="_msg720"> + <trans-unit id="_msg740"> <source xml:space="preserve">Copy amount</source> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg721"> + <trans-unit id="_msg741"> <source xml:space="preserve">Copy fee</source> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg722"> + <trans-unit id="_msg742"> <source xml:space="preserve">Copy after fee</source> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg723"> + <trans-unit id="_msg743"> <source xml:space="preserve">Copy bytes</source> <context-group purpose="location"><context context-type="linenumber">99</context></context-group> </trans-unit> - <trans-unit id="_msg724"> + <trans-unit id="_msg744"> <source xml:space="preserve">Copy change</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> - <trans-unit id="_msg725"> + <trans-unit id="_msg745"> <source xml:space="preserve">%1 (%2 blocks)</source> <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> - <trans-unit id="_msg726"> + <trans-unit id="_msg746"> <source xml:space="preserve">Sign on device</source> <context-group purpose="location"><context context-type="linenumber">202</context></context-group> <note annotates="source" from="developer">"device" usually means a hardware wallet.</note> </trans-unit> - <trans-unit id="_msg727"> + <trans-unit id="_msg747"> <source xml:space="preserve">Connect your hardware wallet first.</source> <context-group purpose="location"><context context-type="linenumber">205</context></context-group> </trans-unit> - <trans-unit id="_msg728"> + <trans-unit id="_msg748"> <source xml:space="preserve">Set external signer script path in Options -> Wallet</source> <context-group purpose="location"><context context-type="linenumber">209</context></context-group> <note annotates="source" from="developer">"External signer" means using devices such as hardware wallets.</note> </trans-unit> - <trans-unit id="_msg729"> + <trans-unit id="_msg749"> <source xml:space="preserve">Cr&eate Unsigned</source> <context-group purpose="location"><context context-type="linenumber">212</context></context-group> </trans-unit> - <trans-unit id="_msg730"> + <trans-unit id="_msg750"> <source xml:space="preserve">Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source> <context-group purpose="location"><context context-type="linenumber">213</context></context-group> </trans-unit> - <trans-unit id="_msg731"> + <trans-unit id="_msg751"> <source xml:space="preserve"> from wallet '%1'</source> <context-group purpose="location"><context context-type="linenumber">305</context></context-group> </trans-unit> - <trans-unit id="_msg732"> + <trans-unit id="_msg752"> <source xml:space="preserve">%1 to '%2'</source> <context-group purpose="location"><context context-type="linenumber">316</context></context-group> </trans-unit> - <trans-unit id="_msg733"> + <trans-unit id="_msg753"> <source xml:space="preserve">%1 to %2</source> <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg734"> + <trans-unit id="_msg754"> <source xml:space="preserve">To review recipient list click "Show Details…"</source> <context-group purpose="location"><context context-type="linenumber">388</context></context-group> </trans-unit> - <trans-unit id="_msg735"> + <trans-unit id="_msg755"> <source xml:space="preserve">Sign failed</source> <context-group purpose="location"><context context-type="linenumber">450</context></context-group> </trans-unit> - <trans-unit id="_msg736"> + <trans-unit id="_msg756"> <source xml:space="preserve">External signer not found</source> <context-group purpose="location"><context context-type="linenumber">455</context></context-group> <note annotates="source" from="developer">"External signer" means using devices such as hardware wallets.</note> </trans-unit> - <trans-unit id="_msg737"> + <trans-unit id="_msg757"> <source xml:space="preserve">External signer failure</source> <context-group purpose="location"><context context-type="linenumber">461</context></context-group> <note annotates="source" from="developer">"External signer" means using devices such as hardware wallets.</note> </trans-unit> - <trans-unit id="_msg738"> + <trans-unit id="_msg758"> <source xml:space="preserve">Save Transaction Data</source> <context-group purpose="location"><context context-type="linenumber">425</context></context-group> </trans-unit> - <trans-unit id="_msg739"> + <trans-unit id="_msg759"> <source xml:space="preserve">Partially Signed Transaction (Binary)</source> <context-group purpose="location"><context context-type="linenumber">427</context></context-group> <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note> </trans-unit> - <trans-unit id="_msg740"> + <trans-unit id="_msg760"> <source xml:space="preserve">PSBT saved</source> <context-group purpose="location"><context context-type="linenumber">435</context></context-group> <note annotates="source" from="developer">Popup message when a PSBT has been saved to a file</note> </trans-unit> - <trans-unit id="_msg741"> + <trans-unit id="_msg761"> <source xml:space="preserve">External balance:</source> <context-group purpose="location"><context context-type="linenumber">708</context></context-group> </trans-unit> - <trans-unit id="_msg742"> + <trans-unit id="_msg762"> <source xml:space="preserve">or</source> <context-group purpose="location"><context context-type="linenumber">384</context></context-group> </trans-unit> - <trans-unit id="_msg743"> + <trans-unit id="_msg763"> <source xml:space="preserve">You can increase the fee later (signals Replace-By-Fee, BIP-125).</source> <context-group purpose="location"><context context-type="linenumber">366</context></context-group> </trans-unit> - <trans-unit id="_msg744"> + <trans-unit id="_msg764"> <source xml:space="preserve">Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> <note annotates="source" from="developer">Text to inform a user attempting to create a transaction of their current options. At this stage, a user can only create a PSBT. This string is displayed when private keys are disabled and an external signer is not available.</note> </trans-unit> - <trans-unit id="_msg745"> + <trans-unit id="_msg765"> <source xml:space="preserve">Do you want to create this transaction?</source> <context-group purpose="location"><context context-type="linenumber">329</context></context-group> <note annotates="source" from="developer">Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify that the displayed transaction details represent the transaction the user intends to create.</note> </trans-unit> - <trans-unit id="_msg746"> + <trans-unit id="_msg766"> <source xml:space="preserve">Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.</source> <context-group purpose="location"><context context-type="linenumber">340</context></context-group> <note annotates="source" from="developer">Text to inform a user attempting to create a transaction of their current options. At this stage, a user can send their transaction or create a PSBT. This string is displayed when both private keys and PSBT controls are enabled.</note> </trans-unit> - <trans-unit id="_msg747"> + <trans-unit id="_msg767"> <source xml:space="preserve">Please, review your transaction.</source> <context-group purpose="location"><context context-type="linenumber">343</context></context-group> <note annotates="source" from="developer">Text to prompt a user to review the details of the transaction they are attempting to send.</note> </trans-unit> - <trans-unit id="_msg748"> + <trans-unit id="_msg768"> <source xml:space="preserve">Transaction fee</source> <context-group purpose="location"><context context-type="linenumber">351</context></context-group> </trans-unit> - <trans-unit id="_msg749"> + <trans-unit id="_msg769"> <source xml:space="preserve">%1 kvB</source> <context-group purpose="location"><context context-type="linenumber">356</context></context-group> <context-group><context context-type="x-gettext-msgctxt">PSBT transaction creation</context></context-group> <note annotates="source" from="developer">When reviewing a newly created PSBT (via Send flow), the transaction fee is shown, with "virtual size" of the transaction displayed for context</note> </trans-unit> - <trans-unit id="_msg750"> + <trans-unit id="_msg770"> <source xml:space="preserve">Not signalling Replace-By-Fee, BIP-125.</source> <context-group purpose="location"><context context-type="linenumber">368</context></context-group> </trans-unit> - <trans-unit id="_msg751"> + <trans-unit id="_msg771"> <source xml:space="preserve">Total Amount</source> <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> - <trans-unit id="_msg752"> + <trans-unit id="_msg772"> <source xml:space="preserve">Unsigned Transaction</source> <context-group purpose="location"><context context-type="linenumber">405</context></context-group> <context-group><context context-type="x-gettext-msgctxt">PSBT copied</context></context-group> <note annotates="source" from="developer">Caption of "PSBT has been copied" messagebox</note> </trans-unit> - <trans-unit id="_msg753"> + <trans-unit id="_msg773"> <source xml:space="preserve">The PSBT has been copied to the clipboard. You can also save it.</source> <context-group purpose="location"><context context-type="linenumber">406</context></context-group> </trans-unit> - <trans-unit id="_msg754"> + <trans-unit id="_msg774"> <source xml:space="preserve">PSBT saved to disk</source> <context-group purpose="location"><context context-type="linenumber">435</context></context-group> </trans-unit> - <trans-unit id="_msg755"> + <trans-unit id="_msg775"> <source xml:space="preserve">Confirm send coins</source> <context-group purpose="location"><context context-type="linenumber">484</context></context-group> </trans-unit> - <trans-unit id="_msg756"> + <trans-unit id="_msg776"> <source xml:space="preserve">Watch-only balance:</source> <context-group purpose="location"><context context-type="linenumber">711</context></context-group> </trans-unit> - <trans-unit id="_msg757"> + <trans-unit id="_msg777"> <source xml:space="preserve">The recipient address is not valid. Please recheck.</source> <context-group purpose="location"><context context-type="linenumber">735</context></context-group> </trans-unit> - <trans-unit id="_msg758"> + <trans-unit id="_msg778"> <source xml:space="preserve">The amount to pay must be larger than 0.</source> <context-group purpose="location"><context context-type="linenumber">738</context></context-group> </trans-unit> - <trans-unit id="_msg759"> + <trans-unit id="_msg779"> <source xml:space="preserve">The amount exceeds your balance.</source> <context-group purpose="location"><context context-type="linenumber">741</context></context-group> </trans-unit> - <trans-unit id="_msg760"> + <trans-unit id="_msg780"> <source xml:space="preserve">The total exceeds your balance when the %1 transaction fee is included.</source> <context-group purpose="location"><context context-type="linenumber">744</context></context-group> </trans-unit> - <trans-unit id="_msg761"> + <trans-unit id="_msg781"> <source xml:space="preserve">Duplicate address found: addresses should only be used once each.</source> <context-group purpose="location"><context context-type="linenumber">747</context></context-group> </trans-unit> - <trans-unit id="_msg762"> + <trans-unit id="_msg782"> <source xml:space="preserve">Transaction creation failed!</source> <context-group purpose="location"><context context-type="linenumber">750</context></context-group> </trans-unit> - <trans-unit id="_msg763"> + <trans-unit id="_msg783"> <source xml:space="preserve">A fee higher than %1 is considered an absurdly high fee.</source> <context-group purpose="location"><context context-type="linenumber">754</context></context-group> </trans-unit> - <trans-unit id="_msg764"> + <trans-unit id="_msg784"> <source xml:space="preserve">%1/kvB</source> <context-group purpose="location"><context context-type="linenumber">833</context></context-group> <context-group purpose="location"><context context-type="linenumber">868</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">882</context></context-group> - <trans-unit id="_msg765[0]"> + <trans-unit id="_msg785[0]"> <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source> </trans-unit> - <trans-unit id="_msg765[1]"> + <trans-unit id="_msg785[1]"> <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source> </trans-unit> </group> - <trans-unit id="_msg766"> + <trans-unit id="_msg786"> <source xml:space="preserve">Warning: Invalid Bitcoin address</source> <context-group purpose="location"><context context-type="linenumber">977</context></context-group> </trans-unit> - <trans-unit id="_msg767"> + <trans-unit id="_msg787"> <source xml:space="preserve">Warning: Unknown change address</source> <context-group purpose="location"><context context-type="linenumber">982</context></context-group> </trans-unit> - <trans-unit id="_msg768"> + <trans-unit id="_msg788"> <source xml:space="preserve">Confirm custom change address</source> <context-group purpose="location"><context context-type="linenumber">985</context></context-group> </trans-unit> - <trans-unit id="_msg769"> + <trans-unit id="_msg789"> <source xml:space="preserve">The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> <context-group purpose="location"><context context-type="linenumber">985</context></context-group> </trans-unit> - <trans-unit id="_msg770"> + <trans-unit id="_msg790"> <source xml:space="preserve">(no label)</source> <context-group purpose="location"><context context-type="linenumber">1006</context></context-group> </trans-unit> @@ -3549,68 +3640,68 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../forms/sendcoinsentry.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsEntry"> - <trans-unit id="_msg771"> + <trans-unit id="_msg791"> <source xml:space="preserve">A&mount:</source> <context-group purpose="location"><context context-type="linenumber">151</context></context-group> </trans-unit> - <trans-unit id="_msg772"> + <trans-unit id="_msg792"> <source xml:space="preserve">Pay &To:</source> <context-group purpose="location"><context context-type="linenumber">35</context></context-group> </trans-unit> - <trans-unit id="_msg773"> + <trans-unit id="_msg793"> <source xml:space="preserve">&Label:</source> <context-group purpose="location"><context context-type="linenumber">128</context></context-group> </trans-unit> - <trans-unit id="_msg774"> + <trans-unit id="_msg794"> <source xml:space="preserve">Choose previously used address</source> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> - <trans-unit id="_msg775"> + <trans-unit id="_msg795"> <source xml:space="preserve">The Bitcoin address to send the payment to</source> <context-group purpose="location"><context context-type="linenumber">53</context></context-group> </trans-unit> - <trans-unit id="_msg776"> + <trans-unit id="_msg796"> <source xml:space="preserve">Alt+A</source> <context-group purpose="location"><context context-type="linenumber">76</context></context-group> </trans-unit> - <trans-unit id="_msg777"> + <trans-unit id="_msg797"> <source xml:space="preserve">Paste address from clipboard</source> <context-group purpose="location"><context context-type="linenumber">83</context></context-group> </trans-unit> - <trans-unit id="_msg778"> + <trans-unit id="_msg798"> <source xml:space="preserve">Alt+P</source> <context-group purpose="location"><context context-type="linenumber">99</context></context-group> </trans-unit> - <trans-unit id="_msg779"> + <trans-unit id="_msg799"> <source xml:space="preserve">Remove this entry</source> <context-group purpose="location"><context context-type="linenumber">106</context></context-group> </trans-unit> - <trans-unit id="_msg780"> + <trans-unit id="_msg800"> <source xml:space="preserve">The amount to send in the selected unit</source> <context-group purpose="location"><context context-type="linenumber">166</context></context-group> </trans-unit> - <trans-unit id="_msg781"> + <trans-unit id="_msg801"> <source xml:space="preserve">The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> <context-group purpose="location"><context context-type="linenumber">173</context></context-group> </trans-unit> - <trans-unit id="_msg782"> + <trans-unit id="_msg802"> <source xml:space="preserve">S&ubtract fee from amount</source> <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> - <trans-unit id="_msg783"> + <trans-unit id="_msg803"> <source xml:space="preserve">Use available balance</source> <context-group purpose="location"><context context-type="linenumber">183</context></context-group> </trans-unit> - <trans-unit id="_msg784"> + <trans-unit id="_msg804"> <source xml:space="preserve">Message:</source> <context-group purpose="location"><context context-type="linenumber">192</context></context-group> </trans-unit> - <trans-unit id="_msg785"> + <trans-unit id="_msg805"> <source xml:space="preserve">Enter a label for this address to add it to the list of used addresses</source> <context-group purpose="location"><context context-type="linenumber">141</context></context-group> <context-group purpose="location"><context context-type="linenumber">144</context></context-group> </trans-unit> - <trans-unit id="_msg786"> + <trans-unit id="_msg806"> <source xml:space="preserve">A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source> <context-group purpose="location"><context context-type="linenumber">202</context></context-group> </trans-unit> @@ -3618,11 +3709,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../sendcoinsdialog.h" datatype="c" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendConfirmationDialog"> - <trans-unit id="_msg787"> + <trans-unit id="_msg807"> <source xml:space="preserve">Send</source> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg788"> + <trans-unit id="_msg808"> <source xml:space="preserve">Create Unsigned</source> <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> @@ -3630,105 +3721,105 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../forms/signverifymessagedialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog"> - <trans-unit id="_msg789"> + <trans-unit id="_msg809"> <source xml:space="preserve">Signatures - Sign / Verify a Message</source> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg790"> + <trans-unit id="_msg810"> <source xml:space="preserve">&Sign Message</source> <context-group purpose="location"><context context-type="linenumber">27</context></context-group> </trans-unit> - <trans-unit id="_msg791"> + <trans-unit id="_msg811"> <source xml:space="preserve">You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> <context-group purpose="location"><context context-type="linenumber">33</context></context-group> </trans-unit> - <trans-unit id="_msg792"> + <trans-unit id="_msg812"> <source xml:space="preserve">The Bitcoin address to sign the message with</source> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg793"> + <trans-unit id="_msg813"> <source xml:space="preserve">Choose previously used address</source> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> <context-group purpose="location"><context context-type="linenumber">274</context></context-group> </trans-unit> - <trans-unit id="_msg794"> + <trans-unit id="_msg814"> <source xml:space="preserve">Alt+A</source> <context-group purpose="location"><context context-type="linenumber">68</context></context-group> <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg795"> + <trans-unit id="_msg815"> <source xml:space="preserve">Paste address from clipboard</source> <context-group purpose="location"><context context-type="linenumber">78</context></context-group> </trans-unit> - <trans-unit id="_msg796"> + <trans-unit id="_msg816"> <source xml:space="preserve">Alt+P</source> <context-group purpose="location"><context context-type="linenumber">88</context></context-group> </trans-unit> - <trans-unit id="_msg797"> + <trans-unit id="_msg817"> <source xml:space="preserve">Enter the message you want to sign here</source> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> <context-group purpose="location"><context context-type="linenumber">103</context></context-group> </trans-unit> - <trans-unit id="_msg798"> + <trans-unit id="_msg818"> <source xml:space="preserve">Signature</source> <context-group purpose="location"><context context-type="linenumber">110</context></context-group> </trans-unit> - <trans-unit id="_msg799"> + <trans-unit id="_msg819"> <source xml:space="preserve">Copy the current signature to the system clipboard</source> <context-group purpose="location"><context context-type="linenumber">140</context></context-group> </trans-unit> - <trans-unit id="_msg800"> + <trans-unit id="_msg820"> <source xml:space="preserve">Sign the message to prove you own this Bitcoin address</source> <context-group purpose="location"><context context-type="linenumber">161</context></context-group> </trans-unit> - <trans-unit id="_msg801"> + <trans-unit id="_msg821"> <source xml:space="preserve">Sign &Message</source> <context-group purpose="location"><context context-type="linenumber">164</context></context-group> </trans-unit> - <trans-unit id="_msg802"> + <trans-unit id="_msg822"> <source xml:space="preserve">Reset all sign message fields</source> <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> - <trans-unit id="_msg803"> + <trans-unit id="_msg823"> <source xml:space="preserve">Clear &All</source> <context-group purpose="location"><context context-type="linenumber">181</context></context-group> <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg804"> + <trans-unit id="_msg824"> <source xml:space="preserve">&Verify Message</source> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg805"> + <trans-unit id="_msg825"> <source xml:space="preserve">Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> <context-group purpose="location"><context context-type="linenumber">246</context></context-group> </trans-unit> - <trans-unit id="_msg806"> + <trans-unit id="_msg826"> <source xml:space="preserve">The Bitcoin address the message was signed with</source> <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg807"> + <trans-unit id="_msg827"> <source xml:space="preserve">The signed message to verify</source> <context-group purpose="location"><context context-type="linenumber">296</context></context-group> <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> - <trans-unit id="_msg808"> + <trans-unit id="_msg828"> <source xml:space="preserve">The signature given when the message was signed</source> <context-group purpose="location"><context context-type="linenumber">306</context></context-group> <context-group purpose="location"><context context-type="linenumber">309</context></context-group> </trans-unit> - <trans-unit id="_msg809"> + <trans-unit id="_msg829"> <source xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</source> <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> - <trans-unit id="_msg810"> + <trans-unit id="_msg830"> <source xml:space="preserve">Verify &Message</source> <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg811"> + <trans-unit id="_msg831"> <source xml:space="preserve">Reset all verify message fields</source> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg812"> + <trans-unit id="_msg832"> <source xml:space="preserve">Click "Sign Message" to generate signature</source> <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> @@ -3736,61 +3827,61 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../signverifymessagedialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog"> - <trans-unit id="_msg813"> + <trans-unit id="_msg833"> <source xml:space="preserve">The entered address is invalid.</source> <context-group purpose="location"><context context-type="linenumber">119</context></context-group> <context-group purpose="location"><context context-type="linenumber">218</context></context-group> </trans-unit> - <trans-unit id="_msg814"> + <trans-unit id="_msg834"> <source xml:space="preserve">Please check the address and try again.</source> <context-group purpose="location"><context context-type="linenumber">119</context></context-group> <context-group purpose="location"><context context-type="linenumber">126</context></context-group> <context-group purpose="location"><context context-type="linenumber">219</context></context-group> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg815"> + <trans-unit id="_msg835"> <source xml:space="preserve">The entered address does not refer to a key.</source> <context-group purpose="location"><context context-type="linenumber">126</context></context-group> <context-group purpose="location"><context context-type="linenumber">225</context></context-group> </trans-unit> - <trans-unit id="_msg816"> + <trans-unit id="_msg836"> <source xml:space="preserve">Wallet unlock was cancelled.</source> <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> - <trans-unit id="_msg817"> + <trans-unit id="_msg837"> <source xml:space="preserve">No error</source> <context-group purpose="location"><context context-type="linenumber">145</context></context-group> </trans-unit> - <trans-unit id="_msg818"> + <trans-unit id="_msg838"> <source xml:space="preserve">Private key for the entered address is not available.</source> <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> - <trans-unit id="_msg819"> + <trans-unit id="_msg839"> <source xml:space="preserve">Message signing failed.</source> <context-group purpose="location"><context context-type="linenumber">151</context></context-group> </trans-unit> - <trans-unit id="_msg820"> + <trans-unit id="_msg840"> <source xml:space="preserve">Message signed.</source> <context-group purpose="location"><context context-type="linenumber">163</context></context-group> </trans-unit> - <trans-unit id="_msg821"> + <trans-unit id="_msg841"> <source xml:space="preserve">The signature could not be decoded.</source> <context-group purpose="location"><context context-type="linenumber">232</context></context-group> </trans-unit> - <trans-unit id="_msg822"> + <trans-unit id="_msg842"> <source xml:space="preserve">Please check the signature and try again.</source> <context-group purpose="location"><context context-type="linenumber">233</context></context-group> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg823"> + <trans-unit id="_msg843"> <source xml:space="preserve">The signature did not match the message digest.</source> <context-group purpose="location"><context context-type="linenumber">239</context></context-group> </trans-unit> - <trans-unit id="_msg824"> + <trans-unit id="_msg844"> <source xml:space="preserve">Message verification failed.</source> <context-group purpose="location"><context context-type="linenumber">245</context></context-group> </trans-unit> - <trans-unit id="_msg825"> + <trans-unit id="_msg845"> <source xml:space="preserve">Message verified.</source> <context-group purpose="location"><context context-type="linenumber">213</context></context-group> </trans-unit> @@ -3798,11 +3889,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../splashscreen.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SplashScreen"> - <trans-unit id="_msg826"> + <trans-unit id="_msg846"> <source xml:space="preserve">(press q to shutdown and continue later)</source> <context-group purpose="location"><context context-type="linenumber">177</context></context-group> </trans-unit> - <trans-unit id="_msg827"> + <trans-unit id="_msg847"> <source xml:space="preserve">press q to shutdown</source> <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> @@ -3810,7 +3901,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../trafficgraphwidget.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TrafficGraphWidget"> - <trans-unit id="_msg828"> + <trans-unit id="_msg848"> <source xml:space="preserve">kB/s</source> <context-group purpose="location"><context context-type="linenumber">74</context></context-group> </trans-unit> @@ -3818,84 +3909,84 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../transactiondesc.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDesc"> - <trans-unit id="_msg829"> + <trans-unit id="_msg849"> <source xml:space="preserve">conflicted with a transaction with %1 confirmations</source> <context-group purpose="location"><context context-type="linenumber">44</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents an unconfirmed transaction that conflicts with a confirmed transaction.</note> </trans-unit> - <trans-unit id="_msg830"> + <trans-unit id="_msg850"> <source xml:space="preserve">0/unconfirmed, in memory pool</source> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents an unconfirmed transaction that is in the memory pool.</note> </trans-unit> - <trans-unit id="_msg831"> + <trans-unit id="_msg851"> <source xml:space="preserve">0/unconfirmed, not in memory pool</source> <context-group purpose="location"><context context-type="linenumber">56</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents an unconfirmed transaction that is not in the memory pool.</note> </trans-unit> - <trans-unit id="_msg832"> + <trans-unit id="_msg852"> <source xml:space="preserve">abandoned</source> <context-group purpose="location"><context context-type="linenumber">62</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents an abandoned transaction.</note> </trans-unit> - <trans-unit id="_msg833"> + <trans-unit id="_msg853"> <source xml:space="preserve">%1/unconfirmed</source> <context-group purpose="location"><context context-type="linenumber">70</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents a transaction confirmed in at least one block, but less than 6 blocks.</note> </trans-unit> - <trans-unit id="_msg834"> + <trans-unit id="_msg854"> <source xml:space="preserve">%1 confirmations</source> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> <note annotates="source" from="developer">Text explaining the current status of a transaction, shown in the status field of the details window for this transaction. This status represents a transaction confirmed in 6 or more blocks.</note> </trans-unit> - <trans-unit id="_msg835"> + <trans-unit id="_msg855"> <source xml:space="preserve">Status</source> <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> - <trans-unit id="_msg836"> + <trans-unit id="_msg856"> <source xml:space="preserve">Date</source> <context-group purpose="location"><context context-type="linenumber">128</context></context-group> </trans-unit> - <trans-unit id="_msg837"> + <trans-unit id="_msg857"> <source xml:space="preserve">Source</source> <context-group purpose="location"><context context-type="linenumber">135</context></context-group> </trans-unit> - <trans-unit id="_msg838"> + <trans-unit id="_msg858"> <source xml:space="preserve">Generated</source> <context-group purpose="location"><context context-type="linenumber">135</context></context-group> </trans-unit> - <trans-unit id="_msg839"> + <trans-unit id="_msg859"> <source xml:space="preserve">From</source> <context-group purpose="location"><context context-type="linenumber">140</context></context-group> <context-group purpose="location"><context context-type="linenumber">154</context></context-group> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg840"> + <trans-unit id="_msg860"> <source xml:space="preserve">unknown</source> <context-group purpose="location"><context context-type="linenumber">154</context></context-group> </trans-unit> - <trans-unit id="_msg841"> + <trans-unit id="_msg861"> <source xml:space="preserve">To</source> <context-group purpose="location"><context context-type="linenumber">155</context></context-group> <context-group purpose="location"><context context-type="linenumber">175</context></context-group> <context-group purpose="location"><context context-type="linenumber">245</context></context-group> </trans-unit> - <trans-unit id="_msg842"> + <trans-unit id="_msg862"> <source xml:space="preserve">own address</source> <context-group purpose="location"><context context-type="linenumber">157</context></context-group> <context-group purpose="location"><context context-type="linenumber">252</context></context-group> </trans-unit> - <trans-unit id="_msg843"> + <trans-unit id="_msg863"> <source xml:space="preserve">watch-only</source> <context-group purpose="location"><context context-type="linenumber">157</context></context-group> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> <context-group purpose="location"><context context-type="linenumber">254</context></context-group> </trans-unit> - <trans-unit id="_msg844"> + <trans-unit id="_msg864"> <source xml:space="preserve">label</source> <context-group purpose="location"><context context-type="linenumber">159</context></context-group> </trans-unit> - <trans-unit id="_msg845"> + <trans-unit id="_msg865"> <source xml:space="preserve">Credit</source> <context-group purpose="location"><context context-type="linenumber">195</context></context-group> <context-group purpose="location"><context context-type="linenumber">207</context></context-group> @@ -3905,98 +3996,98 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </trans-unit> <group restype="x-gettext-plurals"> <context-group purpose="location"><context context-type="linenumber">197</context></context-group> - <trans-unit id="_msg846[0]"> + <trans-unit id="_msg866[0]"> <source xml:space="preserve">matures in %n more block(s)</source> </trans-unit> - <trans-unit id="_msg846[1]"> + <trans-unit id="_msg866[1]"> <source xml:space="preserve">matures in %n more block(s)</source> </trans-unit> </group> - <trans-unit id="_msg847"> + <trans-unit id="_msg867"> <source xml:space="preserve">not accepted</source> <context-group purpose="location"><context context-type="linenumber">199</context></context-group> </trans-unit> - <trans-unit id="_msg848"> + <trans-unit id="_msg868"> <source xml:space="preserve">Debit</source> <context-group purpose="location"><context context-type="linenumber">259</context></context-group> <context-group purpose="location"><context context-type="linenumber">285</context></context-group> <context-group purpose="location"><context context-type="linenumber">348</context></context-group> </trans-unit> - <trans-unit id="_msg849"> + <trans-unit id="_msg869"> <source xml:space="preserve">Total debit</source> <context-group purpose="location"><context context-type="linenumber">269</context></context-group> </trans-unit> - <trans-unit id="_msg850"> + <trans-unit id="_msg870"> <source xml:space="preserve">Total credit</source> <context-group purpose="location"><context context-type="linenumber">270</context></context-group> </trans-unit> - <trans-unit id="_msg851"> + <trans-unit id="_msg871"> <source xml:space="preserve">Transaction fee</source> <context-group purpose="location"><context context-type="linenumber">275</context></context-group> </trans-unit> - <trans-unit id="_msg852"> + <trans-unit id="_msg872"> <source xml:space="preserve">Net amount</source> <context-group purpose="location"><context context-type="linenumber">297</context></context-group> </trans-unit> - <trans-unit id="_msg853"> + <trans-unit id="_msg873"> <source xml:space="preserve">Message</source> <context-group purpose="location"><context context-type="linenumber">303</context></context-group> <context-group purpose="location"><context context-type="linenumber">315</context></context-group> </trans-unit> - <trans-unit id="_msg854"> + <trans-unit id="_msg874"> <source xml:space="preserve">Comment</source> <context-group purpose="location"><context context-type="linenumber">305</context></context-group> </trans-unit> - <trans-unit id="_msg855"> + <trans-unit id="_msg875"> <source xml:space="preserve">Transaction ID</source> <context-group purpose="location"><context context-type="linenumber">307</context></context-group> </trans-unit> - <trans-unit id="_msg856"> + <trans-unit id="_msg876"> <source xml:space="preserve">Transaction total size</source> <context-group purpose="location"><context context-type="linenumber">308</context></context-group> </trans-unit> - <trans-unit id="_msg857"> + <trans-unit id="_msg877"> <source xml:space="preserve">Transaction virtual size</source> <context-group purpose="location"><context context-type="linenumber">309</context></context-group> </trans-unit> - <trans-unit id="_msg858"> + <trans-unit id="_msg878"> <source xml:space="preserve">Output index</source> <context-group purpose="location"><context context-type="linenumber">310</context></context-group> </trans-unit> - <trans-unit id="_msg859"> + <trans-unit id="_msg879"> <source xml:space="preserve"> (Certificate was not verified)</source> <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg860"> + <trans-unit id="_msg880"> <source xml:space="preserve">Merchant</source> <context-group purpose="location"><context context-type="linenumber">329</context></context-group> </trans-unit> - <trans-unit id="_msg861"> + <trans-unit id="_msg881"> <source xml:space="preserve">Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source> <context-group purpose="location"><context context-type="linenumber">337</context></context-group> </trans-unit> - <trans-unit id="_msg862"> + <trans-unit id="_msg882"> <source xml:space="preserve">Debug information</source> <context-group purpose="location"><context context-type="linenumber">345</context></context-group> </trans-unit> - <trans-unit id="_msg863"> + <trans-unit id="_msg883"> <source xml:space="preserve">Transaction</source> <context-group purpose="location"><context context-type="linenumber">353</context></context-group> </trans-unit> - <trans-unit id="_msg864"> + <trans-unit id="_msg884"> <source xml:space="preserve">Inputs</source> <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> - <trans-unit id="_msg865"> + <trans-unit id="_msg885"> <source xml:space="preserve">Amount</source> <context-group purpose="location"><context context-type="linenumber">377</context></context-group> </trans-unit> - <trans-unit id="_msg866"> + <trans-unit id="_msg886"> <source xml:space="preserve">true</source> <context-group purpose="location"><context context-type="linenumber">378</context></context-group> <context-group purpose="location"><context context-type="linenumber">379</context></context-group> </trans-unit> - <trans-unit id="_msg867"> + <trans-unit id="_msg887"> <source xml:space="preserve">false</source> <context-group purpose="location"><context context-type="linenumber">378</context></context-group> <context-group purpose="location"><context context-type="linenumber">379</context></context-group> @@ -4005,7 +4096,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../forms/transactiondescdialog.ui" datatype="x-trolltech-designer-ui" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog"> - <trans-unit id="_msg868"> + <trans-unit id="_msg888"> <source xml:space="preserve">This pane shows a detailed description of the transaction</source> <context-group purpose="location"><context context-type="linenumber">20</context></context-group> </trans-unit> @@ -4013,7 +4104,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../transactiondescdialog.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog"> - <trans-unit id="_msg869"> + <trans-unit id="_msg889"> <source xml:space="preserve">Details for %1</source> <context-group purpose="location"><context context-type="linenumber">18</context></context-group> </trans-unit> @@ -4021,306 +4112,298 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../transactiontablemodel.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionTableModel"> - <trans-unit id="_msg870"> + <trans-unit id="_msg890"> <source xml:space="preserve">Date</source> - <context-group purpose="location"><context context-type="linenumber">257</context></context-group> + <context-group purpose="location"><context context-type="linenumber">258</context></context-group> </trans-unit> - <trans-unit id="_msg871"> + <trans-unit id="_msg891"> <source xml:space="preserve">Type</source> - <context-group purpose="location"><context context-type="linenumber">257</context></context-group> + <context-group purpose="location"><context context-type="linenumber">258</context></context-group> </trans-unit> - <trans-unit id="_msg872"> + <trans-unit id="_msg892"> <source xml:space="preserve">Label</source> - <context-group purpose="location"><context context-type="linenumber">257</context></context-group> + <context-group purpose="location"><context context-type="linenumber">258</context></context-group> </trans-unit> - <trans-unit id="_msg873"> + <trans-unit id="_msg893"> <source xml:space="preserve">Unconfirmed</source> - <context-group purpose="location"><context context-type="linenumber">317</context></context-group> + <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> - <trans-unit id="_msg874"> + <trans-unit id="_msg894"> <source xml:space="preserve">Abandoned</source> - <context-group purpose="location"><context context-type="linenumber">320</context></context-group> + <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg875"> + <trans-unit id="_msg895"> <source xml:space="preserve">Confirming (%1 of %2 recommended confirmations)</source> - <context-group purpose="location"><context context-type="linenumber">323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> - <trans-unit id="_msg876"> + <trans-unit id="_msg896"> <source xml:space="preserve">Confirmed (%1 confirmations)</source> - <context-group purpose="location"><context context-type="linenumber">326</context></context-group> + <context-group purpose="location"><context context-type="linenumber">327</context></context-group> </trans-unit> - <trans-unit id="_msg877"> + <trans-unit id="_msg897"> <source xml:space="preserve">Conflicted</source> - <context-group purpose="location"><context context-type="linenumber">329</context></context-group> + <context-group purpose="location"><context context-type="linenumber">330</context></context-group> </trans-unit> - <trans-unit id="_msg878"> + <trans-unit id="_msg898"> <source xml:space="preserve">Immature (%1 confirmations, will be available after %2)</source> - <context-group purpose="location"><context context-type="linenumber">332</context></context-group> + <context-group purpose="location"><context context-type="linenumber">333</context></context-group> </trans-unit> - <trans-unit id="_msg879"> + <trans-unit id="_msg899"> <source xml:space="preserve">Generated but not accepted</source> - <context-group purpose="location"><context context-type="linenumber">335</context></context-group> + <context-group purpose="location"><context context-type="linenumber">336</context></context-group> </trans-unit> - <trans-unit id="_msg880"> + <trans-unit id="_msg900"> <source xml:space="preserve">Received with</source> - <context-group purpose="location"><context context-type="linenumber">374</context></context-group> + <context-group purpose="location"><context context-type="linenumber">375</context></context-group> </trans-unit> - <trans-unit id="_msg881"> + <trans-unit id="_msg901"> <source xml:space="preserve">Received from</source> - <context-group purpose="location"><context context-type="linenumber">376</context></context-group> + <context-group purpose="location"><context context-type="linenumber">377</context></context-group> </trans-unit> - <trans-unit id="_msg882"> + <trans-unit id="_msg902"> <source xml:space="preserve">Sent to</source> - <context-group purpose="location"><context context-type="linenumber">379</context></context-group> - </trans-unit> - <trans-unit id="_msg883"> - <source xml:space="preserve">Payment to yourself</source> - <context-group purpose="location"><context context-type="linenumber">381</context></context-group> + <context-group purpose="location"><context context-type="linenumber">380</context></context-group> </trans-unit> - <trans-unit id="_msg884"> + <trans-unit id="_msg903"> <source xml:space="preserve">Mined</source> - <context-group purpose="location"><context context-type="linenumber">383</context></context-group> + <context-group purpose="location"><context context-type="linenumber">382</context></context-group> </trans-unit> - <trans-unit id="_msg885"> + <trans-unit id="_msg904"> <source xml:space="preserve">watch-only</source> - <context-group purpose="location"><context context-type="linenumber">411</context></context-group> + <context-group purpose="location"><context context-type="linenumber">410</context></context-group> </trans-unit> - <trans-unit id="_msg886"> + <trans-unit id="_msg905"> <source xml:space="preserve">(n/a)</source> - <context-group purpose="location"><context context-type="linenumber">427</context></context-group> + <context-group purpose="location"><context context-type="linenumber">424</context></context-group> </trans-unit> - <trans-unit id="_msg887"> + <trans-unit id="_msg906"> <source xml:space="preserve">(no label)</source> - <context-group purpose="location"><context context-type="linenumber">634</context></context-group> + <context-group purpose="location"><context context-type="linenumber">629</context></context-group> </trans-unit> - <trans-unit id="_msg888"> + <trans-unit id="_msg907"> <source xml:space="preserve">Transaction status. Hover over this field to show number of confirmations.</source> - <context-group purpose="location"><context context-type="linenumber">673</context></context-group> + <context-group purpose="location"><context context-type="linenumber">668</context></context-group> </trans-unit> - <trans-unit id="_msg889"> + <trans-unit id="_msg908"> <source xml:space="preserve">Date and time that the transaction was received.</source> - <context-group purpose="location"><context context-type="linenumber">675</context></context-group> + <context-group purpose="location"><context context-type="linenumber">670</context></context-group> </trans-unit> - <trans-unit id="_msg890"> + <trans-unit id="_msg909"> <source xml:space="preserve">Type of transaction.</source> - <context-group purpose="location"><context context-type="linenumber">677</context></context-group> + <context-group purpose="location"><context context-type="linenumber">672</context></context-group> </trans-unit> - <trans-unit id="_msg891"> + <trans-unit id="_msg910"> <source xml:space="preserve">Whether or not a watch-only address is involved in this transaction.</source> - <context-group purpose="location"><context context-type="linenumber">679</context></context-group> + <context-group purpose="location"><context context-type="linenumber">674</context></context-group> </trans-unit> - <trans-unit id="_msg892"> + <trans-unit id="_msg911"> <source xml:space="preserve">User-defined intent/purpose of the transaction.</source> - <context-group purpose="location"><context context-type="linenumber">681</context></context-group> + <context-group purpose="location"><context context-type="linenumber">676</context></context-group> </trans-unit> - <trans-unit id="_msg893"> + <trans-unit id="_msg912"> <source xml:space="preserve">Amount removed from or added to balance.</source> - <context-group purpose="location"><context context-type="linenumber">683</context></context-group> + <context-group purpose="location"><context context-type="linenumber">678</context></context-group> </trans-unit> </group> </body></file> <file original="../transactionview.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionView"> - <trans-unit id="_msg894"> + <trans-unit id="_msg913"> <source xml:space="preserve">All</source> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> <context-group purpose="location"><context context-type="linenumber">89</context></context-group> </trans-unit> - <trans-unit id="_msg895"> + <trans-unit id="_msg914"> <source xml:space="preserve">Today</source> <context-group purpose="location"><context context-type="linenumber">74</context></context-group> </trans-unit> - <trans-unit id="_msg896"> + <trans-unit id="_msg915"> <source xml:space="preserve">This week</source> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> </trans-unit> - <trans-unit id="_msg897"> + <trans-unit id="_msg916"> <source xml:space="preserve">This month</source> <context-group purpose="location"><context context-type="linenumber">76</context></context-group> </trans-unit> - <trans-unit id="_msg898"> + <trans-unit id="_msg917"> <source xml:space="preserve">Last month</source> <context-group purpose="location"><context context-type="linenumber">77</context></context-group> </trans-unit> - <trans-unit id="_msg899"> + <trans-unit id="_msg918"> <source xml:space="preserve">This year</source> <context-group purpose="location"><context context-type="linenumber">78</context></context-group> </trans-unit> - <trans-unit id="_msg900"> + <trans-unit id="_msg919"> <source xml:space="preserve">Received with</source> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg901"> + <trans-unit id="_msg920"> <source xml:space="preserve">Sent to</source> <context-group purpose="location"><context context-type="linenumber">92</context></context-group> </trans-unit> - <trans-unit id="_msg902"> - <source xml:space="preserve">To yourself</source> - <context-group purpose="location"><context context-type="linenumber">94</context></context-group> - </trans-unit> - <trans-unit id="_msg903"> + <trans-unit id="_msg921"> <source xml:space="preserve">Mined</source> - <context-group purpose="location"><context context-type="linenumber">95</context></context-group> + <context-group purpose="location"><context context-type="linenumber">94</context></context-group> </trans-unit> - <trans-unit id="_msg904"> + <trans-unit id="_msg922"> <source xml:space="preserve">Other</source> - <context-group purpose="location"><context context-type="linenumber">96</context></context-group> + <context-group purpose="location"><context context-type="linenumber">95</context></context-group> </trans-unit> - <trans-unit id="_msg905"> + <trans-unit id="_msg923"> <source xml:space="preserve">Enter address, transaction id, or label to search</source> - <context-group purpose="location"><context context-type="linenumber">101</context></context-group> + <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> - <trans-unit id="_msg906"> + <trans-unit id="_msg924"> <source xml:space="preserve">Min amount</source> - <context-group purpose="location"><context context-type="linenumber">105</context></context-group> + <context-group purpose="location"><context context-type="linenumber">104</context></context-group> </trans-unit> - <trans-unit id="_msg907"> + <trans-unit id="_msg925"> <source xml:space="preserve">Range…</source> <context-group purpose="location"><context context-type="linenumber">79</context></context-group> </trans-unit> - <trans-unit id="_msg908"> + <trans-unit id="_msg926"> <source xml:space="preserve">&Copy address</source> - <context-group purpose="location"><context context-type="linenumber">169</context></context-group> + <context-group purpose="location"><context context-type="linenumber">168</context></context-group> </trans-unit> - <trans-unit id="_msg909"> + <trans-unit id="_msg927"> <source xml:space="preserve">Copy &label</source> - <context-group purpose="location"><context context-type="linenumber">170</context></context-group> + <context-group purpose="location"><context context-type="linenumber">169</context></context-group> </trans-unit> - <trans-unit id="_msg910"> + <trans-unit id="_msg928"> <source xml:space="preserve">Copy &amount</source> - <context-group purpose="location"><context context-type="linenumber">171</context></context-group> + <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> - <trans-unit id="_msg911"> + <trans-unit id="_msg929"> <source xml:space="preserve">Copy transaction &ID</source> - <context-group purpose="location"><context context-type="linenumber">172</context></context-group> + <context-group purpose="location"><context context-type="linenumber">171</context></context-group> </trans-unit> - <trans-unit id="_msg912"> + <trans-unit id="_msg930"> <source xml:space="preserve">Copy &raw transaction</source> - <context-group purpose="location"><context context-type="linenumber">173</context></context-group> + <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> - <trans-unit id="_msg913"> + <trans-unit id="_msg931"> <source xml:space="preserve">Copy full transaction &details</source> - <context-group purpose="location"><context context-type="linenumber">174</context></context-group> + <context-group purpose="location"><context context-type="linenumber">173</context></context-group> </trans-unit> - <trans-unit id="_msg914"> + <trans-unit id="_msg932"> <source xml:space="preserve">&Show transaction details</source> - <context-group purpose="location"><context context-type="linenumber">175</context></context-group> + <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg915"> + <trans-unit id="_msg933"> <source xml:space="preserve">Increase transaction &fee</source> - <context-group purpose="location"><context context-type="linenumber">177</context></context-group> + <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> - <trans-unit id="_msg916"> + <trans-unit id="_msg934"> <source xml:space="preserve">A&bandon transaction</source> - <context-group purpose="location"><context context-type="linenumber">180</context></context-group> + <context-group purpose="location"><context context-type="linenumber">179</context></context-group> </trans-unit> - <trans-unit id="_msg917"> + <trans-unit id="_msg935"> <source xml:space="preserve">&Edit address label</source> - <context-group purpose="location"><context context-type="linenumber">181</context></context-group> + <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg918"> + <trans-unit id="_msg936"> <source xml:space="preserve">Show in %1</source> - <context-group purpose="location"><context context-type="linenumber">240</context></context-group> + <context-group purpose="location"><context context-type="linenumber">239</context></context-group> <note annotates="source" from="developer">Transactions table context menu action to show the selected transaction in a third-party block explorer. %1 is a stand-in argument for the URL of the explorer.</note> </trans-unit> - <trans-unit id="_msg919"> + <trans-unit id="_msg937"> <source xml:space="preserve">Export Transaction History</source> - <context-group purpose="location"><context context-type="linenumber">359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">358</context></context-group> </trans-unit> - <trans-unit id="_msg920"> + <trans-unit id="_msg938"> <source xml:space="preserve">Comma separated file</source> - <context-group purpose="location"><context context-type="linenumber">362</context></context-group> + <context-group purpose="location"><context context-type="linenumber">361</context></context-group> <note annotates="source" from="developer">Expanded name of the CSV file format. See: https://en.wikipedia.org/wiki/Comma-separated_values.</note> </trans-unit> - <trans-unit id="_msg921"> + <trans-unit id="_msg939"> <source xml:space="preserve">Confirmed</source> - <context-group purpose="location"><context context-type="linenumber">371</context></context-group> + <context-group purpose="location"><context context-type="linenumber">370</context></context-group> </trans-unit> - <trans-unit id="_msg922"> + <trans-unit id="_msg940"> <source xml:space="preserve">Watch-only</source> - <context-group purpose="location"><context context-type="linenumber">373</context></context-group> + <context-group purpose="location"><context context-type="linenumber">372</context></context-group> </trans-unit> - <trans-unit id="_msg923"> + <trans-unit id="_msg941"> <source xml:space="preserve">Date</source> - <context-group purpose="location"><context context-type="linenumber">374</context></context-group> + <context-group purpose="location"><context context-type="linenumber">373</context></context-group> </trans-unit> - <trans-unit id="_msg924"> + <trans-unit id="_msg942"> <source xml:space="preserve">Type</source> - <context-group purpose="location"><context context-type="linenumber">375</context></context-group> + <context-group purpose="location"><context context-type="linenumber">374</context></context-group> </trans-unit> - <trans-unit id="_msg925"> + <trans-unit id="_msg943"> <source xml:space="preserve">Label</source> - <context-group purpose="location"><context context-type="linenumber">376</context></context-group> + <context-group purpose="location"><context context-type="linenumber">375</context></context-group> </trans-unit> - <trans-unit id="_msg926"> + <trans-unit id="_msg944"> <source xml:space="preserve">Address</source> - <context-group purpose="location"><context context-type="linenumber">377</context></context-group> + <context-group purpose="location"><context context-type="linenumber">376</context></context-group> </trans-unit> - <trans-unit id="_msg927"> + <trans-unit id="_msg945"> <source xml:space="preserve">ID</source> - <context-group purpose="location"><context context-type="linenumber">379</context></context-group> + <context-group purpose="location"><context context-type="linenumber">378</context></context-group> </trans-unit> - <trans-unit id="_msg928"> + <trans-unit id="_msg946"> <source xml:space="preserve">Exporting Failed</source> - <context-group purpose="location"><context context-type="linenumber">382</context></context-group> + <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> - <trans-unit id="_msg929"> + <trans-unit id="_msg947"> <source xml:space="preserve">There was an error trying to save the transaction history to %1.</source> - <context-group purpose="location"><context context-type="linenumber">382</context></context-group> + <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> - <trans-unit id="_msg930"> + <trans-unit id="_msg948"> <source xml:space="preserve">Exporting Successful</source> - <context-group purpose="location"><context context-type="linenumber">386</context></context-group> + <context-group purpose="location"><context context-type="linenumber">385</context></context-group> </trans-unit> - <trans-unit id="_msg931"> + <trans-unit id="_msg949"> <source xml:space="preserve">The transaction history was successfully saved to %1.</source> - <context-group purpose="location"><context context-type="linenumber">386</context></context-group> + <context-group purpose="location"><context context-type="linenumber">385</context></context-group> </trans-unit> - <trans-unit id="_msg932"> + <trans-unit id="_msg950"> <source xml:space="preserve">Range:</source> - <context-group purpose="location"><context context-type="linenumber">556</context></context-group> + <context-group purpose="location"><context context-type="linenumber">555</context></context-group> </trans-unit> - <trans-unit id="_msg933"> + <trans-unit id="_msg951"> <source xml:space="preserve">to</source> - <context-group purpose="location"><context context-type="linenumber">564</context></context-group> + <context-group purpose="location"><context context-type="linenumber">563</context></context-group> </trans-unit> </group> </body></file> <file original="../walletframe.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletFrame"> - <trans-unit id="_msg934"> + <trans-unit id="_msg952"> <source xml:space="preserve">No wallet has been loaded. Go to File > Open Wallet to load a wallet. - OR -</source> <context-group purpose="location"><context context-type="linenumber">45</context></context-group> </trans-unit> - <trans-unit id="_msg935"> + <trans-unit id="_msg953"> <source xml:space="preserve">Create a new wallet</source> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg936"> + <trans-unit id="_msg954"> <source xml:space="preserve">Error</source> <context-group purpose="location"><context context-type="linenumber">201</context></context-group> <context-group purpose="location"><context context-type="linenumber">211</context></context-group> <context-group purpose="location"><context context-type="linenumber">229</context></context-group> </trans-unit> - <trans-unit id="_msg937"> + <trans-unit id="_msg955"> <source xml:space="preserve">Unable to decode PSBT from clipboard (invalid base64)</source> <context-group purpose="location"><context context-type="linenumber">201</context></context-group> </trans-unit> - <trans-unit id="_msg938"> + <trans-unit id="_msg956"> <source xml:space="preserve">Load Transaction Data</source> <context-group purpose="location"><context context-type="linenumber">207</context></context-group> </trans-unit> - <trans-unit id="_msg939"> + <trans-unit id="_msg957"> <source xml:space="preserve">Partially Signed Transaction (*.psbt)</source> <context-group purpose="location"><context context-type="linenumber">208</context></context-group> </trans-unit> - <trans-unit id="_msg940"> + <trans-unit id="_msg958"> <source xml:space="preserve">PSBT file must be smaller than 100 MiB</source> <context-group purpose="location"><context context-type="linenumber">211</context></context-group> </trans-unit> - <trans-unit id="_msg941"> + <trans-unit id="_msg959"> <source xml:space="preserve">Unable to decode PSBT</source> <context-group purpose="location"><context context-type="linenumber">229</context></context-group> </trans-unit> @@ -4328,73 +4411,73 @@ Go to File > Open Wallet to load a wallet. </body></file> <file original="../walletmodel.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletModel"> - <trans-unit id="_msg942"> + <trans-unit id="_msg960"> <source xml:space="preserve">Send Coins</source> <context-group purpose="location"><context context-type="linenumber">228</context></context-group> <context-group purpose="location"><context context-type="linenumber">241</context></context-group> </trans-unit> - <trans-unit id="_msg943"> + <trans-unit id="_msg961"> <source xml:space="preserve">Fee bump error</source> <context-group purpose="location"><context context-type="linenumber">488</context></context-group> <context-group purpose="location"><context context-type="linenumber">543</context></context-group> <context-group purpose="location"><context context-type="linenumber">558</context></context-group> <context-group purpose="location"><context context-type="linenumber">563</context></context-group> </trans-unit> - <trans-unit id="_msg944"> + <trans-unit id="_msg962"> <source xml:space="preserve">Increasing transaction fee failed</source> <context-group purpose="location"><context context-type="linenumber">488</context></context-group> </trans-unit> - <trans-unit id="_msg945"> + <trans-unit id="_msg963"> <source xml:space="preserve">Do you want to increase the fee?</source> <context-group purpose="location"><context context-type="linenumber">495</context></context-group> <note annotates="source" from="developer">Asks a user if they would like to manually increase the fee of a transaction that has already been created.</note> </trans-unit> - <trans-unit id="_msg946"> + <trans-unit id="_msg964"> <source xml:space="preserve">Current fee:</source> <context-group purpose="location"><context context-type="linenumber">499</context></context-group> </trans-unit> - <trans-unit id="_msg947"> + <trans-unit id="_msg965"> <source xml:space="preserve">Increase:</source> <context-group purpose="location"><context context-type="linenumber">503</context></context-group> </trans-unit> - <trans-unit id="_msg948"> + <trans-unit id="_msg966"> <source xml:space="preserve">New fee:</source> <context-group purpose="location"><context context-type="linenumber">507</context></context-group> </trans-unit> - <trans-unit id="_msg949"> + <trans-unit id="_msg967"> <source xml:space="preserve">Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.</source> <context-group purpose="location"><context context-type="linenumber">515</context></context-group> </trans-unit> - <trans-unit id="_msg950"> + <trans-unit id="_msg968"> <source xml:space="preserve">Confirm fee bump</source> <context-group purpose="location"><context context-type="linenumber">520</context></context-group> </trans-unit> - <trans-unit id="_msg951"> + <trans-unit id="_msg969"> <source xml:space="preserve">Can't draft transaction.</source> <context-group purpose="location"><context context-type="linenumber">543</context></context-group> </trans-unit> - <trans-unit id="_msg952"> + <trans-unit id="_msg970"> <source xml:space="preserve">PSBT copied</source> <context-group purpose="location"><context context-type="linenumber">550</context></context-group> </trans-unit> - <trans-unit id="_msg953"> + <trans-unit id="_msg971"> <source xml:space="preserve">Copied to clipboard</source> <context-group purpose="location"><context context-type="linenumber">550</context></context-group> <context-group><context context-type="x-gettext-msgctxt">Fee-bump PSBT saved</context></context-group> </trans-unit> - <trans-unit id="_msg954"> + <trans-unit id="_msg972"> <source xml:space="preserve">Can't sign transaction.</source> <context-group purpose="location"><context context-type="linenumber">558</context></context-group> </trans-unit> - <trans-unit id="_msg955"> + <trans-unit id="_msg973"> <source xml:space="preserve">Could not commit transaction</source> <context-group purpose="location"><context context-type="linenumber">563</context></context-group> </trans-unit> - <trans-unit id="_msg956"> + <trans-unit id="_msg974"> <source xml:space="preserve">Can't display address</source> <context-group purpose="location"><context context-type="linenumber">577</context></context-group> </trans-unit> - <trans-unit id="_msg957"> + <trans-unit id="_msg975"> <source xml:space="preserve">default wallet</source> <context-group purpose="location"><context context-type="linenumber">595</context></context-group> </trans-unit> @@ -4402,40 +4485,40 @@ Go to File > Open Wallet to load a wallet. </body></file> <file original="../walletview.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletView"> - <trans-unit id="_msg958"> + <trans-unit id="_msg976"> <source xml:space="preserve">&Export</source> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg959"> + <trans-unit id="_msg977"> <source xml:space="preserve">Export the data in the current tab to a file</source> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg960"> + <trans-unit id="_msg978"> <source xml:space="preserve">Backup Wallet</source> <context-group purpose="location"><context context-type="linenumber">214</context></context-group> </trans-unit> - <trans-unit id="_msg961"> + <trans-unit id="_msg979"> <source xml:space="preserve">Wallet Data</source> <context-group purpose="location"><context context-type="linenumber">216</context></context-group> <note annotates="source" from="developer">Name of the wallet data file format.</note> </trans-unit> - <trans-unit id="_msg962"> + <trans-unit id="_msg980"> <source xml:space="preserve">Backup Failed</source> <context-group purpose="location"><context context-type="linenumber">222</context></context-group> </trans-unit> - <trans-unit id="_msg963"> + <trans-unit id="_msg981"> <source xml:space="preserve">There was an error trying to save the wallet data to %1.</source> <context-group purpose="location"><context context-type="linenumber">222</context></context-group> </trans-unit> - <trans-unit id="_msg964"> + <trans-unit id="_msg982"> <source xml:space="preserve">Backup Successful</source> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg965"> + <trans-unit id="_msg983"> <source xml:space="preserve">The wallet data was successfully saved to %1.</source> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg966"> + <trans-unit id="_msg984"> <source xml:space="preserve">Cancel</source> <context-group purpose="location"><context context-type="linenumber">263</context></context-group> </trans-unit> @@ -4443,874 +4526,870 @@ Go to File > Open Wallet to load a wallet. </body></file> <file original="../bitcoinstrings.cpp" datatype="cpp" source-language="en"><body> <group restype="x-trolltech-linguist-context" resname="bitcoin-core"> - <trans-unit id="_msg967"> + <trans-unit id="_msg985"> <source xml:space="preserve">The %s developers</source> <context-group purpose="location"><context context-type="linenumber">12</context></context-group> </trans-unit> - <trans-unit id="_msg968"> + <trans-unit id="_msg986"> <source xml:space="preserve">%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source> <context-group purpose="location"><context context-type="linenumber">13</context></context-group> </trans-unit> - <trans-unit id="_msg969"> + <trans-unit id="_msg987"> <source xml:space="preserve">%s failed to validate the -assumeutxo snapshot state. This indicates a hardware problem, or a bug in the software, or a bad software modification that allowed an invalid snapshot to be loaded. As a result of this, the node will shut down and stop using any state that was built on the snapshot, resetting the chain height from %d to %d. On the next restart, the node will resume syncing from %d without using any snapshot data. Please report this incident to %s, including how you obtained the snapshot. The invalid snapshot chainstate will be left on disk in case it is helpful in diagnosing the issue that caused this error.</source> <context-group purpose="location"><context context-type="linenumber">16</context></context-group> </trans-unit> - <trans-unit id="_msg970"> + <trans-unit id="_msg988"> <source xml:space="preserve">%s request to listen on port %u. This port is considered "bad" and thus it is unlikely that any peer will connect to it. See doc/p2p-bad-ports.md for details and a full list.</source> <context-group purpose="location"><context context-type="linenumber">28</context></context-group> </trans-unit> - <trans-unit id="_msg971"> + <trans-unit id="_msg989"> <source xml:space="preserve">Cannot downgrade wallet from version %i to version %i. Wallet version unchanged.</source> <context-group purpose="location"><context context-type="linenumber">32</context></context-group> </trans-unit> - <trans-unit id="_msg972"> + <trans-unit id="_msg990"> <source xml:space="preserve">Cannot obtain a lock on data directory %s. %s is probably already running.</source> <context-group purpose="location"><context context-type="linenumber">35</context></context-group> </trans-unit> - <trans-unit id="_msg973"> + <trans-unit id="_msg991"> <source xml:space="preserve">Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified.</source> <context-group purpose="location"><context context-type="linenumber">40</context></context-group> </trans-unit> - <trans-unit id="_msg974"> + <trans-unit id="_msg992"> <source xml:space="preserve">Disk space for %s may not accommodate the block files. Approximately %u GB of data will be stored in this directory.</source> <context-group purpose="location"><context context-type="linenumber">44</context></context-group> </trans-unit> - <trans-unit id="_msg975"> + <trans-unit id="_msg993"> <source xml:space="preserve">Distributed under the MIT software license, see the accompanying file %s or %s</source> <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg976"> + <trans-unit id="_msg994"> <source xml:space="preserve">Error loading wallet. Wallet requires blocks to be downloaded, and software does not currently support loading wallets while blocks are being downloaded out of order when using assumeutxo snapshots. Wallet should be able to load successfully after node sync reaches height %s</source> <context-group purpose="location"><context context-type="linenumber">53</context></context-group> </trans-unit> - <trans-unit id="_msg977"> + <trans-unit id="_msg995"> <source xml:space="preserve">Error reading %s! Transaction data may be missing or incorrect. Rescanning wallet.</source> <context-group purpose="location"><context context-type="linenumber">61</context></context-group> </trans-unit> - <trans-unit id="_msg978"> + <trans-unit id="_msg996"> <source xml:space="preserve">Error: Dumpfile format record is incorrect. Got "%s", expected "format".</source> <context-group purpose="location"><context context-type="linenumber">67</context></context-group> </trans-unit> - <trans-unit id="_msg979"> + <trans-unit id="_msg997"> <source xml:space="preserve">Error: Dumpfile identifier record is incorrect. Got "%s", expected "%s".</source> <context-group purpose="location"><context context-type="linenumber">69</context></context-group> </trans-unit> - <trans-unit id="_msg980"> + <trans-unit id="_msg998"> <source xml:space="preserve">Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source> <context-group purpose="location"><context context-type="linenumber">71</context></context-group> </trans-unit> - <trans-unit id="_msg981"> + <trans-unit id="_msg999"> <source xml:space="preserve">Error: Legacy wallets only support the "legacy", "p2sh-segwit", and "bech32" address types</source> <context-group purpose="location"><context context-type="linenumber">77</context></context-group> </trans-unit> - <trans-unit id="_msg982"> + <trans-unit id="_msg1000"> <source xml:space="preserve">Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted.</source> <context-group purpose="location"><context context-type="linenumber">83</context></context-group> </trans-unit> - <trans-unit id="_msg983"> + <trans-unit id="_msg1001"> <source xml:space="preserve">File %s already exists. If you are sure this is what you want, move it out of the way first.</source> - <context-group purpose="location"><context context-type="linenumber">92</context></context-group> + <context-group purpose="location"><context context-type="linenumber">95</context></context-group> </trans-unit> - <trans-unit id="_msg984"> + <trans-unit id="_msg1002"> <source xml:space="preserve">Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start.</source> - <context-group purpose="location"><context context-type="linenumber">101</context></context-group> + <context-group purpose="location"><context context-type="linenumber">104</context></context-group> </trans-unit> - <trans-unit id="_msg985"> + <trans-unit id="_msg1003"> <source xml:space="preserve">More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source> - <context-group purpose="location"><context context-type="linenumber">105</context></context-group> + <context-group purpose="location"><context context-type="linenumber">108</context></context-group> </trans-unit> - <trans-unit id="_msg986"> + <trans-unit id="_msg1004"> <source xml:space="preserve">No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.</source> - <context-group purpose="location"><context context-type="linenumber">108</context></context-group> + <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> - <trans-unit id="_msg987"> + <trans-unit id="_msg1005"> <source xml:space="preserve">No dump file provided. To use dump, -dumpfile=<filename> must be provided.</source> - <context-group purpose="location"><context context-type="linenumber">111</context></context-group> + <context-group purpose="location"><context context-type="linenumber">114</context></context-group> </trans-unit> - <trans-unit id="_msg988"> + <trans-unit id="_msg1006"> <source xml:space="preserve">No wallet file format provided. To use createfromdump, -format=<format> must be provided.</source> - <context-group purpose="location"><context context-type="linenumber">113</context></context-group> + <context-group purpose="location"><context context-type="linenumber">116</context></context-group> </trans-unit> - <trans-unit id="_msg989"> + <trans-unit id="_msg1007"> <source xml:space="preserve">Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source> - <context-group purpose="location"><context context-type="linenumber">129</context></context-group> + <context-group purpose="location"><context context-type="linenumber">132</context></context-group> </trans-unit> - <trans-unit id="_msg990"> + <trans-unit id="_msg1008"> <source xml:space="preserve">Please contribute if you find %s useful. Visit %s for further information about the software.</source> - <context-group purpose="location"><context context-type="linenumber">132</context></context-group> + <context-group purpose="location"><context context-type="linenumber">135</context></context-group> </trans-unit> - <trans-unit id="_msg991"> + <trans-unit id="_msg1009"> <source xml:space="preserve">Prune configured below the minimum of %d MiB. Please use a higher number.</source> - <context-group purpose="location"><context context-type="linenumber">135</context></context-group> + <context-group purpose="location"><context context-type="linenumber">138</context></context-group> </trans-unit> - <trans-unit id="_msg992"> + <trans-unit id="_msg1010"> <source xml:space="preserve">Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.</source> - <context-group purpose="location"><context context-type="linenumber">137</context></context-group> + <context-group purpose="location"><context context-type="linenumber">140</context></context-group> </trans-unit> - <trans-unit id="_msg993"> + <trans-unit id="_msg1011"> <source xml:space="preserve">Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source> - <context-group purpose="location"><context context-type="linenumber">140</context></context-group> + <context-group purpose="location"><context context-type="linenumber">143</context></context-group> </trans-unit> - <trans-unit id="_msg994"> + <trans-unit id="_msg1012"> <source xml:space="preserve">Rename of '%s' -> '%s' failed. You should resolve this by manually moving or deleting the invalid snapshot directory %s, otherwise you will encounter the same error again on the next startup.</source> - <context-group purpose="location"><context context-type="linenumber">143</context></context-group> + <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg995"> + <trans-unit id="_msg1013"> <source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source> - <context-group purpose="location"><context context-type="linenumber">147</context></context-group> + <context-group purpose="location"><context context-type="linenumber">150</context></context-group> </trans-unit> - <trans-unit id="_msg996"> + <trans-unit id="_msg1014"> <source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> <context-group purpose="location"><context context-type="linenumber">153</context></context-group> </trans-unit> - <trans-unit id="_msg997"> - <source xml:space="preserve">The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.</source> - <context-group purpose="location"><context context-type="linenumber">158</context></context-group> - </trans-unit> - <trans-unit id="_msg998"> + <trans-unit id="_msg1015"> <source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source> - <context-group purpose="location"><context context-type="linenumber">169</context></context-group> + <context-group purpose="location"><context context-type="linenumber">165</context></context-group> </trans-unit> - <trans-unit id="_msg999"> + <trans-unit id="_msg1016"> <source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source> - <context-group purpose="location"><context context-type="linenumber">171</context></context-group> + <context-group purpose="location"><context context-type="linenumber">167</context></context-group> </trans-unit> - <trans-unit id="_msg1000"> + <trans-unit id="_msg1017"> <source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> - <context-group purpose="location"><context context-type="linenumber">175</context></context-group> + <context-group purpose="location"><context context-type="linenumber">171</context></context-group> </trans-unit> - <trans-unit id="_msg1001"> + <trans-unit id="_msg1018"> <source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source> - <context-group purpose="location"><context context-type="linenumber">178</context></context-group> + <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg1002"> + <trans-unit id="_msg1019"> <source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source> - <context-group purpose="location"><context context-type="linenumber">181</context></context-group> + <context-group purpose="location"><context context-type="linenumber">177</context></context-group> </trans-unit> - <trans-unit id="_msg1003"> + <trans-unit id="_msg1020"> <source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source> - <context-group purpose="location"><context context-type="linenumber">184</context></context-group> + <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg1004"> + <trans-unit id="_msg1021"> <source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> - <context-group purpose="location"><context context-type="linenumber">186</context></context-group> + <context-group purpose="location"><context context-type="linenumber">182</context></context-group> </trans-unit> - <trans-unit id="_msg1005"> + <trans-unit id="_msg1022"> <source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source> - <context-group purpose="location"><context context-type="linenumber">195</context></context-group> + <context-group purpose="location"><context context-type="linenumber">191</context></context-group> </trans-unit> - <trans-unit id="_msg1006"> + <trans-unit id="_msg1023"> <source xml:space="preserve">Unknown wallet file format "%s" provided. Please provide one of "bdb" or "sqlite".</source> - <context-group purpose="location"><context context-type="linenumber">205</context></context-group> + <context-group purpose="location"><context context-type="linenumber">201</context></context-group> </trans-unit> - <trans-unit id="_msg1007"> + <trans-unit id="_msg1024"> <source xml:space="preserve">Unsupported category-specific logging level %1$s=%2$s. Expected %1$s=<category>:<loglevel>. Valid categories: %3$s. Valid loglevels: %4$s.</source> - <context-group purpose="location"><context context-type="linenumber">213</context></context-group> + <context-group purpose="location"><context context-type="linenumber">209</context></context-group> </trans-unit> - <trans-unit id="_msg1008"> + <trans-unit id="_msg1025"> <source xml:space="preserve">Unsupported chainstate database format found. Please restart with -reindex-chainstate. This will rebuild the chainstate database.</source> - <context-group purpose="location"><context context-type="linenumber">216</context></context-group> + <context-group purpose="location"><context context-type="linenumber">212</context></context-group> </trans-unit> - <trans-unit id="_msg1009"> + <trans-unit id="_msg1026"> <source xml:space="preserve">Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future.</source> - <context-group purpose="location"><context context-type="linenumber">219</context></context-group> + <context-group purpose="location"><context context-type="linenumber">215</context></context-group> </trans-unit> - <trans-unit id="_msg1010"> + <trans-unit id="_msg1027"> <source xml:space="preserve">Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet.</source> - <context-group purpose="location"><context context-type="linenumber">223</context></context-group> + <context-group purpose="location"><context context-type="linenumber">219</context></context-group> </trans-unit> - <trans-unit id="_msg1011"> + <trans-unit id="_msg1028"> <source xml:space="preserve">Warning: Dumpfile wallet format "%s" does not match command line specified format "%s".</source> - <context-group purpose="location"><context context-type="linenumber">228</context></context-group> + <context-group purpose="location"><context context-type="linenumber">224</context></context-group> </trans-unit> - <trans-unit id="_msg1012"> + <trans-unit id="_msg1029"> <source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source> - <context-group purpose="location"><context context-type="linenumber">231</context></context-group> + <context-group purpose="location"><context context-type="linenumber">227</context></context-group> </trans-unit> - <trans-unit id="_msg1013"> + <trans-unit id="_msg1030"> <source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> - <context-group purpose="location"><context context-type="linenumber">233</context></context-group> + <context-group purpose="location"><context context-type="linenumber">229</context></context-group> </trans-unit> - <trans-unit id="_msg1014"> + <trans-unit id="_msg1031"> <source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source> - <context-group purpose="location"><context context-type="linenumber">236</context></context-group> + <context-group purpose="location"><context context-type="linenumber">232</context></context-group> </trans-unit> - <trans-unit id="_msg1015"> + <trans-unit id="_msg1032"> <source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> - <context-group purpose="location"><context context-type="linenumber">239</context></context-group> + <context-group purpose="location"><context context-type="linenumber">235</context></context-group> </trans-unit> - <trans-unit id="_msg1016"> + <trans-unit id="_msg1033"> <source xml:space="preserve">%s is set very high!</source> - <context-group purpose="location"><context context-type="linenumber">248</context></context-group> + <context-group purpose="location"><context context-type="linenumber">244</context></context-group> </trans-unit> - <trans-unit id="_msg1017"> + <trans-unit id="_msg1034"> <source xml:space="preserve">-maxmempool must be at least %d MB</source> - <context-group purpose="location"><context context-type="linenumber">249</context></context-group> + <context-group purpose="location"><context context-type="linenumber">245</context></context-group> </trans-unit> - <trans-unit id="_msg1018"> + <trans-unit id="_msg1035"> <source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source> - <context-group purpose="location"><context context-type="linenumber">250</context></context-group> + <context-group purpose="location"><context context-type="linenumber">246</context></context-group> </trans-unit> - <trans-unit id="_msg1019"> + <trans-unit id="_msg1036"> <source xml:space="preserve">Cannot resolve -%s address: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">252</context></context-group> + <context-group purpose="location"><context context-type="linenumber">248</context></context-group> </trans-unit> - <trans-unit id="_msg1020"> + <trans-unit id="_msg1037"> <source xml:space="preserve">Cannot set -forcednsseed to true when setting -dnsseed to false.</source> - <context-group purpose="location"><context context-type="linenumber">253</context></context-group> + <context-group purpose="location"><context context-type="linenumber">249</context></context-group> </trans-unit> - <trans-unit id="_msg1021"> + <trans-unit id="_msg1038"> <source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source> - <context-group purpose="location"><context context-type="linenumber">254</context></context-group> + <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg1022"> + <trans-unit id="_msg1039"> <source xml:space="preserve">Cannot write to data directory '%s'; check permissions.</source> - <context-group purpose="location"><context context-type="linenumber">255</context></context-group> - </trans-unit> - <trans-unit id="_msg1023"> - <source xml:space="preserve">The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.</source> - <context-group purpose="location"><context context-type="linenumber">150</context></context-group> + <context-group purpose="location"><context context-type="linenumber">251</context></context-group> </trans-unit> - <trans-unit id="_msg1024"> + <trans-unit id="_msg1040"> <source xml:space="preserve">%s is set very high! Fees this large could be paid on a single transaction.</source> <context-group purpose="location"><context context-type="linenumber">26</context></context-group> </trans-unit> - <trans-unit id="_msg1025"> + <trans-unit id="_msg1041"> <source xml:space="preserve">Cannot provide specific connections and have addrman find outgoing connections at the same time.</source> <context-group purpose="location"><context context-type="linenumber">37</context></context-group> </trans-unit> - <trans-unit id="_msg1026"> + <trans-unit id="_msg1042"> <source xml:space="preserve">Error loading %s: External signer wallet being loaded without external signer support compiled</source> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg1027"> + <trans-unit id="_msg1043"> <source xml:space="preserve">Error reading %s! All keys read correctly, but transaction data or address metadata may be missing or incorrect.</source> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg1028"> + <trans-unit id="_msg1044"> <source xml:space="preserve">Error: Address book data in wallet cannot be identified to belong to migrated wallets</source> <context-group purpose="location"><context context-type="linenumber">64</context></context-group> </trans-unit> - <trans-unit id="_msg1029"> + <trans-unit id="_msg1045"> <source xml:space="preserve">Error: Duplicate descriptors created during migration. Your wallet may be corrupted.</source> <context-group purpose="location"><context context-type="linenumber">74</context></context-group> </trans-unit> - <trans-unit id="_msg1030"> + <trans-unit id="_msg1046"> <source xml:space="preserve">Error: Transaction %s in wallet cannot be identified to belong to migrated wallets</source> <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg1031"> - <source xml:space="preserve">Failed to rename invalid peers.dat file. Please move or delete it and try again.</source> + <trans-unit id="_msg1047"> + <source xml:space="preserve">Failed to calculate bump fees, because unconfirmed UTXOs depend on enormous cluster of unconfirmed transactions.</source> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg1032"> - <source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable %s.</source> + <trans-unit id="_msg1048"> + <source xml:space="preserve">Failed to rename invalid peers.dat file. Please move or delete it and try again.</source> <context-group purpose="location"><context context-type="linenumber">89</context></context-group> </trans-unit> - <trans-unit id="_msg1033"> + <trans-unit id="_msg1049"> + <source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable %s.</source> + <context-group purpose="location"><context context-type="linenumber">92</context></context-group> + </trans-unit> + <trans-unit id="_msg1050"> <source xml:space="preserve">Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6</source> - <context-group purpose="location"><context context-type="linenumber">95</context></context-group> + <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg1034"> + <trans-unit id="_msg1051"> <source xml:space="preserve">Invalid amount for %s=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source> - <context-group purpose="location"><context context-type="linenumber">98</context></context-group> + <context-group purpose="location"><context context-type="linenumber">101</context></context-group> </trans-unit> - <trans-unit id="_msg1035"> + <trans-unit id="_msg1052"> <source xml:space="preserve">Outbound connections restricted to CJDNS (-onlynet=cjdns) but -cjdnsreachable is not provided</source> - <context-group purpose="location"><context context-type="linenumber">116</context></context-group> + <context-group purpose="location"><context context-type="linenumber">119</context></context-group> </trans-unit> - <trans-unit id="_msg1036"> + <trans-unit id="_msg1053"> <source xml:space="preserve">Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is explicitly forbidden: -onion=0</source> - <context-group purpose="location"><context context-type="linenumber">119</context></context-group> + <context-group purpose="location"><context context-type="linenumber">122</context></context-group> </trans-unit> - <trans-unit id="_msg1037"> + <trans-unit id="_msg1054"> <source xml:space="preserve">Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is not provided: none of -proxy, -onion or -listenonion is given</source> - <context-group purpose="location"><context context-type="linenumber">122</context></context-group> + <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> - <trans-unit id="_msg1038"> + <trans-unit id="_msg1055"> <source xml:space="preserve">Outbound connections restricted to i2p (-onlynet=i2p) but -i2psam is not provided</source> - <context-group purpose="location"><context context-type="linenumber">126</context></context-group> + <context-group purpose="location"><context context-type="linenumber">129</context></context-group> </trans-unit> - <trans-unit id="_msg1039"> + <trans-unit id="_msg1056"> <source xml:space="preserve">The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs</source> - <context-group purpose="location"><context context-type="linenumber">162</context></context-group> + <context-group purpose="location"><context context-type="linenumber">158</context></context-group> </trans-unit> - <trans-unit id="_msg1040"> + <trans-unit id="_msg1057"> <source xml:space="preserve">The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually</source> - <context-group purpose="location"><context context-type="linenumber">165</context></context-group> + <context-group purpose="location"><context context-type="linenumber">161</context></context-group> </trans-unit> - <trans-unit id="_msg1041"> + <trans-unit id="_msg1058"> <source xml:space="preserve">Transaction requires one destination of non-0 value, a non-0 feerate, or a pre-selected input</source> - <context-group purpose="location"><context context-type="linenumber">189</context></context-group> + <context-group purpose="location"><context context-type="linenumber">185</context></context-group> </trans-unit> - <trans-unit id="_msg1042"> + <trans-unit id="_msg1059"> <source xml:space="preserve">UTXO snapshot failed to validate. Restart to resume normal initial block download, or try loading a different snapshot.</source> - <context-group purpose="location"><context context-type="linenumber">192</context></context-group> + <context-group purpose="location"><context context-type="linenumber">188</context></context-group> </trans-unit> - <trans-unit id="_msg1043"> + <trans-unit id="_msg1060"> <source xml:space="preserve">Unconfirmed UTXOs are available, but spending them creates a chain of transactions that will be rejected by the mempool</source> - <context-group purpose="location"><context context-type="linenumber">198</context></context-group> + <context-group purpose="location"><context context-type="linenumber">194</context></context-group> </trans-unit> - <trans-unit id="_msg1044"> + <trans-unit id="_msg1061"> <source xml:space="preserve">Unexpected legacy entry in descriptor wallet found. Loading wallet %s The wallet might have been tampered with or created with malicious intent. </source> - <context-group purpose="location"><context context-type="linenumber">201</context></context-group> + <context-group purpose="location"><context context-type="linenumber">197</context></context-group> </trans-unit> - <trans-unit id="_msg1045"> + <trans-unit id="_msg1062"> <source xml:space="preserve">Unrecognized descriptor found. Loading wallet %s The wallet might had been created on a newer version. Please try running the latest software version. </source> - <context-group purpose="location"><context context-type="linenumber">208</context></context-group> + <context-group purpose="location"><context context-type="linenumber">204</context></context-group> </trans-unit> - <trans-unit id="_msg1046"> + <trans-unit id="_msg1063"> <source xml:space="preserve"> Unable to cleanup failed migration</source> - <context-group purpose="location"><context context-type="linenumber">242</context></context-group> + <context-group purpose="location"><context context-type="linenumber">238</context></context-group> </trans-unit> - <trans-unit id="_msg1047"> + <trans-unit id="_msg1064"> <source xml:space="preserve"> Unable to restore backup of wallet.</source> - <context-group purpose="location"><context context-type="linenumber">245</context></context-group> + <context-group purpose="location"><context context-type="linenumber">241</context></context-group> </trans-unit> - <trans-unit id="_msg1048"> + <trans-unit id="_msg1065"> <source xml:space="preserve">Block verification was interrupted</source> - <context-group purpose="location"><context context-type="linenumber">251</context></context-group> + <context-group purpose="location"><context context-type="linenumber">247</context></context-group> </trans-unit> - <trans-unit id="_msg1049"> + <trans-unit id="_msg1066"> <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source> - <context-group purpose="location"><context context-type="linenumber">256</context></context-group> + <context-group purpose="location"><context context-type="linenumber">252</context></context-group> </trans-unit> - <trans-unit id="_msg1050"> + <trans-unit id="_msg1067"> <source xml:space="preserve">Copyright (C) %i-%i</source> - <context-group purpose="location"><context context-type="linenumber">257</context></context-group> + <context-group purpose="location"><context context-type="linenumber">253</context></context-group> </trans-unit> - <trans-unit id="_msg1051"> + <trans-unit id="_msg1068"> <source xml:space="preserve">Corrupted block database detected</source> - <context-group purpose="location"><context context-type="linenumber">258</context></context-group> + <context-group purpose="location"><context context-type="linenumber">254</context></context-group> </trans-unit> - <trans-unit id="_msg1052"> + <trans-unit id="_msg1069"> <source xml:space="preserve">Could not find asmap file %s</source> - <context-group purpose="location"><context context-type="linenumber">259</context></context-group> + <context-group purpose="location"><context context-type="linenumber">255</context></context-group> </trans-unit> - <trans-unit id="_msg1053"> + <trans-unit id="_msg1070"> <source xml:space="preserve">Could not parse asmap file %s</source> - <context-group purpose="location"><context context-type="linenumber">260</context></context-group> + <context-group purpose="location"><context context-type="linenumber">256</context></context-group> </trans-unit> - <trans-unit id="_msg1054"> + <trans-unit id="_msg1071"> <source xml:space="preserve">Disk space is too low!</source> - <context-group purpose="location"><context context-type="linenumber">261</context></context-group> + <context-group purpose="location"><context context-type="linenumber">257</context></context-group> </trans-unit> - <trans-unit id="_msg1055"> + <trans-unit id="_msg1072"> <source xml:space="preserve">Do you want to rebuild the block database now?</source> - <context-group purpose="location"><context context-type="linenumber">262</context></context-group> + <context-group purpose="location"><context context-type="linenumber">258</context></context-group> </trans-unit> - <trans-unit id="_msg1056"> + <trans-unit id="_msg1073"> <source xml:space="preserve">Done loading</source> - <context-group purpose="location"><context context-type="linenumber">263</context></context-group> + <context-group purpose="location"><context context-type="linenumber">259</context></context-group> </trans-unit> - <trans-unit id="_msg1057"> + <trans-unit id="_msg1074"> <source xml:space="preserve">Dump file %s does not exist.</source> - <context-group purpose="location"><context context-type="linenumber">264</context></context-group> + <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg1058"> + <trans-unit id="_msg1075"> <source xml:space="preserve">Error creating %s</source> - <context-group purpose="location"><context context-type="linenumber">265</context></context-group> + <context-group purpose="location"><context context-type="linenumber">261</context></context-group> </trans-unit> - <trans-unit id="_msg1059"> + <trans-unit id="_msg1076"> <source xml:space="preserve">Error initializing block database</source> - <context-group purpose="location"><context context-type="linenumber">266</context></context-group> + <context-group purpose="location"><context context-type="linenumber">262</context></context-group> </trans-unit> - <trans-unit id="_msg1060"> + <trans-unit id="_msg1077"> <source xml:space="preserve">Error initializing wallet database environment %s!</source> - <context-group purpose="location"><context context-type="linenumber">267</context></context-group> + <context-group purpose="location"><context context-type="linenumber">263</context></context-group> </trans-unit> - <trans-unit id="_msg1061"> + <trans-unit id="_msg1078"> <source xml:space="preserve">Error loading %s</source> - <context-group purpose="location"><context context-type="linenumber">268</context></context-group> + <context-group purpose="location"><context context-type="linenumber">264</context></context-group> </trans-unit> - <trans-unit id="_msg1062"> + <trans-unit id="_msg1079"> <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source> - <context-group purpose="location"><context context-type="linenumber">269</context></context-group> + <context-group purpose="location"><context context-type="linenumber">265</context></context-group> </trans-unit> - <trans-unit id="_msg1063"> + <trans-unit id="_msg1080"> <source xml:space="preserve">Error loading %s: Wallet corrupted</source> - <context-group purpose="location"><context context-type="linenumber">270</context></context-group> + <context-group purpose="location"><context context-type="linenumber">266</context></context-group> </trans-unit> - <trans-unit id="_msg1064"> + <trans-unit id="_msg1081"> <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source> - <context-group purpose="location"><context context-type="linenumber">271</context></context-group> + <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg1065"> + <trans-unit id="_msg1082"> <source xml:space="preserve">Error loading block database</source> - <context-group purpose="location"><context context-type="linenumber">272</context></context-group> + <context-group purpose="location"><context context-type="linenumber">268</context></context-group> </trans-unit> - <trans-unit id="_msg1066"> + <trans-unit id="_msg1083"> <source xml:space="preserve">Error opening block database</source> - <context-group purpose="location"><context context-type="linenumber">273</context></context-group> + <context-group purpose="location"><context context-type="linenumber">269</context></context-group> </trans-unit> - <trans-unit id="_msg1067"> + <trans-unit id="_msg1084"> <source xml:space="preserve">Error reading configuration file: %s</source> - <context-group purpose="location"><context context-type="linenumber">274</context></context-group> + <context-group purpose="location"><context context-type="linenumber">270</context></context-group> </trans-unit> - <trans-unit id="_msg1068"> + <trans-unit id="_msg1085"> <source xml:space="preserve">Error reading from database, shutting down.</source> - <context-group purpose="location"><context context-type="linenumber">275</context></context-group> + <context-group purpose="location"><context context-type="linenumber">271</context></context-group> </trans-unit> - <trans-unit id="_msg1069"> + <trans-unit id="_msg1086"> <source xml:space="preserve">Error reading next record from wallet database</source> - <context-group purpose="location"><context context-type="linenumber">276</context></context-group> + <context-group purpose="location"><context context-type="linenumber">272</context></context-group> </trans-unit> - <trans-unit id="_msg1070"> + <trans-unit id="_msg1087"> <source xml:space="preserve">Error: Cannot extract destination from the generated scriptpubkey</source> - <context-group purpose="location"><context context-type="linenumber">277</context></context-group> + <context-group purpose="location"><context context-type="linenumber">273</context></context-group> </trans-unit> - <trans-unit id="_msg1071"> + <trans-unit id="_msg1088"> <source xml:space="preserve">Error: Could not add watchonly tx to watchonly wallet</source> - <context-group purpose="location"><context context-type="linenumber">278</context></context-group> + <context-group purpose="location"><context context-type="linenumber">274</context></context-group> </trans-unit> - <trans-unit id="_msg1072"> + <trans-unit id="_msg1089"> <source xml:space="preserve">Error: Could not delete watchonly transactions</source> - <context-group purpose="location"><context context-type="linenumber">279</context></context-group> + <context-group purpose="location"><context context-type="linenumber">275</context></context-group> </trans-unit> - <trans-unit id="_msg1073"> + <trans-unit id="_msg1090"> <source xml:space="preserve">Error: Couldn't create cursor into database</source> - <context-group purpose="location"><context context-type="linenumber">280</context></context-group> + <context-group purpose="location"><context context-type="linenumber">276</context></context-group> </trans-unit> - <trans-unit id="_msg1074"> + <trans-unit id="_msg1091"> <source xml:space="preserve">Error: Disk space is low for %s</source> - <context-group purpose="location"><context context-type="linenumber">281</context></context-group> + <context-group purpose="location"><context context-type="linenumber">277</context></context-group> </trans-unit> - <trans-unit id="_msg1075"> + <trans-unit id="_msg1092"> <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source> - <context-group purpose="location"><context context-type="linenumber">282</context></context-group> + <context-group purpose="location"><context context-type="linenumber">278</context></context-group> </trans-unit> - <trans-unit id="_msg1076"> + <trans-unit id="_msg1093"> <source xml:space="preserve">Error: Failed to create new watchonly wallet</source> - <context-group purpose="location"><context context-type="linenumber">283</context></context-group> + <context-group purpose="location"><context context-type="linenumber">279</context></context-group> </trans-unit> - <trans-unit id="_msg1077"> + <trans-unit id="_msg1094"> <source xml:space="preserve">Error: Got key that was not hex: %s</source> - <context-group purpose="location"><context context-type="linenumber">284</context></context-group> + <context-group purpose="location"><context context-type="linenumber">280</context></context-group> </trans-unit> - <trans-unit id="_msg1078"> + <trans-unit id="_msg1095"> <source xml:space="preserve">Error: Got value that was not hex: %s</source> - <context-group purpose="location"><context context-type="linenumber">285</context></context-group> + <context-group purpose="location"><context context-type="linenumber">281</context></context-group> </trans-unit> - <trans-unit id="_msg1079"> + <trans-unit id="_msg1096"> <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source> - <context-group purpose="location"><context context-type="linenumber">286</context></context-group> + <context-group purpose="location"><context context-type="linenumber">282</context></context-group> </trans-unit> - <trans-unit id="_msg1080"> + <trans-unit id="_msg1097"> <source xml:space="preserve">Error: Missing checksum</source> - <context-group purpose="location"><context context-type="linenumber">287</context></context-group> + <context-group purpose="location"><context context-type="linenumber">283</context></context-group> </trans-unit> - <trans-unit id="_msg1081"> + <trans-unit id="_msg1098"> <source xml:space="preserve">Error: No %s addresses available.</source> - <context-group purpose="location"><context context-type="linenumber">288</context></context-group> + <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg1082"> + <trans-unit id="_msg1099"> <source xml:space="preserve">Error: Not all watchonly txs could be deleted</source> - <context-group purpose="location"><context context-type="linenumber">289</context></context-group> + <context-group purpose="location"><context context-type="linenumber">285</context></context-group> </trans-unit> - <trans-unit id="_msg1083"> + <trans-unit id="_msg1100"> <source xml:space="preserve">Error: This wallet already uses SQLite</source> - <context-group purpose="location"><context context-type="linenumber">290</context></context-group> + <context-group purpose="location"><context context-type="linenumber">286</context></context-group> </trans-unit> - <trans-unit id="_msg1084"> + <trans-unit id="_msg1101"> <source xml:space="preserve">Error: This wallet is already a descriptor wallet</source> - <context-group purpose="location"><context context-type="linenumber">291</context></context-group> + <context-group purpose="location"><context context-type="linenumber">287</context></context-group> </trans-unit> - <trans-unit id="_msg1085"> + <trans-unit id="_msg1102"> <source xml:space="preserve">Error: Unable to begin reading all records in the database</source> - <context-group purpose="location"><context context-type="linenumber">292</context></context-group> + <context-group purpose="location"><context context-type="linenumber">288</context></context-group> </trans-unit> - <trans-unit id="_msg1086"> + <trans-unit id="_msg1103"> <source xml:space="preserve">Error: Unable to make a backup of your wallet</source> - <context-group purpose="location"><context context-type="linenumber">293</context></context-group> + <context-group purpose="location"><context context-type="linenumber">289</context></context-group> </trans-unit> - <trans-unit id="_msg1087"> + <trans-unit id="_msg1104"> <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source> - <context-group purpose="location"><context context-type="linenumber">294</context></context-group> + <context-group purpose="location"><context context-type="linenumber">290</context></context-group> </trans-unit> - <trans-unit id="_msg1088"> + <trans-unit id="_msg1105"> <source xml:space="preserve">Error: Unable to read all records in the database</source> - <context-group purpose="location"><context context-type="linenumber">295</context></context-group> + <context-group purpose="location"><context context-type="linenumber">291</context></context-group> </trans-unit> - <trans-unit id="_msg1089"> + <trans-unit id="_msg1106"> <source xml:space="preserve">Error: Unable to remove watchonly address book data</source> - <context-group purpose="location"><context context-type="linenumber">296</context></context-group> + <context-group purpose="location"><context context-type="linenumber">292</context></context-group> </trans-unit> - <trans-unit id="_msg1090"> + <trans-unit id="_msg1107"> <source xml:space="preserve">Error: Unable to write record to new wallet</source> - <context-group purpose="location"><context context-type="linenumber">297</context></context-group> + <context-group purpose="location"><context context-type="linenumber">293</context></context-group> </trans-unit> - <trans-unit id="_msg1091"> + <trans-unit id="_msg1108"> <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source> - <context-group purpose="location"><context context-type="linenumber">298</context></context-group> + <context-group purpose="location"><context context-type="linenumber">294</context></context-group> </trans-unit> - <trans-unit id="_msg1092"> + <trans-unit id="_msg1109"> <source xml:space="preserve">Failed to rescan the wallet during initialization</source> - <context-group purpose="location"><context context-type="linenumber">299</context></context-group> + <context-group purpose="location"><context context-type="linenumber">295</context></context-group> </trans-unit> - <trans-unit id="_msg1093"> + <trans-unit id="_msg1110"> <source xml:space="preserve">Failed to start indexes, shutting down..</source> - <context-group purpose="location"><context context-type="linenumber">300</context></context-group> + <context-group purpose="location"><context context-type="linenumber">296</context></context-group> </trans-unit> - <trans-unit id="_msg1094"> + <trans-unit id="_msg1111"> <source xml:space="preserve">Failed to verify database</source> - <context-group purpose="location"><context context-type="linenumber">301</context></context-group> + <context-group purpose="location"><context context-type="linenumber">297</context></context-group> </trans-unit> - <trans-unit id="_msg1095"> + <trans-unit id="_msg1112"> <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source> - <context-group purpose="location"><context context-type="linenumber">302</context></context-group> + <context-group purpose="location"><context context-type="linenumber">298</context></context-group> </trans-unit> - <trans-unit id="_msg1096"> + <trans-unit id="_msg1113"> <source xml:space="preserve">Ignoring duplicate -wallet %s.</source> - <context-group purpose="location"><context context-type="linenumber">303</context></context-group> + <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> - <trans-unit id="_msg1097"> + <trans-unit id="_msg1114"> <source xml:space="preserve">Importing…</source> - <context-group purpose="location"><context context-type="linenumber">304</context></context-group> + <context-group purpose="location"><context context-type="linenumber">300</context></context-group> </trans-unit> - <trans-unit id="_msg1098"> + <trans-unit id="_msg1115"> <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source> - <context-group purpose="location"><context context-type="linenumber">305</context></context-group> + <context-group purpose="location"><context context-type="linenumber">301</context></context-group> </trans-unit> - <trans-unit id="_msg1099"> + <trans-unit id="_msg1116"> <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source> - <context-group purpose="location"><context context-type="linenumber">306</context></context-group> + <context-group purpose="location"><context context-type="linenumber">302</context></context-group> </trans-unit> - <trans-unit id="_msg1100"> + <trans-unit id="_msg1117"> <source xml:space="preserve">Input not found or already spent</source> - <context-group purpose="location"><context context-type="linenumber">307</context></context-group> + <context-group purpose="location"><context context-type="linenumber">303</context></context-group> </trans-unit> - <trans-unit id="_msg1101"> + <trans-unit id="_msg1118"> <source xml:space="preserve">Insufficient dbcache for block verification</source> - <context-group purpose="location"><context context-type="linenumber">308</context></context-group> + <context-group purpose="location"><context context-type="linenumber">304</context></context-group> </trans-unit> - <trans-unit id="_msg1102"> + <trans-unit id="_msg1119"> <source xml:space="preserve">Insufficient funds</source> - <context-group purpose="location"><context context-type="linenumber">309</context></context-group> + <context-group purpose="location"><context context-type="linenumber">305</context></context-group> </trans-unit> - <trans-unit id="_msg1103"> + <trans-unit id="_msg1120"> <source xml:space="preserve">Invalid -i2psam address or hostname: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">310</context></context-group> + <context-group purpose="location"><context context-type="linenumber">306</context></context-group> </trans-unit> - <trans-unit id="_msg1104"> + <trans-unit id="_msg1121"> <source xml:space="preserve">Invalid -onion address or hostname: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">311</context></context-group> + <context-group purpose="location"><context context-type="linenumber">307</context></context-group> </trans-unit> - <trans-unit id="_msg1105"> + <trans-unit id="_msg1122"> <source xml:space="preserve">Invalid -proxy address or hostname: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">312</context></context-group> + <context-group purpose="location"><context context-type="linenumber">308</context></context-group> </trans-unit> - <trans-unit id="_msg1106"> + <trans-unit id="_msg1123"> <source xml:space="preserve">Invalid P2P permission: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">313</context></context-group> + <context-group purpose="location"><context context-type="linenumber">309</context></context-group> </trans-unit> - <trans-unit id="_msg1107"> + <trans-unit id="_msg1124"> <source xml:space="preserve">Invalid amount for %s=<amount>: '%s' (must be at least %s)</source> - <context-group purpose="location"><context context-type="linenumber">314</context></context-group> + <context-group purpose="location"><context context-type="linenumber">310</context></context-group> </trans-unit> - <trans-unit id="_msg1108"> + <trans-unit id="_msg1125"> <source xml:space="preserve">Invalid amount for %s=<amount>: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">315</context></context-group> + <context-group purpose="location"><context context-type="linenumber">311</context></context-group> </trans-unit> - <trans-unit id="_msg1109"> + <trans-unit id="_msg1126"> <source xml:space="preserve">Invalid amount for -%s=<amount>: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">316</context></context-group> + <context-group purpose="location"><context context-type="linenumber">312</context></context-group> </trans-unit> - <trans-unit id="_msg1110"> + <trans-unit id="_msg1127"> <source xml:space="preserve">Invalid netmask specified in -whitelist: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">317</context></context-group> + <context-group purpose="location"><context context-type="linenumber">313</context></context-group> </trans-unit> - <trans-unit id="_msg1111"> + <trans-unit id="_msg1128"> <source xml:space="preserve">Invalid port specified in %s: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">318</context></context-group> + <context-group purpose="location"><context context-type="linenumber">314</context></context-group> </trans-unit> - <trans-unit id="_msg1112"> + <trans-unit id="_msg1129"> <source xml:space="preserve">Invalid pre-selected input %s</source> - <context-group purpose="location"><context context-type="linenumber">319</context></context-group> + <context-group purpose="location"><context context-type="linenumber">315</context></context-group> </trans-unit> - <trans-unit id="_msg1113"> + <trans-unit id="_msg1130"> <source xml:space="preserve">Listening for incoming connections failed (listen returned error %s)</source> - <context-group purpose="location"><context context-type="linenumber">320</context></context-group> + <context-group purpose="location"><context context-type="linenumber">316</context></context-group> </trans-unit> - <trans-unit id="_msg1114"> + <trans-unit id="_msg1131"> <source xml:space="preserve">Loading P2P addresses…</source> - <context-group purpose="location"><context context-type="linenumber">321</context></context-group> + <context-group purpose="location"><context context-type="linenumber">317</context></context-group> </trans-unit> - <trans-unit id="_msg1115"> + <trans-unit id="_msg1132"> <source xml:space="preserve">Loading banlist…</source> - <context-group purpose="location"><context context-type="linenumber">322</context></context-group> + <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> - <trans-unit id="_msg1116"> + <trans-unit id="_msg1133"> <source xml:space="preserve">Loading block index…</source> - <context-group purpose="location"><context context-type="linenumber">323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">319</context></context-group> </trans-unit> - <trans-unit id="_msg1117"> + <trans-unit id="_msg1134"> <source xml:space="preserve">Loading wallet…</source> - <context-group purpose="location"><context context-type="linenumber">324</context></context-group> + <context-group purpose="location"><context context-type="linenumber">320</context></context-group> </trans-unit> - <trans-unit id="_msg1118"> + <trans-unit id="_msg1135"> <source xml:space="preserve">Missing amount</source> - <context-group purpose="location"><context context-type="linenumber">325</context></context-group> + <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg1119"> + <trans-unit id="_msg1136"> <source xml:space="preserve">Missing solving data for estimating transaction size</source> - <context-group purpose="location"><context context-type="linenumber">326</context></context-group> + <context-group purpose="location"><context context-type="linenumber">322</context></context-group> </trans-unit> - <trans-unit id="_msg1120"> + <trans-unit id="_msg1137"> <source xml:space="preserve">Need to specify a port with -whitebind: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">327</context></context-group> + <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> - <trans-unit id="_msg1121"> + <trans-unit id="_msg1138"> <source xml:space="preserve">No addresses available</source> - <context-group purpose="location"><context context-type="linenumber">328</context></context-group> + <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> - <trans-unit id="_msg1122"> + <trans-unit id="_msg1139"> <source xml:space="preserve">Not enough file descriptors available.</source> - <context-group purpose="location"><context context-type="linenumber">329</context></context-group> + <context-group purpose="location"><context context-type="linenumber">325</context></context-group> </trans-unit> - <trans-unit id="_msg1123"> + <trans-unit id="_msg1140"> <source xml:space="preserve">Not found pre-selected input %s</source> - <context-group purpose="location"><context context-type="linenumber">330</context></context-group> + <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg1124"> + <trans-unit id="_msg1141"> <source xml:space="preserve">Not solvable pre-selected input %s</source> - <context-group purpose="location"><context context-type="linenumber">331</context></context-group> + <context-group purpose="location"><context context-type="linenumber">327</context></context-group> </trans-unit> - <trans-unit id="_msg1125"> + <trans-unit id="_msg1142"> <source xml:space="preserve">Prune cannot be configured with a negative value.</source> - <context-group purpose="location"><context context-type="linenumber">332</context></context-group> + <context-group purpose="location"><context context-type="linenumber">328</context></context-group> </trans-unit> - <trans-unit id="_msg1126"> + <trans-unit id="_msg1143"> <source xml:space="preserve">Prune mode is incompatible with -txindex.</source> - <context-group purpose="location"><context context-type="linenumber">333</context></context-group> + <context-group purpose="location"><context context-type="linenumber">329</context></context-group> </trans-unit> - <trans-unit id="_msg1127"> + <trans-unit id="_msg1144"> <source xml:space="preserve">Pruning blockstore…</source> - <context-group purpose="location"><context context-type="linenumber">334</context></context-group> + <context-group purpose="location"><context context-type="linenumber">330</context></context-group> </trans-unit> - <trans-unit id="_msg1128"> + <trans-unit id="_msg1145"> <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source> - <context-group purpose="location"><context context-type="linenumber">335</context></context-group> + <context-group purpose="location"><context context-type="linenumber">331</context></context-group> </trans-unit> - <trans-unit id="_msg1129"> + <trans-unit id="_msg1146"> <source xml:space="preserve">Replaying blocks…</source> - <context-group purpose="location"><context context-type="linenumber">336</context></context-group> + <context-group purpose="location"><context context-type="linenumber">332</context></context-group> </trans-unit> - <trans-unit id="_msg1130"> + <trans-unit id="_msg1147"> <source xml:space="preserve">Rescanning…</source> - <context-group purpose="location"><context context-type="linenumber">337</context></context-group> + <context-group purpose="location"><context context-type="linenumber">333</context></context-group> </trans-unit> - <trans-unit id="_msg1131"> + <trans-unit id="_msg1148"> <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source> - <context-group purpose="location"><context context-type="linenumber">338</context></context-group> + <context-group purpose="location"><context context-type="linenumber">334</context></context-group> </trans-unit> - <trans-unit id="_msg1132"> + <trans-unit id="_msg1149"> <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source> - <context-group purpose="location"><context context-type="linenumber">339</context></context-group> + <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg1133"> + <trans-unit id="_msg1150"> <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source> - <context-group purpose="location"><context context-type="linenumber">340</context></context-group> + <context-group purpose="location"><context context-type="linenumber">336</context></context-group> </trans-unit> - <trans-unit id="_msg1134"> + <trans-unit id="_msg1151"> <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source> - <context-group purpose="location"><context context-type="linenumber">341</context></context-group> + <context-group purpose="location"><context context-type="linenumber">337</context></context-group> </trans-unit> - <trans-unit id="_msg1135"> + <trans-unit id="_msg1152"> <source xml:space="preserve">Section [%s] is not recognized.</source> - <context-group purpose="location"><context context-type="linenumber">342</context></context-group> + <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg1136"> + <trans-unit id="_msg1153"> <source xml:space="preserve">Signing transaction failed</source> - <context-group purpose="location"><context context-type="linenumber">345</context></context-group> + <context-group purpose="location"><context context-type="linenumber">341</context></context-group> </trans-unit> - <trans-unit id="_msg1137"> + <trans-unit id="_msg1154"> <source xml:space="preserve">Specified -walletdir "%s" does not exist</source> - <context-group purpose="location"><context context-type="linenumber">346</context></context-group> + <context-group purpose="location"><context context-type="linenumber">342</context></context-group> </trans-unit> - <trans-unit id="_msg1138"> + <trans-unit id="_msg1155"> <source xml:space="preserve">Specified -walletdir "%s" is a relative path</source> - <context-group purpose="location"><context context-type="linenumber">347</context></context-group> + <context-group purpose="location"><context context-type="linenumber">343</context></context-group> </trans-unit> - <trans-unit id="_msg1139"> + <trans-unit id="_msg1156"> <source xml:space="preserve">Specified -walletdir "%s" is not a directory</source> - <context-group purpose="location"><context context-type="linenumber">348</context></context-group> + <context-group purpose="location"><context context-type="linenumber">344</context></context-group> </trans-unit> - <trans-unit id="_msg1140"> + <trans-unit id="_msg1157"> <source xml:space="preserve">Specified blocks directory "%s" does not exist.</source> - <context-group purpose="location"><context context-type="linenumber">349</context></context-group> + <context-group purpose="location"><context context-type="linenumber">345</context></context-group> </trans-unit> - <trans-unit id="_msg1141"> + <trans-unit id="_msg1158"> <source xml:space="preserve">Specified data directory "%s" does not exist.</source> - <context-group purpose="location"><context context-type="linenumber">350</context></context-group> + <context-group purpose="location"><context context-type="linenumber">346</context></context-group> </trans-unit> - <trans-unit id="_msg1142"> + <trans-unit id="_msg1159"> <source xml:space="preserve">Starting network threads…</source> - <context-group purpose="location"><context context-type="linenumber">351</context></context-group> + <context-group purpose="location"><context context-type="linenumber">347</context></context-group> </trans-unit> - <trans-unit id="_msg1143"> + <trans-unit id="_msg1160"> <source xml:space="preserve">The source code is available from %s.</source> - <context-group purpose="location"><context context-type="linenumber">352</context></context-group> + <context-group purpose="location"><context context-type="linenumber">348</context></context-group> </trans-unit> - <trans-unit id="_msg1144"> + <trans-unit id="_msg1161"> <source xml:space="preserve">The specified config file %s does not exist</source> - <context-group purpose="location"><context context-type="linenumber">353</context></context-group> + <context-group purpose="location"><context context-type="linenumber">349</context></context-group> </trans-unit> - <trans-unit id="_msg1145"> + <trans-unit id="_msg1162"> <source xml:space="preserve">The transaction amount is too small to pay the fee</source> - <context-group purpose="location"><context context-type="linenumber">354</context></context-group> + <context-group purpose="location"><context context-type="linenumber">350</context></context-group> </trans-unit> - <trans-unit id="_msg1146"> + <trans-unit id="_msg1163"> <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source> - <context-group purpose="location"><context context-type="linenumber">355</context></context-group> + <context-group purpose="location"><context context-type="linenumber">351</context></context-group> </trans-unit> - <trans-unit id="_msg1147"> + <trans-unit id="_msg1164"> <source xml:space="preserve">This is experimental software.</source> - <context-group purpose="location"><context context-type="linenumber">356</context></context-group> + <context-group purpose="location"><context context-type="linenumber">352</context></context-group> </trans-unit> - <trans-unit id="_msg1148"> + <trans-unit id="_msg1165"> <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source> - <context-group purpose="location"><context context-type="linenumber">357</context></context-group> + <context-group purpose="location"><context context-type="linenumber">353</context></context-group> </trans-unit> - <trans-unit id="_msg1149"> + <trans-unit id="_msg1166"> <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source> - <context-group purpose="location"><context context-type="linenumber">358</context></context-group> + <context-group purpose="location"><context context-type="linenumber">354</context></context-group> </trans-unit> - <trans-unit id="_msg1150"> + <trans-unit id="_msg1167"> <source xml:space="preserve">Transaction amount too small</source> - <context-group purpose="location"><context context-type="linenumber">359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">355</context></context-group> </trans-unit> - <trans-unit id="_msg1151"> + <trans-unit id="_msg1168"> <source xml:space="preserve">Transaction amounts must not be negative</source> - <context-group purpose="location"><context context-type="linenumber">360</context></context-group> + <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> - <trans-unit id="_msg1152"> + <trans-unit id="_msg1169"> <source xml:space="preserve">Transaction change output index out of range</source> - <context-group purpose="location"><context context-type="linenumber">361</context></context-group> + <context-group purpose="location"><context context-type="linenumber">357</context></context-group> </trans-unit> - <trans-unit id="_msg1153"> + <trans-unit id="_msg1170"> <source xml:space="preserve">Transaction has too long of a mempool chain</source> - <context-group purpose="location"><context context-type="linenumber">362</context></context-group> + <context-group purpose="location"><context context-type="linenumber">358</context></context-group> </trans-unit> - <trans-unit id="_msg1154"> + <trans-unit id="_msg1171"> <source xml:space="preserve">Transaction must have at least one recipient</source> - <context-group purpose="location"><context context-type="linenumber">363</context></context-group> + <context-group purpose="location"><context context-type="linenumber">359</context></context-group> </trans-unit> - <trans-unit id="_msg1155"> + <trans-unit id="_msg1172"> <source xml:space="preserve">Transaction needs a change address, but we can't generate it.</source> - <context-group purpose="location"><context context-type="linenumber">364</context></context-group> + <context-group purpose="location"><context context-type="linenumber">360</context></context-group> </trans-unit> - <trans-unit id="_msg1156"> + <trans-unit id="_msg1173"> <source xml:space="preserve">Transaction too large</source> - <context-group purpose="location"><context context-type="linenumber">365</context></context-group> + <context-group purpose="location"><context context-type="linenumber">361</context></context-group> </trans-unit> - <trans-unit id="_msg1157"> + <trans-unit id="_msg1174"> <source xml:space="preserve">Unable to allocate memory for -maxsigcachesize: '%s' MiB</source> - <context-group purpose="location"><context context-type="linenumber">366</context></context-group> + <context-group purpose="location"><context context-type="linenumber">362</context></context-group> </trans-unit> - <trans-unit id="_msg1158"> + <trans-unit id="_msg1175"> <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source> - <context-group purpose="location"><context context-type="linenumber">367</context></context-group> + <context-group purpose="location"><context context-type="linenumber">363</context></context-group> </trans-unit> - <trans-unit id="_msg1159"> + <trans-unit id="_msg1176"> <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source> - <context-group purpose="location"><context context-type="linenumber">368</context></context-group> + <context-group purpose="location"><context context-type="linenumber">364</context></context-group> </trans-unit> - <trans-unit id="_msg1160"> + <trans-unit id="_msg1177"> <source xml:space="preserve">Unable to create the PID file '%s': %s</source> - <context-group purpose="location"><context context-type="linenumber">369</context></context-group> + <context-group purpose="location"><context context-type="linenumber">365</context></context-group> </trans-unit> - <trans-unit id="_msg1161"> + <trans-unit id="_msg1178"> <source xml:space="preserve">Unable to find UTXO for external input</source> - <context-group purpose="location"><context context-type="linenumber">370</context></context-group> + <context-group purpose="location"><context context-type="linenumber">366</context></context-group> </trans-unit> - <trans-unit id="_msg1162"> + <trans-unit id="_msg1179"> <source xml:space="preserve">Unable to generate initial keys</source> - <context-group purpose="location"><context context-type="linenumber">371</context></context-group> + <context-group purpose="location"><context context-type="linenumber">367</context></context-group> </trans-unit> - <trans-unit id="_msg1163"> + <trans-unit id="_msg1180"> <source xml:space="preserve">Unable to generate keys</source> - <context-group purpose="location"><context context-type="linenumber">372</context></context-group> + <context-group purpose="location"><context context-type="linenumber">368</context></context-group> </trans-unit> - <trans-unit id="_msg1164"> + <trans-unit id="_msg1181"> <source xml:space="preserve">Unable to open %s for writing</source> - <context-group purpose="location"><context context-type="linenumber">373</context></context-group> + <context-group purpose="location"><context context-type="linenumber">369</context></context-group> </trans-unit> - <trans-unit id="_msg1165"> + <trans-unit id="_msg1182"> <source xml:space="preserve">Unable to parse -maxuploadtarget: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">374</context></context-group> + <context-group purpose="location"><context context-type="linenumber">370</context></context-group> </trans-unit> - <trans-unit id="_msg1166"> + <trans-unit id="_msg1183"> <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source> - <context-group purpose="location"><context context-type="linenumber">375</context></context-group> + <context-group purpose="location"><context context-type="linenumber">371</context></context-group> </trans-unit> - <trans-unit id="_msg1167"> + <trans-unit id="_msg1184"> <source xml:space="preserve">Unable to unload the wallet before migrating</source> - <context-group purpose="location"><context context-type="linenumber">376</context></context-group> + <context-group purpose="location"><context context-type="linenumber">372</context></context-group> </trans-unit> - <trans-unit id="_msg1168"> + <trans-unit id="_msg1185"> <source xml:space="preserve">Unknown -blockfilterindex value %s.</source> - <context-group purpose="location"><context context-type="linenumber">377</context></context-group> + <context-group purpose="location"><context context-type="linenumber">373</context></context-group> </trans-unit> - <trans-unit id="_msg1169"> + <trans-unit id="_msg1186"> <source xml:space="preserve">Unknown address type '%s'</source> - <context-group purpose="location"><context context-type="linenumber">378</context></context-group> + <context-group purpose="location"><context context-type="linenumber">374</context></context-group> </trans-unit> - <trans-unit id="_msg1170"> + <trans-unit id="_msg1187"> <source xml:space="preserve">Unknown change type '%s'</source> - <context-group purpose="location"><context context-type="linenumber">379</context></context-group> + <context-group purpose="location"><context context-type="linenumber">375</context></context-group> </trans-unit> - <trans-unit id="_msg1171"> + <trans-unit id="_msg1188"> <source xml:space="preserve">Unknown network specified in -onlynet: '%s'</source> - <context-group purpose="location"><context context-type="linenumber">380</context></context-group> + <context-group purpose="location"><context context-type="linenumber">376</context></context-group> </trans-unit> - <trans-unit id="_msg1172"> + <trans-unit id="_msg1189"> <source xml:space="preserve">Unknown new rules activated (versionbit %i)</source> - <context-group purpose="location"><context context-type="linenumber">381</context></context-group> + <context-group purpose="location"><context context-type="linenumber">377</context></context-group> </trans-unit> - <trans-unit id="_msg1173"> + <trans-unit id="_msg1190"> <source xml:space="preserve">Unsupported global logging level %s=%s. Valid values: %s.</source> - <context-group purpose="location"><context context-type="linenumber">382</context></context-group> + <context-group purpose="location"><context context-type="linenumber">378</context></context-group> </trans-unit> - <trans-unit id="_msg1174"> + <trans-unit id="_msg1191"> <source xml:space="preserve">acceptstalefeeestimates is not supported on %s chain.</source> - <context-group purpose="location"><context context-type="linenumber">388</context></context-group> + <context-group purpose="location"><context context-type="linenumber">384</context></context-group> </trans-unit> - <trans-unit id="_msg1175"> + <trans-unit id="_msg1192"> <source xml:space="preserve">Unsupported logging category %s=%s.</source> - <context-group purpose="location"><context context-type="linenumber">383</context></context-group> + <context-group purpose="location"><context context-type="linenumber">379</context></context-group> </trans-unit> - <trans-unit id="_msg1176"> + <trans-unit id="_msg1193"> <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source> - <context-group purpose="location"><context context-type="linenumber">384</context></context-group> + <context-group purpose="location"><context context-type="linenumber">380</context></context-group> </trans-unit> - <trans-unit id="_msg1177"> + <trans-unit id="_msg1194"> <source xml:space="preserve">Verifying blocks…</source> - <context-group purpose="location"><context context-type="linenumber">385</context></context-group> + <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> - <trans-unit id="_msg1178"> + <trans-unit id="_msg1195"> <source xml:space="preserve">Verifying wallet(s)…</source> - <context-group purpose="location"><context context-type="linenumber">386</context></context-group> + <context-group purpose="location"><context context-type="linenumber">382</context></context-group> </trans-unit> - <trans-unit id="_msg1179"> + <trans-unit id="_msg1196"> <source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source> - <context-group purpose="location"><context context-type="linenumber">387</context></context-group> + <context-group purpose="location"><context context-type="linenumber">383</context></context-group> </trans-unit> - <trans-unit id="_msg1180"> + <trans-unit id="_msg1197"> <source xml:space="preserve">Settings file could not be read</source> - <context-group purpose="location"><context context-type="linenumber">343</context></context-group> + <context-group purpose="location"><context context-type="linenumber">339</context></context-group> </trans-unit> - <trans-unit id="_msg1181"> + <trans-unit id="_msg1198"> <source xml:space="preserve">Settings file could not be written</source> - <context-group purpose="location"><context context-type="linenumber">344</context></context-group> + <context-group purpose="location"><context context-type="linenumber">340</context></context-group> </trans-unit> </group> </body></file> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 998a4e5cbe..ceaa3ac46b 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -169,7 +169,7 @@ public: bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const WalletModel* wallet_model) { std::vector< std::vector<std::string> > stack; - stack.push_back(std::vector<std::string>()); + stack.emplace_back(); enum CmdParseState { @@ -197,7 +197,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes } // Make sure stack is not empty before adding something if (stack.empty()) { - stack.push_back(std::vector<std::string>()); + stack.emplace_back(); } stack.back().push_back(strArg); }; @@ -206,7 +206,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes if (nDepthInsideSensitive) { if (!--nDepthInsideSensitive) { assert(filter_begin_pos); - filter_ranges.push_back(std::make_pair(filter_begin_pos, chpos)); + filter_ranges.emplace_back(filter_begin_pos, chpos); filter_begin_pos = 0; } } @@ -306,7 +306,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes if (nDepthInsideSensitive) { ++nDepthInsideSensitive; } - stack.push_back(std::vector<std::string>()); + stack.emplace_back(); } // don't allow commands after executed commands on baselevel diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index ca2fa2d672..b1ef489cc3 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -250,15 +250,14 @@ void CreateWalletActivity::createWallet() std::string name = m_create_wallet_dialog->walletName().toStdString(); uint64_t flags = 0; + // Enable descriptors by default. + flags |= WALLET_FLAG_DESCRIPTORS; if (m_create_wallet_dialog->isDisablePrivateKeysChecked()) { flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS; } if (m_create_wallet_dialog->isMakeBlankWalletChecked()) { flags |= WALLET_FLAG_BLANK_WALLET; } - if (m_create_wallet_dialog->isDescriptorWalletChecked()) { - flags |= WALLET_FLAG_DESCRIPTORS; - } if (m_create_wallet_dialog->isExternalSignerChecked()) { flags |= WALLET_FLAG_EXTERNAL_SIGNER; } diff --git a/src/rest.cpp b/src/rest.cpp index ba149c1a9e..e094039366 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -798,7 +798,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std:: return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); txid.SetHex(strTxid); - vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput)); + vOutPoints.emplace_back(txid, (uint32_t)nOutput); } if (vOutPoints.size() > 0) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0f4941b40c..6d2b84cb6c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1455,7 +1455,7 @@ static RPCHelpMan getchaintips() } else if (block->nStatus & BLOCK_FAILED_MASK) { // This block or one of its ancestors is invalid. status = "invalid"; - } else if (!block->HaveTxsDownloaded()) { + } else if (!block->HaveNumChainTxs()) { // This block cannot be connected because full block data for it or one of its parents is missing. status = "headers-only"; } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -1930,7 +1930,7 @@ static RPCHelpMan getblockstats() // New feerate uses satoshis per virtual byte instead of per serialized byte CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0; if (do_feerate_percentiles) { - feerate_array.emplace_back(std::make_pair(feerate, weight)); + feerate_array.emplace_back(feerate, weight); } maxfeerate = std::max(maxfeerate, feerate); minfeerate = std::min(minfeerate, feerate); @@ -2667,7 +2667,7 @@ UniValue CreateUTXOSnapshot( tip->nHeight, tip->GetBlockHash().ToString(), fs::PathToString(path), fs::PathToString(temppath))); - SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx}; + SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count}; afile << metadata; @@ -2694,9 +2694,7 @@ UniValue CreateUTXOSnapshot( result.pushKV("base_height", tip->nHeight); result.pushKV("path", path.u8string()); result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString()); - // Cast required because univalue doesn't have serialization specified for - // `unsigned int`, nChainTx's type. - result.pushKV("nchaintx", uint64_t{tip->nChainTx}); + result.pushKV("nchaintx", tip->nChainTx); return result; } @@ -2707,7 +2705,7 @@ static RPCHelpMan loadtxoutset() "Load the serialized UTXO set from disk.\n" "Once this snapshot is loaded, its contents will be " "deserialized into a second chainstate data structure, which is then used to sync to " - "the network's tip under a security model very much like `assumevalid`. " + "the network's tip. " "Meanwhile, the original chainstate will complete the initial block download process in " "the background, eventually validating up to the block that the snapshot is based upon.\n\n" @@ -2739,6 +2737,7 @@ static RPCHelpMan loadtxoutset() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(request.params[0].get_str()))}; FILE* file{fsbridge::fopen(path, "rb")}; @@ -2753,14 +2752,16 @@ static RPCHelpMan loadtxoutset() afile >> metadata; uint256 base_blockhash = metadata.m_base_blockhash; + if (!chainman.GetParams().AssumeutxoForBlockhash(base_blockhash).has_value()) { + throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot, " + "assumeutxo block hash in snapshot metadata not recognized (%s)", base_blockhash.ToString())); + } int max_secs_to_wait_for_headers = 60 * 10; CBlockIndex* snapshot_start_block = nullptr; LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n", base_blockhash.ToString()); - ChainstateManager& chainman = *node.chainman; - while (max_secs_to_wait_for_headers > 0) { snapshot_start_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(base_blockhash)); @@ -2807,6 +2808,7 @@ const std::vector<RPCResult> RPCHelpForChainstate{ {RPCResult::Type::STR_HEX, "snapshot_blockhash", /*optional=*/true, "the base block of the snapshot this chainstate is based on, if any"}, {RPCResult::Type::NUM, "coins_db_cache_bytes", "size of the coinsdb cache"}, {RPCResult::Type::NUM, "coins_tip_cache_bytes", "size of the coinstip cache"}, + {RPCResult::Type::BOOL, "validated", "whether the chainstate is fully validated. True if all blocks in the chainstate were validated, false if the chain is based on a snapshot and the snapshot has not yet been validated."}, }; static RPCHelpMan getchainstates() @@ -2818,8 +2820,7 @@ return RPCHelpMan{ RPCResult{ RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "headers", "the number of headers seen so far"}, - {RPCResult::Type::OBJ, "normal", /*optional=*/true, "fully validated chainstate containing blocks this node has validated starting from the genesis block", RPCHelpForChainstate}, - {RPCResult::Type::OBJ, "snapshot", /*optional=*/true, "only present if an assumeutxo snapshot is loaded. Partially validated chainstate containing blocks this node has validated starting from the snapshot. After the snapshot is validated (when the 'normal' chainstate advances far enough to validate it), this chainstate will replace and become the 'normal' chainstate.", RPCHelpForChainstate}, + {RPCResult::Type::ARR, "chainstates", "list of the chainstates ordered by work, with the most-work (active) chainstate last", {{RPCResult::Type::OBJ, "", "", RPCHelpForChainstate},}}, } }, RPCExamples{ @@ -2831,10 +2832,9 @@ return RPCHelpMan{ LOCK(cs_main); UniValue obj(UniValue::VOBJ); - NodeContext& node = EnsureAnyNodeContext(request.context); - ChainstateManager& chainman = *node.chainman; + ChainstateManager& chainman = EnsureAnyChainman(request.context); - auto make_chain_data = [&](const Chainstate& cs) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + auto make_chain_data = [&](const Chainstate& cs, bool validated) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); UniValue data(UniValue::VOBJ); if (!cs.m_chain.Tip()) { @@ -2852,20 +2852,18 @@ return RPCHelpMan{ if (cs.m_from_snapshot_blockhash) { data.pushKV("snapshot_blockhash", cs.m_from_snapshot_blockhash->ToString()); } + data.pushKV("validated", validated); return data; }; - if (chainman.GetAll().size() > 1) { - for (Chainstate* chainstate : chainman.GetAll()) { - obj.pushKV( - chainstate->m_from_snapshot_blockhash ? "snapshot" : "normal", - make_chain_data(*chainstate)); - } - } else { - obj.pushKV("normal", make_chain_data(chainman.ActiveChainstate())); - } obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1); + const auto& chainstates = chainman.GetAll(); + UniValue obj_chainstates{UniValue::VARR}; + for (Chainstate* cs : chainstates) { + obj_chainstates.push_back(make_chain_data(*cs, !cs->m_from_snapshot_blockhash || chainstates.size() == 1)); + } + obj.pushKV("chainstates", std::move(obj_chainstates)); return obj; } }; diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 705608bd47..136969eb87 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -819,11 +819,11 @@ static RPCHelpMan savemempool() static RPCHelpMan submitpackage() { return RPCHelpMan{"submitpackage", - "Submit a package of raw transactions (serialized, hex-encoded) to local node (-regtest only).\n" + "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n" + "The package must consist of a child with its parents, and none of the parents may depend on one another.\n" "The package will be validated according to consensus and mempool policy rules. If all transactions pass, they will be accepted to mempool.\n" "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n" - "Warning: until package relay is in use, successful submission does not mean the transaction will propagate to other nodes on the network.\n" - "Currently, each transaction is broadcasted individually after submission, which means they must meet other nodes' feerate requirements alone.\n" + "Warning: successful submission does not mean the transactions will propagate throughout the network.\n" , { {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.", @@ -862,9 +862,6 @@ static RPCHelpMan submitpackage() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - if (Params().GetChainType() != ChainType::REGTEST) { - throw std::runtime_error("submitpackage is for regression testing (-regtest mode) only"); - } const UniValue raw_transactions = request.params[0].get_array(); if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) { throw JSONRPCError(RPC_INVALID_PARAMETER, @@ -881,6 +878,9 @@ static RPCHelpMan submitpackage() } txns.emplace_back(MakeTransactionRef(std::move(mtx))); } + if (!IsChildWithParentsTree(txns)) { + throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other."); + } NodeContext& node = EnsureAnyNodeContext(request.context); CTxMemPool& mempool = EnsureMemPool(node); @@ -983,7 +983,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t) {"blockchain", &getrawmempool}, {"blockchain", &importmempool}, {"blockchain", &savemempool}, - {"hidden", &submitpackage}, + {"rawtransactions", &submitpackage}, }; for (const auto& c : commands) { t.appendCommand(c.name, &c); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 96d06b6b9f..63788c3a03 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -585,8 +585,8 @@ static UniValue GetNetworksInfo() UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); obj.pushKV("name", GetNetworkName(network)); - obj.pushKV("limited", !IsReachable(network)); - obj.pushKV("reachable", IsReachable(network)); + obj.pushKV("limited", !g_reachable_nets.Contains(network)); + obj.pushKV("reachable", g_reachable_nets.Contains(network)); obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string()); obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); @@ -730,7 +730,7 @@ static RPCHelpMan setban() if (!isSubnet) { const std::optional<CNetAddr> addr{LookupHost(request.params[0].get_str(), false)}; if (addr.has_value()) { - netAddr = addr.value(); + netAddr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0})); } } else @@ -949,10 +949,7 @@ static RPCHelpMan addpeeraddress() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - NodeContext& node = EnsureAnyNodeContext(request.context); - if (!node.addrman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); - } + AddrMan& addrman = EnsureAnyAddrman(request.context); const std::string& addr_string{request.params[0].get_str()}; const auto port{request.params[1].getInt<uint16_t>()}; @@ -968,11 +965,11 @@ static RPCHelpMan addpeeraddress() address.nTime = Now<NodeSeconds>(); // The source address is set equal to the address. This is equivalent to the peer // announcing itself. - if (node.addrman->Add({address}, address)) { + if (addrman.Add({address}, address)) { success = true; if (tried) { // Attempt to move the address to the tried addresses table. - node.addrman->Good(address); + addrman.Good(address); } } } @@ -1033,50 +1030,40 @@ static RPCHelpMan sendmsgtopeer() static RPCHelpMan getaddrmaninfo() { - return RPCHelpMan{"getaddrmaninfo", - "\nProvides information about the node's address manager by returning the number of " - "addresses in the `new` and `tried` tables and their sum for all networks.\n" - "This RPC is for testing only.\n", - {}, - RPCResult{ - RPCResult::Type::OBJ_DYN, "", "json object with network type as keys", - { - {RPCResult::Type::OBJ, "network", "the network (" + Join(GetNetworkNames(), ", ") + ")", - { - {RPCResult::Type::NUM, "new", "number of addresses in the new table, which represent potential peers the node has discovered but hasn't yet successfully connected to."}, - {RPCResult::Type::NUM, "tried", "number of addresses in the tried table, which represent peers the node has successfully connected to in the past."}, - {RPCResult::Type::NUM, "total", "total number of addresses in both new/tried tables"}, - }}, - } - }, - RPCExamples{ - HelpExampleCli("getaddrmaninfo", "") - + HelpExampleRpc("getaddrmaninfo", "") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - NodeContext& node = EnsureAnyNodeContext(request.context); - if (!node.addrman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); - } - - UniValue ret(UniValue::VOBJ); - for (int n = 0; n < NET_MAX; ++n) { - enum Network network = static_cast<enum Network>(n); - if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue; - UniValue obj(UniValue::VOBJ); - obj.pushKV("new", node.addrman->Size(network, true)); - obj.pushKV("tried", node.addrman->Size(network, false)); - obj.pushKV("total", node.addrman->Size(network)); - ret.pushKV(GetNetworkName(network), obj); - } - UniValue obj(UniValue::VOBJ); - obj.pushKV("new", node.addrman->Size(std::nullopt, true)); - obj.pushKV("tried", node.addrman->Size(std::nullopt, false)); - obj.pushKV("total", node.addrman->Size()); - ret.pushKV("all_networks", obj); - return ret; - }, + return RPCHelpMan{ + "getaddrmaninfo", + "\nProvides information about the node's address manager by returning the number of " + "addresses in the `new` and `tried` tables and their sum for all networks.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ_DYN, "", "json object with network type as keys", { + {RPCResult::Type::OBJ, "network", "the network (" + Join(GetNetworkNames(), ", ") + ", all_networks)", { + {RPCResult::Type::NUM, "new", "number of addresses in the new table, which represent potential peers the node has discovered but hasn't yet successfully connected to."}, + {RPCResult::Type::NUM, "tried", "number of addresses in the tried table, which represent peers the node has successfully connected to in the past."}, + {RPCResult::Type::NUM, "total", "total number of addresses in both new/tried tables"}, + }}, + }}, + RPCExamples{HelpExampleCli("getaddrmaninfo", "") + HelpExampleRpc("getaddrmaninfo", "")}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + AddrMan& addrman = EnsureAnyAddrman(request.context); + + UniValue ret(UniValue::VOBJ); + for (int n = 0; n < NET_MAX; ++n) { + enum Network network = static_cast<enum Network>(n); + if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue; + UniValue obj(UniValue::VOBJ); + obj.pushKV("new", addrman.Size(network, true)); + obj.pushKV("tried", addrman.Size(network, false)); + obj.pushKV("total", addrman.Size(network)); + ret.pushKV(GetNetworkName(network), obj); + } + UniValue obj(UniValue::VOBJ); + obj.pushKV("new", addrman.Size(std::nullopt, true)); + obj.pushKV("tried", addrman.Size(std::nullopt, false)); + obj.pushKV("total", addrman.Size()); + ret.pushKV("all_networks", obj); + return ret; + }, }; } @@ -1135,14 +1122,11 @@ static RPCHelpMan getrawaddrman() + HelpExampleRpc("getrawaddrman", "") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - NodeContext& node = EnsureAnyNodeContext(request.context); - if (!node.addrman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); - } + AddrMan& addrman = EnsureAnyAddrman(request.context); UniValue ret(UniValue::VOBJ); - ret.pushKV("new", AddrmanTableToJSON(node.addrman->GetEntries(false))); - ret.pushKV("tried", AddrmanTableToJSON(node.addrman->GetEntries(true))); + ret.pushKV("new", AddrmanTableToJSON(addrman.GetEntries(false))); + ret.pushKV("tried", AddrmanTableToJSON(addrman.GetEntries(true))); return ret; }, }; @@ -1164,10 +1148,10 @@ void RegisterNetRPCCommands(CRPCTable& t) {"network", &clearbanned}, {"network", &setnetworkactive}, {"network", &getnodeaddresses}, + {"network", &getaddrmaninfo}, {"hidden", &addconnection}, {"hidden", &addpeeraddress}, {"hidden", &sendmsgtopeer}, - {"hidden", &getaddrmaninfo}, {"hidden", &getrawaddrman}, }; for (const auto& c : commands) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 31ca126862..16705b3ce2 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1581,10 +1581,10 @@ static RPCHelpMan createpsbt() PartiallySignedTransaction psbtx; psbtx.tx = rawTx; for (unsigned int i = 0; i < rawTx.vin.size(); ++i) { - psbtx.inputs.push_back(PSBTInput()); + psbtx.inputs.emplace_back(); } for (unsigned int i = 0; i < rawTx.vout.size(); ++i) { - psbtx.outputs.push_back(PSBTOutput()); + psbtx.outputs.emplace_back(); } // Serialize the PSBT @@ -1648,10 +1648,10 @@ static RPCHelpMan converttopsbt() PartiallySignedTransaction psbtx; psbtx.tx = tx; for (unsigned int i = 0; i < tx.vin.size(); ++i) { - psbtx.inputs.push_back(PSBTInput()); + psbtx.inputs.emplace_back(); } for (unsigned int i = 0; i < tx.vout.size(); ++i) { - psbtx.outputs.push_back(PSBTOutput()); + psbtx.outputs.emplace_back(); } // Serialize the PSBT diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index daf751111f..d3c5a19326 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -88,7 +88,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& vCommands.reserve(mapCommands.size()); for (const auto& entry : mapCommands) - vCommands.push_back(make_pair(entry.second.front()->category + entry.first, entry.second.front())); + vCommands.emplace_back(entry.second.front()->category + entry.first, entry.second.front()); sort(vCommands.begin(), vCommands.end()); JSONRPCRequest jreq = helpreq; diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp index 1d4afb3758..efd4a43c28 100644 --- a/src/rpc/server_util.cpp +++ b/src/rpc/server_util.cpp @@ -108,3 +108,16 @@ PeerManager& EnsurePeerman(const NodeContext& node) } return *node.peerman; } + +AddrMan& EnsureAddrman(const NodeContext& node) +{ + if (!node.addrman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); + } + return *node.addrman; +} + +AddrMan& EnsureAnyAddrman(const std::any& context) +{ + return EnsureAddrman(EnsureAnyNodeContext(context)); +} diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h index 9af9572431..a4a53166b4 100644 --- a/src/rpc/server_util.h +++ b/src/rpc/server_util.h @@ -7,6 +7,7 @@ #include <any> +class AddrMan; class ArgsManager; class CBlockPolicyEstimator; class CConnman; @@ -31,5 +32,7 @@ CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node); CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); CConnman& EnsureConnman(const node::NodeContext& node); PeerManager& EnsurePeerman(const node::NodeContext& node); +AddrMan& EnsureAddrman(const node::NodeContext& node); +AddrMan& EnsureAnyAddrman(const std::any& context); #endif // BITCOIN_RPC_SERVER_UTIL_H diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 4fab481b39..71005cfb6e 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -72,14 +72,34 @@ static bool verify_flags(unsigned int flags) static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, const unsigned char *txTo , unsigned int txToLen, + const UTXO *spentOutputs, unsigned int spentOutputsLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { if (!verify_flags(flags)) { return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS); } + + if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT && spentOutputs == nullptr) { + return set_error(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); + } + try { TxInputStream stream(PROTOCOL_VERSION, txTo, txToLen); CTransaction tx(deserialize, stream); + + std::vector<CTxOut> spent_outputs; + if (spentOutputs != nullptr) { + if (spentOutputsLen != tx.vin.size()) { + return set_error(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_MISMATCH); + } + for (size_t i = 0; i < spentOutputsLen; i++) { + CScript spk = CScript(spentOutputs[i].scriptPubKey, spentOutputs[i].scriptPubKey + spentOutputs[i].scriptPubKeySize); + const CAmount& value = spentOutputs[i].value; + CTxOut tx_out = CTxOut(value, spk); + spent_outputs.push_back(tx_out); + } + } + if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); if (GetSerializeSize(tx, PROTOCOL_VERSION) != txToLen) @@ -89,18 +109,34 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP set_error(err, bitcoinconsensus_ERR_OK); PrecomputedTransactionData txdata(tx); + + if (spentOutputs != nullptr && flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT) { + txdata.Init(tx, std::move(spent_outputs)); + } + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } } +int bitcoinconsensus_verify_script_with_spent_outputs(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, + const unsigned char *txTo , unsigned int txToLen, + const UTXO *spentOutputs, unsigned int spentOutputsLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) +{ + CAmount am(amount); + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err); +} + int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { CAmount am(amount); - return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); + UTXO *spentOutputs = nullptr; + unsigned int spentOutputsLen = 0; + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err); } @@ -113,7 +149,9 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i } CAmount am(0); - return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); + UTXO *spentOutputs = nullptr; + unsigned int spentOutputsLen = 0; + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, spentOutputs, spentOutputsLen, nIn, flags, err); } unsigned int bitcoinconsensus_version() diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index f2f2ff8686..a202b5ba06 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -31,7 +31,7 @@ extern "C" { #endif -#define BITCOINCONSENSUS_API_VER 1 +#define BITCOINCONSENSUS_API_VER 2 typedef enum bitcoinconsensus_error_t { @@ -41,6 +41,8 @@ typedef enum bitcoinconsensus_error_t bitcoinconsensus_ERR_TX_DESERIALIZE, bitcoinconsensus_ERR_AMOUNT_REQUIRED, bitcoinconsensus_ERR_INVALID_FLAGS, + bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED, + bitcoinconsensus_ERR_SPENT_OUTPUTS_MISMATCH } bitcoinconsensus_error; /** Script verification flags */ @@ -53,11 +55,19 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT = (1U << 17), // enable TAPROOT (BIPs 341 & 342) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY | - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS | + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT }; +typedef struct { + const unsigned char *scriptPubKey; + unsigned int scriptPubKeySize; + int64_t value; +} UTXO; + /// Returns 1 if the input nIn of the serialized transaction pointed to by /// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under /// the additional constraints specified by flags. @@ -70,6 +80,11 @@ EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); +EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_spent_outputs(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, + const unsigned char *txTo , unsigned int txToLen, + const UTXO *spentOutputs, unsigned int spentOutputsLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); + EXPORT_SYMBOL unsigned int bitcoinconsensus_version(); #ifdef __cplusplus diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 2f3f2c7a1d..7e62d75583 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -1114,16 +1114,33 @@ public: class ScriptMaker { //! Keys contained in the Miniscript (the evaluation of DescriptorImpl::m_pubkey_args). const std::vector<CPubKey>& m_keys; + //! The script context we're operating within (Tapscript or P2WSH). + const miniscript::MiniscriptContext m_script_ctx; + + //! Get the ripemd160(sha256()) hash of this key. + //! Any key that is valid in a descriptor serializes as 32 bytes within a Tapscript context. So we + //! must not hash the sign-bit byte in this case. + uint160 GetHash160(uint32_t key) const { + if (miniscript::IsTapscript(m_script_ctx)) { + return Hash160(XOnlyPubKey{m_keys[key]}); + } + return m_keys[key].GetID(); + } public: - ScriptMaker(const std::vector<CPubKey>& keys LIFETIMEBOUND) : m_keys(keys) {} + ScriptMaker(const std::vector<CPubKey>& keys LIFETIMEBOUND, const miniscript::MiniscriptContext script_ctx) : m_keys(keys), m_script_ctx{script_ctx} {} std::vector<unsigned char> ToPKBytes(uint32_t key) const { - return {m_keys[key].begin(), m_keys[key].end()}; + // In Tapscript keys always serialize as x-only, whether an x-only key was used in the descriptor or not. + if (!miniscript::IsTapscript(m_script_ctx)) { + return {m_keys[key].begin(), m_keys[key].end()}; + } + const XOnlyPubKey xonly_pubkey{m_keys[key]}; + return {xonly_pubkey.begin(), xonly_pubkey.end()}; } std::vector<unsigned char> ToPKHBytes(uint32_t key) const { - auto id = m_keys[key].GetID(); + auto id = GetHash160(key); return {id.begin(), id.end()}; } }; @@ -1164,8 +1181,15 @@ protected: std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& provider) const override { - for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key); - return Vector(m_node->ToScript(ScriptMaker(keys))); + const auto script_ctx{m_node->GetMsCtx()}; + for (const auto& key : keys) { + if (miniscript::IsTapscript(script_ctx)) { + provider.pubkeys.emplace(Hash160(XOnlyPubKey{key}), key); + } else { + provider.pubkeys.emplace(key.GetID(), key); + } + } + return Vector(m_node->ToScript(ScriptMaker(keys, script_ctx))); } public: @@ -1290,6 +1314,10 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S if (IsHex(str)) { std::vector<unsigned char> data = ParseHex(str); CPubKey pubkey(data); + if (pubkey.IsValid() && !pubkey.IsValidNonHybrid()) { + error = "Hybrid public keys are not allowed"; + return nullptr; + } if (pubkey.IsFullyValid()) { if (permit_uncompressed || pubkey.IsCompressed()) { return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false); @@ -1385,8 +1413,16 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c 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) +std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext ctx, const SigningProvider& provider) { + // Key cannot be hybrid + if (!pubkey.IsValidNonHybrid()) { + return nullptr; + } + // Uncompressed is only allowed in TOP and P2SH contexts + if (ctx != ParseScriptContext::TOP && ctx != ParseScriptContext::P2SH && !pubkey.IsCompressed()) { + return nullptr; + } std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, false); KeyOriginInfo info; if (provider.GetKeyOrigin(pubkey.GetID(), info)) { @@ -1397,9 +1433,7 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider) { - unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02}; - std::copy(xkey.begin(), xkey.end(), full_key + 1); - CPubKey pubkey(full_key); + CPubKey pubkey{xkey.GetEvenCorrespondingCPubKey()}; std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true); KeyOriginInfo info; if (provider.GetKeyOriginByXOnly(xkey, info)) { @@ -1422,18 +1456,32 @@ struct KeyParser { mutable std::vector<std::unique_ptr<PubkeyProvider>> m_keys; //! Used to detect key parsing errors within a Miniscript. mutable std::string m_key_parsing_error; + //! The script context we're operating within (Tapscript or P2WSH). + const miniscript::MiniscriptContext m_script_ctx; + //! The number of keys that were parsed before starting to parse this Miniscript descriptor. + uint32_t m_offset; - KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND) : m_out(out), m_in(in) {} + KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND, + miniscript::MiniscriptContext ctx, uint32_t offset = 0) + : m_out(out), m_in(in), m_script_ctx(ctx), m_offset(offset) {} bool KeyCompare(const Key& a, const Key& b) const { return *m_keys.at(a) < *m_keys.at(b); } + ParseScriptContext ParseContext() const { + switch (m_script_ctx) { + case miniscript::MiniscriptContext::P2WSH: return ParseScriptContext::P2WSH; + case miniscript::MiniscriptContext::TAPSCRIPT: return ParseScriptContext::P2TR; + } + assert(false); + } + template<typename I> std::optional<Key> FromString(I begin, I end) const { assert(m_out); Key key = m_keys.size(); - auto pk = ParsePubkey(key, {&*begin, &*end}, ParseScriptContext::P2WSH, *m_out, m_key_parsing_error); + auto pk = ParsePubkey(m_offset + key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error); if (!pk) return {}; m_keys.push_back(std::move(pk)); return key; @@ -1447,11 +1495,20 @@ struct KeyParser { template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const { assert(m_in); - CPubKey pubkey(begin, end); - if (pubkey.IsValid()) { - Key key = m_keys.size(); - m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in)); - return key; + Key key = m_keys.size(); + if (miniscript::IsTapscript(m_script_ctx) && end - begin == 32) { + XOnlyPubKey pubkey; + std::copy(begin, end, pubkey.begin()); + if (auto pubkey_provider = InferPubkey(pubkey.GetEvenCorrespondingCPubKey(), ParseContext(), *m_in)) { + m_keys.push_back(std::move(pubkey_provider)); + return key; + } + } else if (!miniscript::IsTapscript(m_script_ctx)) { + CPubKey pubkey(begin, end); + if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) { + m_keys.push_back(std::move(pubkey_provider)); + return key; + } } return {}; } @@ -1465,12 +1522,18 @@ struct KeyParser { CKeyID keyid(hash); CPubKey pubkey; if (m_in->GetPubKey(keyid, pubkey)) { - Key key = m_keys.size(); - m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in)); - return key; + if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) { + Key key = m_keys.size(); + m_keys.push_back(std::move(pubkey_provider)); + return key; + } } return {}; } + + miniscript::MiniscriptContext MsContext() const { + return m_script_ctx; + } }; /** Parse a script in a particular context. */ @@ -1496,8 +1559,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } ++key_exp_index; return std::make_unique<PKHDescriptor>(std::move(pubkey)); - } else if (Func("pkh", expr)) { - error = "Can only have pkh at top level, in sh(), or in wsh()"; + } else if (ctx != ParseScriptContext::P2TR && Func("pkh", expr)) { + // Under Taproot, always the Miniscript parser deal with it. + error = "Can only have pkh at top level, in sh(), wsh(), or in tr()"; return nullptr; } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { @@ -1710,11 +1774,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } // Process miniscript expressions. { - KeyParser parser(&out, nullptr); + const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT}; + KeyParser parser(/*out = */&out, /* in = */nullptr, /* ctx = */script_ctx, key_exp_index); auto node = miniscript::FromString(std::string(expr.begin(), expr.end()), parser); if (node) { - if (ctx != ParseScriptContext::P2WSH) { - error = "Miniscript expressions can only be used in wsh"; + if (ctx != ParseScriptContext::P2WSH && ctx != ParseScriptContext::P2TR) { + error = "Miniscript expressions can only be used in wsh or tr."; return nullptr; } if (parser.m_key_parsing_error != "") { @@ -1749,6 +1814,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const // A signature check is required for a miniscript to be sane. Therefore no sane miniscript // may have an empty list of public keys. CHECK_NONFATAL(!parser.m_keys.empty()); + key_exp_index += parser.m_keys.size(); return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node)); } } @@ -1795,8 +1861,8 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo if (txntype == TxoutType::PUBKEY && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) { CPubKey pubkey(data[0]); - if (pubkey.IsValid()) { - return std::make_unique<PKDescriptor>(InferPubkey(pubkey, ctx, provider)); + if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) { + return std::make_unique<PKDescriptor>(std::move(pubkey_provider)); } } if (txntype == TxoutType::PUBKEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) { @@ -1804,7 +1870,9 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CKeyID keyid(hash); CPubKey pubkey; if (provider.GetPubKey(keyid, pubkey)) { - return std::make_unique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider)); + if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) { + return std::make_unique<PKHDescriptor>(std::move(pubkey_provider)); + } } } if (txntype == TxoutType::WITNESS_V0_KEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) { @@ -1812,16 +1880,24 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo CKeyID keyid(hash); CPubKey pubkey; if (provider.GetPubKey(keyid, pubkey)) { - return std::make_unique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider)); + if (auto pubkey_provider = InferPubkey(pubkey, ParseScriptContext::P2WPKH, provider)) { + return std::make_unique<WPKHDescriptor>(std::move(pubkey_provider)); + } } } if (txntype == TxoutType::MULTISIG && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) { + bool ok = true; std::vector<std::unique_ptr<PubkeyProvider>> providers; for (size_t i = 1; i + 1 < data.size(); ++i) { CPubKey pubkey(data[i]); - providers.push_back(InferPubkey(pubkey, ctx, provider)); + if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) { + providers.push_back(std::move(pubkey_provider)); + } else { + ok = false; + break; + } } - return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers)); + if (ok) return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers)); } if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) { uint160 hash(data[0]); @@ -1882,8 +1958,9 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo } } - if (ctx == ParseScriptContext::P2WSH) { - KeyParser parser(nullptr, &provider); + if (ctx == ParseScriptContext::P2WSH || ctx == ParseScriptContext::P2TR) { + const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT}; + KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */script_ctx); auto node = miniscript::FromScript(script, parser); if (node && node->IsSane()) { return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node)); diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp index 19556a9775..344a81bdf0 100644 --- a/src/script/miniscript.cpp +++ b/src/script/miniscript.cpp @@ -6,6 +6,7 @@ #include <vector> #include <script/script.h> #include <script/miniscript.h> +#include <serialize.h> #include <assert.h> @@ -32,7 +33,8 @@ Type SanitizeType(Type e) { return e; } -Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) { +Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, + size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx) { // Sanity check on data if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) { assert(data_size == 32); @@ -44,7 +46,7 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty // Sanity check on k if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) { assert(k >= 1 && k < 0x80000000UL); - } else if (fragment == Fragment::MULTI) { + } else if (fragment == Fragment::MULTI || fragment == Fragment::MULTI_A) { assert(k >= 1 && k <= n_keys); } else if (fragment == Fragment::THRESH) { assert(k >= 1 && k <= n_subs); @@ -68,7 +70,11 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) { assert(n_keys == 1); } else if (fragment == Fragment::MULTI) { - assert(n_keys >= 1 && n_keys <= 20); + assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTISIG); + assert(!IsTapscript(ms_ctx)); + } else if (fragment == Fragment::MULTI_A) { + assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTI_A); + assert(IsTapscript(ms_ctx)); } else { assert(n_keys == 0); } @@ -113,7 +119,8 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty "e"_mst.If(x << "f"_mst) | // e=f_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "ms"_mst) | // m=m_x, s=s_x - // NOTE: 'd:' is not 'u' under P2WSH as MINIMALIF is only a policy rule there. + // NOTE: 'd:' is 'u' under Tapscript but not P2WSH as MINIMALIF is only a policy rule there. + "u"_mst.If(IsTapscript(ms_ctx)) | "ndx"_mst; // n, d, x case Fragment::WRAP_V: return "V"_mst.If(x << "B"_mst) | // V=B_x @@ -210,7 +217,12 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty ((x << "h"_mst) && (y << "g"_mst)) || ((x << "i"_mst) && (y << "j"_mst)) || ((x << "j"_mst) && (y << "i"_mst)))); // k=k_x*k_y*k_z* !(g_x*h_y + h_x*g_y + i_x*j_y + j_x*i_y) - case Fragment::MULTI: return "Bnudemsk"_mst; + case Fragment::MULTI: { + return "Bnudemsk"_mst; + } + case Fragment::MULTI_A: { + return "Budemsk"_mst; + } case Fragment::THRESH: { bool all_e = true; bool all_m = true; @@ -246,11 +258,12 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty assert(false); } -size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) { +size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, + size_t n_keys, MiniscriptContext ms_ctx) { switch (fragment) { case Fragment::JUST_1: case Fragment::JUST_0: return 1; - case Fragment::PK_K: return 34; + case Fragment::PK_K: return IsTapscript(ms_ctx) ? 33 : 34; case Fragment::PK_H: return 3 + 21; case Fragment::OLDER: case Fragment::AFTER: return 1 + BuildScript(k).size(); @@ -259,6 +272,7 @@ size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_ case Fragment::HASH160: case Fragment::RIPEMD160: return 4 + 2 + 21; case Fragment::MULTI: return 1 + BuildScript(n_keys).size() + BuildScript(k).size() + 34 * n_keys; + case Fragment::MULTI_A: return (1 + 32 + 1) * n_keys + BuildScript(k).size() + 1; case Fragment::AND_V: return subsize; case Fragment::WRAP_V: return subsize + (sub0typ << "x"_mst); case Fragment::WRAP_S: @@ -372,9 +386,13 @@ std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script) // Decompose OP_EQUALVERIFY into OP_EQUAL OP_VERIFY out.emplace_back(OP_EQUAL, std::vector<unsigned char>()); opcode = OP_VERIFY; + } else if (opcode == OP_NUMEQUALVERIFY) { + // Decompose OP_NUMEQUALVERIFY into OP_NUMEQUAL OP_VERIFY + out.emplace_back(OP_NUMEQUAL, std::vector<unsigned char>()); + opcode = OP_VERIFY; } else if (IsPushdataOp(opcode)) { if (!CheckMinimalPush(push_data, opcode)) return {}; - } else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) { + } else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL || opcode == OP_NUMEQUAL) && (*it == OP_VERIFY)) { // Rule out non minimal VERIFY sequences return {}; } diff --git a/src/script/miniscript.h b/src/script/miniscript.h index 4c6bd0bb1d..76b952350b 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -20,6 +20,7 @@ #include <primitives/transaction.h> #include <script/script.h> #include <span.h> +#include <util/check.h> #include <util/spanparsing.h> #include <util/strencodings.h> #include <util/string.h> @@ -44,8 +45,8 @@ namespace miniscript { * - When satisfied, pushes nothing. * - Cannot be dissatisfied. * - This can be obtained by adding an OP_VERIFY to a B, modifying the last opcode - * of a B to its -VERIFY version (only for OP_CHECKSIG, OP_CHECKSIGVERIFY - * and OP_EQUAL), or by combining a V fragment under some conditions. + * of a B to its -VERIFY version (only for OP_CHECKSIG, OP_CHECKSIGVERIFY, + * OP_NUMEQUAL and OP_EQUAL), or by combining a V fragment under some conditions. * - For example vc:pk_k(key) = <key> OP_CHECKSIGVERIFY * - "K" Key: * - Takes its inputs from the top of the stack. @@ -216,7 +217,8 @@ enum class Fragment { OR_I, //!< OP_IF [X] OP_ELSE [Y] OP_ENDIF ANDOR, //!< [X] OP_NOTIF [Z] OP_ELSE [Y] OP_ENDIF THRESH, //!< [X1] ([Xn] OP_ADD)* [k] OP_EQUAL - MULTI, //!< [k] [key_n]* [n] OP_CHECKMULTISIG + MULTI, //!< [k] [key_n]* [n] OP_CHECKMULTISIG (only available within P2WSH context) + MULTI_A, //!< [key_0] OP_CHECKSIG ([key_n] OP_CHECKSIGADD)* [k] OP_NUMEQUAL (only within Tapscript ctx) // AND_N(X,Y) is represented as ANDOR(X,Y,0) // WRAP_T(X) is represented as AND_V(X,1) // WRAP_L(X) is represented as OR_I(0,X) @@ -229,13 +231,56 @@ enum class Availability { MAYBE, }; +enum class MiniscriptContext { + P2WSH, + TAPSCRIPT, +}; + +/** Whether the context Tapscript, ensuring the only other possibility is P2WSH. */ +constexpr bool IsTapscript(MiniscriptContext ms_ctx) +{ + switch (ms_ctx) { + case MiniscriptContext::P2WSH: return false; + case MiniscriptContext::TAPSCRIPT: return true; + } + assert(false); +} + namespace internal { +//! The maximum size of a witness item for a Miniscript under Tapscript context. (A BIP340 signature with a sighash type byte.) +static constexpr uint32_t MAX_TAPMINISCRIPT_STACK_ELEM_SIZE{65}; + +//! nVersion + nLockTime +constexpr uint32_t TX_OVERHEAD{4 + 4}; +//! prevout + nSequence + scriptSig +constexpr uint32_t TXIN_BYTES_NO_WITNESS{36 + 4 + 1}; +//! nValue + script len + OP_0 + pushdata 32. +constexpr uint32_t P2WSH_TXOUT_BYTES{8 + 1 + 1 + 33}; +//! Data other than the witness in a transaction. Overhead + vin count + one vin + vout count + one vout + segwit marker +constexpr uint32_t TX_BODY_LEEWAY_WEIGHT{(TX_OVERHEAD + GetSizeOfCompactSize(1) + TXIN_BYTES_NO_WITNESS + GetSizeOfCompactSize(1) + P2WSH_TXOUT_BYTES) * WITNESS_SCALE_FACTOR + 2}; +//! Maximum possible stack size to spend a Taproot output (excluding the script itself). +constexpr uint32_t MAX_TAPSCRIPT_SAT_SIZE{GetSizeOfCompactSize(MAX_STACK_SIZE) + (GetSizeOfCompactSize(MAX_TAPMINISCRIPT_STACK_ELEM_SIZE) + MAX_TAPMINISCRIPT_STACK_ELEM_SIZE) * MAX_STACK_SIZE + GetSizeOfCompactSize(TAPROOT_CONTROL_MAX_SIZE) + TAPROOT_CONTROL_MAX_SIZE}; +/** The maximum size of a script depending on the context. */ +constexpr uint32_t MaxScriptSize(MiniscriptContext ms_ctx) +{ + if (IsTapscript(ms_ctx)) { + // Leaf scripts under Tapscript are not explicitly limited in size. They are only implicitly + // bounded by the maximum standard size of a spending transaction. Let the maximum script + // size conservatively be small enough such that even a maximum sized witness and a reasonably + // sized spending transaction can spend an output paying to this script without running into + // the maximum standard tx size limit. + constexpr auto max_size{MAX_STANDARD_TX_WEIGHT - TX_BODY_LEEWAY_WEIGHT - MAX_TAPSCRIPT_SAT_SIZE}; + return max_size - GetSizeOfCompactSize(max_size); + } + return MAX_STANDARD_P2WSH_SCRIPT_SIZE; +} + //! Helper function for Node::CalcType. -Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys); +Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx); //! Helper function for Node::CalcScriptLen. -size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys); +size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx); //! A helper sanitizer/checker for the output of CalcType. Type SanitizeType(Type x); @@ -328,13 +373,112 @@ struct Ops { Ops(uint32_t in_count, MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : count(in_count), sat(in_sat), dsat(in_dsat) {}; }; +/** A data structure to help the calculation of stack size limits. + * + * Conceptually, every SatInfo object corresponds to a (possibly empty) set of script execution + * traces (sequences of opcodes). + * - SatInfo{} corresponds to the empty set. + * - SatInfo{n, e} corresponds to a single trace whose net effect is removing n elements from the + * stack (may be negative for a net increase), and reaches a maximum of e stack elements more + * than it ends with. + * - operator| is the union operation: (a | b) corresponds to the union of the traces in a and the + * traces in b. + * - operator+ is the concatenation operator: (a + b) corresponds to the set of traces formed by + * concatenating any trace in a with any trace in b. + * + * Its fields are: + * - valid is true if the set is non-empty. + * - netdiff (if valid) is the largest difference between stack size at the beginning and at the + * end of the script across all traces in the set. + * - exec (if valid) is the largest difference between stack size anywhere during execution and at + * the end of the script, across all traces in the set (note that this is not necessarily due + * to the same trace as the one that resulted in the value for netdiff). + * + * This allows us to build up stack size limits for any script efficiently, by starting from the + * individual opcodes miniscripts correspond to, using concatenation to construct scripts, and + * using the union operation to choose between execution branches. Since any top-level script + * satisfaction ends with a single stack element, we know that for a full script: + * - netdiff+1 is the maximal initial stack size (relevant for P2WSH stack limits). + * - exec+1 is the maximal stack size reached during execution (relevant for P2TR stack limits). + * + * Mathematically, SatInfo forms a semiring: + * - operator| is the semiring addition operator, with identity SatInfo{}, and which is commutative + * and associative. + * - operator+ is the semiring multiplication operator, with identity SatInfo{0}, and which is + * associative. + * - operator+ is distributive over operator|, so (a + (b | c)) = (a+b | a+c). This means we do not + * need to actually materialize all possible full execution traces over the whole script (which + * may be exponential in the length of the script); instead we can use the union operation at the + * individual subexpression level, and concatenate the result with subexpressions before and + * after it. + * - It is not a commutative semiring, because a+b can differ from b+a. For example, "OP_1 OP_DROP" + * has exec=1, while "OP_DROP OP_1" has exec=0. + */ +struct SatInfo { + //! Whether a canonical satisfaction/dissatisfaction is possible at all. + const bool valid; + //! How much higher the stack size at start of execution can be compared to at the end. + const int32_t netdiff; + //! Mow much higher the stack size can be during execution compared to at the end. + const int32_t exec; + + /** Empty script set. */ + constexpr SatInfo() noexcept : valid(false), netdiff(0), exec(0) {} + + /** Script set with a single script in it, with specified netdiff and exec. */ + constexpr SatInfo(int32_t in_netdiff, int32_t in_exec) noexcept : + valid{true}, netdiff{in_netdiff}, exec{in_exec} {} + + /** Script set union. */ + constexpr friend SatInfo operator|(const SatInfo& a, const SatInfo& b) noexcept + { + // Union with an empty set is itself. + if (!a.valid) return b; + if (!b.valid) return a; + // Otherwise the netdiff and exec of the union is the maximum of the individual values. + return {std::max(a.netdiff, b.netdiff), std::max(a.exec, b.exec)}; + } + + /** Script set concatenation. */ + constexpr friend SatInfo operator+(const SatInfo& a, const SatInfo& b) noexcept + { + // Concatenation with an empty set yields an empty set. + if (!a.valid || !b.valid) return {}; + // Otherwise, the maximum stack size difference for the combined scripts is the sum of the + // netdiffs, and the maximum stack size difference anywhere is either b.exec (if the + // maximum occurred in b) or b.netdiff+a.exec (if the maximum occurred in a). + return {a.netdiff + b.netdiff, std::max(b.exec, b.netdiff + a.exec)}; + } + + /** The empty script. */ + static constexpr SatInfo Empty() noexcept { return {0, 0}; } + /** A script consisting of a single push opcode. */ + static constexpr SatInfo Push() noexcept { return {-1, 0}; } + /** A script consisting of a single hash opcode. */ + static constexpr SatInfo Hash() noexcept { return {0, 0}; } + /** A script consisting of just a repurposed nop (OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY). */ + static constexpr SatInfo Nop() noexcept { return {0, 0}; } + /** A script consisting of just OP_IF or OP_NOTIF. Note that OP_ELSE and OP_ENDIF have no stack effect. */ + static constexpr SatInfo If() noexcept { return {1, 1}; } + /** A script consisting of just a binary operator (OP_BOOLAND, OP_BOOLOR, OP_ADD). */ + static constexpr SatInfo BinaryOp() noexcept { return {1, 1}; } + + // Scripts for specific individual opcodes. + static constexpr SatInfo OP_DUP() noexcept { return {-1, 0}; } + static constexpr SatInfo OP_IFDUP(bool nonzero) noexcept { return {nonzero ? -1 : 0, 0}; } + static constexpr SatInfo OP_EQUALVERIFY() noexcept { return {2, 2}; } + static constexpr SatInfo OP_EQUAL() noexcept { return {1, 1}; } + static constexpr SatInfo OP_SIZE() noexcept { return {-1, 0}; } + static constexpr SatInfo OP_CHECKSIG() noexcept { return {1, 1}; } + static constexpr SatInfo OP_0NOTEQUAL() noexcept { return {0, 0}; } + static constexpr SatInfo OP_VERIFY() noexcept { return {1, 1}; } +}; + struct StackSize { - //! Maximum stack size to satisfy; - MaxInt<uint32_t> sat; - //! Maximum stack size to dissatisfy; - MaxInt<uint32_t> dsat; + const SatInfo sat, dsat; - StackSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {}; + constexpr StackSize(SatInfo in_sat, SatInfo in_dsat) noexcept : sat(in_sat), dsat(in_dsat) {}; + constexpr StackSize(SatInfo in_both) noexcept : sat(in_both), dsat(in_both) {}; }; struct WitnessSize { @@ -362,7 +506,22 @@ struct Node { //! The data bytes in this expression (only for HASH160/HASH256/SHA256/RIPEMD10). const std::vector<unsigned char> data; //! Subexpressions (for WRAP_*/AND_*/OR_*/ANDOR/THRESH) - const std::vector<NodeRef<Key>> subs; + mutable std::vector<NodeRef<Key>> subs; + //! The Script context for this node. Either P2WSH or Tapscript. + const MiniscriptContext m_script_ctx; + + /* Destroy the shared pointers iteratively to avoid a stack-overflow due to recursive calls + * to the subs' destructors. */ + ~Node() { + while (!subs.empty()) { + auto node = std::move(subs.back()); + subs.pop_back(); + while (!node->subs.empty()) { + subs.push_back(std::move(node->subs.back())); + node->subs.pop_back(); + } + } + } private: //! Cached ops counts. @@ -390,7 +549,7 @@ private: subsize += sub->ScriptSize(); } Type sub0type = subs.size() > 0 ? subs[0]->GetType() : ""_mst; - return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size()); + return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size(), m_script_ctx); } /* Apply a recursive algorithm to a Miniscript tree, without actual recursive calls. @@ -557,7 +716,7 @@ private: Type y = subs.size() > 1 ? subs[1]->GetType() : ""_mst; Type z = subs.size() > 2 ? subs[2]->GetType() : ""_mst; - return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size())); + return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size(), m_script_ctx)); } public: @@ -578,7 +737,8 @@ public: }; // The upward function computes for a node, given its followed-by-OP_VERIFY status // and the CScripts of its child nodes, the CScript of the node. - auto upfn = [&ctx](bool verify, const Node& node, Span<CScript> subs) -> CScript { + const bool is_tapscript{IsTapscript(m_script_ctx)}; + auto upfn = [&ctx, is_tapscript](bool verify, const Node& node, Span<CScript> subs) -> CScript { switch (node.fragment) { case Fragment::PK_K: return BuildScript(ctx.ToPKBytes(node.keys[0])); case Fragment::PK_H: return BuildScript(OP_DUP, OP_HASH160, ctx.ToPKHBytes(node.keys[0]), OP_EQUALVERIFY); @@ -611,12 +771,21 @@ public: case Fragment::OR_I: return BuildScript(OP_IF, subs[0], OP_ELSE, subs[1], OP_ENDIF); case Fragment::ANDOR: return BuildScript(std::move(subs[0]), OP_NOTIF, subs[2], OP_ELSE, subs[1], OP_ENDIF); case Fragment::MULTI: { + CHECK_NONFATAL(!is_tapscript); CScript script = BuildScript(node.k); for (const auto& key : node.keys) { script = BuildScript(std::move(script), ctx.ToPKBytes(key)); } return BuildScript(std::move(script), node.keys.size(), verify ? OP_CHECKMULTISIGVERIFY : OP_CHECKMULTISIG); } + case Fragment::MULTI_A: { + CHECK_NONFATAL(is_tapscript); + CScript script = BuildScript(ctx.ToPKBytes(*node.keys.begin()), OP_CHECKSIG); + for (auto it = node.keys.begin() + 1; it != node.keys.end(); ++it) { + script = BuildScript(std::move(script), ctx.ToPKBytes(*it), OP_CHECKSIGADD); + } + return BuildScript(std::move(script), node.k, verify ? OP_NUMEQUALVERIFY : OP_NUMEQUAL); + } case Fragment::THRESH: { CScript script = std::move(subs[0]); for (size_t i = 1; i < subs.size(); ++i) { @@ -646,7 +815,8 @@ public: }; // The upward function computes for a node, given whether its parent is a wrapper, // and the string representations of its child nodes, the string representation of the node. - auto upfn = [&ctx](bool wrapped, const Node& node, Span<std::string> subs) -> std::optional<std::string> { + const bool is_tapscript{IsTapscript(m_script_ctx)}; + auto upfn = [&ctx, is_tapscript](bool wrapped, const Node& node, Span<std::string> subs) -> std::optional<std::string> { std::string ret = wrapped ? ":" : ""; switch (node.fragment) { @@ -710,6 +880,7 @@ public: if (node.subs[2]->fragment == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")"; return std::move(ret) + "andor(" + std::move(subs[0]) + "," + std::move(subs[1]) + "," + std::move(subs[2]) + ")"; case Fragment::MULTI: { + CHECK_NONFATAL(!is_tapscript); auto str = std::move(ret) + "multi(" + ::ToString(node.k); for (const auto& key : node.keys) { auto key_str = ctx.ToString(key); @@ -718,6 +889,16 @@ public: } return std::move(str) + ")"; } + case Fragment::MULTI_A: { + CHECK_NONFATAL(is_tapscript); + auto str = std::move(ret) + "multi_a(" + ::ToString(node.k); + for (const auto& key : node.keys) { + auto key_str = ctx.ToString(key); + if (!key_str) return {}; + str += "," + std::move(*key_str); + } + return std::move(str) + ")"; + } case Fragment::THRESH: { auto str = std::move(ret) + "thresh(" + ::ToString(node.k); for (auto& sub : subs) { @@ -783,6 +964,7 @@ private: return {count, sat, dsat}; } case Fragment::MULTI: return {1, (uint32_t)keys.size(), (uint32_t)keys.size()}; + case Fragment::MULTI_A: return {(uint32_t)keys.size() + 1, 0, 0}; case Fragment::WRAP_S: case Fragment::WRAP_C: case Fragment::WRAP_N: return {1 + subs[0]->ops.count, subs[0]->ops.sat, subs[0]->ops.dsat}; @@ -808,63 +990,130 @@ private: } internal::StackSize CalcStackSize() const { + using namespace internal; switch (fragment) { - case Fragment::JUST_0: return {{}, 0}; - case Fragment::JUST_1: + case Fragment::JUST_0: return {{}, SatInfo::Push()}; + case Fragment::JUST_1: return {SatInfo::Push(), {}}; case Fragment::OLDER: - case Fragment::AFTER: return {0, {}}; - case Fragment::PK_K: return {1, 1}; - case Fragment::PK_H: return {2, 2}; + case Fragment::AFTER: return {SatInfo::Push() + SatInfo::Nop(), {}}; + case Fragment::PK_K: return {SatInfo::Push()}; + case Fragment::PK_H: return {SatInfo::OP_DUP() + SatInfo::Hash() + SatInfo::Push() + SatInfo::OP_EQUALVERIFY()}; case Fragment::SHA256: case Fragment::RIPEMD160: case Fragment::HASH256: - case Fragment::HASH160: return {1, {}}; + case Fragment::HASH160: return { + SatInfo::OP_SIZE() + SatInfo::Push() + SatInfo::OP_EQUALVERIFY() + SatInfo::Hash() + SatInfo::Push() + SatInfo::OP_EQUAL(), + {} + }; case Fragment::ANDOR: { - const auto sat{(subs[0]->ss.sat + subs[1]->ss.sat) | (subs[0]->ss.dsat + subs[2]->ss.sat)}; - const auto dsat{subs[0]->ss.dsat + subs[2]->ss.dsat}; - return {sat, dsat}; + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + const auto& z{subs[2]->ss}; + return { + (x.sat + SatInfo::If() + y.sat) | (x.dsat + SatInfo::If() + z.sat), + x.dsat + SatInfo::If() + z.dsat + }; + } + case Fragment::AND_V: { + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return {x.sat + y.sat, {}}; + } + case Fragment::AND_B: { + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return {x.sat + y.sat + SatInfo::BinaryOp(), x.dsat + y.dsat + SatInfo::BinaryOp()}; } - case Fragment::AND_V: return {subs[0]->ss.sat + subs[1]->ss.sat, {}}; - case Fragment::AND_B: return {subs[0]->ss.sat + subs[1]->ss.sat, subs[0]->ss.dsat + subs[1]->ss.dsat}; case Fragment::OR_B: { - const auto sat{(subs[0]->ss.dsat + subs[1]->ss.sat) | (subs[0]->ss.sat + subs[1]->ss.dsat)}; - const auto dsat{subs[0]->ss.dsat + subs[1]->ss.dsat}; - return {sat, dsat}; + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return { + ((x.sat + y.dsat) | (x.dsat + y.sat)) + SatInfo::BinaryOp(), + x.dsat + y.dsat + SatInfo::BinaryOp() + }; } - case Fragment::OR_C: return {subs[0]->ss.sat | (subs[0]->ss.dsat + subs[1]->ss.sat), {}}; - case Fragment::OR_D: return {subs[0]->ss.sat | (subs[0]->ss.dsat + subs[1]->ss.sat), subs[0]->ss.dsat + subs[1]->ss.dsat}; - case Fragment::OR_I: return {(subs[0]->ss.sat + 1) | (subs[1]->ss.sat + 1), (subs[0]->ss.dsat + 1) | (subs[1]->ss.dsat + 1)}; - case Fragment::MULTI: return {k + 1, k + 1}; + case Fragment::OR_C: { + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return {(x.sat + SatInfo::If()) | (x.dsat + SatInfo::If() + y.sat), {}}; + } + case Fragment::OR_D: { + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return { + (x.sat + SatInfo::OP_IFDUP(true) + SatInfo::If()) | (x.dsat + SatInfo::OP_IFDUP(false) + SatInfo::If() + y.sat), + x.dsat + SatInfo::OP_IFDUP(false) + SatInfo::If() + y.dsat + }; + } + case Fragment::OR_I: { + const auto& x{subs[0]->ss}; + const auto& y{subs[1]->ss}; + return {SatInfo::If() + (x.sat | y.sat), SatInfo::If() + (x.dsat | y.dsat)}; + } + // multi(k, key1, key2, ..., key_n) starts off with k+1 stack elements (a 0, plus k + // signatures), then reaches n+k+3 stack elements after pushing the n keys, plus k and + // n itself, and ends with 1 stack element (success or failure). Thus, it net removes + // k elements (from k+1 to 1), while reaching k+n+2 more than it ends with. + case Fragment::MULTI: return {SatInfo(k, k + keys.size() + 2)}; + // multi_a(k, key1, key2, ..., key_n) starts off with n stack elements (the + // signatures), reaches 1 more (after the first key push), and ends with 1. Thus it net + // removes n-1 elements (from n to 1) while reaching n more than it ends with. + case Fragment::MULTI_A: return {SatInfo(keys.size() - 1, keys.size())}; case Fragment::WRAP_A: case Fragment::WRAP_N: - case Fragment::WRAP_S: - case Fragment::WRAP_C: return subs[0]->ss; - case Fragment::WRAP_D: return {1 + subs[0]->ss.sat, 1}; - case Fragment::WRAP_V: return {subs[0]->ss.sat, {}}; - case Fragment::WRAP_J: return {subs[0]->ss.sat, 1}; + case Fragment::WRAP_S: return subs[0]->ss; + case Fragment::WRAP_C: return { + subs[0]->ss.sat + SatInfo::OP_CHECKSIG(), + subs[0]->ss.dsat + SatInfo::OP_CHECKSIG() + }; + case Fragment::WRAP_D: return { + SatInfo::OP_DUP() + SatInfo::If() + subs[0]->ss.sat, + SatInfo::OP_DUP() + SatInfo::If() + }; + case Fragment::WRAP_V: return {subs[0]->ss.sat + SatInfo::OP_VERIFY(), {}}; + case Fragment::WRAP_J: return { + SatInfo::OP_SIZE() + SatInfo::OP_0NOTEQUAL() + SatInfo::If() + subs[0]->ss.sat, + SatInfo::OP_SIZE() + SatInfo::OP_0NOTEQUAL() + SatInfo::If() + }; case Fragment::THRESH: { - auto sats = Vector(internal::MaxInt<uint32_t>(0)); - for (const auto& sub : subs) { - auto next_sats = Vector(sats[0] + sub->ss.dsat); - for (size_t j = 1; j < sats.size(); ++j) next_sats.push_back((sats[j] + sub->ss.dsat) | (sats[j - 1] + sub->ss.sat)); - next_sats.push_back(sats[sats.size() - 1] + sub->ss.sat); + // sats[j] is the SatInfo corresponding to all traces reaching j satisfactions. + auto sats = Vector(SatInfo::Empty()); + for (size_t i = 0; i < subs.size(); ++i) { + // Loop over the subexpressions, processing them one by one. After adding + // element i we need to add OP_ADD (if i>0). + auto add = i ? SatInfo::BinaryOp() : SatInfo::Empty(); + // Construct a variable that will become the next sats, starting with index 0. + auto next_sats = Vector(sats[0] + subs[i]->ss.dsat + add); + // Then loop to construct next_sats[1..i]. + for (size_t j = 1; j < sats.size(); ++j) { + next_sats.push_back(((sats[j] + subs[i]->ss.dsat) | (sats[j - 1] + subs[i]->ss.sat)) + add); + } + // Finally construct next_sats[i+1]. + next_sats.push_back(sats[sats.size() - 1] + subs[i]->ss.sat + add); + // Switch over. sats = std::move(next_sats); } - assert(k <= sats.size()); - return {sats[k], sats[0]}; + // To satisfy thresh we need k satisfactions; to dissatisfy we need 0. In both + // cases a push of k and an OP_EQUAL follow. + return { + sats[k] + SatInfo::Push() + SatInfo::OP_EQUAL(), + sats[0] + SatInfo::Push() + SatInfo::OP_EQUAL() + }; } } assert(false); } internal::WitnessSize CalcWitnessSize() const { + const uint32_t sig_size = IsTapscript(m_script_ctx) ? 1 + 65 : 1 + 72; + const uint32_t pubkey_size = IsTapscript(m_script_ctx) ? 1 + 32 : 1 + 33; switch (fragment) { case Fragment::JUST_0: return {{}, 0}; case Fragment::JUST_1: case Fragment::OLDER: case Fragment::AFTER: return {0, {}}; - case Fragment::PK_K: return {1 + 72, 1}; - case Fragment::PK_H: return {1 + 72 + 1 + 33, 1 + 1 + 33}; + case Fragment::PK_K: return {sig_size, 1}; + case Fragment::PK_H: return {sig_size + pubkey_size, 1 + pubkey_size}; case Fragment::SHA256: case Fragment::RIPEMD160: case Fragment::HASH256: @@ -884,7 +1133,8 @@ private: case Fragment::OR_C: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), {}}; case Fragment::OR_D: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), subs[0]->ws.dsat + subs[1]->ws.dsat}; case Fragment::OR_I: return {(subs[0]->ws.sat + 1 + 1) | (subs[1]->ws.sat + 1), (subs[0]->ws.dsat + 1 + 1) | (subs[1]->ws.dsat + 1)}; - case Fragment::MULTI: return {k * (1 + 72) + 1, k + 1}; + case Fragment::MULTI: return {k * sig_size + 1, k + 1}; + case Fragment::MULTI_A: return {k * sig_size + static_cast<uint32_t>(keys.size()) - k, static_cast<uint32_t>(keys.size())}; case Fragment::WRAP_A: case Fragment::WRAP_N: case Fragment::WRAP_S: @@ -925,6 +1175,34 @@ private: Availability avail = ctx.Sign(node.keys[0], sig); return {ZERO + InputStack(key), (InputStack(std::move(sig)).SetWithSig() + InputStack(key)).SetAvailable(avail)}; } + case Fragment::MULTI_A: { + // sats[j] represents the best stack containing j valid signatures (out of the first i keys). + // In the loop below, these stacks are built up using a dynamic programming approach. + std::vector<InputStack> sats = Vector(EMPTY); + for (size_t i = 0; i < node.keys.size(); ++i) { + // Get the signature for the i'th key in reverse order (the signature for the first key needs to + // be at the top of the stack, contrary to CHECKMULTISIG's satisfaction). + std::vector<unsigned char> sig; + Availability avail = ctx.Sign(node.keys[node.keys.size() - 1 - i], sig); + // Compute signature stack for just this key. + auto sat = InputStack(std::move(sig)).SetWithSig().SetAvailable(avail); + // Compute the next sats vector: next_sats[0] is a copy of sats[0] (no signatures). All further + // next_sats[j] are equal to either the existing sats[j] + ZERO, or sats[j-1] plus a signature + // for the current (i'th) key. The very last element needs all signatures filled. + std::vector<InputStack> next_sats; + next_sats.push_back(sats[0] + ZERO); + for (size_t j = 1; j < sats.size(); ++j) next_sats.push_back((sats[j] + ZERO) | (std::move(sats[j - 1]) + sat)); + next_sats.push_back(std::move(sats[sats.size() - 1]) + std::move(sat)); + // Switch over. + sats = std::move(next_sats); + } + // The dissatisfaction consists of as many empty vectors as there are keys, which is the same as + // satisfying 0 keys. + auto& nsat{sats[0]}; + assert(node.k != 0); + assert(node.k <= sats.size()); + return {std::move(nsat), std::move(sats[node.k])}; + } case Fragment::MULTI: { // sats[j] represents the best stack containing j valid signatures (out of the first i keys). // In the loop below, these stacks are built up using a dynamic programming approach. @@ -1205,19 +1483,36 @@ public: //! Check the ops limit of this script against the consensus limit. bool CheckOpsLimit() const { + if (IsTapscript(m_script_ctx)) return true; if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT; return true; } - /** Return the maximum number of stack elements needed to satisfy this script non-malleably. - * This does not account for the P2WSH script push. */ + /** Whether this node is of type B, K or W. (That is, anything but V.) */ + bool IsBKW() const { + return !((GetType() & "BKW"_mst) == ""_mst); + } + + /** Return the maximum number of stack elements needed to satisfy this script non-malleably. */ std::optional<uint32_t> GetStackSize() const { if (!ss.sat.valid) return {}; - return ss.sat.value; + return ss.sat.netdiff + static_cast<int32_t>(IsBKW()); + } + + //! Return the maximum size of the stack during execution of this script. + std::optional<uint32_t> GetExecStackSize() const { + if (!ss.sat.valid) return {}; + return ss.sat.exec + static_cast<int32_t>(IsBKW()); } //! Check the maximum stack size for this script against the policy limit. bool CheckStackSize() const { + // Since in Tapscript there is no standardness limit on the script and witness sizes, we may run + // into the maximum stack size while executing the script. Make sure it doesn't happen. + if (IsTapscript(m_script_ctx)) { + if (const auto exec_ss = GetExecStackSize()) return exec_ss <= MAX_STACK_SIZE; + return true; + } if (const auto ss = GetStackSize()) return *ss <= MAX_STANDARD_P2WSH_STACK_ITEMS; return true; } @@ -1235,6 +1530,9 @@ public: //! Return the expression type. Type GetType() const { return typ; } + //! Return the script context for this node. + MiniscriptContext GetMsCtx() const { return m_script_ctx; } + //! Find an insane subnode which has no insane children. Nullptr if there is none. const Node* FindInsaneSub() const { return TreeEval<const Node*>([](const Node& node, Span<const Node*> subs) -> const Node* { @@ -1259,6 +1557,7 @@ public: case Fragment::PK_K: case Fragment::PK_H: case Fragment::MULTI: + case Fragment::MULTI_A: case Fragment::AFTER: case Fragment::OLDER: case Fragment::HASH256: @@ -1286,7 +1585,10 @@ public: } //! Check whether this node is valid at all. - bool IsValid() const { return !(GetType() == ""_mst) && ScriptSize() <= MAX_STANDARD_P2WSH_SCRIPT_SIZE; } + bool IsValid() const { + if (GetType() == ""_mst) return false; + return ScriptSize() <= internal::MaxScriptSize(m_script_ctx); + } //! Check whether this node is valid as a script on its own. bool IsValidTopLevel() const { return IsValid() && GetType() << "B"_mst; } @@ -1328,20 +1630,32 @@ public: bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; } // Constructors with various argument combinations, which bypass the duplicate key check. - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) + : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) + : fragment(nt), k(val), data(std::move(arg)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) + : fragment(nt), k(val), keys(std::move(key)), m_script_ctx{script_ctx}, subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) + : fragment(nt), k(val), keys(std::move(key)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) + : fragment(nt), k(val), subs(std::move(sub)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, MiniscriptContext script_ctx, Fragment nt, uint32_t val = 0) + : fragment(nt), k(val), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} // Constructors with various argument combinations, which do perform the duplicate key check. - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); } - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(arg), val) { DuplicateKeyCheck(ctx);} - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(key), val) { DuplicateKeyCheck(ctx); } - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(key), val) { DuplicateKeyCheck(ctx); } - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), val) { DuplicateKeyCheck(ctx); } - template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, val) { DuplicateKeyCheck(ctx); } + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); } + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(arg), val) { DuplicateKeyCheck(ctx);} + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), std::move(key), val) { DuplicateKeyCheck(ctx); } + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(key), val) { DuplicateKeyCheck(ctx); } + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), val) { DuplicateKeyCheck(ctx); } + template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) + : Node(internal::NoDupCheck{}, ctx.MsContext(), nt, val) { DuplicateKeyCheck(ctx); } }; namespace internal { @@ -1429,14 +1743,14 @@ std::optional<std::pair<std::vector<unsigned char>, int>> ParseHexStrEnd(Span<co /** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */ template<typename Key> -void BuildBack(Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false) +void BuildBack(const MiniscriptContext script_ctx, Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false) { NodeRef<Key> child = std::move(constructed.back()); constructed.pop_back(); if (reverse) { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, nt, Vector(std::move(child), std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(child), std::move(constructed.back()))); } else { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, nt, Vector(std::move(constructed.back()), std::move(child))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(constructed.back()), std::move(child))); } } @@ -1461,6 +1775,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) // (instead transforming another opcode into its VERIFY form). However, the v: wrapper has // to be interleaved with other fragments to be valid, so this is not a concern. size_t script_size{1}; + size_t max_size{internal::MaxScriptSize(ctx.MsContext())}; // The two integers are used to hold state for thresh() std::vector<std::tuple<ParseContext, int64_t, int64_t>> to_parse; @@ -1468,8 +1783,43 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1); + // Parses a multi() or multi_a() from its string representation. Returns false on parsing error. + const auto parse_multi_exp = [&](Span<const char>& in, const bool is_multi_a) -> bool { + const auto max_keys{is_multi_a ? MAX_PUBKEYS_PER_MULTI_A : MAX_PUBKEYS_PER_MULTISIG}; + const auto required_ctx{is_multi_a ? MiniscriptContext::TAPSCRIPT : MiniscriptContext::P2WSH}; + if (ctx.MsContext() != required_ctx) return false; + // Get threshold + int next_comma = FindNextChar(in, ','); + if (next_comma < 1) return false; + int64_t k; + if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return false; + in = in.subspan(next_comma + 1); + // Get keys. It is compatible for both compressed and x-only keys. + std::vector<Key> keys; + while (next_comma != -1) { + next_comma = FindNextChar(in, ','); + int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma; + if (key_length < 1) return false; + auto key = ctx.FromString(in.begin(), in.begin() + key_length); + if (!key) return false; + keys.push_back(std::move(*key)); + in = in.subspan(key_length + 1); + } + if (keys.size() < 1 || keys.size() > max_keys) return false; + if (k < 1 || k > (int64_t)keys.size()) return false; + if (is_multi_a) { + // (push + xonly-key + CHECKSIG[ADD]) * n + k + OP_NUMEQUAL(VERIFY), minus one. + script_size += (1 + 32 + 1) * keys.size() + BuildScript(k).size(); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI_A, std::move(keys), k)); + } else { + script_size += 2 + (keys.size() > 16) + (k > 16) + 34 * keys.size(); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI, std::move(keys), k)); + } + return true; + }; + while (!to_parse.empty()) { - if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script_size > max_size) return {}; // Get the current context we are decoding within auto [cur_context, n, k] = to_parse.back(); @@ -1488,7 +1838,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) // If there is no colon, this loop won't execute bool last_was_v{false}; for (size_t j = 0; colon_index && j < *colon_index; ++j) { - if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script_size > max_size) return {}; if (in[j] == 'a') { script_size += 2; to_parse.emplace_back(ParseContext::ALT, -1, -1); @@ -1521,7 +1871,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) } else if (in[j] == 'l') { // The l: wrapper is equivalent to or_i(0,X) script_size += 4; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_0)); to_parse.emplace_back(ParseContext::OR_I, -1, -1); } else { return {}; @@ -1534,63 +1884,63 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) } case ParseContext::EXPR: { if (Const("0", in)) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_0)); } else if (Const("1", in)) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_1)); } else if (Const("pk(", in)) { auto res = ParseKeyEnd<Key, Ctx>(in, ctx); if (!res) return {}; auto& [key, key_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(key)))))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_K, Vector(std::move(key)))))); in = in.subspan(key_size + 1); - script_size += 34; + script_size += IsTapscript(ctx.MsContext()) ? 33 : 34; } else if (Const("pkh(", in)) { auto res = ParseKeyEnd<Key>(in, ctx); if (!res) return {}; auto& [key, key_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(key)))))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_H, Vector(std::move(key)))))); in = in.subspan(key_size + 1); script_size += 24; } else if (Const("pk_k(", in)) { auto res = ParseKeyEnd<Key>(in, ctx); if (!res) return {}; auto& [key, key_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(key)))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_K, Vector(std::move(key)))); in = in.subspan(key_size + 1); - script_size += 33; + script_size += IsTapscript(ctx.MsContext()) ? 32 : 33; } else if (Const("pk_h(", in)) { auto res = ParseKeyEnd<Key>(in, ctx); if (!res) return {}; auto& [key, key_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(key)))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_H, Vector(std::move(key)))); in = in.subspan(key_size + 1); script_size += 23; } else if (Const("sha256(", in)) { auto res = ParseHexStrEnd(in, 32, ctx); if (!res) return {}; auto& [hash, hash_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::SHA256, std::move(hash))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::SHA256, std::move(hash))); in = in.subspan(hash_size + 1); script_size += 38; } else if (Const("ripemd160(", in)) { auto res = ParseHexStrEnd(in, 20, ctx); if (!res) return {}; auto& [hash, hash_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::RIPEMD160, std::move(hash))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::RIPEMD160, std::move(hash))); in = in.subspan(hash_size + 1); script_size += 26; } else if (Const("hash256(", in)) { auto res = ParseHexStrEnd(in, 32, ctx); if (!res) return {}; auto& [hash, hash_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH256, std::move(hash))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::HASH256, std::move(hash))); in = in.subspan(hash_size + 1); script_size += 38; } else if (Const("hash160(", in)) { auto res = ParseHexStrEnd(in, 20, ctx); if (!res) return {}; auto& [hash, hash_size] = *res; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH160, std::move(hash))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::HASH160, std::move(hash))); in = in.subspan(hash_size + 1); script_size += 26; } else if (Const("after(", in)) { @@ -1599,7 +1949,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) int64_t num; if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {}; if (num < 1 || num >= 0x80000000L) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AFTER, num)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, num)); in = in.subspan(arg_size + 1); script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff); } else if (Const("older(", in)) { @@ -1608,30 +1958,13 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) int64_t num; if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {}; if (num < 1 || num >= 0x80000000L) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OLDER, num)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, num)); in = in.subspan(arg_size + 1); script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff); } else if (Const("multi(", in)) { - // Get threshold - int next_comma = FindNextChar(in, ','); - if (next_comma < 1) return {}; - if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return {}; - in = in.subspan(next_comma + 1); - // Get keys - std::vector<Key> keys; - while (next_comma != -1) { - next_comma = FindNextChar(in, ','); - int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma; - if (key_length < 1) return {}; - auto key = ctx.FromString(in.begin(), in.begin() + key_length); - if (!key) return {}; - keys.push_back(std::move(*key)); - in = in.subspan(key_length + 1); - } - if (keys.size() < 1 || keys.size() > 20) return {}; - if (k < 1 || k > (int64_t)keys.size()) return {}; - script_size += 2 + (keys.size() > 16) + (k > 16) + 34 * keys.size(); - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::MULTI, std::move(keys), k)); + if (!parse_multi_exp(in, /* is_multi_a = */false)) return {}; + } else if (Const("multi_a(", in)) { + if (!parse_multi_exp(in, /* is_multi_a = */true)) return {}; } else if (Const("thresh(", in)) { int next_comma = FindNextChar(in, ','); if (next_comma < 1) return {}; @@ -1684,70 +2017,70 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) break; } case ParseContext::ALT: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_A, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_A, Vector(std::move(constructed.back()))); break; } case ParseContext::SWAP: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_S, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_S, Vector(std::move(constructed.back()))); break; } case ParseContext::CHECK: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_C, Vector(std::move(constructed.back()))); break; } case ParseContext::DUP_IF: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_D, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_D, Vector(std::move(constructed.back()))); break; } case ParseContext::NON_ZERO: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_J, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_J, Vector(std::move(constructed.back()))); break; } case ParseContext::ZERO_NOTEQUAL: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_N, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_N, Vector(std::move(constructed.back()))); break; } case ParseContext::VERIFY: { script_size += (constructed.back()->GetType() << "x"_mst); - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_V, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_V, Vector(std::move(constructed.back()))); break; } case ParseContext::WRAP_U: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_0))); break; } case ParseContext::WRAP_T: { - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_1))); break; } case ParseContext::AND_B: { - BuildBack(Fragment::AND_B, constructed); + BuildBack(ctx.MsContext(), Fragment::AND_B, constructed); break; } case ParseContext::AND_N: { auto mid = std::move(constructed.back()); constructed.pop_back(); - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(ctx, Fragment::JUST_0))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_0))); break; } case ParseContext::AND_V: { - BuildBack(Fragment::AND_V, constructed); + BuildBack(ctx.MsContext(), Fragment::AND_V, constructed); break; } case ParseContext::OR_B: { - BuildBack(Fragment::OR_B, constructed); + BuildBack(ctx.MsContext(), Fragment::OR_B, constructed); break; } case ParseContext::OR_C: { - BuildBack(Fragment::OR_C, constructed); + BuildBack(ctx.MsContext(), Fragment::OR_C, constructed); break; } case ParseContext::OR_D: { - BuildBack(Fragment::OR_D, constructed); + BuildBack(ctx.MsContext(), Fragment::OR_D, constructed); break; } case ParseContext::OR_I: { - BuildBack(Fragment::OR_I, constructed); + BuildBack(ctx.MsContext(), Fragment::OR_I, constructed); break; } case ParseContext::ANDOR: { @@ -1755,7 +2088,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) constructed.pop_back(); auto mid = std::move(constructed.back()); constructed.pop_back(); - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right))); break; } case ParseContext::THRESH: { @@ -1775,7 +2108,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) constructed.pop_back(); } std::reverse(subs.begin(), subs.end()); - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::THRESH, std::move(subs), k)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::THRESH, std::move(subs), k)); } else { return {}; } @@ -1808,8 +2141,8 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) * Construct a vector with one element per opcode in the script, in reverse order. * Each element is a pair consisting of the opcode, as well as the data pushed by * the opcode (including OP_n), if any. OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY, - * and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, OP_EQUAL - * respectively, plus OP_VERIFY. + * OP_NUMEQUALVERIFY and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, + * OP_EQUAL and OP_NUMEQUAL respectively, plus OP_VERIFY. */ std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script); @@ -1911,27 +2244,27 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) // Constants if (in[0].first == OP_1) { ++in; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_1)); break; } if (in[0].first == OP_0) { ++in; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::JUST_0)); break; } // Public keys - if (in[0].second.size() == 33) { + if (in[0].second.size() == 33 || in[0].second.size() == 32) { auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end()); if (!key) return {}; ++in; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(*key)))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_K, Vector(std::move(*key)))); break; } if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) { auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end()); if (!key) return {}; in += 5; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(*key)))); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::PK_H, Vector(std::move(*key)))); break; } // Time locks @@ -1939,37 +2272,38 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && (num = ParseScriptNumber(in[1]))) { in += 2; if (*num < 1 || *num > 0x7FFFFFFFL) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OLDER, *num)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, *num)); break; } if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && (num = ParseScriptNumber(in[1]))) { in += 2; if (num < 1 || num > 0x7FFFFFFFL) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AFTER, *num)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, *num)); break; } // Hashes if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && (num = ParseScriptNumber(in[5])) && num == 32 && in[6].first == OP_SIZE) { if (in[2].first == OP_SHA256 && in[1].second.size() == 32) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::SHA256, in[1].second)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::SHA256, in[1].second)); in += 7; break; } else if (in[2].first == OP_RIPEMD160 && in[1].second.size() == 20) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::RIPEMD160, in[1].second)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::RIPEMD160, in[1].second)); in += 7; break; } else if (in[2].first == OP_HASH256 && in[1].second.size() == 32) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH256, in[1].second)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::HASH256, in[1].second)); in += 7; break; } else if (in[2].first == OP_HASH160 && in[1].second.size() == 20) { - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH160, in[1].second)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::HASH160, in[1].second)); in += 7; break; } } // Multi if (last - in >= 3 && in[0].first == OP_CHECKMULTISIG) { + if (IsTapscript(ctx.MsContext())) return {}; std::vector<Key> keys; const auto n = ParseScriptNumber(in[1]); if (!n || last - in < 3 + *n) return {}; @@ -1984,7 +2318,37 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) if (!k || *k < 1 || *k > *n) return {}; in += 3 + *n; std::reverse(keys.begin(), keys.end()); - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::MULTI, std::move(keys), *k)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI, std::move(keys), *k)); + break; + } + // Tapscript's equivalent of multi + if (last - in >= 4 && in[0].first == OP_NUMEQUAL) { + if (!IsTapscript(ctx.MsContext())) return {}; + // The necessary threshold of signatures. + const auto k = ParseScriptNumber(in[1]); + if (!k) return {}; + if (*k < 1 || *k > MAX_PUBKEYS_PER_MULTI_A) return {}; + if (last - in < 2 + *k * 2) return {}; + std::vector<Key> keys; + keys.reserve(*k); + // Walk through the expected (pubkey, CHECKSIG[ADD]) pairs. + for (int pos = 2;; pos += 2) { + if (last - in < pos + 2) return {}; + // Make sure it's indeed an x-only pubkey and a CHECKSIG[ADD], then parse the key. + if (in[pos].first != OP_CHECKSIGADD && in[pos].first != OP_CHECKSIG) return {}; + if (in[pos + 1].second.size() != 32) return {}; + auto key = ctx.FromPKBytes(in[pos + 1].second.begin(), in[pos + 1].second.end()); + if (!key) return {}; + keys.push_back(std::move(*key)); + // Make sure early we don't parse an arbitrary large expression. + if (keys.size() > MAX_PUBKEYS_PER_MULTI_A) return {}; + // OP_CHECKSIG means it was the last one to parse. + if (in[pos].first == OP_CHECKSIG) break; + } + if (keys.size() < (size_t)*k) return {}; + in += 2 + keys.size() * 2; + std::reverse(keys.begin(), keys.end()); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI_A, std::move(keys), *k)); break; } /** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather @@ -2079,63 +2443,63 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) case DecodeContext::SWAP: { if (in >= last || in[0].first != OP_SWAP || constructed.empty()) return {}; ++in; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_S, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_S, Vector(std::move(constructed.back()))); break; } case DecodeContext::ALT: { if (in >= last || in[0].first != OP_TOALTSTACK || constructed.empty()) return {}; ++in; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_A, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_A, Vector(std::move(constructed.back()))); break; } case DecodeContext::CHECK: { if (constructed.empty()) return {}; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_C, Vector(std::move(constructed.back()))); break; } case DecodeContext::DUP_IF: { if (constructed.empty()) return {}; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_D, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_D, Vector(std::move(constructed.back()))); break; } case DecodeContext::VERIFY: { if (constructed.empty()) return {}; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_V, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_V, Vector(std::move(constructed.back()))); break; } case DecodeContext::NON_ZERO: { if (constructed.empty()) return {}; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_J, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_J, Vector(std::move(constructed.back()))); break; } case DecodeContext::ZERO_NOTEQUAL: { if (constructed.empty()) return {}; - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_N, Vector(std::move(constructed.back()))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::WRAP_N, Vector(std::move(constructed.back()))); break; } case DecodeContext::AND_V: { if (constructed.size() < 2) return {}; - BuildBack(Fragment::AND_V, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::AND_V, constructed, /*reverse=*/true); break; } case DecodeContext::AND_B: { if (constructed.size() < 2) return {}; - BuildBack(Fragment::AND_B, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::AND_B, constructed, /*reverse=*/true); break; } case DecodeContext::OR_B: { if (constructed.size() < 2) return {}; - BuildBack(Fragment::OR_B, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::OR_B, constructed, /*reverse=*/true); break; } case DecodeContext::OR_C: { if (constructed.size() < 2) return {}; - BuildBack(Fragment::OR_C, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::OR_C, constructed, /*reverse=*/true); break; } case DecodeContext::OR_D: { if (constructed.size() < 2) return {}; - BuildBack(Fragment::OR_D, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::OR_D, constructed, /*reverse=*/true); break; } case DecodeContext::ANDOR: { @@ -2145,7 +2509,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) NodeRef<Key> right = std::move(constructed.back()); constructed.pop_back(); NodeRef<Key> mid = std::move(constructed.back()); - constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right))); + constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right))); break; } case DecodeContext::THRESH_W: { @@ -2169,7 +2533,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) constructed.pop_back(); subs.push_back(std::move(sub)); } - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::THRESH, std::move(subs), k)); + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::THRESH, std::move(subs), k)); break; } case DecodeContext::ENDIF: { @@ -2219,7 +2583,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx) if (in >= last) return {}; if (in[0].first == OP_IF) { ++in; - BuildBack(Fragment::OR_I, constructed, /*reverse=*/true); + BuildBack(ctx.MsContext(), Fragment::OR_I, constructed, /*reverse=*/true); } else if (in[0].first == OP_NOTIF) { ++in; to_parse.emplace_back(DecodeContext::ANDOR, -1, -1); @@ -2252,7 +2616,7 @@ template<typename Ctx> inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) { using namespace internal; // A too large Script is necessarily invalid, don't bother parsing it. - if (script.size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script.size() > MaxScriptSize(ctx.MsContext())) return {}; auto decomposed = DecomposeScript(script); if (!decomposed) return {}; auto it = decomposed->begin(); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 92b7ad50b5..251a8420f7 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -114,12 +114,17 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd pubkey = it->second.first; return true; } - // Look for pubkey in pubkey list + // Look for pubkey in pubkey lists const auto& pk_it = sigdata.misc_pubkeys.find(address); if (pk_it != sigdata.misc_pubkeys.end()) { pubkey = pk_it->second.first; return true; } + const auto& tap_pk_it = sigdata.tap_pubkeys.find(address); + if (tap_pk_it != sigdata.tap_pubkeys.end()) { + pubkey = tap_pk_it->second.GetEvenCorrespondingCPubKey(); + return true; + } // Query the underlying provider return provider.GetPubKey(address, pubkey); } @@ -171,49 +176,158 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur return false; } -static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, Span<const unsigned char> script_bytes, std::vector<valtype>& result) +template<typename M, typename K, typename V> +miniscript::Availability MsLookupHelper(const M& map, const K& key, V& value) { - // Only BIP342 tapscript signing is supported for now. - if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false; - SigVersion sigversion = SigVersion::TAPSCRIPT; + auto it = map.find(key); + if (it != map.end()) { + value = it->second; + return miniscript::Availability::YES; + } + return miniscript::Availability::NO; +} - uint256 leaf_hash = ComputeTapleafHash(leaf_version, script_bytes); - CScript script = CScript(script_bytes.begin(), script_bytes.end()); +/** + * Context for solving a Miniscript. + * If enough material (access to keys, hash preimages, ..) is given, produces a valid satisfaction. + */ +template<typename Pk> +struct Satisfier { + using Key = Pk; - // <xonly pubkey> OP_CHECKSIG - if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) { - XOnlyPubKey pubkey{Span{script}.subspan(1, 32)}; - std::vector<unsigned char> sig; - if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) { - result = Vector(std::move(sig)); - return true; - } - return false; + const SigningProvider& m_provider; + SignatureData& m_sig_data; + const BaseSignatureCreator& m_creator; + const CScript& m_witness_script; + //! The context of the script we are satisfying (either P2WSH or Tapscript). + const miniscript::MiniscriptContext m_script_ctx; + + explicit Satisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND, + const BaseSignatureCreator& creator LIFETIMEBOUND, + const CScript& witscript LIFETIMEBOUND, + miniscript::MiniscriptContext script_ctx) : m_provider(provider), + m_sig_data(sig_data), + m_creator(creator), + m_witness_script(witscript), + m_script_ctx(script_ctx) {} + + static bool KeyCompare(const Key& a, const Key& b) { + return a < b; } - // multi_a scripts (<key> OP_CHECKSIG <key> OP_CHECKSIGADD <key> OP_CHECKSIGADD <k> OP_NUMEQUAL) - if (auto match = MatchMultiA(script)) { - std::vector<std::vector<unsigned char>> sigs; - int good_sigs = 0; - for (size_t i = 0; i < match->second.size(); ++i) { - XOnlyPubKey pubkey{*(match->second.rbegin() + i)}; - std::vector<unsigned char> sig; - bool good_sig = CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion); - if (good_sig && good_sigs < match->first) { - ++good_sigs; - sigs.push_back(std::move(sig)); - } else { - sigs.emplace_back(); - } + //! Get a CPubKey from a key hash. Note the key hash may be of an xonly pubkey. + template<typename I> + std::optional<CPubKey> CPubFromPKHBytes(I first, I last) const { + assert(last - first == 20); + CPubKey pubkey; + CKeyID key_id; + std::copy(first, last, key_id.begin()); + if (GetPubKey(m_provider, m_sig_data, key_id, pubkey)) return pubkey; + m_sig_data.missing_pubkeys.push_back(key_id); + return {}; + } + + //! Conversion to raw public key. + std::vector<unsigned char> ToPKBytes(const Key& key) const { return {key.begin(), key.end()}; } + + //! Time lock satisfactions. + bool CheckAfter(uint32_t value) const { return m_creator.Checker().CheckLockTime(CScriptNum(value)); } + bool CheckOlder(uint32_t value) const { return m_creator.Checker().CheckSequence(CScriptNum(value)); } + + //! Hash preimage satisfactions. + miniscript::Availability SatSHA256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { + return MsLookupHelper(m_sig_data.sha256_preimages, hash, preimage); + } + miniscript::Availability SatRIPEMD160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { + return MsLookupHelper(m_sig_data.ripemd160_preimages, hash, preimage); + } + miniscript::Availability SatHASH256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { + return MsLookupHelper(m_sig_data.hash256_preimages, hash, preimage); + } + miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { + return MsLookupHelper(m_sig_data.hash160_preimages, hash, preimage); + } + + miniscript::MiniscriptContext MsContext() const { + return m_script_ctx; + } +}; + +/** Miniscript satisfier specific to P2WSH context. */ +struct WshSatisfier: Satisfier<CPubKey> { + explicit WshSatisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND, + const BaseSignatureCreator& creator LIFETIMEBOUND, const CScript& witscript LIFETIMEBOUND) + : Satisfier(provider, sig_data, creator, witscript, miniscript::MiniscriptContext::P2WSH) {} + + //! Conversion from a raw compressed public key. + template <typename I> + std::optional<CPubKey> FromPKBytes(I first, I last) const { + CPubKey pubkey{first, last}; + if (pubkey.IsValid()) return pubkey; + return {}; + } + + //! Conversion from a raw compressed public key hash. + template<typename I> + std::optional<CPubKey> FromPKHBytes(I first, I last) const { + return Satisfier::CPubFromPKHBytes(first, last); + } + + //! Satisfy an ECDSA signature check. + miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { + if (CreateSig(m_creator, m_sig_data, m_provider, sig, key, m_witness_script, SigVersion::WITNESS_V0)) { + return miniscript::Availability::YES; } - if (good_sigs == match->first) { - result = std::move(sigs); - return true; + return miniscript::Availability::NO; + } +}; + +/** Miniscript satisfier specific to Tapscript context. */ +struct TapSatisfier: Satisfier<XOnlyPubKey> { + const uint256& m_leaf_hash; + + explicit TapSatisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND, + const BaseSignatureCreator& creator LIFETIMEBOUND, const CScript& script LIFETIMEBOUND, + const uint256& leaf_hash LIFETIMEBOUND) + : Satisfier(provider, sig_data, creator, script, miniscript::MiniscriptContext::TAPSCRIPT), + m_leaf_hash(leaf_hash) {} + + //! Conversion from a raw xonly public key. + template <typename I> + std::optional<XOnlyPubKey> FromPKBytes(I first, I last) const { + CHECK_NONFATAL(last - first == 32); + XOnlyPubKey pubkey; + std::copy(first, last, pubkey.begin()); + return pubkey; + } + + //! Conversion from a raw xonly public key hash. + template<typename I> + std::optional<XOnlyPubKey> FromPKHBytes(I first, I last) const { + if (auto pubkey = Satisfier::CPubFromPKHBytes(first, last)) return XOnlyPubKey{*pubkey}; + return {}; + } + + //! Satisfy a BIP340 signature check. + miniscript::Availability Sign(const XOnlyPubKey& key, std::vector<unsigned char>& sig) const { + if (CreateTaprootScriptSig(m_creator, m_sig_data, m_provider, sig, key, m_leaf_hash, SigVersion::TAPSCRIPT)) { + return miniscript::Availability::YES; } - return false; + return miniscript::Availability::NO; } +}; - return false; +static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, Span<const unsigned char> script_bytes, std::vector<valtype>& result) +{ + // Only BIP342 tapscript signing is supported for now. + if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false; + + uint256 leaf_hash = ComputeTapleafHash(leaf_version, script_bytes); + CScript script = CScript(script_bytes.begin(), script_bytes.end()); + + TapSatisfier ms_satisfier{provider, sigdata, creator, script, leaf_hash}; + const auto ms = miniscript::FromScript(script, ms_satisfier); + return ms && ms->Satisfy(ms_satisfier, result) == miniscript::Availability::YES; } static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result) @@ -319,7 +433,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::SCRIPTHASH: { uint160 h160{vSolutions[0]}; if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) { - ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); + ret.emplace_back(scriptRet.begin(), scriptRet.end()); return true; } // Could not find redeemScript, add to missing @@ -328,7 +442,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator } case TxoutType::MULTISIG: { size_t required = vSolutions.front()[0]; - ret.push_back(valtype()); // workaround CHECKMULTISIG bug + ret.emplace_back(); // workaround CHECKMULTISIG bug for (size_t i = 1; i < vSolutions.size() - 1; ++i) { CPubKey pubkey = CPubKey(vSolutions[i]); // We need to always call CreateSig in order to fill sigdata with all @@ -342,7 +456,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator } bool ok = ret.size() == required + 1; for (size_t i = 0; i + ret.size() < required + 1; ++i) { - ret.push_back(valtype()); + ret.emplace_back(); } return ok; } @@ -352,7 +466,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::WITNESS_V0_SCRIPTHASH: if (GetCScript(provider, sigdata, CScriptID{RIPEMD160(vSolutions[0])}, scriptRet)) { - ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); + ret.emplace_back(scriptRet.begin(), scriptRet.end()); return true; } // Could not find witnessScript, add to missing @@ -382,92 +496,6 @@ static CScript PushAll(const std::vector<valtype>& values) return result; } -template<typename M, typename K, typename V> -miniscript::Availability MsLookupHelper(const M& map, const K& key, V& value) -{ - auto it = map.find(key); - if (it != map.end()) { - value = it->second; - return miniscript::Availability::YES; - } - return miniscript::Availability::NO; -} - -/** - * Context for solving a Miniscript. - * If enough material (access to keys, hash preimages, ..) is given, produces a valid satisfaction. - */ -struct Satisfier { - typedef CPubKey Key; - - const SigningProvider& m_provider; - SignatureData& m_sig_data; - const BaseSignatureCreator& m_creator; - const CScript& m_witness_script; - - explicit Satisfier(const SigningProvider& provider LIFETIMEBOUND, SignatureData& sig_data LIFETIMEBOUND, - const BaseSignatureCreator& creator LIFETIMEBOUND, - const CScript& witscript LIFETIMEBOUND) : m_provider(provider), - m_sig_data(sig_data), - m_creator(creator), - m_witness_script(witscript) {} - - static bool KeyCompare(const Key& a, const Key& b) { - return a < b; - } - - //! Conversion from a raw public key. - template <typename I> - std::optional<Key> FromPKBytes(I first, I last) const - { - Key pubkey{first, last}; - if (pubkey.IsValid()) return pubkey; - return {}; - } - - //! Conversion from a raw public key hash. - template<typename I> - std::optional<Key> FromPKHBytes(I first, I last) const { - assert(last - first == 20); - Key pubkey; - CKeyID key_id; - std::copy(first, last, key_id.begin()); - if (GetPubKey(m_provider, m_sig_data, key_id, pubkey)) return pubkey; - m_sig_data.missing_pubkeys.push_back(key_id); - return {}; - } - - //! Conversion to raw public key. - std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; } - - //! Satisfy a signature check. - miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { - if (CreateSig(m_creator, m_sig_data, m_provider, sig, key, m_witness_script, SigVersion::WITNESS_V0)) { - return miniscript::Availability::YES; - } - return miniscript::Availability::NO; - } - - //! Time lock satisfactions. - bool CheckAfter(uint32_t value) const { return m_creator.Checker().CheckLockTime(CScriptNum(value)); } - bool CheckOlder(uint32_t value) const { return m_creator.Checker().CheckSequence(CScriptNum(value)); } - - - //! Hash preimage satisfactions. - miniscript::Availability SatSHA256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { - return MsLookupHelper(m_sig_data.sha256_preimages, hash, preimage); - } - miniscript::Availability SatRIPEMD160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { - return MsLookupHelper(m_sig_data.ripemd160_preimages, hash, preimage); - } - miniscript::Availability SatHASH256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { - return MsLookupHelper(m_sig_data.hash256_preimages, hash, preimage); - } - miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { - return MsLookupHelper(m_sig_data.hash160_preimages, hash, preimage); - } -}; - bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { if (sigdata.complete) return true; @@ -512,11 +540,11 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato // isn't fully solved. For instance the CHECKMULTISIG satisfaction in SignStep() pushes partial signatures // and the extractor relies on this behaviour to combine witnesses. if (!solved && result.empty()) { - Satisfier ms_satisfier{provider, sigdata, creator, witnessscript}; + WshSatisfier ms_satisfier{provider, sigdata, creator, witnessscript}; const auto ms = miniscript::FromScript(witnessscript, ms_satisfier); solved = ms && ms->Satisfy(ms_satisfier, result) == miniscript::Availability::YES; } - result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); + result.emplace_back(witnessscript.begin(), witnessscript.end()); sigdata.scriptWitness.stack = result; sigdata.witness = true; @@ -533,7 +561,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato if (!sigdata.witness) sigdata.scriptWitness.stack.clear(); if (P2SH) { - result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end())); + result.emplace_back(subscript.begin(), subscript.end()); } sigdata.scriptSig = PushAll(result); diff --git a/src/script/sign.h b/src/script/sign.h index 4d7dade44e..ace2ba7856 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -79,6 +79,7 @@ struct SignatureData { std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash. std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> taproot_misc_pubkeys; ///< Miscellaneous Taproot pubkeys involved in this input along with their leaf script hashes and key origin data. Also includes the Taproot internal key (may have no leaf script hashes). + std::map<CKeyID, XOnlyPubKey> tap_pubkeys; ///< Misc Taproot pubkeys involved in this input, by hash. (Equivalent of misc_pubkeys but for Taproot.) std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any) diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 168b3030cc..ff02ab5a12 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -368,6 +368,8 @@ TaprootBuilder& TaprootBuilder::Add(int depth, Span<const unsigned char> script, /* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */ NodeInfo node; node.hash = ComputeTapleafHash(leaf_version, script); + // due to bug in clang-tidy-17: + // NOLINTNEXTLINE(modernize-use-emplace) if (track) node.leaves.emplace_back(LeafInfo{std::vector<unsigned char>(script.begin(), script.end()), leaf_version, {}}); /* Insert into the branch. */ Insert(std::move(node), depth); @@ -569,7 +571,7 @@ std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> TaprootBui assert(leaf.merkle_branch.size() <= TAPROOT_CONTROL_MAX_NODE_COUNT); uint8_t depth = (uint8_t)leaf.merkle_branch.size(); uint8_t leaf_ver = (uint8_t)leaf.leaf_version; - tuples.push_back(std::make_tuple(depth, leaf_ver, leaf.script)); + tuples.emplace_back(depth, leaf_ver, leaf.script); } } return tuples; diff --git a/src/serialize.h b/src/serialize.h index e53ff9fa4c..8b15178ec0 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -316,7 +316,7 @@ template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t * size <= UINT_MAX -- 5 bytes (254 + 4 bytes) * size > UINT_MAX -- 9 bytes (255 + 8 bytes) */ -inline unsigned int GetSizeOfCompactSize(uint64_t nSize) +constexpr inline unsigned int GetSizeOfCompactSize(uint64_t nSize) { if (nSize < 253) return sizeof(unsigned char); else if (nSize <= std::numeric_limits<uint16_t>::max()) return sizeof(unsigned char) + sizeof(uint16_t); diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index b01ba81c5f..bfefc3ff97 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -98,8 +98,8 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Test: reset addrman and test AddrMan::Add multiple addresses works as expected addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)); std::vector<CAddress> vAddr; - vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE)); - vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE)); + vAddr.emplace_back(ResolveService("250.1.1.3", 8333), NODE_NONE); + vAddr.emplace_back(ResolveService("250.1.1.4", 8333), NODE_NONE); BOOST_CHECK(addrman->Add(vAddr, source)); BOOST_CHECK(addrman->Size() >= 1); } diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index fe3d8995e9..cbbec94e00 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -29,7 +29,7 @@ struct TestVector { explicit TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {} TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) { - vDerive.push_back(TestDerivation()); + vDerive.emplace_back(); TestDerivation &der = vDerive.back(); der.pub = pub; der.prv = prv; diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 3a30ef453e..f4f4e39f40 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -384,6 +384,54 @@ void Check(const std::string& prv, const std::string& pub, const std::string& no } } +void CheckInferDescriptor(const std::string& script_hex, const std::string& expected_desc, const std::vector<std::string>& hex_scripts, const std::vector<std::pair<std::string, std::string>>& origin_pubkeys) +{ + std::vector<unsigned char> script_bytes{ParseHex(script_hex)}; + const CScript& script{script_bytes.begin(), script_bytes.end()}; + + FlatSigningProvider provider; + for (const std::string& prov_script_hex : hex_scripts) { + std::vector<unsigned char> prov_script_bytes{ParseHex(prov_script_hex)}; + const CScript& prov_script{prov_script_bytes.begin(), prov_script_bytes.end()}; + provider.scripts.emplace(CScriptID(prov_script), prov_script); + } + for (const auto& [pubkey_hex, origin_str] : origin_pubkeys) { + CPubKey origin_pubkey{ParseHex(pubkey_hex)}; + provider.pubkeys.emplace(origin_pubkey.GetID(), origin_pubkey); + + if (!origin_str.empty()) { + using namespace spanparsing; + KeyOriginInfo info; + Span<const char> origin_sp{origin_str}; + std::vector<Span<const char>> origin_split = Split(origin_sp, "/"); + std::string fpr_str(origin_split[0].begin(), origin_split[0].end()); + auto fpr_bytes = ParseHex(fpr_str); + std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint); + for (size_t i = 1; i < origin_split.size(); ++i) { + Span<const char> elem = origin_split[i]; + bool hardened = false; + if (elem.size() > 0) { + const char last = elem[elem.size() - 1]; + if (last == '\'' || last == 'h') { + elem = elem.first(elem.size() - 1); + hardened = true; + } + } + uint32_t p; + assert(ParseUInt32(std::string(elem.begin(), elem.end()), &p)); + info.path.push_back(p | (((uint32_t)hardened) << 31)); + } + + provider.origins.emplace(origin_pubkey.GetID(), std::make_pair(origin_pubkey, info)); + } + } + + std::string checksum{GetDescriptorChecksum(expected_desc)}; + + std::unique_ptr<Descriptor> desc = InferDescriptor(script, provider); + BOOST_CHECK_EQUAL(desc->ToString(), expected_desc + "#" + checksum); +} + } BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup) @@ -409,6 +457,11 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "pk(): Uncompressed keys are not allowed"); // No uncompressed keys in witness CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness + // Equivalent single-key hybrid is not allowed + CheckUnparsable("", "combo(07a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(): Hybrid public keys are not allowed"); + CheckUnparsable("", "pk(0623542d61708e3fc48ba78fbe8fcc983ba94a520bc33f82b8e45e51dbc47af2726bcf181925eee1bdd868b109314f3ea92a6fc23d6b66057d3acfba04d6b08b58)", "pk(): Hybrid public keys are not allowed"); + CheckUnparsable("", "pkh(07a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(): Hybrid public keys are not allowed"); + // Some unconventional single-key constructions Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY); Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY); @@ -533,11 +586,15 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Invalid checksum CheckUnparsable("wsh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))#abcdef12", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))#abcdef12", "Provided checksum 'abcdef12' does not match computed checksum 'tyzp6a7p'"); - // Only p2wsh context is valid - CheckUnparsable("sh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh"); + // Only p2wsh or tr contexts are valid + CheckUnparsable("sh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); CheckUnparsable("tr(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "tr(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "tr(): key 'and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10))' is not valid"); - CheckUnparsable("raw(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh"); + CheckUnparsable("raw(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); CheckUnparsable("", "tr(034D2224bbbbbbbbbbcbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb40,{{{{{{{{{{{{{{{{{{{{{{multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008')", "'multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008'' is not a valid descriptor function"); + // No uncompressed keys allowed + CheckUnparsable("", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(049228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4))),after(10)))", "A function is needed within P2WSH"); + // No hybrid keys allowed + CheckUnparsable("", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4))),after(10)))", "A function is needed within P2WSH"); // Insane at top level CheckUnparsable("wsh(and_b(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "wsh(and_b(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "and_b(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)) is invalid"); // Invalid sub @@ -573,6 +630,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Same for hash256 Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + // Can have a Miniscript expression under tr() if it's alone. + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"512033982eebe204dc66508e4b19cfc31b5ffc6e1bfcbf6e5597dfc2521a52270795"}}, OutputType::BECH32M); + // Can have a pkh() expression alone as tr() script path (because pkh() is valid Miniscript). + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51201e9875f690f5847404e4c5951e2f029887df0525691ee11a682afd37b608aad4"}}, OutputType::BECH32M); + // Can have a Miniscript expression under tr() if it's part of a tree. + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL),pk(L3Enys1jFgTq4E24b8Uom1kAz6cNkz3Z82XZpBKCE2ztErq9fqvJ)},thresh(1,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5),pk(0dd6b52b192ab195558d22dd8437a9ec4519ee5ded496c0d55bc9b1a8b0e8c2b)},thresh(1,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{{pkh(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5),pk(0dd6b52b192ab195558d22dd8437a9ec4519ee5ded496c0d55bc9b1a8b0e8c2b)},thresh(1,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766))})", MISSING_PRIVKEYS | XONLY_KEYS, {{"5120d8ea39b29de2b550b68bd2ada8b075c888c2b2df3290c7a35856482747848934"}}, OutputType::BECH32M); + // Can have two Miniscripts in a Taproot with mixed private and public keys, and mixed ranged extended keys and raw keys. + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xprv9wCN7tTqN5ATsmBGEijuNeUgQjma9tv3GmdWLmbYiuArPsAMj6tD1uASiBfm47kdoi7bDBAVxUZNLM2MkeouPK5menDTyCNZtExQrKhVu7C/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xpub6ABiXPzjCSim6FFjLkGujnRQxmc4ZMdtdzZ79A1AHEhqGfVWGeCTZhUvZTSf1mNnGUtyNqgfE9eWaYdYReDKbPYqgqi9LLVZSmWnLQRx477/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(v:pk(xpub6AGbgdKcAGeUWaGNKH2o3sRvjtvJCGZ1NwrHqMJDwD4bN1QuwPQSsdeAYkPZGPt2FTAyu6nWGsC3fN2nsBELrLPcRNuwwr5k1X7yW5WV4aX/*),pk(02daf6e3477fc3906a1997820ed2940c8f5fa0942946d0368f981b001fdd85afcb)),and_v(v:pk(xpub6ABiXPzjCSim6FFjLkGujnRQxmc4ZMdtdzZ79A1AHEhqGfVWGeCTZhUvZTSf1mNnGUtyNqgfE9eWaYdYReDKbPYqgqi9LLVZSmWnLQRx477/*),pk(03272c0c1ae2c07528283b91ca57b45d2cc84e7960e1f17f58815372285f35e99a))})", MISSING_PRIVKEYS | XONLY_KEYS | RANGE | MIXED_PUBKEYS, {{"5120793185cd1a9a0bb710fa57df3845ac4ddf7df63b74beadce2573cbb0b508b3a4"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}, {0}}); + // Can sign for a Miniscript expression containing a hash challenge inside a Taproot tree. (Fails without the + // preimages and the sequence, passes with.) + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE | SIGNABLE_FAILS, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M); + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/42, /*preimages=*/{{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + + // Basic sh(pkh()) with key origin + CheckInferDescriptor("a9141a31ad23bf49c247dd531a623c2ef57da3c400c587", "sh(pkh([deadbeef/0h/0h/0]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", {"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}, {{"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", "deadbeef/0h/0h/0"}}); + // p2pk script with hybrid key must infer as raw() + CheckInferDescriptor("41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac", "raw(41069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4ac)", {}, {{"069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // p2pkh script with hybrid key must infer as addr() + CheckInferDescriptor("76a91445ff7c2327866472639d507334a9a00119dfd32688ac", "addr(17P7ge56F2QcdHxxRBa2NyzmejFggPwBJ9)", {}, {{"069228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // p2wpkh script with uncompressed key must infer as addr() + CheckInferDescriptor("001422e363a523947a110d9a9eb114820de183aca313", "addr(bc1qyt3k8ffrj3apzrv6n6c3fqsduxp6egcnk2r66j)", {}, {{"049228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4", ""}}); + // Infer pkh() from p2pkh with uncompressed key + CheckInferDescriptor("76a914a31725c74421fadc50d35520ab8751ed120af80588ac", "pkh(04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31)", {}, {{"04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31", ""}}); + // Infer pk() from p2pk with uncompressed key + CheckInferDescriptor("4104032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220ac", "pk(04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220)", {}, {{"04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220", ""}}); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp index 49b9898228..0363f317b6 100644 --- a/src/test/fuzz/chain.cpp +++ b/src/test/fuzz/chain.cpp @@ -29,7 +29,7 @@ FUZZ_TARGET(chain) (void)disk_block_index->GetBlockTimeMax(); (void)disk_block_index->GetMedianTimePast(); (void)disk_block_index->GetUndoPos(); - (void)disk_block_index->HaveTxsDownloaded(); + (void)disk_block_index->HaveNumChainTxs(); (void)disk_block_index->IsValid(); } diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp index deb0ed65ca..38a8c6798e 100644 --- a/src/test/fuzz/fees.cpp +++ b/src/test/fuzz/fees.cpp @@ -17,7 +17,8 @@ FUZZ_TARGET(fees) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CFeeRate minimal_incremental_fee{ConsumeMoney(fuzzed_data_provider)}; - FeeFilterRounder fee_filter_rounder{minimal_incremental_fee}; + FastRandomContext rng{/*fDeterministic=*/true}; + FeeFilterRounder fee_filter_rounder{minimal_incremental_fee, rng}; LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { const CAmount current_minimum_fee = ConsumeMoney(fuzzed_data_provider); const CAmount rounded_fee = fee_filter_rounder.round(current_minimum_fee); diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index e17e2bad60..2f53943c31 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -25,7 +25,7 @@ void initialize_miner() static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); g_setup = testing_setup.get(); for (uint32_t i = 0; i < uint32_t{100}; ++i) { - g_available_coins.push_back(COutPoint{uint256::ZERO, i}); + g_available_coins.emplace_back(uint256::ZERO, i); } } @@ -45,11 +45,11 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner) const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50); for (size_t n{0}; n < num_inputs; ++n) { auto prevout = available_coins.front(); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); available_coins.pop_front(); } for (uint32_t n{0}; n < num_outputs; ++n) { - mtx.vout.push_back(CTxOut(100, P2WSH_OP_TRUE)); + mtx.vout.emplace_back(100, P2WSH_OP_TRUE); } CTransactionRef tx = MakeTransactionRef(mtx); TestMemPoolEntryHelper entry; @@ -60,14 +60,14 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner) // All outputs are available to spend for (uint32_t n{0}; n < num_outputs; ++n) { if (fuzzed_data_provider.ConsumeBool()) { - available_coins.push_back(COutPoint{tx->GetHash(), n}); + available_coins.emplace_back(tx->GetHash(), n); } } if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) { // Add outpoint from this tx (may or not be spent by a later tx) - outpoints.push_back(COutPoint{tx->GetHash(), - (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size())}); + outpoints.emplace_back(tx->GetHash(), + (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size())); } else { // Add some random outpoint (will be interpreted as confirmed or not yet submitted // to mempool). @@ -123,11 +123,11 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(2, 5); for (size_t n{0}; n < num_inputs; ++n) { auto prevout = available_coins.at(0); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); available_coins.pop_front(); } for (uint32_t n{0}; n < num_outputs; ++n) { - mtx.vout.push_back(CTxOut(100, P2WSH_OP_TRUE)); + mtx.vout.emplace_back(100, P2WSH_OP_TRUE); } CTransactionRef tx = MakeTransactionRef(mtx); @@ -136,9 +136,9 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) // MiniMiner interprets spent coins as to-be-replaced and excludes them. for (uint32_t n{0}; n < num_outputs - 1; ++n) { if (fuzzed_data_provider.ConsumeBool()) { - available_coins.push_front(COutPoint{tx->GetHash(), n}); + available_coins.emplace_front(tx->GetHash(), n); } else { - available_coins.push_back(COutPoint{tx->GetHash(), n}); + available_coins.emplace_back(tx->GetHash(), n); } } diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 0246507da1..8c73edfa9d 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -7,6 +7,7 @@ #include <key.h> #include <script/miniscript.h> #include <script/script.h> +#include <script/signingprovider.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> @@ -14,6 +15,13 @@ namespace { +using Fragment = miniscript::Fragment; +using NodeRef = miniscript::NodeRef<CPubKey>; +using Node = miniscript::Node<CPubKey>; +using Type = miniscript::Type; +using MsCtx = miniscript::MiniscriptContext; +using miniscript::operator"" _mst; + //! Some pre-computed data for more efficient string roundtrips and to simulate challenges. struct TestData { typedef CPubKey Key; @@ -23,6 +31,7 @@ struct TestData { std::map<Key, int> dummy_key_idx_map; std::map<CKeyID, Key> dummy_keys_map; std::map<Key, std::pair<std::vector<unsigned char>, bool>> dummy_sigs; + std::map<XOnlyPubKey, std::pair<std::vector<unsigned char>, bool>> schnorr_sigs; // Precomputed hashes of each kind. std::vector<std::vector<unsigned char>> sha256; @@ -37,6 +46,11 @@ struct TestData { //! Set the precomputed data. void Init() { unsigned char keydata[32] = {1}; + // All our signatures sign (and are required to sign) this constant message. + auto const MESSAGE_HASH{uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065")}; + // We don't pass additional randomness when creating a schnorr signature. + auto const EMPTY_AUX{uint256S("")}; + for (size_t i = 0; i < 256; i++) { keydata[31] = i; CKey privkey; @@ -46,11 +60,18 @@ struct TestData { dummy_keys.push_back(pubkey); dummy_key_idx_map.emplace(pubkey, i); dummy_keys_map.insert({pubkey.GetID(), pubkey}); + XOnlyPubKey xonly_pubkey{pubkey}; + dummy_key_idx_map.emplace(xonly_pubkey, i); + uint160 xonly_hash{Hash160(xonly_pubkey)}; + dummy_keys_map.emplace(xonly_hash, pubkey); - std::vector<unsigned char> sig; - privkey.Sign(uint256S(""), sig); + std::vector<unsigned char> sig, schnorr_sig(64); + privkey.Sign(MESSAGE_HASH, sig); sig.push_back(1); // SIGHASH_ALL dummy_sigs.insert({pubkey, {sig, i & 1}}); + assert(privkey.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX)); + schnorr_sig.push_back(1); // Maximally-sized signature has sighash byte + schnorr_sigs.emplace(XOnlyPubKey{pubkey}, std::make_pair(std::move(schnorr_sig), i & 1)); std::vector<unsigned char> hash; hash.resize(32); @@ -70,6 +91,19 @@ struct TestData { if (i & 1) hash160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32); } } + + //! Get the (Schnorr or ECDSA, depending on context) signature for this pubkey. + const std::pair<std::vector<unsigned char>, bool>* GetSig(const MsCtx script_ctx, const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + const auto it = dummy_sigs.find(key); + if (it == dummy_sigs.end()) return nullptr; + return &it->second; + } else { + const auto it = schnorr_sigs.find(XOnlyPubKey{key}); + if (it == schnorr_sigs.end()) return nullptr; + return &it->second; + } + } } TEST_DATA; /** @@ -80,6 +114,10 @@ struct TestData { struct ParserContext { typedef CPubKey Key; + const MsCtx script_ctx; + + constexpr ParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {} + bool KeyCompare(const Key& a, const Key& b) const { return a < b; } @@ -92,14 +130,20 @@ struct ParserContext { return HexStr(Span{&idx, 1}); } - std::vector<unsigned char> ToPKBytes(const Key& key) const - { - return {key.begin(), key.end()}; + std::vector<unsigned char> ToPKBytes(const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + return {key.begin(), key.end()}; + } + const XOnlyPubKey xonly_pubkey{key}; + return {xonly_pubkey.begin(), xonly_pubkey.end()}; } - std::vector<unsigned char> ToPKHBytes(const Key& key) const - { - const auto h = Hash160(key); + std::vector<unsigned char> ToPKHBytes(const Key& key) const { + if (!miniscript::IsTapscript(script_ctx)) { + const auto h = Hash160(key); + return {h.begin(), h.end()}; + } + const auto h = Hash160(XOnlyPubKey{key}); return {h.begin(), h.end()}; } @@ -113,10 +157,15 @@ struct ParserContext { template<typename I> std::optional<Key> FromPKBytes(I first, I last) const { - CPubKey key; - key.Set(first, last); - if (!key.IsValid()) return {}; - return key; + if (!miniscript::IsTapscript(script_ctx)) { + Key key{first, last}; + if (key.IsValid()) return key; + return {}; + } + if (last - first != 32) return {}; + XOnlyPubKey xonly_pubkey; + std::copy(first, last, xonly_pubkey.begin()); + return xonly_pubkey.GetEvenCorrespondingCPubKey(); } template<typename I> @@ -128,10 +177,18 @@ struct ParserContext { if (it == TEST_DATA.dummy_keys_map.end()) return {}; return it->second; } -} PARSER_CTX; + + MsCtx MsContext() const { + return script_ctx; + } +}; //! Context that implements naive conversion from/to script only, for roundtrip testing. struct ScriptParserContext { + const MsCtx script_ctx; + + constexpr ScriptParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {} + //! For Script roundtrip we never need the key from a key hash. struct Key { bool is_hash; @@ -172,10 +229,17 @@ struct ScriptParserContext { key.is_hash = true; return key; } -} SCRIPT_PARSER_CONTEXT; + + MsCtx MsContext() const { + return script_ctx; + } +}; //! Context to produce a satisfaction for a Miniscript node using the pre-computed data. -struct SatisfierContext: ParserContext { +struct SatisfierContext : ParserContext { + + constexpr SatisfierContext(MsCtx ctx) noexcept : ParserContext(ctx) {} + // Timelock challenges satisfaction. Make the value (deterministically) vary to explore different // paths. bool CheckAfter(uint32_t value) const { return value % 2; } @@ -183,15 +247,11 @@ struct SatisfierContext: ParserContext { // Signature challenges fulfilled with a dummy signature, if it was one of our dummy keys. miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { - const auto it = TEST_DATA.dummy_sigs.find(key); - if (it == TEST_DATA.dummy_sigs.end()) return miniscript::Availability::NO; - if (it->second.second) { - // Key is "available" - sig = it->second.first; - return miniscript::Availability::YES; - } else { - return miniscript::Availability::NO; + bool sig_available{false}; + if (auto res = TEST_DATA.GetSig(script_ctx, key)) { + std::tie(sig, sig_available) = *res; } + return sig_available ? miniscript::Availability::YES : miniscript::Availability::NO; } //! Lookup generalization for all the hash satisfactions below @@ -215,12 +275,10 @@ struct SatisfierContext: ParserContext { miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return LookupHash(hash, preimage, TEST_DATA.hash160_preimages); } -} SATISFIER_CTX; +}; //! Context to check a satisfaction against the pre-computed data. -struct CheckerContext: BaseSignatureChecker { - TestData *test_data; - +const struct CheckerContext: BaseSignatureChecker { // Signature checker methods. Checks the right dummy signature is used. bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override @@ -230,12 +288,19 @@ struct CheckerContext: BaseSignatureChecker { if (it == TEST_DATA.dummy_sigs.end()) return false; return it->second.first == sig; } + bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion, + ScriptExecutionData&, ScriptError*) const override { + XOnlyPubKey pk{pubkey}; + auto it = TEST_DATA.schnorr_sigs.find(pk); + if (it == TEST_DATA.schnorr_sigs.end()) return false; + return it->second.first == sig; + } bool CheckLockTime(const CScriptNum& nLockTime) const override { return nLockTime.GetInt64() & 1; } bool CheckSequence(const CScriptNum& nSequence) const override { return nSequence.GetInt64() & 1; } } CHECKER_CTX; //! Context to check for duplicates when instancing a Node. -struct KeyComparator { +const struct KeyComparator { bool KeyCompare(const CPubKey& a, const CPubKey& b) const { return a < b; } @@ -244,11 +309,8 @@ struct KeyComparator { // A dummy scriptsig to pass to VerifyScript (we always use Segwit v0). const CScript DUMMY_SCRIPTSIG; -using Fragment = miniscript::Fragment; -using NodeRef = miniscript::NodeRef<CPubKey>; -using Node = miniscript::Node<CPubKey>; -using Type = miniscript::Type; -using miniscript::operator"" _mst; +//! Public key to be used as internal key for dummy Taproot spends. +const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; //! Construct a miniscript node as a shared_ptr. template<typename... Args> NodeRef MakeNodeRef(Args&&... args) { @@ -321,9 +383,10 @@ std::optional<uint32_t> ConsumeTimeLock(FuzzedDataProvider& provider) { * - For pk_k(), pk_h(), and all hashes, the next byte defines the index of the value in the test data. * - For multi(), the next 2 bytes define respectively the threshold and the number of keys. Then as many * bytes as the number of keys define the index of each key in the test data. + * - For multi_a(), same as for multi() but the threshold and the keys count are encoded on two bytes. * - For thresh(), the next byte defines the threshold value and the following one the number of subs. */ -std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type type_needed) { +std::optional<NodeInfo> ConsumeNodeStable(MsCtx script_ctx, FuzzedDataProvider& provider, Type type_needed) { bool allow_B = (type_needed == ""_mst) || (type_needed << "B"_mst); bool allow_K = (type_needed == ""_mst) || (type_needed << "K"_mst); bool allow_V = (type_needed == ""_mst) || (type_needed << "V"_mst); @@ -367,7 +430,7 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ if (!allow_B) return {}; return {{Fragment::HASH160, ConsumeHash160(provider)}}; case 10: { - if (!allow_B) return {}; + if (!allow_B || IsTapscript(script_ctx)) return {}; const auto k = provider.ConsumeIntegral<uint8_t>(); const auto n_keys = provider.ConsumeIntegral<uint8_t>(); if (n_keys > 20 || k == 0 || k > n_keys) return {}; @@ -428,6 +491,15 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ case 26: if (!allow_B) return {}; return {{{"B"_mst}, Fragment::WRAP_N}}; + case 27: { + if (!allow_B || !IsTapscript(script_ctx)) return {}; + const auto k = provider.ConsumeIntegral<uint16_t>(); + const auto n_keys = provider.ConsumeIntegral<uint16_t>(); + if (n_keys > 999 || k == 0 || k > n_keys) return {}; + std::vector<CPubKey> keys{n_keys}; + for (auto& key: keys) key = ConsumePubKey(provider); + return {{Fragment::MULTI_A, k, std::move(keys)}}; + } default: break; } @@ -444,10 +516,16 @@ std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type typ struct SmartInfo { using recipe = std::pair<Fragment, std::vector<Type>>; - std::map<Type, std::vector<recipe>> table; + std::map<Type, std::vector<recipe>> wsh_table, tap_table; void Init() { + Init(wsh_table, MsCtx::P2WSH); + Init(tap_table, MsCtx::TAPSCRIPT); + } + + void Init(std::map<Type, std::vector<recipe>>& table, MsCtx script_ctx) + { /* Construct a set of interesting type requirements to reason with (sections of BKVWzondu). */ std::vector<Type> types; for (int base = 0; base < 4; ++base) { /* select from B,K,V,W */ @@ -495,7 +573,7 @@ struct SmartInfo std::sort(types.begin(), types.end()); // Iterate over all possible fragments. - for (int fragidx = 0; fragidx <= int(Fragment::MULTI); ++fragidx) { + for (int fragidx = 0; fragidx <= int(Fragment::MULTI_A); ++fragidx) { int sub_count = 0; //!< The minimum number of child nodes this recipe has. int sub_range = 1; //!< The maximum number of child nodes for this recipe is sub_count+sub_range-1. size_t data_size = 0; @@ -503,6 +581,12 @@ struct SmartInfo uint32_t k = 0; Fragment frag{fragidx}; + // Only produce recipes valid in the given context. + if ((!miniscript::IsTapscript(script_ctx) && frag == Fragment::MULTI_A) + || (miniscript::IsTapscript(script_ctx) && frag == Fragment::MULTI)) { + continue; + } + // Based on the fragment, determine #subs/data/k/keys to pass to ComputeType. */ switch (frag) { case Fragment::PK_K: @@ -510,6 +594,7 @@ struct SmartInfo n_keys = 1; break; case Fragment::MULTI: + case Fragment::MULTI_A: n_keys = 1; k = 1; break; @@ -568,7 +653,7 @@ struct SmartInfo if (subs > 0) subt.push_back(x); if (subs > 1) subt.push_back(y); if (subs > 2) subt.push_back(z); - Type res = miniscript::internal::ComputeType(frag, x, y, z, subt, k, data_size, subs, n_keys); + Type res = miniscript::internal::ComputeType(frag, x, y, z, subt, k, data_size, subs, n_keys, script_ctx); // Continue if the result is not a valid node. if ((res << "K"_mst) + (res << "V"_mst) + (res << "B"_mst) + (res << "W"_mst) != 1) continue; @@ -685,10 +770,11 @@ struct SmartInfo * (as improvements to the tables or changes to the typing rules could invalidate * everything). */ -std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type_needed) { +std::optional<NodeInfo> ConsumeNodeSmart(MsCtx script_ctx, FuzzedDataProvider& provider, Type type_needed) { /** Table entry for the requested type. */ - auto recipes_it = SMARTINFO.table.find(type_needed); - assert(recipes_it != SMARTINFO.table.end()); + const auto& table{IsTapscript(script_ctx) ? SMARTINFO.tap_table : SMARTINFO.wsh_table}; + auto recipes_it = table.find(type_needed); + assert(recipes_it != table.end()); /** Pick one recipe from the available ones for that type. */ const auto& [frag, subt] = PickValue(provider, recipes_it->second); @@ -704,6 +790,13 @@ std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type for (auto& key: keys) key = ConsumePubKey(provider); return {{frag, k, std::move(keys)}}; } + case Fragment::MULTI_A: { + const auto n_keys = provider.ConsumeIntegralInRange<uint16_t>(1, 999); + const auto k = provider.ConsumeIntegralInRange<uint16_t>(1, n_keys); + std::vector<CPubKey> keys{n_keys}; + for (auto& key: keys) key = ConsumePubKey(provider); + return {{frag, k, std::move(keys)}}; + } case Fragment::OLDER: case Fragment::AFTER: return {{frag, provider.ConsumeIntegralInRange<uint32_t>(1, 0x7FFFFFF)}}; @@ -760,7 +853,7 @@ std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type * a NodeRef whose Type() matches the type fed to ConsumeNode. */ template<typename F> -NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { +NodeRef GenNode(MsCtx script_ctx, F ConsumeNode, Type root_type, bool strict_valid = false) { /** A stack of miniscript Nodes being built up. */ std::vector<NodeRef> stack; /** The queue of instructions. */ @@ -781,7 +874,8 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { // Update predicted resource limits. Since every leaf Miniscript node is at least one // byte long, we move one byte from each child to their parent. A similar technique is // used in the miniscript::internal::Parse function to prevent runaway string parsing. - scriptsize += miniscript::internal::ComputeScriptLen(node_info->fragment, ""_mst, node_info->subtypes.size(), node_info->k, node_info->subtypes.size(), node_info->keys.size()) - 1; + scriptsize += miniscript::internal::ComputeScriptLen(node_info->fragment, ""_mst, node_info->subtypes.size(), node_info->k, node_info->subtypes.size(), + node_info->keys.size(), script_ctx) - 1; if (scriptsize > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; switch (node_info->fragment) { case Fragment::JUST_0: @@ -826,6 +920,9 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { case Fragment::MULTI: ops += 1; break; + case Fragment::MULTI_A: + ops += node_info->keys.size() + 1; + break; case Fragment::WRAP_A: ops += 2; break; @@ -874,11 +971,11 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { // Construct new NodeRef. NodeRef node; if (info.keys.empty()) { - node = MakeNodeRef(info.fragment, std::move(sub), std::move(info.hash), info.k); + node = MakeNodeRef(script_ctx, info.fragment, std::move(sub), std::move(info.hash), info.k); } else { assert(sub.empty()); assert(info.hash.empty()); - node = MakeNodeRef(info.fragment, std::move(info.keys), info.k); + node = MakeNodeRef(script_ctx, info.fragment, std::move(info.keys), info.k); } // Verify acceptability. if (!node || (node->GetType() & "KVWB"_mst) == ""_mst) { @@ -894,8 +991,10 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { ops += 1; scriptsize += 1; } - if (ops > MAX_OPS_PER_SCRIPT) return {}; - if (scriptsize > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (!miniscript::IsTapscript(script_ctx) && ops > MAX_OPS_PER_SCRIPT) return {}; + if (scriptsize > miniscript::internal::MaxScriptSize(script_ctx)) { + return {}; + } // Move it to the stack. stack.push_back(std::move(node)); todo.pop_back(); @@ -908,44 +1007,67 @@ NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) { return std::move(stack[0]); } +//! The spk for this script under the given context. If it's a Taproot output also record the spend data. +CScript ScriptPubKey(MsCtx ctx, const CScript& script, TaprootBuilder& builder) +{ + if (!miniscript::IsTapscript(ctx)) return CScript() << OP_0 << WitnessV0ScriptHash(script); + + // For Taproot outputs we always use a tree with a single script and a dummy internal key. + builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT); + builder.Finalize(XOnlyPubKey{NUMS_PK}); + return GetScriptForDestination(builder.GetOutput()); +} + +//! Fill the witness with the data additional to the script satisfaction. +void SatisfactionToWitness(MsCtx ctx, CScriptWitness& witness, const CScript& script, TaprootBuilder& builder) { + // For P2WSH, it's only the witness script. + witness.stack.emplace_back(script.begin(), script.end()); + if (!miniscript::IsTapscript(ctx)) return; + // For Tapscript we also need the control block. + witness.stack.push_back(*builder.GetSpendData().scripts.begin()->second.begin()); +} + /** Perform various applicable tests on a miniscript Node. */ -void TestNode(const NodeRef& node, FuzzedDataProvider& provider) +void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& provider) { if (!node) return; // Check that it roundtrips to text representation - std::optional<std::string> str{node->ToString(PARSER_CTX)}; + const ParserContext parser_ctx{script_ctx}; + std::optional<std::string> str{node->ToString(parser_ctx)}; assert(str); - auto parsed = miniscript::FromString(*str, PARSER_CTX); + auto parsed = miniscript::FromString(*str, parser_ctx); assert(parsed); assert(*parsed == *node); // Check consistency between script size estimation and real size. - auto script = node->ToScript(PARSER_CTX); + auto script = node->ToScript(parser_ctx); assert(node->ScriptSize() == script.size()); // Check consistency of "x" property with the script (type K is excluded, because it can end // with a push of a key, which could match these opcodes). if (!(node->GetType() << "K"_mst)) { bool ends_in_verify = !(node->GetType() << "x"_mst); - assert(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL)); + assert(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL || script.back() == OP_NUMEQUAL)); } // The rest of the checks only apply when testing a valid top-level script. if (!node->IsValidTopLevel()) return; // Check roundtrip to script - auto decoded = miniscript::FromScript(script, PARSER_CTX); + auto decoded = miniscript::FromScript(script, parser_ctx); assert(decoded); // Note we can't use *decoded == *node because the miniscript representation may differ, so we check that: // - The script corresponding to that decoded form matches exactly // - The type matches exactly - assert(decoded->ToScript(PARSER_CTX) == script); + assert(decoded->ToScript(parser_ctx) == script); assert(decoded->GetType() == node->GetType()); - const auto node_ops{node->GetOps()}; - if (provider.ConsumeBool() && node_ops && *node_ops < MAX_OPS_PER_SCRIPT && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { - // Optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. + // Optionally pad the script or the witness in order to increase the sensitivity of the tests of + // the resources limits logic. + CScriptWitness witness_mal, witness_nonmal; + if (provider.ConsumeBool()) { + // Under P2WSH, optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. // This makes the script obviously not actually miniscript-compatible anymore, but the // signatures constructed in this test don't commit to the script anyway, so the same // miniscript satisfier will work. This increases the sensitivity of the test to the ops @@ -954,31 +1076,57 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) // maximal. // Do not pad more than what would cause MAX_STANDARD_P2WSH_SCRIPT_SIZE to be reached, however, // as that also invalidates scripts. - int add = std::min<int>( - MAX_OPS_PER_SCRIPT - *node_ops, - MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); - for (int i = 0; i < add; ++i) script.push_back(OP_NOP); + const auto node_ops{node->GetOps()}; + if (!IsTapscript(script_ctx) && node_ops && *node_ops < MAX_OPS_PER_SCRIPT + && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { + int add = std::min<int>( + MAX_OPS_PER_SCRIPT - *node_ops, + MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); + for (int i = 0; i < add; ++i) script.push_back(OP_NOP); + } + + // Under Tapscript, optionally pad the stack up to the limit minus the calculated maximum execution stack + // size to assert a Miniscript would never add more elements to the stack during execution than anticipated. + const auto node_exec_ss{node->GetExecStackSize()}; + if (miniscript::IsTapscript(script_ctx) && node_exec_ss && *node_exec_ss < MAX_STACK_SIZE) { + unsigned add{(unsigned)MAX_STACK_SIZE - *node_exec_ss}; + witness_mal.stack.resize(add); + witness_nonmal.stack.resize(add); + script.reserve(add); + for (unsigned i = 0; i < add; ++i) script.push_back(OP_NIP); + } } + const SatisfierContext satisfier_ctx{script_ctx}; + + // Get the ScriptPubKey for this script, filling spend data if it's Taproot. + TaprootBuilder builder; + const CScript script_pubkey{ScriptPubKey(script_ctx, script, builder)}; + // Run malleable satisfaction algorithm. - const CScript script_pubkey = CScript() << OP_0 << WitnessV0ScriptHash(script); - CScriptWitness witness_mal; - const bool mal_success = node->Satisfy(SATISFIER_CTX, witness_mal.stack, false) == miniscript::Availability::YES; - witness_mal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + std::vector<std::vector<unsigned char>> stack_mal; + const bool mal_success = node->Satisfy(satisfier_ctx, stack_mal, false) == miniscript::Availability::YES; // Run non-malleable satisfaction algorithm. - CScriptWitness witness_nonmal; - const bool nonmal_success = node->Satisfy(SATISFIER_CTX, witness_nonmal.stack, true) == miniscript::Availability::YES; - witness_nonmal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + std::vector<std::vector<unsigned char>> stack_nonmal; + const bool nonmal_success = node->Satisfy(satisfier_ctx, stack_nonmal, true) == miniscript::Availability::YES; if (nonmal_success) { - // Non-malleable satisfactions are bounded by GetStackSize(). - assert(witness_nonmal.stack.size() <= *node->GetStackSize() + 1); + // Non-malleable satisfactions are bounded by the satisfaction size plus: + // - For P2WSH spends, the witness script + // - For Tapscript spends, both the witness script and the control block + const size_t max_stack_size{*node->GetStackSize() + 1 + miniscript::IsTapscript(script_ctx)}; + assert(stack_nonmal.size() <= max_stack_size); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. assert(mal_success); - assert(witness_nonmal.stack == witness_mal.stack); + assert(stack_nonmal == stack_mal); + // Compute witness size (excluding script push, control block, and witness count encoding). + const size_t wit_size = GetSerializeSize(stack_nonmal, PROTOCOL_VERSION) - GetSizeOfCompactSize(stack_nonmal.size()); + assert(wit_size <= *node->GetWitnessSize()); // Test non-malleable satisfaction. + witness_nonmal.stack.insert(witness_nonmal.stack.end(), std::make_move_iterator(stack_nonmal.begin()), std::make_move_iterator(stack_nonmal.end())); + SatisfactionToWitness(script_ctx, witness_nonmal, script, builder); ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(). @@ -992,6 +1140,8 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) { // Test malleable satisfaction only if it's different from the non-malleable one. + witness_mal.stack.insert(witness_mal.stack.end(), std::make_move_iterator(stack_mal.begin()), std::make_move_iterator(stack_mal.end())); + SatisfactionToWitness(script_ctx, witness_mal, script, builder); ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only @@ -1008,21 +1158,20 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) // algorithm succeeds. Given that under IsSane() both satisfactions // are identical, this implies that for such nodes, the non-malleable // satisfaction will also match the expected policy. - bool satisfiable = node->IsSatisfiable([](const Node& node) -> bool { + const auto is_key_satisfiable = [script_ctx](const CPubKey& pubkey) -> bool { + auto sig_ptr{TEST_DATA.GetSig(script_ctx, pubkey)}; + return sig_ptr != nullptr && sig_ptr->second; + }; + bool satisfiable = node->IsSatisfiable([&](const Node& node) -> bool { switch (node.fragment) { case Fragment::PK_K: - case Fragment::PK_H: { - auto it = TEST_DATA.dummy_sigs.find(node.keys[0]); - assert(it != TEST_DATA.dummy_sigs.end()); - return it->second.second; - } - case Fragment::MULTI: { - size_t sats = 0; - for (const auto& key : node.keys) { - auto it = TEST_DATA.dummy_sigs.find(key); - assert(it != TEST_DATA.dummy_sigs.end()); - sats += it->second.second; - } + case Fragment::PK_H: + return is_key_satisfiable(node.keys[0]); + case Fragment::MULTI: + case Fragment::MULTI_A: { + size_t sats = std::count_if(node.keys.begin(), node.keys.end(), [&](const auto& key) { + return size_t(is_key_satisfiable(key)); + }); return sats >= node.k; } case Fragment::OLDER: @@ -1061,10 +1210,13 @@ void FuzzInitSmart() /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeStable. */ FUZZ_TARGET(miniscript_stable, .init = FuzzInit) { - FuzzedDataProvider provider(buffer.data(), buffer.size()); - TestNode(GenNode([&](Type needed_type) { - return ConsumeNodeStable(provider, needed_type); - }, ""_mst), provider); + // Run it under both P2WSH and Tapscript contexts. + for (const auto script_ctx: {MsCtx::P2WSH, MsCtx::TAPSCRIPT}) { + FuzzedDataProvider provider(buffer.data(), buffer.size()); + TestNode(script_ctx, GenNode(script_ctx, [&](Type needed_type) { + return ConsumeNodeStable(script_ctx, provider, needed_type); + }, ""_mst), provider); + } } /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeSmart. */ @@ -1074,22 +1226,25 @@ FUZZ_TARGET(miniscript_smart, .init = FuzzInitSmart) static constexpr std::array<Type, 4> BASE_TYPES{"B"_mst, "V"_mst, "K"_mst, "W"_mst}; FuzzedDataProvider provider(buffer.data(), buffer.size()); - TestNode(GenNode([&](Type needed_type) { - return ConsumeNodeSmart(provider, needed_type); + const auto script_ctx{(MsCtx)provider.ConsumeBool()}; + TestNode(script_ctx, GenNode(script_ctx, [&](Type needed_type) { + return ConsumeNodeSmart(script_ctx, provider, needed_type); }, PickValue(provider, BASE_TYPES), true), provider); } /* Fuzz tests that test parsing from a string, and roundtripping via string. */ FUZZ_TARGET(miniscript_string, .init = FuzzInit) { + if (buffer.empty()) return; FuzzedDataProvider provider(buffer.data(), buffer.size()); - auto str = provider.ConsumeRemainingBytesAsString(); - auto parsed = miniscript::FromString(str, PARSER_CTX); + auto str = provider.ConsumeBytesAsString(provider.remaining_bytes() - 1); + const ParserContext parser_ctx{(MsCtx)provider.ConsumeBool()}; + auto parsed = miniscript::FromString(str, parser_ctx); if (!parsed) return; - const auto str2 = parsed->ToString(PARSER_CTX); + const auto str2 = parsed->ToString(parser_ctx); assert(str2); - auto parsed2 = miniscript::FromString(*str2, PARSER_CTX); + auto parsed2 = miniscript::FromString(*str2, parser_ctx); assert(parsed2); assert(*parsed == *parsed2); } @@ -1101,8 +1256,9 @@ FUZZ_TARGET(miniscript_script) const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider); if (!script) return; - const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT); + const ScriptParserContext script_parser_ctx{(MsCtx)fuzzed_data_provider.ConsumeBool()}; + const auto ms = miniscript::FromScript(*script, script_parser_ctx); if (!ms) return; - assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script); + assert(ms->ToScript(script_parser_ctx) == *script); } diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp index fcd66b234e..846389863d 100644 --- a/src/test/fuzz/script_bitcoin_consensus.cpp +++ b/src/test/fuzz/script_bitcoin_consensus.cpp @@ -28,4 +28,23 @@ FUZZ_TARGET(script_bitcoin_consensus) } (void)bitcoinconsensus_verify_script(random_bytes_1.data(), random_bytes_1.size(), random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p); (void)bitcoinconsensus_verify_script_with_amount(random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p); + + std::vector<UTXO> spent_outputs; + std::vector<std::vector<unsigned char>> spent_spks; + if (n_in <= 24386) { + spent_outputs.reserve(n_in); + spent_spks.reserve(n_in); + for (size_t i = 0; i < n_in; ++i) { + spent_spks.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider)); + const CAmount value{ConsumeMoney(fuzzed_data_provider)}; + const auto spk_size{static_cast<unsigned>(spent_spks.back().size())}; + spent_outputs.push_back({.scriptPubKey = spent_spks.back().data(), .scriptPubKeySize = spk_size, .value = value}); + } + } + + const auto spent_outs_size{static_cast<unsigned>(spent_outputs.size())}; + + (void)bitcoinconsensus_verify_script_with_spent_outputs( + random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), + spent_outputs.data(), spent_outs_size, n_in, flags, err_p); } diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index d23e997719..5a286c05d2 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -36,7 +36,11 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept } else if (network == Network::NET_IPV6) { if (fuzzed_data_provider.remaining_bytes() >= 16) { in6_addr v6_addr = {}; - memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16); + auto addr_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(16); + if (addr_bytes[0] == CJDNS_PREFIX) { // Avoid generating IPv6 addresses that look like CJDNS. + addr_bytes[0] = 0x55; // Just an arbitrary number, anything != CJDNS_PREFIX would do. + } + memcpy(v6_addr.s6_addr, addr_bytes.data(), 16); net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; } } else if (network == Network::NET_INTERNAL) { diff --git a/src/test/headers_sync_chainwork_tests.cpp b/src/test/headers_sync_chainwork_tests.cpp index 41241ebee2..b710ad1801 100644 --- a/src/test/headers_sync_chainwork_tests.cpp +++ b/src/test/headers_sync_chainwork_tests.cpp @@ -40,7 +40,7 @@ void HeadersGeneratorSetup::GenerateHeaders(std::vector<CBlockHeader>& headers, uint256 prev_hash = starting_hash; while (headers.size() < count) { - headers.push_back(CBlockHeader()); + headers.emplace_back(); CBlockHeader& next_header = headers.back();; next_header.nVersion = nVersion; next_header.hashPrevBlock = prev_hash; diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index b69317c4d9..996c379962 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -20,6 +20,7 @@ #include <script/interpreter.h> #include <script/miniscript.h> #include <script/script_error.h> +#include <script/signingprovider.h> namespace { @@ -30,7 +31,9 @@ struct TestData { //! A map from the public keys to their CKeyIDs (faster than hashing every time). std::map<CPubKey, CKeyID> pkhashes; std::map<CKeyID, CPubKey> pkmap; + std::map<XOnlyPubKey, CKeyID> xonly_pkhashes; std::map<CPubKey, std::vector<unsigned char>> signatures; + std::map<XOnlyPubKey, std::vector<unsigned char>> schnorr_signatures; // Various precomputed hashes std::vector<std::vector<unsigned char>> sha256; @@ -46,6 +49,9 @@ struct TestData { { // All our signatures sign (and are required to sign) this constant message. auto const MESSAGE_HASH = uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065"); + // We don't pass additional randomness when creating a schnorr signature. + auto const EMPTY_AUX{uint256S("")}; + // We generate 255 public keys and 255 hashes of each type. for (int i = 1; i <= 255; ++i) { // This 32-byte array functions as both private key data and hash preimage (31 zero bytes plus any nonzero byte). @@ -60,12 +66,19 @@ struct TestData { pubkeys.push_back(pubkey); pkhashes.emplace(pubkey, keyid); pkmap.emplace(keyid, pubkey); + XOnlyPubKey xonly_pubkey{pubkey}; + uint160 xonly_hash{Hash160(xonly_pubkey)}; + xonly_pkhashes.emplace(xonly_pubkey, xonly_hash); + pkmap.emplace(xonly_hash, pubkey); // Compute ECDSA signatures on MESSAGE_HASH with the private keys. - std::vector<unsigned char> sig; + std::vector<unsigned char> sig, schnorr_sig(64); BOOST_CHECK(key.Sign(MESSAGE_HASH, sig)); sig.push_back(1); // sighash byte signatures.emplace(pubkey, sig); + BOOST_CHECK(key.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX)); + schnorr_sig.push_back(1); // Maximally sized Schnorr sigs have a sighash byte. + schnorr_signatures.emplace(XOnlyPubKey{pubkey}, schnorr_sig); // Compute various hashes std::vector<unsigned char> hash; @@ -114,19 +127,32 @@ typedef std::pair<ChallengeType, uint32_t> Challenge; struct KeyConverter { typedef CPubKey Key; + const miniscript::MiniscriptContext m_script_ctx; + + constexpr KeyConverter(miniscript::MiniscriptContext ctx) noexcept : m_script_ctx{ctx} {} + bool KeyCompare(const Key& a, const Key& b) const { return a < b; } //! Convert a public key to bytes. - std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; } + std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { + if (!miniscript::IsTapscript(m_script_ctx)) { + return {key.begin(), key.end()}; + } + const XOnlyPubKey xonly_pubkey{key}; + return {xonly_pubkey.begin(), xonly_pubkey.end()}; + } //! Convert a public key to its Hash160 bytes (precomputed). - std::vector<unsigned char> ToPKHBytes(const CPubKey& key) const - { - auto it = g_testdata->pkhashes.find(key); - assert(it != g_testdata->pkhashes.end()); - return {it->second.begin(), it->second.end()}; + std::vector<unsigned char> ToPKHBytes(const CPubKey& key) const { + if (!miniscript::IsTapscript(m_script_ctx)) { + auto hash = g_testdata->pkhashes.at(key); + return {hash.begin(), hash.end()}; + } + const XOnlyPubKey xonly_key{key}; + auto hash = g_testdata->xonly_pkhashes.at(xonly_key); + return {hash.begin(), hash.end()}; } //! Parse a public key from a range of hex characters. @@ -140,9 +166,15 @@ struct KeyConverter { template<typename I> std::optional<Key> FromPKBytes(I first, I last) const { - Key key{first, last}; - if (key.IsValid()) return key; - return {}; + if (!miniscript::IsTapscript(m_script_ctx)) { + Key key{first, last}; + if (key.IsValid()) return key; + return {}; + } + if (last - first != 32) return {}; + XOnlyPubKey xonly_pubkey; + std::copy(first, last, xonly_pubkey.begin()); + return xonly_pubkey.GetEvenCorrespondingCPubKey(); } template<typename I> @@ -150,18 +182,23 @@ struct KeyConverter { assert(last - first == 20); CKeyID keyid; std::copy(first, last, keyid.begin()); - auto it = g_testdata->pkmap.find(keyid); - assert(it != g_testdata->pkmap.end()); - return it->second; + return g_testdata->pkmap.at(keyid); } std::optional<std::string> ToString(const Key& key) const { return HexStr(ToPKBytes(key)); } + + miniscript::MiniscriptContext MsContext() const { + return m_script_ctx; + } }; /** A class that encapsulates all signing/hash revealing operations. */ struct Satisfier : public KeyConverter { + + Satisfier(miniscript::MiniscriptContext ctx) noexcept : KeyConverter{ctx} {} + //! Which keys/timelocks/hash preimages are available. std::set<Challenge> supported; @@ -178,9 +215,15 @@ struct Satisfier : public KeyConverter { //! Produce a signature for the given key. miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const { if (supported.count(Challenge(ChallengeType::PK, ChallengeNumber(key)))) { - auto it = g_testdata->signatures.find(key); - if (it == g_testdata->signatures.end()) return miniscript::Availability::NO; - sig = it->second; + if (!miniscript::IsTapscript(m_script_ctx)) { + auto it = g_testdata->signatures.find(key); + if (it == g_testdata->signatures.end()) return miniscript::Availability::NO; + sig = it->second; + } else { + auto it = g_testdata->schnorr_signatures.find(XOnlyPubKey{key}); + if (it == g_testdata->schnorr_signatures.end()) return miniscript::Availability::NO; + sig = it->second; + } return miniscript::Availability::YES; } return miniscript::Availability::NO; @@ -226,6 +269,14 @@ public: return sig == it->second; } + bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion, + ScriptExecutionData&, ScriptError*) const override { + XOnlyPubKey pk{pubkey}; + auto it = g_testdata->schnorr_signatures.find(pk); + if (it == g_testdata->schnorr_signatures.end()) return false; + return sig == it->second; + } + bool CheckLockTime(const CScriptNum& locktime) const override { // Delegate to Satisfier. return ctx.CheckAfter(locktime.GetInt64()); @@ -237,8 +288,8 @@ public: } }; -//! Singleton instance of KeyConverter. -const KeyConverter CONVERTER{}; +//! Public key to be used as internal key for dummy Taproot spends. +const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; using Fragment = miniscript::Fragment; using NodeRef = miniscript::NodeRef<CPubKey>; @@ -271,37 +322,66 @@ std::set<Challenge> FindChallenges(const NodeRef& ref) { return chal; } +//! The spk for this script under the given context. If it's a Taproot output also record the spend data. +CScript ScriptPubKey(miniscript::MiniscriptContext ctx, const CScript& script, TaprootBuilder& builder) +{ + if (!miniscript::IsTapscript(ctx)) return CScript() << OP_0 << WitnessV0ScriptHash(script); + + // For Taproot outputs we always use a tree with a single script and a dummy internal key. + builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT); + builder.Finalize(XOnlyPubKey{NUMS_PK}); + return GetScriptForDestination(builder.GetOutput()); +} + +//! Fill the witness with the data additional to the script satisfaction. +void SatisfactionToWitness(miniscript::MiniscriptContext ctx, CScriptWitness& witness, const CScript& script, TaprootBuilder& builder) { + // For P2WSH, it's only the witness script. + witness.stack.emplace_back(script.begin(), script.end()); + if (!miniscript::IsTapscript(ctx)) return; + // For Tapscript we also need the control block. + witness.stack.push_back(*builder.GetSpendData().scripts.begin()->second.begin()); +} + /** Run random satisfaction tests. */ -void TestSatisfy(const std::string& testcase, const NodeRef& node) { - auto script = node->ToScript(CONVERTER); +void TestSatisfy(const KeyConverter& converter, const std::string& testcase, const NodeRef& node) { + auto script = node->ToScript(converter); auto challenges = FindChallenges(node); // Find all challenges in the generated miniscript. std::vector<Challenge> challist(challenges.begin(), challenges.end()); for (int iter = 0; iter < 3; ++iter) { Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx); - Satisfier satisfier; + Satisfier satisfier(converter.MsContext()); TestSignatureChecker checker(satisfier); bool prev_mal_success = false, prev_nonmal_success = false; // Go over all challenges involved in this miniscript in random order. for (int add = -1; add < (int)challist.size(); ++add) { if (add >= 0) satisfier.supported.insert(challist[add]); // The first iteration does not add anything + // Get the ScriptPubKey for this script, filling spend data if it's Taproot. + TaprootBuilder builder; + const CScript script_pubkey{ScriptPubKey(converter.MsContext(), script, builder)}; + // Run malleable satisfaction algorithm. - const CScript script_pubkey = CScript() << OP_0 << WitnessV0ScriptHash(script); CScriptWitness witness_mal; const bool mal_success = node->Satisfy(satisfier, witness_mal.stack, false) == miniscript::Availability::YES; - witness_mal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + SatisfactionToWitness(converter.MsContext(), witness_mal, script, builder); // Run non-malleable satisfaction algorithm. CScriptWitness witness_nonmal; const bool nonmal_success = node->Satisfy(satisfier, witness_nonmal.stack, true) == miniscript::Availability::YES; - witness_nonmal.stack.push_back(std::vector<unsigned char>(script.begin(), script.end())); + // Compute witness size (excluding script push, control block, and witness count encoding). + const size_t wit_size = GetSerializeSize(witness_nonmal.stack, PROTOCOL_VERSION) - GetSizeOfCompactSize(witness_nonmal.stack.size()); + SatisfactionToWitness(converter.MsContext(), witness_nonmal, script, builder); if (nonmal_success) { - // Non-malleable satisfactions are bounded by GetStackSize(). - BOOST_CHECK(witness_nonmal.stack.size() <= *node->GetStackSize() + 1); + // Non-malleable satisfactions are bounded by the satisfaction size plus: + // - For P2WSH spends, the witness script + // - For Tapscript spends, both the witness script and the control block + const size_t max_stack_size{*node->GetStackSize() + 1 + miniscript::IsTapscript(converter.MsContext())}; + BOOST_CHECK(witness_nonmal.stack.size() <= max_stack_size); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. BOOST_CHECK(mal_success); BOOST_CHECK(witness_nonmal.stack == witness_mal.stack); + assert(wit_size <= *node->GetWitnessSize()); // Test non-malleable satisfaction. ScriptError serror; @@ -351,37 +431,66 @@ void TestSatisfy(const std::string& testcase, const NodeRef& node) { } enum TestMode : int { + //! Invalid under any context TESTMODE_INVALID = 0, + //! Valid under any context unless overridden TESTMODE_VALID = 1, TESTMODE_NONMAL = 2, TESTMODE_NEEDSIG = 4, - TESTMODE_TIMELOCKMIX = 8 + TESTMODE_TIMELOCKMIX = 8, + //! Invalid only under P2WSH context + TESTMODE_P2WSH_INVALID = 16, + //! Invalid only under Tapscript context + TESTMODE_TAPSCRIPT_INVALID = 32, }; -void Test(const std::string& ms, const std::string& hexscript, int mode, int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt) +void Test(const std::string& ms, const std::string& hexscript, int mode, const KeyConverter& converter, + int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt, + std::optional<uint32_t> stack_exec = {}) { - auto node = miniscript::FromString(ms, CONVERTER); - if (mode == TESTMODE_INVALID) { + auto node = miniscript::FromString(ms, converter); + const bool is_tapscript{miniscript::IsTapscript(converter.MsContext())}; + if (mode == TESTMODE_INVALID || ((mode & TESTMODE_P2WSH_INVALID) && !is_tapscript) || ((mode & TESTMODE_TAPSCRIPT_INVALID) && is_tapscript)) { BOOST_CHECK_MESSAGE(!node || !node->IsValid(), "Unexpectedly valid: " + ms); } else { BOOST_CHECK_MESSAGE(node, "Unparseable: " + ms); BOOST_CHECK_MESSAGE(node->IsValid(), "Invalid: " + ms); BOOST_CHECK_MESSAGE(node->IsValidTopLevel(), "Invalid top level: " + ms); - auto computed_script = node->ToScript(CONVERTER); + auto computed_script = node->ToScript(converter); BOOST_CHECK_MESSAGE(node->ScriptSize() == computed_script.size(), "Script size mismatch: " + ms); if (hexscript != "?") BOOST_CHECK_MESSAGE(HexStr(computed_script) == hexscript, "Script mismatch: " + ms + " (" + HexStr(computed_script) + " vs " + hexscript + ")"); BOOST_CHECK_MESSAGE(node->IsNonMalleable() == !!(mode & TESTMODE_NONMAL), "Malleability mismatch: " + ms); BOOST_CHECK_MESSAGE(node->NeedsSignature() == !!(mode & TESTMODE_NEEDSIG), "Signature necessity mismatch: " + ms); BOOST_CHECK_MESSAGE((node->GetType() << "k"_mst) == !(mode & TESTMODE_TIMELOCKMIX), "Timelock mix mismatch: " + ms); - auto inferred_miniscript = miniscript::FromScript(computed_script, CONVERTER); + auto inferred_miniscript = miniscript::FromScript(computed_script, converter); BOOST_CHECK_MESSAGE(inferred_miniscript, "Cannot infer miniscript from script: " + ms); - BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(CONVERTER) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); + BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(converter) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); if (opslimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << *node->GetOps() << " vs " << opslimit << ")"); if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << *node->GetStackSize() << " vs " << stacklimit << ")"); if (max_wit_size) BOOST_CHECK_MESSAGE(*node->GetWitnessSize() == *max_wit_size, "Witness size limit mismatch: " << ms << " (" << *node->GetWitnessSize() << " vs " << *max_wit_size << ")"); - TestSatisfy(ms, node); + if (stack_exec) BOOST_CHECK_MESSAGE(*node->GetExecStackSize() == *stack_exec, "Stack execution limit mismatch: " << ms << " (" << *node->GetExecStackSize() << " vs " << *stack_exec << ")"); + TestSatisfy(converter, ms, node); } } + +void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode, + int opslimit, int stacklimit, std::optional<uint32_t> max_wit_size, + std::optional<uint32_t> max_tap_wit_size, + std::optional<uint32_t> stack_exec) +{ + KeyConverter wsh_converter(miniscript::MiniscriptContext::P2WSH); + Test(ms, hexscript, mode, wsh_converter, opslimit, stacklimit, max_wit_size, stack_exec); + KeyConverter tap_converter(miniscript::MiniscriptContext::TAPSCRIPT); + Test(ms, hextapscript == "=" ? hexscript : hextapscript, mode, tap_converter, opslimit, stacklimit, max_tap_wit_size, stack_exec); +} + +void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode) +{ + Test(ms, hexscript, hextapscript, mode, + /*opslimit=*/-1, /*stacklimit=*/-1, + /*max_wit_size=*/std::nullopt, /*max_tap_wit_size=*/std::nullopt, /*stack_exec=*/std::nullopt); +} + } // namespace BOOST_FIXTURE_TEST_SUITE(miniscript_tests, BasicTestingSetup) @@ -391,114 +500,169 @@ BOOST_AUTO_TEST_CASE(fixed_tests) g_testdata.reset(new TestData()); // Validity rules - Test("l:older(1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(1): valid - Test("l:older(0)", "?", TESTMODE_INVALID); // older(0): k must be at least 1 - Test("l:older(2147483647)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(2147483647): valid - Test("l:older(2147483648)", "?", TESTMODE_INVALID); // older(2147483648): k must be below 2^31 - Test("u:after(1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(1): valid - Test("u:after(0)", "?", TESTMODE_INVALID); // after(0): k must be at least 1 - Test("u:after(2147483647)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(2147483647): valid - Test("u:after(2147483648)", "?", TESTMODE_INVALID); // after(2147483648): k must be below 2^31 - Test("andor(0,1,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,B,B): valid - Test("andor(a:0,1,1)", "?", TESTMODE_INVALID); // andor(Wdu,B,B): X must be B - Test("andor(0,a:1,a:1)", "?", TESTMODE_INVALID); // andor(Bdu,W,W): Y and Z must be B/V/K - Test("andor(1,1,1)", "?", TESTMODE_INVALID); // andor(Bu,B,B): X must be d - Test("andor(n:or_i(0,after(1)),1,1)", "?", TESTMODE_VALID); // andor(Bdu,B,B): valid - Test("andor(or_i(0,after(1)),1,1)", "?", TESTMODE_INVALID); // andor(Bd,B,B): X must be u - Test("c:andor(0,pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // andor(Bdu,K,K): valid - Test("t:andor(0,v:1,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,V,V): valid - Test("and_v(v:1,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,B): valid - Test("t:and_v(v:1,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,V): valid - Test("c:and_v(v:1,pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // and_v(V,K): valid - Test("and_v(1,1)", "?", TESTMODE_INVALID); // and_v(B,B): X must be V - Test("and_v(pk_k(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),1)", "?", TESTMODE_INVALID); // and_v(K,B): X must be V - Test("and_v(v:1,a:1)", "?", TESTMODE_INVALID); // and_v(K,W): Y must be B/V/K - Test("and_b(1,a:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_b(B,W): valid - Test("and_b(1,1)", "?", TESTMODE_INVALID); // and_b(B,B): Y must W - Test("and_b(v:1,a:1)", "?", TESTMODE_INVALID); // and_b(V,W): X must be B - Test("and_b(a:1,a:1)", "?", TESTMODE_INVALID); // and_b(W,W): X must be B - Test("and_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:1)", "?", TESTMODE_INVALID); // and_b(K,W): X must be B - Test("or_b(0,a:0)", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_b(Bd,Wd): valid - Test("or_b(1,a:0)", "?", TESTMODE_INVALID); // or_b(B,Wd): X must be d - Test("or_b(0,a:1)", "?", TESTMODE_INVALID); // or_b(Bd,W): Y must be d - Test("or_b(0,0)", "?", TESTMODE_INVALID); // or_b(Bd,Bd): Y must W - Test("or_b(v:0,a:0)", "?", TESTMODE_INVALID); // or_b(V,Wd): X must be B - Test("or_b(a:0,a:0)", "?", TESTMODE_INVALID); // or_b(Wd,Wd): X must be B - Test("or_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:0)", "?", TESTMODE_INVALID); // or_b(Kd,Wd): X must be B - Test("t:or_c(0,v:1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_c(Bdu,V): valid - Test("t:or_c(a:0,v:1)", "?", TESTMODE_INVALID); // or_c(Wdu,V): X must be B - Test("t:or_c(1,v:1)", "?", TESTMODE_INVALID); // or_c(Bu,V): X must be d - Test("t:or_c(n:or_i(0,after(1)),v:1)", "?", TESTMODE_VALID); // or_c(Bdu,V): valid - Test("t:or_c(or_i(0,after(1)),v:1)", "?", TESTMODE_INVALID); // or_c(Bd,V): X must be u - Test("t:or_c(0,1)", "?", TESTMODE_INVALID); // or_c(Bdu,B): Y must be V - Test("or_d(0,1)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_d(Bdu,B): valid - Test("or_d(a:0,1)", "?", TESTMODE_INVALID); // or_d(Wdu,B): X must be B - Test("or_d(1,1)", "?", TESTMODE_INVALID); // or_d(Bu,B): X must be d - Test("or_d(n:or_i(0,after(1)),1)", "?", TESTMODE_VALID); // or_d(Bdu,B): valid - Test("or_d(or_i(0,after(1)),1)", "?", TESTMODE_INVALID); // or_d(Bd,B): X must be u - Test("or_d(0,v:1)", "?", TESTMODE_INVALID); // or_d(Bdu,V): Y must be B - Test("or_i(1,1)", "?", TESTMODE_VALID); // or_i(B,B): valid - Test("t:or_i(v:1,v:1)", "?", TESTMODE_VALID); // or_i(V,V): valid - Test("c:or_i(pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_i(K,K): valid - Test("or_i(a:1,a:1)", "?", TESTMODE_INVALID); // or_i(W,W): X and Y must be B/V/K - Test("or_b(l:after(100),al:after(1000000000))", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid - Test("and_b(after(100),a:after(1000000000))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid - Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k - Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h - + Test("l:older(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(1): valid + Test("l:older(0)", "?", "?", TESTMODE_INVALID); // older(0): k must be at least 1 + Test("l:older(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(2147483647): valid + Test("l:older(2147483648)", "?", "?", TESTMODE_INVALID); // older(2147483648): k must be below 2^31 + Test("u:after(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(1): valid + Test("u:after(0)", "?", "?", TESTMODE_INVALID); // after(0): k must be at least 1 + Test("u:after(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(2147483647): valid + Test("u:after(2147483648)", "?", "?", TESTMODE_INVALID); // after(2147483648): k must be below 2^31 + Test("andor(0,1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,B,B): valid + Test("andor(a:0,1,1)", "?", "?", TESTMODE_INVALID); // andor(Wdu,B,B): X must be B + Test("andor(0,a:1,a:1)", "?", "?", TESTMODE_INVALID); // andor(Bdu,W,W): Y and Z must be B/V/K + Test("andor(1,1,1)", "?", "?", TESTMODE_INVALID); // andor(Bu,B,B): X must be d + Test("andor(n:or_i(0,after(1)),1,1)", "?", "?", TESTMODE_VALID); // andor(Bdu,B,B): valid + Test("andor(or_i(0,after(1)),1,1)", "?", "?", TESTMODE_INVALID); // andor(Bd,B,B): X must be u + Test("c:andor(0,pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // andor(Bdu,K,K): valid + Test("t:andor(0,v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,V,V): valid + Test("and_v(v:1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,B): valid + Test("t:and_v(v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,V): valid + Test("c:and_v(v:1,pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // and_v(V,K): valid + Test("and_v(1,1)", "?", "?", TESTMODE_INVALID); // and_v(B,B): X must be V + Test("and_v(pk_k(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),1)", "?", "?", TESTMODE_INVALID); // and_v(K,B): X must be V + Test("and_v(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_v(K,W): Y must be B/V/K + Test("and_b(1,a:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_b(B,W): valid + Test("and_b(1,1)", "?", "?", TESTMODE_INVALID); // and_b(B,B): Y must W + Test("and_b(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(V,W): X must be B + Test("and_b(a:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(W,W): X must be B + Test("and_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:1)", "?", "?", TESTMODE_INVALID); // and_b(K,W): X must be B + Test("or_b(0,a:0)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_b(Bd,Wd): valid + Test("or_b(1,a:0)", "?", "?", TESTMODE_INVALID); // or_b(B,Wd): X must be d + Test("or_b(0,a:1)", "?", "?", TESTMODE_INVALID); // or_b(Bd,W): Y must be d + Test("or_b(0,0)", "?", "?", TESTMODE_INVALID); // or_b(Bd,Bd): Y must W + Test("or_b(v:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(V,Wd): X must be B + Test("or_b(a:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(Wd,Wd): X must be B + Test("or_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:0)", "?", "?", TESTMODE_INVALID); // or_b(Kd,Wd): X must be B + Test("t:or_c(0,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_c(Bdu,V): valid + Test("t:or_c(a:0,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Wdu,V): X must be B + Test("t:or_c(1,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bu,V): X must be d + Test("t:or_c(n:or_i(0,after(1)),v:1)", "?", "?", TESTMODE_VALID); // or_c(Bdu,V): valid + Test("t:or_c(or_i(0,after(1)),v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bd,V): X must be u + Test("t:or_c(0,1)", "?", "?", TESTMODE_INVALID); // or_c(Bdu,B): Y must be V + Test("or_d(0,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_d(Bdu,B): valid + Test("or_d(a:0,1)", "?", "?", TESTMODE_INVALID); // or_d(Wdu,B): X must be B + Test("or_d(1,1)", "?", "?", TESTMODE_INVALID); // or_d(Bu,B): X must be d + Test("or_d(n:or_i(0,after(1)),1)", "?", "?", TESTMODE_VALID); // or_d(Bdu,B): valid + Test("or_d(or_i(0,after(1)),1)", "?", "?", TESTMODE_INVALID); // or_d(Bd,B): X must be u + Test("or_d(0,v:1)", "?", "?", TESTMODE_INVALID); // or_d(Bdu,V): Y must be B + Test("or_i(1,1)", "?", "?", TESTMODE_VALID); // or_i(B,B): valid + Test("t:or_i(v:1,v:1)", "?", "?", TESTMODE_VALID); // or_i(V,V): valid + Test("c:or_i(pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_i(K,K): valid + Test("or_i(a:1,a:1)", "?", "?", TESTMODE_INVALID); // or_i(W,W): X and Y must be B/V/K + Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid + Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid + Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k + Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", "76a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h // Randomly generated test set that covers the majority of type and node type combinations - Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3); - Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 14, 5, 2 + 2 + 1 + 2 * 73); - Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", TESTMODE_VALID, 14, 5, 2 + 1 + 2 * 73 + 2); - Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2); - Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33); - Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", TESTMODE_VALID | TESTMODE_NONMAL, 13, 5, 1 + 3 * 73); - Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", TESTMODE_VALID | TESTMODE_NONMAL, 15, 7, 2 + 1 + 3 * 73 + 1); - Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33); - Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 11, 5, 2 + 1 + 2 * 73 + 33); - Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", TESTMODE_VALID | TESTMODE_NEEDSIG, 14, 4, 1 + 2 * 73 + 2); - Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", TESTMODE_VALID, 12, 1, 33); - Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", TESTMODE_VALID, 16, 2, 33 + 33); - Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33); - Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 13, 6, 1 + 2 * 73 + 1 + 73 + 1); - Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", TESTMODE_VALID, 14, 2, 33 + 2); - Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", TESTMODE_VALID, 15, 2, 1 + 33); - Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", TESTMODE_VALID | TESTMODE_NEEDSIG, 8, 2, 33 + 73); - Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 10, 5, 1 + 2 * 73 + 73); - Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", TESTMODE_VALID, 14, 2, 33 + 33); - Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", TESTMODE_VALID, 20, 2, 33 + 33); - Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73); - Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33); - Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73); - Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1); - Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73); - Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73); - Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73); - Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73); - Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73); - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2); - Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2); + Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", "=", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3, 3, 3); + Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 2 + 1 + 2 * 73, 0, 7); + Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", "?", TESTMODE_VALID | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 1 + 2 * 73 + 2, 0, 8); + Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", "=", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2, 2, 2); + Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33, 2 + 33 + 33, 4); + Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 13, 5, 1 + 3 * 73, 0, 10); + Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 15, 7, 2 + 1 + 3 * 73 + 1, 0, 10); + Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33, 33, 3); + Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 11, 5, 2 + 1 + 2 * 73 + 33, 0, 8); + Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 4, 1 + 2 * 73 + 2, 0, 8); + Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", "=", TESTMODE_VALID, 12, 1, 33, 33, 4); + Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", "=", TESTMODE_VALID, 16, 2, 33 + 33, 33 + 33, 4); + Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", "=", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33, 33 + 33, 4); + Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 13, 6, 1 + 2 * 73 + 1 + 73 + 1, 0, 10); + Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", "=", TESTMODE_VALID, 14, 2, 33 + 2, 33 + 2, 4); + Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", "=", TESTMODE_VALID, 15, 2, 1 + 33, 1 + 33, 3); + Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 8, 2, 33 + 73, 0, 4); + Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 10, 5, 1 + 2 * 73 + 73, 0, 9); + Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", "=", TESTMODE_VALID, 14, 2, 33 + 33, 33 + 33, 4); + Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", "=", TESTMODE_VALID, 20, 2, 33 + 33, 33 + 33, 4); + Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", "630320a107b16920c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73, 2 + 66, 3); + Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", "76a9141a7ac36cfa8431ab2395d701b0050045ae4a37d188ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33, 1 + 33 + 33 + 33, 6); + Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b26920fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73, 33 + 2 + 66, 5); + Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1, 66 + 1, 3); + Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", "6360b26976a9144d4421361c3289bdad06441ffaee8be8e786f1ad886776a91460d4a7bcbd08f58e58bd208d1069837d7adb16ae8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73, 2 + 33 + 66, 4); + Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", "76a91421ab1a140d0d305b8ff62bdb887d9fef82c9899e88ac7364204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73, 1 + 33 + 66, 5); + Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914a63d1e4d2ed109246c600ec8c19cce546b65b1cc886776a9144d4421361c3289bdad06441ffaee8be8e786f1ad8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73, 33 + 33 + 66, 5); + Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914ceedcb44b38bdbcb614d872223964fd3dca8a434886776a914f678d9b79045452c8c64e9309d0f0046056e26c588686776a914a2a75e1819afa208f6c89ae0da43021116dfcb0c8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73, 2 + 33 + 33 + 66, 5); + Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", "6376a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac6476a9149b652a14674a506079f574d20ca7daef6f9a66bb886776a914ceedcb44b38bdbcb614d872223964fd3dca8a43488686720d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73, 2 + 33 + 66 + 33 + 66, 6); + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2, 66 + 2 + 2, 4); + Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2, 66 + 66 + 2 + 2, 5); + + // Additional Tapscript-related tests + // Edge cases when parsing multi_a from script: + // - no pubkey at all + // - no pubkey before a CHECKSIGADD + // - no pubkey before the CHECKSIG + constexpr KeyConverter tap_converter{miniscript::MiniscriptContext::TAPSCRIPT}; + constexpr KeyConverter wsh_converter{miniscript::MiniscriptContext::P2WSH}; + const auto no_pubkey{ParseHex("ac519c")}; + BOOST_CHECK(miniscript::FromScript({no_pubkey.begin(), no_pubkey.end()}, tap_converter) == nullptr); + const auto incomplete_multi_a{ParseHex("ba20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")}; + BOOST_CHECK(miniscript::FromScript({incomplete_multi_a.begin(), incomplete_multi_a.end()}, tap_converter) == nullptr); + const auto incomplete_multi_a_2{ParseHex("ac2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")}; + BOOST_CHECK(miniscript::FromScript({incomplete_multi_a_2.begin(), incomplete_multi_a_2.end()}, tap_converter) == nullptr); + // Can use multi_a under Tapscript but not P2WSH. + Test("and_v(v:multi_a(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "?", "20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aac205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba529d0400046749b1", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4, 2, {}, {}, 3); + // Can use more than 20 keys in a multi_a. + std::string ms_str_multi_a{"multi_a(1,"}; + for (size_t i = 0; i < 21; ++i) { + ms_str_multi_a += HexStr(g_testdata->pubkeys[i]); + if (i < 20) ms_str_multi_a += ","; + } + ms_str_multi_a += ")"; + Test(ms_str_multi_a, "?", "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba20f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9ba20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ba202f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4ba20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ba205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcba202f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ba20acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeba20a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ba20774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cbba20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aba20f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8ba20499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4ba20d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080eba20e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0aba20defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34ba205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba202b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6cba204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ba20352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5ba519c", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 22, 21, {}, {}, 22); + // Since 'd:' is 'u' we can use it directly inside a thresh. But we can't under P2WSH. + Test("thresh(2,dv:older(42),s:pk(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "7663012ab269687c205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcac937c20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 12, 3, {}, {}, 4); + // We can have a script that has more than 201 ops (n = 99), that needs a stack size > 100 (n = 110), or has a + // script that is larger than 3600 bytes (n = 200). All that can't be under P2WSH. + for (const auto pk_count: {99, 110, 200}) { + std::string ms_str_large; + for (auto i = 0; i < pk_count - 1; ++i) { + ms_str_large += "and_b(pk(" + HexStr(g_testdata->pubkeys[i]) + "),a:"; + } + ms_str_large += "pk(" + HexStr(g_testdata->pubkeys[pk_count - 1]) + ")"; + ms_str_large.insert(ms_str_large.end(), pk_count - 1, ')'); + Test(ms_str_large, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, pk_count + (pk_count - 1) * 3, pk_count, {}, {}, pk_count + 1); + } + // We can have a script that reaches a stack size of 1000 during execution. + std::string ms_stack_limit; + auto count{998}; + for (auto i = 0; i < count; ++i) { + ms_stack_limit += "and_b(older(1),a:"; + } + ms_stack_limit += "pk(" + HexStr(g_testdata->pubkeys[0]) + ")"; + ms_stack_limit.insert(ms_stack_limit.end(), count, ')'); + const auto ms_stack_ok{miniscript::FromString(ms_stack_limit, tap_converter)}; + BOOST_CHECK(ms_stack_ok && ms_stack_ok->CheckStackSize()); + Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1); + // But one more element on the stack during execution will make it fail. And we'd detect that. + count++; + ms_stack_limit = "and_b(older(1),a:" + ms_stack_limit + ")"; + const auto ms_stack_nok{miniscript::FromString(ms_stack_limit, tap_converter)}; + BOOST_CHECK(ms_stack_nok && !ms_stack_nok->CheckStackSize()); + Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1); // Misc unit tests // A Script with a non minimal push is invalid std::vector<unsigned char> nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000"); const CScript nonminpush_script(nonminpush.begin(), nonminpush.end()); - BOOST_CHECK(miniscript::FromScript(nonminpush_script, CONVERTER) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminpush_script, wsh_converter) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminpush_script, tap_converter) == nullptr); // A non-minimal VERIFY (<key> CHECKSIG VERIFY 1) std::vector<unsigned char> nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951"); const CScript nonminverify_script(nonminverify.begin(), nonminverify.end()); - BOOST_CHECK(miniscript::FromScript(nonminverify_script, CONVERTER) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminverify_script, wsh_converter) == nullptr); + BOOST_CHECK(miniscript::FromScript(nonminverify_script, tap_converter) == nullptr); // A threshold as large as the number of subs is valid. - Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); + Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); // A threshold of 1 is valid. - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL); // A threshold with a k larger than the number of subs is invalid - Test("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_INVALID); + Test("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID); // A threshold with a k null is invalid - Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_INVALID); + Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID); // For CHECKMULTISIG the OP cost is the number of keys, but the stack size is the number of sigs (+1) - const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", CONVERTER); + const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", wsh_converter); BOOST_CHECK(ms_multi); BOOST_CHECK_EQUAL(*ms_multi->GetOps(), 4); // 3 pubkeys + CMS BOOST_CHECK_EQUAL(*ms_multi->GetStackSize(), 2); // 1 sig + dummy elem @@ -509,47 +673,46 @@ BOOST_AUTO_TEST_CASE(fixed_tests) // Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't // (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires // its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341). - const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER); + const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", wsh_converter); BOOST_CHECK(ms_minimalif && !ms_minimalif->IsValid()); // A Miniscript with duplicate keys is not sane - const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER); + const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter); BOOST_CHECK(ms_dup1); BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey()); // Same with a disjunction, and different key nodes (pk and pkh) - const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER); + const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter); BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey()); // Same when the duplicates are leaves or a larger tree - const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER); + const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter); BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey()); // Same when the duplicates are on different levels in the tree - const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER); + const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter); BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey()); // Sanity check the opposite is true, too. An otherwise sane Miniscript with no duplicate keys is sane. - const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", CONVERTER); + const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", wsh_converter); BOOST_CHECK(ms_nondup && ms_nondup->CheckDuplicateKey() && ms_nondup->IsSane()); // Test we find the first insane sub closer to be a leaf node. This fragment is insane for two reasons: // 1. It can be spent without a signature // 2. It contains timelock mixes // We'll report the timelock mix error, as it's "deeper" (closer to be a leaf node) than the "no 's' property" // error is. - const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", CONVERTER); + const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter); BOOST_CHECK(ms_ins && ms_ins->IsValid() && !ms_ins->IsSane()); const auto insane_sub = ms_ins->FindInsaneSub(); - BOOST_CHECK(insane_sub && *insane_sub->ToString(CONVERTER) == "and_b(after(1),a:after(1000000000))"); + BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))"); // Timelock tests - Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock - Test("after(1000000000)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock - Test("or_b(l:after(100),al:after(1000000000))", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid - Test("and_b(after(100),a:after(1000000000))", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid + Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock + Test("after(1000000000)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock + Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid + Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid /* This is correctly detected as non-malleable but for the wrong reason. The type system assumes that branches 1 and 2 can be spent together to create a non-malleble witness, but because of mixing of timelocks they cannot be spent together. But since exactly one of the two after's can be satisfied, the witness involving the key cannot be malleated. */ - Test("thresh(2,ltv:after(1000000000),altv:after(100),a:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", TESTMODE_VALID | TESTMODE_TIMELOCKMIX | TESTMODE_NONMAL); // thresh with k = 2 + Test("thresh(2,ltv:after(1000000000),altv:after(100),a:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "?", TESTMODE_VALID | TESTMODE_TIMELOCKMIX | TESTMODE_NONMAL); // thresh with k = 2 // This is actually non-malleable in practice, but we cannot detect it in type system. See above rationale - Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "?", TESTMODE_VALID); // thresh with k = 1 - + Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "?", "?", TESTMODE_VALID); // thresh with k = 1 g_testdata.reset(); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 5976aa3713..7c98c382e4 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -718,47 +718,55 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port) BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network) { - BOOST_CHECK(IsReachable(NET_IPV4)); - BOOST_CHECK(IsReachable(NET_IPV6)); - BOOST_CHECK(IsReachable(NET_ONION)); - BOOST_CHECK(IsReachable(NET_I2P)); - BOOST_CHECK(IsReachable(NET_CJDNS)); - - SetReachable(NET_IPV4, false); - SetReachable(NET_IPV6, false); - SetReachable(NET_ONION, false); - SetReachable(NET_I2P, false); - SetReachable(NET_CJDNS, false); - - BOOST_CHECK(!IsReachable(NET_IPV4)); - BOOST_CHECK(!IsReachable(NET_IPV6)); - BOOST_CHECK(!IsReachable(NET_ONION)); - BOOST_CHECK(!IsReachable(NET_I2P)); - BOOST_CHECK(!IsReachable(NET_CJDNS)); - - SetReachable(NET_IPV4, true); - SetReachable(NET_IPV6, true); - SetReachable(NET_ONION, true); - SetReachable(NET_I2P, true); - SetReachable(NET_CJDNS, true); - - BOOST_CHECK(IsReachable(NET_IPV4)); - BOOST_CHECK(IsReachable(NET_IPV6)); - BOOST_CHECK(IsReachable(NET_ONION)); - BOOST_CHECK(IsReachable(NET_I2P)); - BOOST_CHECK(IsReachable(NET_CJDNS)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS)); + + g_reachable_nets.Remove(NET_IPV4); + g_reachable_nets.Remove(NET_IPV6); + g_reachable_nets.Remove(NET_ONION); + g_reachable_nets.Remove(NET_I2P); + g_reachable_nets.Remove(NET_CJDNS); + + BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_CJDNS)); + + g_reachable_nets.Add(NET_IPV4); + g_reachable_nets.Add(NET_IPV6); + g_reachable_nets.Add(NET_ONION); + g_reachable_nets.Add(NET_I2P); + g_reachable_nets.Add(NET_CJDNS); + + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4)); + BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6)); + BOOST_CHECK(g_reachable_nets.Contains(NET_ONION)); + BOOST_CHECK(g_reachable_nets.Contains(NET_I2P)); + BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS)); } BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal) { - BOOST_CHECK(IsReachable(NET_UNROUTABLE)); - BOOST_CHECK(IsReachable(NET_INTERNAL)); - - SetReachable(NET_UNROUTABLE, false); - SetReachable(NET_INTERNAL, false); - - BOOST_CHECK(IsReachable(NET_UNROUTABLE)); // Ignored for both networks - BOOST_CHECK(IsReachable(NET_INTERNAL)); + // Should be reachable by default. + BOOST_CHECK(g_reachable_nets.Contains(NET_UNROUTABLE)); + BOOST_CHECK(g_reachable_nets.Contains(NET_INTERNAL)); + + g_reachable_nets.RemoveAll(); + + BOOST_CHECK(!g_reachable_nets.Contains(NET_UNROUTABLE)); + BOOST_CHECK(!g_reachable_nets.Contains(NET_INTERNAL)); + + g_reachable_nets.Add(NET_IPV4); + g_reachable_nets.Add(NET_IPV6); + g_reachable_nets.Add(NET_ONION); + g_reachable_nets.Add(NET_I2P); + g_reachable_nets.Add(NET_CJDNS); + g_reachable_nets.Add(NET_UNROUTABLE); + g_reachable_nets.Add(NET_INTERNAL); } CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4) @@ -776,13 +784,13 @@ BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr) { CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1 - SetReachable(NET_IPV4, true); - BOOST_CHECK(IsReachable(addr)); + g_reachable_nets.Add(NET_IPV4); + BOOST_CHECK(g_reachable_nets.Contains(addr)); - SetReachable(NET_IPV4, false); - BOOST_CHECK(!IsReachable(addr)); + g_reachable_nets.Remove(NET_IPV4); + BOOST_CHECK(!g_reachable_nets.Contains(addr)); - SetReachable(NET_IPV4, true); // have to reset this, because this is stateful. + g_reachable_nets.Add(NET_IPV4); // have to reset this, because this is stateful. } @@ -790,7 +798,7 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle) { CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000 - SetReachable(NET_IPV4, true); + g_reachable_nets.Add(NET_IPV4); BOOST_CHECK(!IsLocal(addr)); BOOST_CHECK(AddLocal(addr, 1000)); @@ -915,7 +923,7 @@ BOOST_AUTO_TEST_CASE(advertise_local_address) ConnectionType::OUTBOUND_FULL_RELAY, /*inbound_onion=*/false); }; - SetReachable(NET_CJDNS, true); + g_reachable_nets.Add(NET_CJDNS); CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE}; BOOST_REQUIRE(addr_ipv4.IsValid()); @@ -1080,9 +1088,9 @@ public: bool reject{false}; auto msg = m_transport.GetReceivedMessage({}, reject); if (reject) { - ret.push_back(std::nullopt); + ret.emplace_back(std::nullopt); } else { - ret.push_back(std::move(msg)); + ret.emplace_back(std::move(msg)); } progress = true; } diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp index 25fb5343e3..29d70cb5f8 100644 --- a/src/test/policy_fee_tests.cpp +++ b/src/test/policy_fee_tests.cpp @@ -13,7 +13,8 @@ BOOST_AUTO_TEST_SUITE(policy_fee_tests) BOOST_AUTO_TEST_CASE(FeeRounder) { - FeeFilterRounder fee_rounder{CFeeRate{1000}}; + FastRandomContext rng{/*fDeterministic=*/true}; + FeeFilterRounder fee_rounder{CFeeRate{1000}, rng}; // check that 1000 rounds to 974 or 1071 std::set<CAmount> results; diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp index 10205cd641..fb6a3614c0 100644 --- a/src/test/rbf_tests.cpp +++ b/src/test/rbf_tests.cpp @@ -27,7 +27,7 @@ static inline CTransactionRef make_tx(const std::vector<CTransactionRef>& inputs tx.vin[i].prevout.n = 0; // Add a witness so wtxid != txid CScriptWitness witness; - witness.stack.push_back(std::vector<unsigned char>(i + 10)); + witness.stack.emplace_back(i + 10); tx.vin[i].scriptWitness = witness; } for (size_t i = 0; i < output_values.size(); ++i) { diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 4cad3ec68e..0d2460c606 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -428,11 +428,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; for (int64_t i = 0; i < 100; i++) { - feerates.emplace_back(std::make_pair(1 ,1)); + feerates.emplace_back(1 ,1); } for (int64_t i = 0; i < 100; i++) { - feerates.emplace_back(std::make_pair(2 ,1)); + feerates.emplace_back(2 ,1); } CalculatePercentilesByWeight(result, feerates, total_weight); @@ -447,11 +447,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result2[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 9)); - feerates.emplace_back(std::make_pair(2 , 16)); //10th + 25th percentile - feerates.emplace_back(std::make_pair(4 ,50)); //50th + 75th percentile - feerates.emplace_back(std::make_pair(5 ,10)); - feerates.emplace_back(std::make_pair(9 ,15)); // 90th percentile + feerates.emplace_back(1, 9); + feerates.emplace_back(2 , 16); //10th + 25th percentile + feerates.emplace_back(4 ,50); //50th + 75th percentile + feerates.emplace_back(5 ,10); + feerates.emplace_back(9 ,15); // 90th percentile CalculatePercentilesByWeight(result2, feerates, total_weight); @@ -466,12 +466,12 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result3[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 9)); - feerates.emplace_back(std::make_pair(2 , 11)); // 10th percentile - feerates.emplace_back(std::make_pair(2 , 5)); // 25th percentile - feerates.emplace_back(std::make_pair(4 ,50)); //50th + 75th percentile - feerates.emplace_back(std::make_pair(5 ,10)); - feerates.emplace_back(std::make_pair(9 ,15)); // 90th percentile + feerates.emplace_back(1, 9); + feerates.emplace_back(2 , 11); // 10th percentile + feerates.emplace_back(2 , 5); // 25th percentile + feerates.emplace_back(4 ,50); //50th + 75th percentile + feerates.emplace_back(5 ,10); + feerates.emplace_back(9 ,15); // 90th percentile CalculatePercentilesByWeight(result3, feerates, total_weight); @@ -486,11 +486,11 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) CAmount result4[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 }; feerates.clear(); - feerates.emplace_back(std::make_pair(1, 100)); - feerates.emplace_back(std::make_pair(2, 1)); - feerates.emplace_back(std::make_pair(3, 1)); - feerates.emplace_back(std::make_pair(3, 1)); - feerates.emplace_back(std::make_pair(999999, 1)); + feerates.emplace_back(1, 100); + feerates.emplace_back(2, 1); + feerates.emplace_back(3, 1); + feerates.emplace_back(3, 1); + feerates.emplace_back(999999, 1); CalculatePercentilesByWeight(result4, feerates, total_weight); @@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(check_dup_param_names) auto make_rpc = [](std::vector<std::tuple<std::string, ParamType>> param_names) { std::vector<RPCArg> params; std::vector<RPCArg> options; - auto push_options = [&] { if (!options.empty()) params.emplace_back(RPCArg{strprintf("options%i", params.size()), RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", std::move(options)}); }; + auto push_options = [&] { if (!options.empty()) params.emplace_back(strprintf("options%i", params.size()), RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", std::move(options)); }; for (auto& [param_name, param_type] : param_names) { if (param_type == POSITIONAL) { push_options(); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 94656b229e..624d0b2c12 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1637,6 +1637,37 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags) BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS); } +/* Test bitcoinconsensus_verify_script returns spent outputs required err */ +BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_spent_outputs_required_err) +{ + unsigned int libconsensus_flags{bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT}; + const int nIn{0}; + + CScript scriptPubKey; + CScript scriptSig; + CScriptWitness wit; + + scriptPubKey << OP_EQUAL; + CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)}; + CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)}; + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << spendTx; + + bitcoinconsensus_error err; + int result{bitcoinconsensus_verify_script_with_spent_outputs(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nullptr, 0, nIn, libconsensus_flags, &err)}; + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); + + result = bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err); + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); + + result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err); + BOOST_CHECK_EQUAL(result, 0); + BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED); +} + #endif // defined(HAVE_CONSENSUS_LIB) static std::vector<unsigned int> AllConsensusFlags() @@ -1685,12 +1716,29 @@ static void AssetTest(const UniValue& test) PrecomputedTransactionData txdata; txdata.Init(tx, std::vector<CTxOut>(prevouts)); CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + +#if defined(HAVE_CONSENSUS_LIB) + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << tx; + std::vector<UTXO> utxos; + utxos.resize(prevouts.size()); + for (size_t i = 0; i < prevouts.size(); i++) { + utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data(); + utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size(); + utxos[i].value = prevouts[i].nValue; + } +#endif + for (const auto flags : ALL_CONSENSUS_FLAGS) { // "final": true tests are valid for all flags. Others are only valid with flags that are // a subset of test_flags. if (fin || ((flags & test_flags) == flags)) { bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); BOOST_CHECK(ret); +#if defined(HAVE_CONSENSUS_LIB) + int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr); + BOOST_CHECK(lib_ret == 1); +#endif } } } @@ -1702,11 +1750,28 @@ static void AssetTest(const UniValue& test) PrecomputedTransactionData txdata; txdata.Init(tx, std::vector<CTxOut>(prevouts)); CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + +#if defined(HAVE_CONSENSUS_LIB) + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << tx; + std::vector<UTXO> utxos; + utxos.resize(prevouts.size()); + for (size_t i = 0; i < prevouts.size(); i++) { + utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data(); + utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size(); + utxos[i].value = prevouts[i].nValue; + } +#endif + for (const auto flags : ALL_CONSENSUS_FLAGS) { // If a test is supposed to fail with test_flags, it should also fail with any superset thereof. if ((flags & test_flags) == test_flags) { bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); BOOST_CHECK(!ret); +#if defined(HAVE_CONSENSUS_LIB) + int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr); + BOOST_CHECK(lib_ret == 0); +#endif } } } diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index eb11df0497..fa8ceb8dd6 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -119,16 +119,16 @@ static void CheckValues(const common::Settings& settings, const std::string& sin BOOST_AUTO_TEST_CASE(Simple) { common::Settings settings; - settings.command_line_options["name"].push_back("val1"); - settings.command_line_options["name"].push_back("val2"); - settings.ro_config["section"]["name"].push_back(2); + settings.command_line_options["name"].emplace_back("val1"); + settings.command_line_options["name"].emplace_back("val2"); + settings.ro_config["section"]["name"].emplace_back(2); // The last given arg takes precedence when specified via commandline. CheckValues(settings, R"("val2")", R"(["val1","val2",2])"); common::Settings settings2; - settings2.ro_config["section"]["name"].push_back("val2"); - settings2.ro_config["section"]["name"].push_back("val3"); + settings2.ro_config["section"]["name"].emplace_back("val2"); + settings2.ro_config["section"]["name"].emplace_back("val3"); // The first given arg takes precedence when specified via config file. CheckValues(settings2, R"("val2")", R"(["val2","val3"])"); @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(Simple) BOOST_AUTO_TEST_CASE(NullOverride) { common::Settings settings; - settings.command_line_options["name"].push_back("value"); + settings.command_line_options["name"].emplace_back("value"); BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false, false).write().c_str()); settings.forced_settings["name"] = {}; BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false, false).write().c_str()); @@ -202,11 +202,11 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup) std::vector<common::SettingsValue>& dest) { if (action == SET || action == SECTION_SET) { for (int i = 0; i < 2; ++i) { - dest.push_back(value_prefix + ToString(++value_suffix)); + dest.emplace_back(value_prefix + ToString(++value_suffix)); desc += " " + name_prefix + name + "=" + dest.back().get_str(); } } else if (action == NEGATE || action == SECTION_NEGATE) { - dest.push_back(false); + dest.emplace_back(false); desc += " " + name_prefix + "no" + name; } }; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 178b16772b..9653edd84b 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -100,7 +100,7 @@ void static RandomTransaction(CMutableTransaction& tx, bool fSingle) int ins = (InsecureRandBits(2)) + 1; int outs = fSingle ? ins : (InsecureRandBits(2)) + 1; for (int in = 0; in < ins; in++) { - tx.vin.push_back(CTxIn()); + tx.vin.emplace_back(); CTxIn &txin = tx.vin.back(); txin.prevout.hash = InsecureRand256(); txin.prevout.n = InsecureRandBits(2); @@ -108,7 +108,7 @@ void static RandomTransaction(CMutableTransaction& tx, bool fSingle) txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : std::numeric_limits<uint32_t>::max(); } for (int out = 0; out < outs; out++) { - tx.vout.push_back(CTxOut()); + tx.vout.emplace_back(); CTxOut &txout = tx.vout.back(); txout.nValue = InsecureRandMoneyAmount(); RandomScript(txout.scriptPubKey); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index ec9490d745..c0bed50e1d 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -160,8 +160,8 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey)); CScript scriptSig = CScript(); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); @@ -189,8 +189,8 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig)); scriptSig = CScript() << ToByteVector(scriptSig); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1); @@ -203,9 +203,9 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); CScript scriptSig = CScript(); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); @@ -220,9 +220,9 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); CScript scriptSig = CScript() << ToByteVector(redeemScript); CScriptWitness scriptWitness; - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(0)); - scriptWitness.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(0); + scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp index 571b58156f..4d9a5ef7f3 100644 --- a/src/test/txpackage_tests.cpp +++ b/src/test/txpackage_tests.cpp @@ -162,6 +162,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) BOOST_CHECK_EQUAL(state.GetResult(), PackageValidationResult::PCKG_POLICY); BOOST_CHECK_EQUAL(state.GetRejectReason(), "package-not-sorted"); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); + BOOST_CHECK(IsChildWithParentsTree({tx_parent, tx_child})); } // 24 Parents and 1 Child @@ -172,9 +173,9 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) auto parent = MakeTransactionRef(CreateValidMempoolTransaction(m_coinbase_txns[i + 1], 0, 0, coinbaseKey, spk, CAmount(48 * COIN), false)); package.emplace_back(parent); - child.vin.push_back(CTxIn(COutPoint(parent->GetHash(), 0))); + child.vin.emplace_back(COutPoint(parent->GetHash(), 0)); } - child.vout.push_back(CTxOut(47 * COIN, spk2)); + child.vout.emplace_back(47 * COIN, spk2); // The child must be in the package. BOOST_CHECK(!IsChildWithParents(package)); @@ -187,6 +188,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) PackageValidationState state; BOOST_CHECK(CheckPackage(package, state)); BOOST_CHECK(IsChildWithParents(package)); + BOOST_CHECK(IsChildWithParentsTree(package)); package.erase(package.begin()); BOOST_CHECK(IsChildWithParents(package)); @@ -199,26 +201,27 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) // 2 Parents and 1 Child where one parent depends on the other. { CMutableTransaction mtx_parent; - mtx_parent.vin.push_back(CTxIn(COutPoint(m_coinbase_txns[0]->GetHash(), 0))); - mtx_parent.vout.push_back(CTxOut(20 * COIN, spk)); - mtx_parent.vout.push_back(CTxOut(20 * COIN, spk2)); + mtx_parent.vin.emplace_back(COutPoint(m_coinbase_txns[0]->GetHash(), 0)); + mtx_parent.vout.emplace_back(20 * COIN, spk); + mtx_parent.vout.emplace_back(20 * COIN, spk2); CTransactionRef tx_parent = MakeTransactionRef(mtx_parent); CMutableTransaction mtx_parent_also_child; - mtx_parent_also_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 0))); - mtx_parent_also_child.vout.push_back(CTxOut(20 * COIN, spk)); + mtx_parent_also_child.vin.emplace_back(COutPoint(tx_parent->GetHash(), 0)); + mtx_parent_also_child.vout.emplace_back(20 * COIN, spk); CTransactionRef tx_parent_also_child = MakeTransactionRef(mtx_parent_also_child); CMutableTransaction mtx_child; - mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent->GetHash(), 1))); - mtx_child.vin.push_back(CTxIn(COutPoint(tx_parent_also_child->GetHash(), 0))); - mtx_child.vout.push_back(CTxOut(39 * COIN, spk)); + mtx_child.vin.emplace_back(COutPoint(tx_parent->GetHash(), 1)); + mtx_child.vin.emplace_back(COutPoint(tx_parent_also_child->GetHash(), 0)); + mtx_child.vout.emplace_back(39 * COIN, spk); CTransactionRef tx_child = MakeTransactionRef(mtx_child); PackageValidationState state; BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child, tx_child})); + BOOST_CHECK(!IsChildWithParentsTree({tx_parent, tx_parent_also_child, tx_child})); // IsChildWithParents does not detect unsorted parents. BOOST_CHECK(IsChildWithParents({tx_parent_also_child, tx_parent, tx_child})); BOOST_CHECK(CheckPackage({tx_parent, tx_parent_also_child, tx_child}, state)); @@ -298,7 +301,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup) // missing inputs, so the package validation isn't expected to happen. { CScriptWitness bad_witness; - bad_witness.stack.push_back(std::vector<unsigned char>(1)); + bad_witness.stack.emplace_back(1); CMutableTransaction mtx_parent_invalid{mtx_parent}; mtx_parent_invalid.vin[0].scriptWitness = bad_witness; CTransactionRef tx_parent_invalid = MakeTransactionRef(mtx_parent_invalid); @@ -319,7 +322,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup) } // Child with missing parent. - mtx_child.vin.push_back(CTxIn(COutPoint(package_unrelated[0]->GetHash(), 0))); + mtx_child.vin.emplace_back(COutPoint(package_unrelated[0]->GetHash(), 0)); Package package_missing_parent; package_missing_parent.push_back(tx_parent); package_missing_parent.push_back(MakeTransactionRef(mtx_child)); @@ -401,12 +404,12 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) // Make two children with the same txid but different witnesses. CScriptWitness witness1; - witness1.stack.push_back(std::vector<unsigned char>(1)); - witness1.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + witness1.stack.emplace_back(1); + witness1.stack.emplace_back(witnessScript.begin(), witnessScript.end()); CScriptWitness witness2(witness1); - witness2.stack.push_back(std::vector<unsigned char>(2)); - witness2.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end())); + witness2.stack.emplace_back(2); + witness2.stack.emplace_back(witnessScript.begin(), witnessScript.end()); CKey child_key; child_key.MakeNewKey(true); @@ -526,7 +529,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript acs_script = CScript() << OP_TRUE; CScript acs_spk = GetScriptForDestination(WitnessV0ScriptHash(acs_script)); CScriptWitness acs_witness; - acs_witness.stack.push_back(std::vector<unsigned char>(acs_script.begin(), acs_script.end())); + acs_witness.stack.emplace_back(acs_script.begin(), acs_script.end()); // parent1 will already be in the mempool auto mtx_parent1 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0, @@ -540,11 +543,11 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript grandparent2_script = CScript() << OP_DROP << OP_TRUE; CScript grandparent2_spk = GetScriptForDestination(WitnessV0ScriptHash(grandparent2_script)); CScriptWitness parent2_witness1; - parent2_witness1.stack.push_back(std::vector<unsigned char>(1)); - parent2_witness1.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end())); + parent2_witness1.stack.emplace_back(1); + parent2_witness1.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end()); CScriptWitness parent2_witness2; - parent2_witness2.stack.push_back(std::vector<unsigned char>(2)); - parent2_witness2.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end())); + parent2_witness2.stack.emplace_back(2); + parent2_witness2.stack.emplace_back(grandparent2_script.begin(), grandparent2_script.end()); // Create grandparent2 creating an output with multiple spending paths. Submit to mempool. auto mtx_grandparent2 = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[2], /*input_vout=*/0, @@ -590,13 +593,13 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup) CScript mixed_child_spk = GetScriptForDestination(WitnessV0KeyHash(mixed_grandchild_key.GetPubKey())); CMutableTransaction mtx_mixed_child; - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent1->GetHash(), 0))); - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent2_v1->GetHash(), 0))); - mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent3->GetHash(), 0))); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent1->GetHash(), 0)); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent2_v1->GetHash(), 0)); + mtx_mixed_child.vin.emplace_back(COutPoint(ptx_parent3->GetHash(), 0)); mtx_mixed_child.vin[0].scriptWitness = acs_witness; mtx_mixed_child.vin[1].scriptWitness = acs_witness; mtx_mixed_child.vin[2].scriptWitness = acs_witness; - mtx_mixed_child.vout.push_back(CTxOut((48 + 49 + 50 - 1) * COIN, mixed_child_spk)); + mtx_mixed_child.vout.emplace_back((48 + 49 + 50 - 1) * COIN, mixed_child_spk); CTransactionRef ptx_mixed_child = MakeTransactionRef(mtx_mixed_child); package_mixed.push_back(ptx_mixed_child); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2947bc3fcb..e70c105c8a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -256,6 +256,7 @@ TestingSetup::TestingSetup( m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); // Deterministic randomness for tests. PeerManager::Options peerman_opts; ApplyArgsManOptions(*m_node.args, peerman_opts); + peerman_opts.deterministic_rng = true; m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman, *m_node.mempool, peerman_opts); @@ -399,7 +400,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex for (size_t n{0}; n < num_inputs; ++n) { if (unspent_prevouts.empty()) break; const auto& [prevout, amount] = unspent_prevouts.front(); - mtx.vin.push_back(CTxIn(prevout, CScript())); + mtx.vin.emplace_back(prevout, CScript()); total_in += amount; unspent_prevouts.pop_front(); } @@ -408,7 +409,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex const CAmount amount_per_output = (total_in - fee) / num_outputs; for (size_t n{0}; n < num_outputs; ++n) { CScript spk = CScript() << CScriptNum(num_transactions + n); - mtx.vout.push_back(CTxOut(amount_per_output, spk)); + mtx.vout.emplace_back(amount_per_output, spk); } CTransactionRef ptx = MakeTransactionRef(mtx); mempool_transactions.push_back(ptx); @@ -417,7 +418,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex // it can be used to build a more complex transaction graph. Insert randomly into // unspent_prevouts for extra randomness in the resulting structures. for (size_t n{0}; n < num_outputs; ++n) { - unspent_prevouts.push_back(std::make_pair(COutPoint(ptx->GetHash(), n), amount_per_output)); + unspent_prevouts.emplace_back(COutPoint(ptx->GetHash(), n), amount_per_output); std::swap(unspent_prevouts.back(), unspent_prevouts[det_rand.randrange(unspent_prevouts.size())]); } } @@ -447,8 +448,8 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate) // Manually create an invalid transaction. Manually set the fee in the CTxMemPoolEntry to // achieve the exact target feerate. CMutableTransaction mtx = CMutableTransaction(); - mtx.vin.push_back(CTxIn{COutPoint{g_insecure_rand_ctx.rand256(), 0}}); - mtx.vout.push_back(CTxOut(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE)))); + mtx.vin.emplace_back(COutPoint{g_insecure_rand_ctx.rand256(), 0}); + mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE))); const auto tx{MakeTransactionRef(mtx)}; LockPoints lp; // The new mempool min feerate is equal to the removed package's feerate + incremental feerate. diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 67f71bd266..7d6c96ab40 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1023,9 +1023,9 @@ BOOST_AUTO_TEST_CASE(test_FormatParagraph) BOOST_AUTO_TEST_CASE(test_FormatSubVersion) { std::vector<std::string> comments; - comments.push_back(std::string("comment1")); + comments.emplace_back("comment1"); std::vector<std::string> comments2; - comments2.push_back(std::string("comment1")); + comments2.emplace_back("comment1"); comments2.push_back(SanitizeString(std::string("Comment2; .,_?@-; !\"#$%&'()*+/<=>[]\\^`{|}~"), SAFE_CHARS_UA_COMMENT)); // Semicolon is discouraged but not forbidden by BIP-0014 BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector<std::string>()),std::string("/Test:9.99.0/")); BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:9.99.0(comment1)/")); diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp index ae913939e8..45d3a58fd3 100644 --- a/src/test/util_threadnames_tests.cpp +++ b/src/test/util_threadnames_tests.cpp @@ -40,7 +40,7 @@ std::set<std::string> RenameEnMasse(int num_threads) threads.reserve(num_threads); for (int i = 0; i < num_threads; ++i) { - threads.push_back(std::thread(RenameThisThread, i)); + threads.emplace_back(RenameThisThread, i); } for (std::thread& thread : threads) thread.join(); diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 411371f7c1..64cb5522eb 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -117,7 +117,7 @@ std::shared_ptr<const CBlock> MinerTestingSetup::BadBlock(const uint256& prev_ha auto pblock = Block(prev_hash); CMutableTransaction coinbase_spend; - coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0)); + coinbase_spend.vin.emplace_back(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0); coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]); CTransactionRef tx = MakeTransactionRef(coinbase_spend); @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) std::vector<CTransactionRef> txs; for (int num_txs = 22; num_txs > 0; --num_txs) { CMutableTransaction mtx; - mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}}); + mtx.vin.emplace_back(COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}); mtx.vin[0].scriptWitness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE); mtx.vout.push_back(last_mined->vtx[0]->vout[1]); mtx.vout[0].nValue -= 1000; diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index d34d98c219..2692037273 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -138,11 +138,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo) const auto out110 = *params->AssumeutxoForHeight(110); BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618"); - BOOST_CHECK_EQUAL(out110.nChainTx, 110U); + BOOST_CHECK_EQUAL(out110.nChainTx, 111U); const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")); BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618"); - BOOST_CHECK_EQUAL(out110_2.nChainTx, 110U); + BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 4c99aa5746..60cf31a964 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -409,7 +409,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe // // If NET_ONION is not reachable, then none of -proxy or -onion was given. // Since we are here, then -torcontrol and -torpassword were given. - SetReachable(NET_ONION, true); + g_reachable_nets.Add(NET_ONION); } } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index e021cfb06e..461662ad93 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1021,7 +1021,7 @@ void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx) { for (unsigned int n = 0; n < tx->vout.size(); ++n) { m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false)); - m_non_base_coins.emplace(COutPoint(tx->GetHash(), n)); + m_non_base_coins.emplace(tx->GetHash(), n); } } void CCoinsViewMemPool::Reset() diff --git a/src/validation.cpp b/src/validation.cpp index 30b3dde74f..290db8c9b2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -68,8 +68,8 @@ #include <numeric> #include <optional> #include <string> -#include <utility> #include <tuple> +#include <utility> using kernel::CCoinsStats; using kernel::CoinStatsHashType; @@ -1287,6 +1287,12 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std:: // Transactions must meet two minimum feerates: the mempool minimum fee and min relay fee. // For transactions consisting of exactly one child and its parents, it suffices to use the // package feerate (total modified fees / total virtual size) to check this requirement. + // Note that this is an aggregate feerate; this function has not checked that there are transactions + // too low feerate to pay for themselves, or that the child transactions are higher feerate than + // their parents. Using aggregate feerate may allow "parents pay for child" behavior and permit + // a child that is below mempool minimum feerate. To avoid these behaviors, callers of + // AcceptMultipleTransactions need to restrict txns topology (e.g. to ancestor sets) and check + // the feerates of individuals and subsets. const auto m_total_vsize = std::accumulate(workspaces.cbegin(), workspaces.cend(), int64_t{0}, [](int64_t sum, auto& ws) { return sum + ws.m_vsize; }); const auto m_total_modified_fees = std::accumulate(workspaces.cbegin(), workspaces.cend(), CAmount{0}, @@ -2407,7 +2413,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, CTxUndo undoDummy; if (i > 0) { - blockundo.vtxundo.push_back(CTxUndo()); + blockundo.vtxundo.emplace_back(); } UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); } @@ -2990,7 +2996,7 @@ CBlockIndex* Chainstate::FindMostWorkChain() CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !m_chain.Contains(pindexTest)) { - assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0); + assert(pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0); // Pruned nodes may have entries in setBlockIndexCandidates for // which block files have been deleted. Remove those as candidates @@ -3345,7 +3351,7 @@ bool Chainstate::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) // call preciousblock 2**31-1 times on the same set of tips... m_chainman.nBlockReverseSequenceId--; } - if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) { + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveNumChainTxs()) { setBlockIndexCandidates.insert(pindex); PruneBlockIndexCandidates(); } @@ -3393,7 +3399,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde if (!m_chain.Contains(candidate) && !CBlockIndexWorkComparator()(candidate, pindex->pprev) && candidate->IsValid(BLOCK_VALID_TRANSACTIONS) && - candidate->HaveTxsDownloaded()) { + candidate->HaveNumChainTxs()) { candidate_blocks_by_work.insert(std::make_pair(candidate->nChainWork, candidate)); } } @@ -3482,7 +3488,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde // Loop back over all block index entries and add any missing entries // to setBlockIndexCandidates. for (auto& [_, block_index] : m_blockman.m_block_index) { - if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) { + if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) { setBlockIndexCandidates.insert(&block_index); } } @@ -3514,7 +3520,7 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) { if (!block_index.IsValid() && block_index.GetAncestor(nHeight) == pindex) { block_index.nStatus &= ~BLOCK_FAILED_MASK; m_blockman.m_dirty_blockindex.insert(&block_index); - if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) { + if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) { setBlockIndexCandidates.insert(&block_index); } if (&block_index == m_chainman.m_best_invalid) { @@ -3577,7 +3583,7 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); m_blockman.m_dirty_blockindex.insert(pindexNew); - if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) { + if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveNumChainTxs()) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. std::deque<CBlockIndex*> queue; queue.push_back(pindexNew); @@ -4560,7 +4566,7 @@ bool ChainstateManager::LoadBlockIndex() // here. if (pindex == GetSnapshotBaseBlock() || (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && - (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) { + (pindex->HaveNumChainTxs() || pindex->pprev == nullptr))) { for (Chainstate* chainstate : GetAll()) { chainstate->TryAddBlockIndexCandidate(pindex); @@ -4838,10 +4844,14 @@ void ChainstateManager::CheckBlockIndex() CBlockIndex* pindexFirstAssumeValid = nullptr; // Oldest ancestor of pindex which has BLOCK_ASSUMED_VALID while (pindex != nullptr) { nNodes++; - if (pindex->pprev && pindex->nTx > 0) { - // nChainTx should increase monotonically - assert(pindex->pprev->nChainTx <= pindex->nChainTx); - } + // Make sure nChainTx sum is correctly computed. + unsigned int prev_chain_tx = pindex->pprev ? pindex->pprev->nChainTx : 0; + assert((pindex->nChainTx == pindex->nTx + prev_chain_tx) + // For testing, allow transaction counts to be completely unset. + || (pindex->nChainTx == 0 && pindex->nTx == 0) + // For testing, allow this nChainTx to be unset if previous is also unset. + || (pindex->nChainTx == 0 && prev_chain_tx == 0 && pindex->pprev)); + if (pindexFirstAssumeValid == nullptr && pindex->nStatus & BLOCK_ASSUMED_VALID) pindexFirstAssumeValid = pindex; if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) { @@ -4880,7 +4890,7 @@ void ChainstateManager::CheckBlockIndex() } } } - if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock) + if (!pindex->HaveNumChainTxs()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock) // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. // Unless these indexes are assumed valid and pending block download on a @@ -4910,9 +4920,9 @@ void ChainstateManager::CheckBlockIndex() // actually seen a block's transactions. assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. } - // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded(). - assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded()); - assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded()); + // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveNumChainTxs(). + assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveNumChainTxs()); + assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveNumChainTxs()); assert(pindex->nHeight == nHeight); // nHeight must be consistent. assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. @@ -5361,7 +5371,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // ActivateSnapshot(), but is done so that we avoid doing the long work of staging // a snapshot that isn't actually usable. if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) { - LogPrintf("[snapshot] activation failed - height does not exceed active chainstate\n"); + LogPrintf("[snapshot] activation failed - work does not exceed active chainstate\n"); return false; } @@ -5762,7 +5772,7 @@ ChainstateManager::~ChainstateManager() m_versionbitscache.Clear(); } -bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool) +bool ChainstateManager::DetectSnapshotChainstate() { assert(!m_snapshot_chainstate); std::optional<fs::path> path = node::FindSnapshotChainstateDir(m_options.datadir); diff --git a/src/validation.h b/src/validation.h index 94a00e44a4..7ce60da634 100644 --- a/src/validation.h +++ b/src/validation.h @@ -836,9 +836,10 @@ private: //! Once this pointer is set to a corresponding chainstate, it will not //! be reset until init.cpp:Shutdown(). //! - //! This is especially important when, e.g., calling ActivateBestChain() - //! on all chainstates because we are not able to hold ::cs_main going into - //! that call. + //! It is important for the pointer to not be deleted until shutdown, + //! because cs_main is not always held when the pointer is accessed, for + //! example when calling ActivateBestChain, so there's no way you could + //! prevent code from using the pointer while deleting it. std::unique_ptr<Chainstate> m_ibd_chainstate GUARDED_BY(::cs_main); //! A chainstate initialized on the basis of a UTXO snapshot. If this is @@ -847,17 +848,14 @@ private: //! Once this pointer is set to a corresponding chainstate, it will not //! be reset until init.cpp:Shutdown(). //! - //! This is especially important when, e.g., calling ActivateBestChain() - //! on all chainstates because we are not able to hold ::cs_main going into - //! that call. + //! It is important for the pointer to not be deleted until shutdown, + //! because cs_main is not always held when the pointer is accessed, for + //! example when calling ActivateBestChain, so there's no way you could + //! prevent code from using the pointer while deleting it. std::unique_ptr<Chainstate> m_snapshot_chainstate GUARDED_BY(::cs_main); //! Points to either the ibd or snapshot chainstate; indicates our //! most-work chain. - //! - //! This is especially important when, e.g., calling ActivateBestChain() - //! on all chainstates because we are not able to hold ::cs_main going into - //! that call. Chainstate* m_active_chainstate GUARDED_BY(::cs_main) {nullptr}; CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr}; @@ -1203,7 +1201,7 @@ public: //! When starting up, search the datadir for a chainstate based on a UTXO //! snapshot that is in the process of being validated. - bool DetectSnapshotChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool DetectSnapshotChainstate() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); void ResetChainstates() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index d100b69d3c..3e1ec667e0 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -573,13 +573,13 @@ RPCHelpMan importwallet() } } nTimeBegin = std::min(nTimeBegin, nTime); - keys.push_back(std::make_tuple(key, nTime, fLabel, strLabel)); + keys.emplace_back(key, nTime, fLabel, strLabel); } else if(IsHex(vstr[0])) { std::vector<unsigned char> vData(ParseHex(vstr[0])); CScript script = CScript(vData.begin(), vData.end()); int64_t birth_time = ParseISO8601DateTime(vstr[1]); if (birth_time > 0) nTimeBegin = std::min(nTimeBegin, birth_time); - scripts.push_back(std::pair<CScript, int64_t>(script, birth_time)); + scripts.emplace_back(script, birth_time); } } file.close(); @@ -759,7 +759,7 @@ RPCHelpMan dumpwallet() std::vector<std::pair<int64_t, CKeyID> > vKeyBirth; vKeyBirth.reserve(mapKeyBirth.size()); for (const auto& entry : mapKeyBirth) { - vKeyBirth.push_back(std::make_pair(entry.second, entry.first)); + vKeyBirth.emplace_back(entry.second, entry.first); } mapKeyBirth.clear(); std::sort(vKeyBirth.begin(), vKeyBirth.end()); diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 3774e6a3ef..164ce9afed 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -343,7 +343,7 @@ static RPCHelpMan createwallet() {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."}, {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."}, {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation." - " Setting to \"false\" will create a legacy wallet; however, the legacy wallet type is being deprecated and" + " Setting to \"false\" will create a legacy wallet; This is only possible with the -deprecatedrpc=create_bdb setting because, the legacy wallet type is being deprecated and" " support for creating and opening legacy wallets will be removed in the future."}, {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."}, {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."}, @@ -389,11 +389,16 @@ static RPCHelpMan createwallet() if (!request.params[4].isNull() && request.params[4].get_bool()) { flags |= WALLET_FLAG_AVOID_REUSE; } - if (request.params[5].isNull() || request.params[5].get_bool()) { + if (self.Arg<bool>(5)) { #ifndef USE_SQLITE throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)"); #endif flags |= WALLET_FLAG_DESCRIPTORS; + } else { + if (!context.chain->rpcEnableDeprecated("create_bdb")) { + throw JSONRPCError(RPC_WALLET_ERROR, "BDB wallet creation is deprecated and will be removed in a future release." + " In this release it can be re-enabled temporarily with the -deprecatedrpc=create_bdb setting."); + } } if (!request.params[7].isNull() && request.params[7].get_bool()) { #ifdef ENABLE_EXTERNAL_SIGNER diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index 0a0745b1c5..04c02b0dcc 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -146,7 +146,7 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values.")); break; } - salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); + salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex)); } } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 20f735da12..bc3327cdb2 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1902,7 +1902,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() std::string desc_str; bool watchonly = !desc->ToPrivateString(*this, desc_str); if (watchonly && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - out.watch_descs.push_back({desc->ToString(), creation_time}); + out.watch_descs.emplace_back(desc->ToString(), creation_time); // Get the scriptPubKeys without writing this to the wallet FlatSigningProvider provider; @@ -1971,14 +1971,14 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() assert(IsMine(sh_spk) == ISMINE_NO && IsMine(witprog) == ISMINE_NO && IsMine(sh_wsh_spk) == ISMINE_NO); std::unique_ptr<Descriptor> sh_desc = InferDescriptor(sh_spk, *GetSolvingProvider(sh_spk)); - out.solvable_descs.push_back({sh_desc->ToString(), creation_time}); + out.solvable_descs.emplace_back(sh_desc->ToString(), creation_time); const auto desc = InferDescriptor(witprog, *this); if (desc->IsSolvable()) { std::unique_ptr<Descriptor> wsh_desc = InferDescriptor(witprog, *GetSolvingProvider(witprog)); - out.solvable_descs.push_back({wsh_desc->ToString(), creation_time}); + out.solvable_descs.emplace_back(wsh_desc->ToString(), creation_time); std::unique_ptr<Descriptor> sh_wsh_desc = InferDescriptor(sh_wsh_spk, *GetSolvingProvider(sh_wsh_spk)); - out.solvable_descs.push_back({sh_wsh_desc->ToString(), creation_time}); + out.solvable_descs.emplace_back(sh_wsh_desc->ToString(), creation_time); } } } diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 7e6fba33aa..8314a2ddfa 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -1151,7 +1151,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal( // behavior." const uint32_t nSequence{coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : CTxIn::MAX_SEQUENCE_NONFINAL}; for (const auto& coin : selected_coins) { - txNew.vin.push_back(CTxIn(coin->outpoint, CScript(), nSequence)); + txNew.vin.emplace_back(coin->outpoint, CScript(), nSequence); } DiscourageFeeSniping(txNew, rng_fast, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight()); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 21ed52731a..ad4bb3a9d2 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t index, const CKey& key, const CScript& pubkey) { CMutableTransaction mtx; - mtx.vout.push_back({from.vout[index].nValue - DEFAULT_TRANSACTION_MAXFEE, pubkey}); + mtx.vout.emplace_back(from.vout[index].nValue - DEFAULT_TRANSACTION_MAXFEE, pubkey); mtx.vin.push_back({CTxIn{from.GetHash(), index}}); FillableSigningProvider keystore; keystore.AddKey(key); @@ -945,8 +945,8 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup) const auto op_dest{*Assert(wallet.GetNewDestination(OutputType::BECH32M, ""))}; CMutableTransaction mtx; - mtx.vout.push_back({COIN, GetScriptForDestination(op_dest)}); - mtx.vin.push_back(CTxIn(g_insecure_rand_ctx.rand256(), 0)); + mtx.vout.emplace_back(COIN, GetScriptForDestination(op_dest)); + mtx.vin.emplace_back(g_insecure_rand_ctx.rand256(), 0); const auto& tx_id_to_spend = wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInMempool{})->GetHash(); { @@ -961,7 +961,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup) // 2) Verify that the available balance of this new tx and the old one is updated (prev tx is marked dirty) mtx.vin.clear(); - mtx.vin.push_back(CTxIn(tx_id_to_spend, 0)); + mtx.vin.emplace_back(tx_id_to_spend, 0); wallet.transactionAddedToMempool(MakeTransactionRef(mtx)); const uint256& good_tx_id = mtx.GetHash(); @@ -982,7 +982,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup) // verify that we are not moving forward if the wallet cannot store it GetMockableDatabase(wallet).m_pass = false; mtx.vin.clear(); - mtx.vin.push_back(CTxIn(good_tx_id, 0)); + mtx.vin.emplace_back(good_tx_id, 0); BOOST_CHECK_EXCEPTION(wallet.transactionAddedToMempool(MakeTransactionRef(mtx)), std::runtime_error, HasReason("DB error adding transaction to wallet, write failed")); diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index 1a79d7db4e..3b9cde9832 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -29,10 +29,12 @@ struct TxStateConfirmed { int position_in_block; explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {} + std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); } }; //! State of transaction added to mempool. struct TxStateInMempool { + std::string toString() const { return strprintf("InMempool"); } }; //! State of rejected transaction that conflicts with a confirmed block. @@ -41,6 +43,7 @@ struct TxStateConflicted { int conflicting_block_height; explicit TxStateConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {} + std::string toString() const { return strprintf("Conflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); } }; //! State of transaction not confirmed or conflicting with a known block and @@ -51,6 +54,7 @@ struct TxStateInactive { bool abandoned; explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {} + std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); } }; //! State of transaction loaded in an unrecognized state with unexpected hash or @@ -62,6 +66,7 @@ struct TxStateUnrecognized { int index; TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {} + std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); } }; //! All possible CWalletTx states @@ -109,6 +114,12 @@ static inline int TxStateSerializedIndex(const TxState& state) }, state); } +//! Return TxState or SyncTxState as a string for logging or debugging. +template<typename T> +std::string TxStateString(const T& state) +{ + return std::visit([](const auto& s) { return s.toString(); }, state); +} /** * Cachable amount subdivided into watchonly and spendable parts. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c240e88531..cb8671c8e7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1130,7 +1130,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const } //// debug print - WalletLogPrintf("AddToWallet %s %s%s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + WalletLogPrintf("AddToWallet %s %s%s %s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""), TxStateString(state)); // Write to disk if (fInsertedNew || fUpdated) @@ -4230,7 +4230,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle // Remember this wallet's walletdir to remove after unloading std::vector<fs::path> wallet_dirs; - wallet_dirs.push_back(fs::PathFromString(local_wallet->GetDatabase().Filename()).parent_path()); + wallet_dirs.emplace_back(fs::PathFromString(local_wallet->GetDatabase().Filename()).parent_path()); // Unload the wallet locally assert(local_wallet.use_count() == 1); @@ -4243,7 +4243,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle // Get the directories to remove after unloading for (std::shared_ptr<CWallet>& w : created_wallets) { - wallet_dirs.push_back(fs::PathFromString(w->GetDatabase().Filename()).parent_path()); + wallet_dirs.emplace_back(fs::PathFromString(w->GetDatabase().Filename()).parent_path()); } // Unload the wallets diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py index be0715df32..b422cc8f2d 100755 --- a/test/functional/feature_assumeutxo.py +++ b/test/functional/feature_assumeutxo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-present The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test for assumeutxo, a means of quickly bootstrapping a node using @@ -17,10 +17,8 @@ The assumeutxo value generated and used here is committed to in Interesting test cases could be loading an assumeutxo snapshot file with: -- TODO: An invalid hash -- TODO: Valid hash but invalid snapshot file (bad coin height or truncated file or +- TODO: Valid hash but invalid snapshot file (bad coin height or bad other serialization) -- TODO: Valid snapshot file, but referencing an unknown block - TODO: Valid snapshot file, but referencing a snapshot block that turns out to be invalid, or has an invalid parent - TODO: Valid snapshot file and snapshot block, but the block is not on the @@ -36,7 +34,10 @@ Interesting starting states could be loading a snapshot when the current chain t """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, wait_until_helper +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) START_HEIGHT = 199 SNAPSHOT_BASE_HEIGHT = 299 @@ -62,6 +63,42 @@ class AssumeutxoTest(BitcoinTestFramework): self.add_nodes(3) self.start_nodes(extra_args=self.extra_args) + def test_invalid_snapshot_scenarios(self, valid_snapshot_path): + self.log.info("Test different scenarios of loading invalid snapshot files") + with open(valid_snapshot_path, 'rb') as f: + valid_snapshot_contents = f.read() + bad_snapshot_path = valid_snapshot_path + '.mod' + + self.log.info(" - snapshot file refering to a block that is not in the assumeutxo parameters") + prev_block_hash = self.nodes[0].getblockhash(SNAPSHOT_BASE_HEIGHT - 1) + bogus_block_hash = "0" * 64 # Represents any unknown block hash + for bad_block_hash in [bogus_block_hash, prev_block_hash]: + with open(bad_snapshot_path, 'wb') as f: + # block hash of the snapshot base is stored right at the start (first 32 bytes) + f.write(bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[32:]) + error_details = f"assumeutxo block hash in snapshot metadata not recognized ({bad_block_hash})" + assert_raises_rpc_error(-32603, f"Unable to load UTXO snapshot, {error_details}", self.nodes[1].loadtxoutset, bad_snapshot_path) + + self.log.info(" - snapshot file with wrong number of coins") + valid_num_coins = int.from_bytes(valid_snapshot_contents[32:32 + 8], "little") + for off in [-1, +1]: + with open(bad_snapshot_path, 'wb') as f: + f.write(valid_snapshot_contents[:32]) + f.write((valid_num_coins + off).to_bytes(8, "little")) + f.write(valid_snapshot_contents[32 + 8:]) + expected_log = f"bad snapshot - coins left over after deserializing 298 coins" if off == -1 else f"bad snapshot format or truncated snapshot after deserializing 299 coins" + with self.nodes[1].assert_debug_log([expected_log]): + assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", self.nodes[1].loadtxoutset, bad_snapshot_path) + + self.log.info(" - snapshot file with wrong outpoint hash") + with open(bad_snapshot_path, "wb") as f: + f.write(valid_snapshot_contents[:(32 + 8)]) + f.write(b"\xff" * 32) + f.write(valid_snapshot_contents[(32 + 8 + 32):]) + expected_log = "[snapshot] bad snapshot content hash: expected ef45ccdca5898b6c2145e4581d2b88c56564dd389e4bd75a1aaf6961d3edd3c0, got 29926acf3ac81f908cf4f22515713ca541c08bb0f0ef1b2c3443a007134d69b8" + with self.nodes[1].assert_debug_log([expected_log]): + assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", self.nodes[1].loadtxoutset, bad_snapshot_path) + def run_test(self): """ Bring up two (disconnected) nodes, mine some new blocks on the first, @@ -80,16 +117,13 @@ class AssumeutxoTest(BitcoinTestFramework): self.sync_blocks() - def no_sync(): - pass - # Generate a series of blocks that `n0` will have in the snapshot, # but that n1 doesn't yet see. In order for the snapshot to activate, # though, we have to ferry over the new headers to n1 so that it # isn't waiting forever to see the header of the snapshot's base block # while disconnected from n0. for i in range(100): - self.generate(n0, nblocks=1, sync_fun=no_sync) + self.generate(n0, nblocks=1, sync_fun=self.no_op) newblock = n0.getblock(n0.getbestblockhash(), 0) # make n1 aware of the new header, but don't give it the block. @@ -116,22 +150,27 @@ class AssumeutxoTest(BitcoinTestFramework): # Mine more blocks on top of the snapshot that n1 hasn't yet seen. This # will allow us to test n1's sync-to-tip on top of a snapshot. - self.generate(n0, nblocks=100, sync_fun=no_sync) + self.generate(n0, nblocks=100, sync_fun=self.no_op) assert_equal(n0.getblockcount(), FINAL_HEIGHT) assert_equal(n1.getblockcount(), START_HEIGHT) assert_equal(n0.getblockchaininfo()["blocks"], FINAL_HEIGHT) + self.test_invalid_snapshot_scenarios(dump_output['path']) + self.log.info(f"Loading snapshot into second node from {dump_output['path']}") loaded = n1.loadtxoutset(dump_output['path']) assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT) assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT) - monitor = n1.getchainstates() - assert_equal(monitor['normal']['blocks'], START_HEIGHT) - assert_equal(monitor['snapshot']['blocks'], SNAPSHOT_BASE_HEIGHT) - assert_equal(monitor['snapshot']['snapshot_blockhash'], dump_output['base_hash']) + normal, snapshot = n1.getchainstates()["chainstates"] + assert_equal(normal['blocks'], START_HEIGHT) + assert_equal(normal.get('snapshot_blockhash'), None) + assert_equal(normal['validated'], True) + assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT) + assert_equal(snapshot['snapshot_blockhash'], dump_output['base_hash']) + assert_equal(snapshot['validated'], False) assert_equal(n1.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT) @@ -159,20 +198,11 @@ class AssumeutxoTest(BitcoinTestFramework): self.connect_nodes(0, 1) self.log.info(f"Ensuring snapshot chain syncs to tip. ({FINAL_HEIGHT})") - - def check_for_final_height(): - chainstates = n1.getchainstates() - # The background validation may have completed before we run our first - # check, so accept a final blockheight from either chainstate type. - cs = chainstates.get('snapshot') or chainstates.get('normal') - return cs['blocks'] == FINAL_HEIGHT - - wait_until_helper(check_for_final_height) + self.wait_until(lambda: n1.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT) self.sync_blocks(nodes=(n0, n1)) self.log.info("Ensuring background validation completes") - # N.B.: the `snapshot` key disappears once the background validation is complete. - wait_until_helper(lambda: not n1.getchainstates().get('snapshot')) + self.wait_until(lambda: len(n1.getchainstates()['chainstates']) == 1) # Ensure indexes have synced. completed_idx_state = { @@ -189,8 +219,8 @@ class AssumeutxoTest(BitcoinTestFramework): assert_equal(n.getblockchaininfo()["blocks"], FINAL_HEIGHT) - assert_equal(n.getchainstates()['normal']['blocks'], FINAL_HEIGHT) - assert_equal(n.getchainstates().get('snapshot'), None) + chainstate, = n.getchainstates()['chainstates'] + assert_equal(chainstate['blocks'], FINAL_HEIGHT) if i != 0: # Ensure indexes have synced for the assumeutxo node @@ -208,17 +238,20 @@ class AssumeutxoTest(BitcoinTestFramework): assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT) assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT) - monitor = n2.getchainstates() - assert_equal(monitor['normal']['blocks'], START_HEIGHT) - assert_equal(monitor['snapshot']['blocks'], SNAPSHOT_BASE_HEIGHT) - assert_equal(monitor['snapshot']['snapshot_blockhash'], dump_output['base_hash']) + normal, snapshot = n2.getchainstates()['chainstates'] + assert_equal(normal['blocks'], START_HEIGHT) + assert_equal(normal.get('snapshot_blockhash'), None) + assert_equal(normal['validated'], True) + assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT) + assert_equal(snapshot['snapshot_blockhash'], dump_output['base_hash']) + assert_equal(snapshot['validated'], False) self.connect_nodes(0, 2) - wait_until_helper(lambda: n2.getchainstates()['snapshot']['blocks'] == FINAL_HEIGHT) + self.wait_until(lambda: n2.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT) self.sync_blocks() self.log.info("Ensuring background validation completes") - wait_until_helper(lambda: not n2.getchainstates().get('snapshot')) + self.wait_until(lambda: len(n2.getchainstates()['chainstates']) == 1) completed_idx_state = { 'basic block filter index': COMPLETE_IDX, @@ -234,8 +267,8 @@ class AssumeutxoTest(BitcoinTestFramework): assert_equal(n.getblockchaininfo()["blocks"], FINAL_HEIGHT) - assert_equal(n.getchainstates()['normal']['blocks'], FINAL_HEIGHT) - assert_equal(n.getchainstates().get('snapshot'), None) + chainstate, = n.getchainstates()['chainstates'] + assert_equal(chainstate['blocks'], FINAL_HEIGHT) if i != 0: # Ensure indexes have synced for the assumeutxo node @@ -245,12 +278,12 @@ class AssumeutxoTest(BitcoinTestFramework): self.restart_node(2, extra_args=[ '-reindex-chainstate=1', *self.extra_args[2]]) assert_equal(n2.getblockchaininfo()["blocks"], FINAL_HEIGHT) - wait_until_helper(lambda: n2.getblockcount() == FINAL_HEIGHT) + self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT) self.log.info("Test -reindex of an assumeutxo-synced node") self.restart_node(2, extra_args=['-reindex=1', *self.extra_args[2]]) self.connect_nodes(0, 2) - wait_until_helper(lambda: n2.getblockcount() == FINAL_HEIGHT) + self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT) if __name__ == '__main__': diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py index 76b9277e2f..1a60c13c2c 100755 --- a/test/functional/feature_blocksdir.py +++ b/test/functional/feature_blocksdir.py @@ -5,8 +5,8 @@ """Test the blocksdir option. """ -import os import shutil +from pathlib import Path from test_framework.test_framework import BitcoinTestFramework, initialize_datadir @@ -18,20 +18,20 @@ class BlocksdirTest(BitcoinTestFramework): def run_test(self): self.stop_node(0) - assert os.path.isdir(os.path.join(self.nodes[0].blocks_path)) - assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "blocks")) - shutil.rmtree(self.nodes[0].datadir) + assert self.nodes[0].blocks_path.is_dir() + assert not (self.nodes[0].datadir_path / "blocks").is_dir() + shutil.rmtree(self.nodes[0].datadir_path) initialize_datadir(self.options.tmpdir, 0, self.chain) self.log.info("Starting with nonexistent blocksdir ...") - blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir') + blocksdir_path = Path(self.options.tmpdir) / 'blocksdir' self.nodes[0].assert_start_raises_init_error([f"-blocksdir={blocksdir_path}"], f'Error: Specified blocks directory "{blocksdir_path}" does not exist.') - os.mkdir(blocksdir_path) + blocksdir_path.mkdir() self.log.info("Starting with existing blocksdir ...") self.start_node(0, [f"-blocksdir={blocksdir_path}"]) self.log.info("mining blocks..") self.generatetoaddress(self.nodes[0], 10, self.nodes[0].get_deterministic_priv_key().address) - assert os.path.isfile(os.path.join(blocksdir_path, self.chain, "blocks", "blk00000.dat")) - assert os.path.isdir(os.path.join(self.nodes[0].blocks_path, "index")) + assert (blocksdir_path / self.chain / "blocks" / "blk00000.dat").is_file() + assert (self.nodes[0].blocks_path / "index").is_dir() if __name__ == '__main__': diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 97ee9538dc..dcea662089 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -5,7 +5,7 @@ """Test various command line arguments and configuration file parameters.""" import os -import pathlib +from pathlib import Path import re import sys import tempfile @@ -39,8 +39,8 @@ class ConfArgsTest(BitcoinTestFramework): extra_args=[f'-conf={bad_conf_file_path}'], expected_msg=conf_in_config_file_err, ) - inc_conf_file_path = os.path.join(self.nodes[0].datadir, 'include.conf') - with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf: + inc_conf_file_path = self.nodes[0].datadir_path / 'include.conf' + with open(self.nodes[0].datadir_path / 'bitcoin.conf', 'a', encoding='utf-8') as conf: conf.write(f'includeconf={inc_conf_file_path}\n') with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: conf.write('conf=some.conf\n') @@ -97,8 +97,8 @@ class ConfArgsTest(BitcoinTestFramework): conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass') self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided') - inc_conf_file2_path = os.path.join(self.nodes[0].datadir, 'include2.conf') - with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf: + inc_conf_file2_path = self.nodes[0].datadir_path / 'include2.conf' + with open(self.nodes[0].datadir_path / 'bitcoin.conf', 'a', encoding='utf-8') as conf: conf.write(f'includeconf={inc_conf_file2_path}\n') with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: @@ -123,15 +123,15 @@ class ConfArgsTest(BitcoinTestFramework): # Create a temporary directory that will be treated as the default data # directory by bitcoind. - env, default_datadir = util.get_temp_default_datadir(pathlib.Path(self.options.tmpdir, "test_config_file_log")) + env, default_datadir = util.get_temp_default_datadir(Path(self.options.tmpdir, "test_config_file_log")) default_datadir.mkdir(parents=True) # Write a bitcoin.conf file in the default data directory containing a # datadir= line pointing at the node datadir. node = self.nodes[0] - conf_text = pathlib.Path(node.bitcoinconf).read_text() + conf_text = node.bitcoinconf.read_text() conf_path = default_datadir / "bitcoin.conf" - conf_path.write_text(f"datadir={node.datadir}\n{conf_text}") + conf_path.write_text(f"datadir={node.datadir_path}\n{conf_text}") # Drop the node -datadir= argument during this test, because if it is # specified it would take precedence over the datadir setting in the @@ -218,7 +218,8 @@ class ConfArgsTest(BitcoinTestFramework): def test_seed_peers(self): self.log.info('Test seed peers') - default_data_dir = self.nodes[0].datadir + default_data_dir = self.nodes[0].datadir_path + peer_dat = default_data_dir / 'peers.dat' # Only regtest has no fixed seeds. To avoid connections to random # nodes, regtest is the only network where it is safe to enable # -fixedseeds in tests @@ -229,7 +230,7 @@ class ConfArgsTest(BitcoinTestFramework): # We expect the node will use DNS Seeds, but Regtest mode does not have # any valid DNS seeds. So after 60 seconds, the node should fallback to # fixed seeds - assert not os.path.exists(os.path.join(default_data_dir, "peers.dat")) + assert not peer_dat.exists() start = int(time.time()) with self.nodes[0].assert_debug_log( expected_msgs=[ @@ -248,7 +249,7 @@ class ConfArgsTest(BitcoinTestFramework): # No peers.dat exists and -dnsseed=0 # We expect the node will fallback immediately to fixed seeds - assert not os.path.exists(os.path.join(default_data_dir, "peers.dat")) + assert not peer_dat.exists() with self.nodes[0].assert_debug_log(expected_msgs=[ "Loaded 0 addresses from peers.dat", "DNS seeding disabled", @@ -260,7 +261,7 @@ class ConfArgsTest(BitcoinTestFramework): # No peers.dat exists and dns seeds are disabled. # We expect the node will not add fixed seeds when explicitly disabled. - assert not os.path.exists(os.path.join(default_data_dir, "peers.dat")) + assert not peer_dat.exists() with self.nodes[0].assert_debug_log(expected_msgs=[ "Loaded 0 addresses from peers.dat", "DNS seeding disabled", @@ -271,7 +272,7 @@ class ConfArgsTest(BitcoinTestFramework): # No peers.dat exists and -dnsseed=0, but a -addnode is provided # We expect the node will allow 60 seconds prior to using fixed seeds - assert not os.path.exists(os.path.join(default_data_dir, "peers.dat")) + assert not peer_dat.exists() start = int(time.time()) with self.nodes[0].assert_debug_log( expected_msgs=[ @@ -323,16 +324,16 @@ class ConfArgsTest(BitcoinTestFramework): 'because a conflicting -conf file argument is passed.') node = self.nodes[0] with tempfile.NamedTemporaryFile(dir=self.options.tmpdir, mode="wt", delete=False) as temp_conf: - temp_conf.write(f"datadir={node.datadir}\n") + temp_conf.write(f"datadir={node.datadir_path}\n") node.assert_start_raises_init_error([f"-conf={temp_conf.name}"], re.escape( - f'Error: Data directory "{node.datadir}" contains a "bitcoin.conf" file which is ignored, because a ' + f'Error: Data directory "{node.datadir_path}" contains a "bitcoin.conf" file which is ignored, because a ' f'different configuration file "{temp_conf.name}" from command line argument "-conf={temp_conf.name}" ' f'is being used instead.') + r"[\s\S]*", match=ErrorMatch.FULL_REGEX) # Test that passing a redundant -conf command line argument pointing to # the same bitcoin.conf that would be loaded anyway does not trigger an # error. - self.start_node(0, [f'-conf={node.datadir}/bitcoin.conf']) + self.start_node(0, [f'-conf={node.datadir_path}/bitcoin.conf']) self.stop_node(0) def test_ignored_default_conf(self): @@ -346,7 +347,7 @@ class ConfArgsTest(BitcoinTestFramework): # Create a temporary directory that will be treated as the default data # directory by bitcoind. - env, default_datadir = util.get_temp_default_datadir(pathlib.Path(self.options.tmpdir, "home")) + env, default_datadir = util.get_temp_default_datadir(Path(self.options.tmpdir, "home")) default_datadir.mkdir(parents=True) # Write a bitcoin.conf file in the default data directory containing a @@ -354,7 +355,7 @@ class ConfArgsTest(BitcoinTestFramework): # startup error because the node datadir contains a different # bitcoin.conf that would be ignored. node = self.nodes[0] - (default_datadir / "bitcoin.conf").write_text(f"datadir={node.datadir}\n") + (default_datadir / "bitcoin.conf").write_text(f"datadir={node.datadir_path}\n") # Drop the node -datadir= argument during this test, because if it is # specified it would take precedence over the datadir setting in the @@ -362,7 +363,7 @@ class ConfArgsTest(BitcoinTestFramework): node_args = node.args node.args = [arg for arg in node.args if not arg.startswith("-datadir=")] node.assert_start_raises_init_error([], re.escape( - f'Error: Data directory "{node.datadir}" contains a "bitcoin.conf" file which is ignored, because a ' + f'Error: Data directory "{node.datadir_path}" contains a "bitcoin.conf" file which is ignored, because a ' f'different configuration file "{default_datadir}/bitcoin.conf" from data directory "{default_datadir}" ' f'is being used instead.') + r"[\s\S]*", env=env, match=ErrorMatch.FULL_REGEX) node.args = node_args @@ -392,16 +393,16 @@ class ConfArgsTest(BitcoinTestFramework): # Remove the -datadir argument so it doesn't override the config file self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")] - default_data_dir = self.nodes[0].datadir - new_data_dir = os.path.join(default_data_dir, 'newdatadir') - new_data_dir_2 = os.path.join(default_data_dir, 'newdatadir2') + default_data_dir = self.nodes[0].datadir_path + new_data_dir = default_data_dir / 'newdatadir' + new_data_dir_2 = default_data_dir / 'newdatadir2' # Check that using -datadir argument on non-existent directory fails - self.nodes[0].datadir = new_data_dir + self.nodes[0].datadir_path = new_data_dir self.nodes[0].assert_start_raises_init_error([f'-datadir={new_data_dir}'], f'Error: Specified data directory "{new_data_dir}" does not exist.') # Check that using non-existent datadir in conf file fails - conf_file = os.path.join(default_data_dir, "bitcoin.conf") + conf_file = default_data_dir / "bitcoin.conf" # datadir needs to be set before [chain] section with open(conf_file, encoding='utf8') as f: @@ -413,20 +414,20 @@ class ConfArgsTest(BitcoinTestFramework): self.nodes[0].assert_start_raises_init_error([f'-conf={conf_file}'], f'Error: Error reading configuration file: specified data directory "{new_data_dir}" does not exist.') # Check that an explicitly specified config file that cannot be opened fails - none_existent_conf_file = os.path.join(default_data_dir, "none_existent_bitcoin.conf") - self.nodes[0].assert_start_raises_init_error(['-conf=' + none_existent_conf_file], 'Error: Error reading configuration file: specified config file "' + none_existent_conf_file + '" could not be opened.') + none_existent_conf_file = default_data_dir / "none_existent_bitcoin.conf" + self.nodes[0].assert_start_raises_init_error(['-conf=' + f'{none_existent_conf_file}'], 'Error: Error reading configuration file: specified config file "' + f'{none_existent_conf_file}' + '" could not be opened.') # Create the directory and ensure the config file now works - os.mkdir(new_data_dir) + new_data_dir.mkdir() self.start_node(0, [f'-conf={conf_file}']) self.stop_node(0) - assert os.path.exists(os.path.join(new_data_dir, self.chain, 'blocks')) + assert (new_data_dir / self.chain / 'blocks').exists() # Ensure command line argument overrides datadir in conf - os.mkdir(new_data_dir_2) - self.nodes[0].datadir = new_data_dir_2 + new_data_dir_2.mkdir() + self.nodes[0].datadir_path = new_data_dir_2 self.start_node(0, [f'-datadir={new_data_dir_2}', f'-conf={conf_file}']) - assert os.path.exists(os.path.join(new_data_dir_2, self.chain, 'blocks')) + assert (new_data_dir_2 / self.chain / 'blocks').exists() if __name__ == '__main__': diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py index cf2f21d553..24a68a04bf 100755 --- a/test/functional/feature_filelock.py +++ b/test/functional/feature_filelock.py @@ -28,7 +28,7 @@ class FilelockTest(BitcoinTestFramework): self.log.info("Check that we can't start a second bitcoind instance using the same datadir") expected_msg = f"Error: Cannot obtain a lock on data directory {datadir}. {self.config['environment']['PACKAGE_NAME']} is probably already running." - self.nodes[1].assert_start_raises_init_error(extra_args=[f'-datadir={self.nodes[0].datadir}', '-noserver'], expected_msg=expected_msg) + self.nodes[1].assert_start_raises_init_error(extra_args=[f'-datadir={self.nodes[0].datadir_path}', '-noserver'], expected_msg=expected_msg) if self.is_wallet_compiled(): def check_wallet_filelock(descriptors): diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py index 12d65fde68..5129e0d328 100755 --- a/test/functional/feature_loadblock.py +++ b/test/functional/feature_loadblock.py @@ -10,7 +10,7 @@ To generate that file this test uses the helper scripts available in contrib/linearize. """ -import os +from pathlib import Path import subprocess import sys import tempfile @@ -32,10 +32,10 @@ class LoadblockTest(BitcoinTestFramework): self.generate(self.nodes[0], COINBASE_MATURITY, sync_fun=self.no_op) # Parsing the url of our node to get settings for config file - data_dir = self.nodes[0].datadir + data_dir = self.nodes[0].datadir_path node_url = urllib.parse.urlparse(self.nodes[0].url) - cfg_file = os.path.join(data_dir, "linearize.cfg") - bootstrap_file = os.path.join(self.options.tmpdir, "bootstrap.dat") + cfg_file = data_dir / "linearize.cfg" + bootstrap_file = Path(self.options.tmpdir) / "bootstrap.dat" genesis_block = self.nodes[0].getblockhash(0) blocks_dir = self.nodes[0].blocks_path hash_list = tempfile.NamedTemporaryFile(dir=data_dir, @@ -58,16 +58,16 @@ class LoadblockTest(BitcoinTestFramework): cfg.write(f"hashlist={hash_list.name}\n") base_dir = self.config["environment"]["SRCDIR"] - linearize_dir = os.path.join(base_dir, "contrib", "linearize") + linearize_dir = Path(base_dir) / "contrib" / "linearize" self.log.info("Run linearization of block hashes") - linearize_hashes_file = os.path.join(linearize_dir, "linearize-hashes.py") + linearize_hashes_file = linearize_dir / "linearize-hashes.py" subprocess.run([sys.executable, linearize_hashes_file, cfg_file], stdout=hash_list, check=True) self.log.info("Run linearization of block data") - linearize_data_file = os.path.join(linearize_dir, "linearize-data.py") + linearize_data_file = linearize_dir / "linearize-data.py" subprocess.run([sys.executable, linearize_data_file, cfg_file], check=True) diff --git a/test/functional/feature_settings.py b/test/functional/feature_settings.py index bcae963428..1bacdd447a 100755 --- a/test/functional/feature_settings.py +++ b/test/functional/feature_settings.py @@ -6,7 +6,6 @@ import json -from pathlib import Path from test_framework.test_framework import BitcoinTestFramework from test_framework.test_node import ErrorMatch @@ -21,8 +20,8 @@ class SettingsTest(BitcoinTestFramework): def run_test(self): node, = self.nodes - settings = Path(node.chain_path, "settings.json") - conf = Path(node.datadir, "bitcoin.conf") + settings = node.chain_path / "settings.json" + conf = node.datadir_path / "bitcoin.conf" # Assert empty settings file was created self.stop_node(0) @@ -79,7 +78,7 @@ class SettingsTest(BitcoinTestFramework): self.stop_node(0) # Test alternate settings path - altsettings = Path(node.datadir, "altsettings.json") + altsettings = node.datadir_path / "altsettings.json" with altsettings.open("w") as fp: fp.write('{"key": "value"}') with node.assert_debug_log(expected_msgs=['Setting file arg: key = "value"']): diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index 25ea557217..83bb5121e5 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -94,7 +94,7 @@ class TestBitcoinCli(BitcoinTestFramework): assert_equal(self.nodes[0].cli("-named", "echo", "arg0=0", "arg1=1", "arg2=2", "arg1=3").send_cli(), ['0', '3', '2']) assert_raises_rpc_error(-8, "Parameter args specified multiple times", self.nodes[0].cli("-named", "echo", "args=[0,1,2,3]", "4", "5", "6", ).send_cli) - user, password = get_auth_cookie(self.nodes[0].datadir, self.chain) + user, password = get_auth_cookie(self.nodes[0].datadir_path, self.chain) self.log.info("Test -stdinrpcpass option") assert_equal(BLOCKS, self.nodes[0].cli(f'-rpcuser={user}', '-stdinrpcpass', input=password).getblockcount()) diff --git a/test/functional/interface_usdt_coinselection.py b/test/functional/interface_usdt_coinselection.py index a3c830bb51..aff90ea5fc 100755 --- a/test/functional/interface_usdt_coinselection.py +++ b/test/functional/interface_usdt_coinselection.py @@ -166,7 +166,7 @@ class CoinSelectionTracepointTest(BitcoinTestFramework): ctx.enable_probe(probe="coin_selection:normal_create_tx_internal", fn_name="trace_normal_create_tx") ctx.enable_probe(probe="coin_selection:attempting_aps_create_tx", fn_name="trace_attempt_aps") ctx.enable_probe(probe="coin_selection:aps_create_tx_internal", fn_name="trace_aps_create_tx") - self.bpf = BPF(text=coinselection_tracepoints_program, usdt_contexts=[ctx], debug=0) + self.bpf = BPF(text=coinselection_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) self.log.info("Prepare wallets") self.generate(self.nodes[0], 101) diff --git a/test/functional/interface_usdt_mempool.py b/test/functional/interface_usdt_mempool.py index d1e274480c..0168d9f916 100755 --- a/test/functional/interface_usdt_mempool.py +++ b/test/functional/interface_usdt_mempool.py @@ -119,6 +119,7 @@ int trace_replaced(struct pt_regs *ctx) { replaced_events.perf_submit(ctx, &replaced, sizeof(replaced)); return 0; } + """ @@ -143,7 +144,7 @@ class MempoolTracepointTest(BitcoinTestFramework): node = self.nodes[0] ctx = USDT(pid=node.process.pid) ctx.enable_probe(probe="mempool:added", fn_name="trace_added") - bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_added_event(_, data, __): events.append(bpf["added_events"].event(data)) @@ -180,7 +181,7 @@ class MempoolTracepointTest(BitcoinTestFramework): node = self.nodes[0] ctx = USDT(pid=node.process.pid) ctx.enable_probe(probe="mempool:removed", fn_name="trace_removed") - bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_removed_event(_, data, __): events.append(bpf["removed_events"].event(data)) @@ -226,7 +227,7 @@ class MempoolTracepointTest(BitcoinTestFramework): node = self.nodes[0] ctx = USDT(pid=node.process.pid) ctx.enable_probe(probe="mempool:replaced", fn_name="trace_replaced") - bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_replaced_event(_, data, __): events.append(bpf["replaced_events"].event(data)) @@ -277,7 +278,7 @@ class MempoolTracepointTest(BitcoinTestFramework): self.log.info("Hooking into mempool:rejected tracepoint...") ctx = USDT(pid=node.process.pid) ctx.enable_probe(probe="mempool:rejected", fn_name="trace_rejected") - bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_rejected_event(_, data, __): events.append(bpf["rejected_events"].event(data)) diff --git a/test/functional/interface_usdt_net.py b/test/functional/interface_usdt_net.py index e15ac3c1f2..5d7c8c2304 100755 --- a/test/functional/interface_usdt_net.py +++ b/test/functional/interface_usdt_net.py @@ -114,7 +114,7 @@ class NetTracepointTest(BitcoinTestFramework): fn_name="trace_inbound_message") ctx.enable_probe(probe="net:outbound_message", fn_name="trace_outbound_message") - bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) EXPECTED_INOUTBOUND_VERSION_MSG = 1 checked_inbound_version_msg = 0 diff --git a/test/functional/interface_usdt_utxocache.py b/test/functional/interface_usdt_utxocache.py index 2fc5981451..06cdcd10a0 100755 --- a/test/functional/interface_usdt_utxocache.py +++ b/test/functional/interface_usdt_utxocache.py @@ -175,7 +175,7 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): ctx = USDT(pid=self.nodes[0].process.pid) ctx.enable_probe(probe="utxocache:uncache", fn_name="trace_utxocache_uncache") - bpf = BPF(text=utxocache_changes_program, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=utxocache_changes_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) # The handle_* function is a ctypes callback function called from C. When # we assert in the handle_* function, the AssertError doesn't propagate @@ -244,7 +244,7 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): ctx.enable_probe(probe="utxocache:add", fn_name="trace_utxocache_add") ctx.enable_probe(probe="utxocache:spent", fn_name="trace_utxocache_spent") - bpf = BPF(text=utxocache_changes_program, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=utxocache_changes_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) # The handle_* function is a ctypes callback function called from C. When # we assert in the handle_* function, the AssertError doesn't propagate @@ -333,7 +333,7 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): ctx = USDT(pid=self.nodes[0].process.pid) ctx.enable_probe(probe="utxocache:flush", fn_name="trace_utxocache_flush") - bpf = BPF(text=utxocache_flushes_program, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=utxocache_flushes_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) # The handle_* function is a ctypes callback function called from C. When # we assert in the handle_* function, the AssertError doesn't propagate @@ -390,7 +390,7 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): ctx = USDT(pid=self.nodes[0].process.pid) ctx.enable_probe(probe="utxocache:flush", fn_name="trace_utxocache_flush") - bpf = BPF(text=utxocache_flushes_program, usdt_contexts=[ctx], debug=0) + bpf = BPF(text=utxocache_flushes_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) bpf["utxocache_flush"].open_perf_buffer(handle_utxocache_flush) self.log.info(f"prune blockchain to trigger a flush for pruning") diff --git a/test/functional/interface_usdt_validation.py b/test/functional/interface_usdt_validation.py index e29b2c46eb..30982393d8 100755 --- a/test/functional/interface_usdt_validation.py +++ b/test/functional/interface_usdt_validation.py @@ -94,7 +94,7 @@ class ValidationTracepointTest(BitcoinTestFramework): ctx.enable_probe(probe="validation:block_connected", fn_name="trace_block_connected") bpf = BPF(text=validation_blockconnected_program, - usdt_contexts=[ctx], debug=0) + usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_blockconnected(_, data, __): event = ctypes.cast(data, ctypes.POINTER(Block)).contents diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index b398ef51e1..d316c4b602 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -706,14 +706,14 @@ class SegWitTest(BitcoinTestFramework): # segwit activation. Note that older bitcoind's that are not # segwit-aware would also reject this for failing CLEANSTACK. with self.nodes[0].assert_debug_log( - expected_msgs=(spend_tx.hash, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)')): + expected_msgs=[spend_tx.hash, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)']): test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # Try to put the witness script in the scriptSig, should also fail. spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) spend_tx.rehash() with self.nodes[0].assert_debug_log( - expected_msgs=(spend_tx.hash, 'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)')): + expected_msgs=[spend_tx.hash, 'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)']): test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # Now put the witness script in the witness, should succeed after diff --git a/test/functional/p2p_v2_transport.py b/test/functional/p2p_v2_transport.py index dd564fed88..5ad2194b84 100755 --- a/test/functional/p2p_v2_transport.py +++ b/test/functional/p2p_v2_transport.py @@ -145,9 +145,22 @@ class V2TransportTest(BitcoinTestFramework): wrong_network_magic_prefix = MAGIC_BYTES["signet"] + V1_PREFIX[4:] with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(("127.0.0.1", p2p_port(0))) - with self.nodes[0].assert_debug_log("V2 transport error: V1 peer with wrong MessageStart"): + with self.nodes[0].assert_debug_log(["V2 transport error: V1 peer with wrong MessageStart"]): s.sendall(wrong_network_magic_prefix + b"somepayload") + # Check detection of missing garbage terminator (hits after fixed amount of data if terminator never matches garbage) + MAX_KEY_GARB_AND_GARBTERM_LEN = 64 + 4095 + 16 + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + num_peers = len(self.nodes[0].getpeerinfo()) + s.connect(("127.0.0.1", p2p_port(0))) + self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers + 1) + s.sendall(b'\x00' * (MAX_KEY_GARB_AND_GARBTERM_LEN - 1)) + self.wait_until(lambda: self.nodes[0].getpeerinfo()[-1]["bytesrecv"] == MAX_KEY_GARB_AND_GARBTERM_LEN - 1) + with self.nodes[0].assert_debug_log(["V2 transport error: missing garbage terminator"]): + s.sendall(b'\x00') # send out last byte + # should disconnect immediately + self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers) + if __name__ == '__main__': V2TransportTest().main() diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 664a15a5ec..43cd23fc7a 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -56,7 +56,7 @@ class RPCBindTest(BitcoinTestFramework): self.nodes[0].rpchost = None self.start_nodes([node_args]) # connect to node through non-loopback interface - node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, self.chain, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) + node = get_rpc_proxy(rpc_url(self.nodes[0].datadir_path, 0, self.chain, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() self.stop_nodes() diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py index 2cae602cc2..f378878181 100755 --- a/test/functional/rpc_dumptxoutset.py +++ b/test/functional/rpc_dumptxoutset.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the generation of UTXO snapshots using `dumptxoutset`. """ -from pathlib import Path from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework @@ -29,7 +28,7 @@ class DumptxoutsetTest(BitcoinTestFramework): FILENAME = 'txoutset.dat' out = node.dumptxoutset(FILENAME) - expected_path = Path(node.datadir) / self.chain / FILENAME + expected_path = node.datadir_path / self.chain / FILENAME assert expected_path.is_file() diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 2c7f974d0b..50a022fc7e 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2022 The Bitcoin Core developers +# Copyright (c) 2017-present The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test RPC calls related to net. @@ -315,9 +315,11 @@ class NetTest(BitcoinTestFramework): self.log.debug("Test that adding an address with invalid port fails") assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=-1) - assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress,address="1.2.3.4", port=65536) + assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=65536) self.log.debug("Test that adding a valid address to the tried table succeeds") + self.addr_time = int(time.time()) + node.setmocktime(self.addr_time) assert_equal(node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), {"success": True}) with node.assert_debug_log(expected_msgs=["CheckAddrman: new 0, tried 1, total 1 started"]): addrs = node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks @@ -371,11 +373,6 @@ class NetTest(BitcoinTestFramework): self.log.info("Test getaddrmaninfo") node = self.nodes[1] - self.log.debug("Test that getaddrmaninfo is a hidden RPC") - # It is hidden from general help, but its detailed help may be called directly. - assert "getaddrmaninfo" not in node.help() - assert "getaddrmaninfo" in node.help("getaddrmaninfo") - # current count of ipv4 addresses in addrman is {'new':1, 'tried':1} self.log.info("Test that count of addresses in addrman match expected values") res = node.getaddrmaninfo() @@ -407,8 +404,7 @@ class NetTest(BitcoinTestFramework): assert_equal(result["network"], expected["network"]) assert_equal(result["source"], expected["source"]) assert_equal(result["source_network"], expected["source_network"]) - # To avoid failing on slow test runners, use a 10s vspan here. - assert_approx(result["time"], time.time(), vspan=10) + assert_equal(result["time"], self.addr_time) def check_getrawaddrman_entries(expected): """Utility to compare a getrawaddrman result with expected addrman contents""" diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index 9c4960aa1e..5644a9f5a8 100755 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -335,7 +335,7 @@ class RPCPackagesTest(BitcoinTestFramework): self.log.info("Submitpackage only allows packages of 1 child with its parents") # Chain of 3 transactions has too many generations chain_hex = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=25)] - assert_raises_rpc_error(-25, "not-child-with-parents", node.submitpackage, chain_hex) + assert_raises_rpc_error(-25, "package topology disallowed", node.submitpackage, chain_hex) if __name__ == "__main__": diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index ceb4bbd7de..be4ed624fc 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -77,7 +77,7 @@ from test_framework.messages import ( from test_framework.util import ( MAX_NODES, p2p_port, - wait_until_helper, + wait_until_helper_internal, ) logger = logging.getLogger("TestFramework.p2p") @@ -466,7 +466,7 @@ class P2PInterface(P2PConnection): assert self.is_connected return test_function_in() - wait_until_helper(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor) + wait_until_helper_internal(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor) def wait_for_connect(self, timeout=60): test_function = lambda: self.is_connected @@ -602,7 +602,7 @@ class NetworkThread(threading.Thread): def close(self, timeout=10): """Close the connections and network event loop.""" self.network_event_loop.call_soon_threadsafe(self.network_event_loop.stop) - wait_until_helper(lambda: not self.network_event_loop.is_running(), timeout=timeout) + wait_until_helper_internal(lambda: not self.network_event_loop.is_running(), timeout=timeout) self.network_event_loop.close() self.join(timeout) # Safe to remove event loop. diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a34c34713e..4e6d245b5f 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -33,7 +33,7 @@ from .util import ( get_datadir_path, initialize_datadir, p2p_port, - wait_until_helper, + wait_until_helper_internal, ) @@ -185,7 +185,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): parser.add_argument("--perf", dest="perf", default=False, action="store_true", help="profile running nodes with perf for the duration of the test") parser.add_argument("--valgrind", dest="valgrind", default=False, action="store_true", - help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown. valgrind 3.14 or later required.") + help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown. valgrind 3.14 or later required. Does not apply to previous release binaries.") parser.add_argument("--randomseed", type=int, help="set a random seed for deterministically reproducing a previous test run") parser.add_argument("--timeout-factor", dest="timeout_factor", type=float, help="adjust test timeouts by a factor. Setting it to 0 disables all timeouts") @@ -747,7 +747,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.sync_mempools(nodes) def wait_until(self, test_function, timeout=60): - return wait_until_helper(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor) + return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor) # Private helper methods. These should not be accessed by the subclass test scripts. @@ -1006,5 +1006,5 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): return self.config["components"].getboolean("USE_BDB") def has_blockfile(self, node, filenum: str): - blocksdir = os.path.join(node.datadir, self.chain, 'blocks', '') - return os.path.isfile(os.path.join(blocksdir, f"blk{filenum}.dat")) + blocksdir = node.datadir_path / self.chain / 'blocks' + return (blocksdir / f"blk{filenum}.dat").is_file() diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 6e12f6c964..c77cfbdd91 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -36,7 +36,7 @@ from .util import ( get_auth_cookie, get_rpc_proxy, rpc_url, - wait_until_helper, + wait_until_helper_internal, p2p_port, ) @@ -67,7 +67,7 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False): + def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False): """ Kwargs: start_perf (bool): If True, begin profiling the node with `perf` as soon as @@ -76,10 +76,10 @@ class TestNode(): self.index = i self.p2p_conn_index = 1 - self.datadir = datadir - self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf") - self.stdout_dir = os.path.join(self.datadir, "stdout") - self.stderr_dir = os.path.join(self.datadir, "stderr") + self.datadir_path = datadir_path + self.bitcoinconf = self.datadir_path / "bitcoin.conf" + self.stdout_dir = self.datadir_path / "stdout" + self.stderr_dir = self.datadir_path / "stderr" self.chain = chain self.rpchost = rpchost self.rpc_timeout = timewait @@ -88,7 +88,7 @@ class TestNode(): self.cwd = cwd self.descriptors = descriptors if extra_conf is not None: - append_config(datadir, extra_conf) + append_config(self.datadir_path, extra_conf) # Most callers will just need to add extra args to the standard list below. # For those callers that need more flexibility, they can just set the args property directly. # Note that common args are set in the config file (see initialize_datadir) @@ -99,7 +99,7 @@ class TestNode(): # spam debug.log. self.args = [ self.binary, - "-datadir=" + self.datadir, + f"-datadir={self.datadir_path}", "-logtimemicros", "-debug", "-debugexclude=libevent", @@ -110,10 +110,9 @@ class TestNode(): if self.descriptors is None: self.args.append("-disablewallet") - if use_valgrind: - default_suppressions_file = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "..", "..", "..", "contrib", "valgrind.supp") + # Use valgrind, expect for previous release binaries + if use_valgrind and version is None: + default_suppressions_file = Path(__file__).parents[3] / "contrib" / "valgrind.supp" suppressions_file = os.getenv("VALGRIND_SUPPRESSIONS_FILE", default_suppressions_file) self.args = ["valgrind", "--suppressions={}".format(suppressions_file), @@ -127,7 +126,7 @@ class TestNode(): if self.version_is_at_least(239000): self.args.append("-loglevel=trace") - self.cli = TestNodeCLI(bitcoin_cli, self.datadir) + self.cli = TestNodeCLI(bitcoin_cli, self.datadir_path) self.use_cli = use_cli self.start_perf = start_perf @@ -213,7 +212,7 @@ class TestNode(): # Delete any existing cookie file -- if such a file exists (eg due to # unclean shutdown), it will get overwritten anyway by bitcoind, and # potentially interfere with our attempt to authenticate - delete_cookie_file(self.datadir, self.chain) + delete_cookie_file(self.datadir_path, self.chain) # add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1") @@ -234,11 +233,16 @@ class TestNode(): poll_per_s = 4 for _ in range(poll_per_s * self.rpc_timeout): if self.process.poll() is not None: + # Attach abrupt shutdown error/s to the exception message + self.stderr.seek(0) + str_error = ''.join(line.decode('utf-8') for line in self.stderr) + str_error += "************************\n" if str_error else '' + raise FailedToStartError(self._node_msg( - 'bitcoind exited with status {} during initialization'.format(self.process.returncode))) + f'bitcoind exited with status {self.process.returncode} during initialization. {str_error}')) try: rpc = get_rpc_proxy( - rpc_url(self.datadir, self.index, self.chain, self.rpchost), + rpc_url(self.datadir_path, self.index, self.chain, self.rpchost), self.index, timeout=self.rpc_timeout // 2, # Shorter timeout to allow for one retry in case of ETIMEDOUT coveragedir=self.coverage_dir, @@ -248,7 +252,7 @@ class TestNode(): if self.version_is_at_least(190000): # getmempoolinfo.loaded is available since commit # bb8ae2c (version 0.19.0) - wait_until_helper(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor) + wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor) # Wait for the node to finish reindex, block import, and # loading the mempool. Usually importing happens fast or # even "immediate" when the node is started. However, there @@ -302,7 +306,7 @@ class TestNode(): poll_per_s = 4 for _ in range(poll_per_s * self.rpc_timeout): try: - get_auth_cookie(self.datadir, self.chain) + get_auth_cookie(self.datadir_path, self.chain) self.log.debug("Cookie credentials successfully retrieved") return except ValueError: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting @@ -402,7 +406,7 @@ class TestNode(): def wait_until_stopped(self, *, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False, **kwargs): expected_ret_code = 1 if expect_error else 0 # Whether node shutdown return EXIT_FAILURE or EXIT_SUCCESS - wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor) + wait_until_helper_internal(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor) def replace_in_config(self, replacements): """ @@ -420,10 +424,6 @@ class TestNode(): conf.write(conf_data) @property - def datadir_path(self) -> Path: - return Path(self.datadir) - - @property def chain_path(self) -> Path: return self.datadir_path / self.chain @@ -448,6 +448,9 @@ class TestNode(): def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2): if unexpected_msgs is None: unexpected_msgs = [] + assert_equal(type(expected_msgs), list) + assert_equal(type(unexpected_msgs), list) + time_end = time.time() + timeout * self.timeout_factor prev_size = self.debug_log_size(encoding="utf-8") # Must use same encoding that is used to read() below @@ -551,7 +554,7 @@ class TestNode(): "perf output won't be very useful without debug symbols compiled into bitcoind") output_path = tempfile.NamedTemporaryFile( - dir=self.datadir, + dir=self.datadir_path, prefix="{}.perf.data.".format(profile_name or 'test'), delete=False, ).name @@ -713,7 +716,7 @@ class TestNode(): p.peer_disconnect() del self.p2ps[:] - wait_until_helper(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor) + wait_until_helper_internal(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor) def bumpmocktime(self, seconds): """Fast forward using setmocktime to self.mocktime + seconds. Requires setmocktime to have @@ -778,7 +781,7 @@ class TestNodeCLI(): """Run bitcoin-cli command. Deserializes returned string as python object.""" pos_args = [arg_to_cli(arg) for arg in args] named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()] - p_args = [self.binary, "-datadir=" + self.datadir] + self.options + p_args = [self.binary, f"-datadir={self.datadir}"] + self.options if named_args: p_args += ["-named"] if command is not None: diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 9143397042..61346e9d19 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -241,7 +241,7 @@ def satoshi_round(amount): return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) -def wait_until_helper(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0): +def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0): """Sleep until the predicate resolves to be True. Warning: Note that this method is not recommended to be used in tests as it is @@ -404,6 +404,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect= f.write("upnp=0\n") f.write("natpmp=0\n") f.write("shrinkdebugfile=0\n") + f.write("deprecatedrpc=create_bdb\n") # Required to run the tests # To improve SQLite wallet performance so that the tests don't timeout, use -unsafesqlitesync f.write("unsafesqlitesync=1\n") if disable_autoconnect: @@ -412,7 +413,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect= def get_datadir_path(dirname, n): - return os.path.join(dirname, "node" + str(n)) + return pathlib.Path(dirname) / f"node{n}" def get_temp_default_datadir(temp_dir: pathlib.Path) -> Tuple[dict, pathlib.Path]: diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py index 319f120297..44811918bf 100755 --- a/test/functional/test_framework/wallet_util.py +++ b/test/functional/test_framework/wallet_util.py @@ -122,3 +122,22 @@ def generate_keypair(compressed=True, wif=False): if wif: privkey = bytes_to_wif(privkey.get_bytes(), compressed) return privkey, pubkey + +class WalletUnlock(): + """ + A context manager for unlocking a wallet with a passphrase and automatically locking it afterward. + """ + + MAXIMUM_TIMEOUT = 999000 + + def __init__(self, wallet, passphrase, timeout=MAXIMUM_TIMEOUT): + self.wallet = wallet + self.passphrase = passphrase + self.timeout = timeout + + def __enter__(self): + self.wallet.walletpassphrase(self.passphrase, self.timeout) + + def __exit__(self, *args): + _ = args + self.wallet.walletlock() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 933ea276e7..fbf48a0e4d 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -105,6 +105,7 @@ BASE_SCRIPTS = [ 'feature_maxuploadtarget.py', 'mempool_updatefromblock.py', 'mempool_persist.py --descriptors', + 'wallet_miniscript.py --descriptors', # vv Tests less than 60s vv 'rpc_psbt.py --legacy-wallet', 'rpc_psbt.py --descriptors', @@ -242,7 +243,6 @@ BASE_SCRIPTS = [ 'wallet_keypool.py --legacy-wallet', 'wallet_keypool.py --descriptors', 'wallet_descriptor.py --descriptors', - 'wallet_miniscript.py --descriptors', 'p2p_nobloomfilter_messages.py', 'p2p_filter.py', 'rpc_setban.py', diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index 8b0c8ce405..fc042bca66 100755 --- a/test/functional/tool_wallet.py +++ b/test/functional/tool_wallet.py @@ -32,7 +32,7 @@ class ToolWalletTest(BitcoinTestFramework): self.skip_if_no_wallet_tool() def bitcoin_wallet_process(self, *args): - default_args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain] + default_args = ['-datadir={}'.format(self.nodes[0].datadir_path), '-chain=%s' % self.chain] if not self.options.descriptors and 'create' in args: default_args.append('-legacy') @@ -153,8 +153,8 @@ class ToolWalletTest(BitcoinTestFramework): assert_equal(v, r[k]) def do_tool_createfromdump(self, wallet_name, dumpfile, file_format=None): - dumppath = os.path.join(self.nodes[0].datadir, dumpfile) - rt_dumppath = os.path.join(self.nodes[0].datadir, "rt-{}.dump".format(wallet_name)) + dumppath = self.nodes[0].datadir_path / dumpfile + rt_dumppath = self.nodes[0].datadir_path / "rt-{}.dump".format(wallet_name) dump_data = self.read_dump(dumppath) @@ -324,7 +324,7 @@ class ToolWalletTest(BitcoinTestFramework): self.assert_raises_tool_error('No dump file provided. To use dump, -dumpfile=<filename> must be provided.', '-wallet=todump', 'dump') self.log.info('Checking basic dump') - wallet_dump = os.path.join(self.nodes[0].datadir, "wallet.dump") + wallet_dump = self.nodes[0].datadir_path / "wallet.dump" self.assert_tool_output('The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n', '-wallet=todump', '-dumpfile={}'.format(wallet_dump), 'dump') dump_data = self.read_dump(wallet_dump) @@ -339,7 +339,7 @@ class ToolWalletTest(BitcoinTestFramework): self.log.info('Checking createfromdump arguments') self.assert_raises_tool_error('No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.', '-wallet=todump', 'createfromdump') - non_exist_dump = os.path.join(self.nodes[0].datadir, "wallet.nodump") + non_exist_dump = self.nodes[0].datadir_path / "wallet.nodump" self.assert_raises_tool_error('Unknown wallet file format "notaformat" provided. Please provide one of "bdb" or "sqlite".', '-wallet=todump', '-format=notaformat', '-dumpfile={}'.format(wallet_dump), 'createfromdump') self.assert_raises_tool_error('Dump file {} does not exist.'.format(non_exist_dump), '-wallet=todump', '-dumpfile={}'.format(non_exist_dump), 'createfromdump') wallet_path = self.nodes[0].wallets_path / "todump2" @@ -354,17 +354,17 @@ class ToolWalletTest(BitcoinTestFramework): self.do_tool_createfromdump("load-sqlite", "wallet.dump", "sqlite") self.log.info('Checking createfromdump handling of magic and versions') - bad_ver_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_ver1.dump") + bad_ver_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_ver1.dump" dump_data["BITCOIN_CORE_WALLET_DUMP"] = "0" self.write_dump(dump_data, bad_ver_wallet_dump) self.assert_raises_tool_error('Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version 0', '-wallet=badload', '-dumpfile={}'.format(bad_ver_wallet_dump), 'createfromdump') assert not (self.nodes[0].wallets_path / "badload").is_dir() - bad_ver_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_ver2.dump") + bad_ver_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_ver2.dump" dump_data["BITCOIN_CORE_WALLET_DUMP"] = "2" self.write_dump(dump_data, bad_ver_wallet_dump) self.assert_raises_tool_error('Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version 2', '-wallet=badload', '-dumpfile={}'.format(bad_ver_wallet_dump), 'createfromdump') assert not (self.nodes[0].wallets_path / "badload").is_dir() - bad_magic_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_magic.dump") + bad_magic_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_magic.dump" del dump_data["BITCOIN_CORE_WALLET_DUMP"] dump_data["not_the_right_magic"] = "1" self.write_dump(dump_data, bad_magic_wallet_dump, "not_the_right_magic") @@ -372,19 +372,19 @@ class ToolWalletTest(BitcoinTestFramework): assert not (self.nodes[0].wallets_path / "badload").is_dir() self.log.info('Checking createfromdump handling of checksums') - bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum1.dump") + bad_sum_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_sum1.dump" dump_data = orig_dump.copy() checksum = dump_data["checksum"] dump_data["checksum"] = "1" * 64 self.write_dump(dump_data, bad_sum_wallet_dump) self.assert_raises_tool_error('Error: Dumpfile checksum does not match. Computed {}, expected {}'.format(checksum, "1" * 64), '-wallet=bad', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') assert not (self.nodes[0].wallets_path / "badload").is_dir() - bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum2.dump") + bad_sum_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_sum2.dump" del dump_data["checksum"] self.write_dump(dump_data, bad_sum_wallet_dump, skip_checksum=True) self.assert_raises_tool_error('Error: Missing checksum', '-wallet=badload', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') assert not (self.nodes[0].wallets_path / "badload").is_dir() - bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum3.dump") + bad_sum_wallet_dump = self.nodes[0].datadir_path / "wallet-bad_sum3.dump" dump_data["checksum"] = "2" * 10 self.write_dump(dump_data, bad_sum_wallet_dump) self.assert_raises_tool_error('Error: Checksum is not the correct size', '-wallet=badload', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') @@ -452,7 +452,7 @@ class ToolWalletTest(BitcoinTestFramework): self.assert_tool_output(expected_output, "-wallet=conflicts", "info") def run_test(self): - self.wallet_path = os.path.join(self.nodes[0].wallets_path, self.default_wallet_name, self.wallet_data_filename) + self.wallet_path = self.nodes[0].wallets_path / self.default_wallet_name / self.wallet_data_filename self.test_invalid_tool_commands_and_args() # Warning: The following tests are order-dependent. self.test_tool_wallet_info() diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py index 9f6f54c7a6..eb3e0ae728 100755 --- a/test/functional/wallet_backup.py +++ b/test/functional/wallet_backup.py @@ -109,36 +109,35 @@ class WalletBackupTest(BitcoinTestFramework): self.stop_node(2) def erase_three(self): - os.remove(os.path.join(self.nodes[0].wallets_path, self.default_wallet_name, self.wallet_data_filename)) - os.remove(os.path.join(self.nodes[1].wallets_path, self.default_wallet_name, self.wallet_data_filename)) - os.remove(os.path.join(self.nodes[2].wallets_path, self.default_wallet_name, self.wallet_data_filename)) + for node_num in range(3): + (self.nodes[node_num].wallets_path / self.default_wallet_name / self.wallet_data_filename).unlink() def restore_invalid_wallet(self): node = self.nodes[3] - invalid_wallet_file = os.path.join(self.nodes[0].datadir, 'invalid_wallet_file.bak') + invalid_wallet_file = self.nodes[0].datadir_path / 'invalid_wallet_file.bak' open(invalid_wallet_file, 'a', encoding="utf8").write('invald wallet') wallet_name = "res0" - not_created_wallet_file = os.path.join(node.wallets_path, wallet_name) + not_created_wallet_file = node.wallets_path / wallet_name error_message = "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(not_created_wallet_file) assert_raises_rpc_error(-18, error_message, node.restorewallet, wallet_name, invalid_wallet_file) - assert not os.path.exists(not_created_wallet_file) + assert not not_created_wallet_file.exists() def restore_nonexistent_wallet(self): node = self.nodes[3] - nonexistent_wallet_file = os.path.join(self.nodes[0].datadir, 'nonexistent_wallet.bak') + nonexistent_wallet_file = self.nodes[0].datadir_path / 'nonexistent_wallet.bak' wallet_name = "res0" assert_raises_rpc_error(-8, "Backup file does not exist", node.restorewallet, wallet_name, nonexistent_wallet_file) - not_created_wallet_file = os.path.join(node.wallets_path, wallet_name) - assert not os.path.exists(not_created_wallet_file) + not_created_wallet_file = node.wallets_path / wallet_name + assert not not_created_wallet_file.exists() def restore_wallet_existent_name(self): node = self.nodes[3] - backup_file = os.path.join(self.nodes[0].datadir, 'wallet.bak') + backup_file = self.nodes[0].datadir_path / 'wallet.bak' wallet_name = "res0" - wallet_file = os.path.join(node.wallets_path, wallet_name) + wallet_file = node.wallets_path / wallet_name error_message = "Failed to create database path '{}'. Database already exists.".format(wallet_file) assert_raises_rpc_error(-36, error_message, node.restorewallet, wallet_name, backup_file) - assert os.path.exists(wallet_file) + assert wallet_file.exists() def run_test(self): self.log.info("Generating initial blockchain") @@ -159,14 +158,12 @@ class WalletBackupTest(BitcoinTestFramework): self.log.info("Backing up") - self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak')) - self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, 'wallet.bak')) - self.nodes[2].backupwallet(os.path.join(self.nodes[2].datadir, 'wallet.bak')) + for node_num in range(3): + self.nodes[node_num].backupwallet(self.nodes[node_num].datadir_path / 'wallet.bak') if not self.options.descriptors: - self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump')) - self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump')) - self.nodes[2].dumpwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump')) + for node_num in range(3): + self.nodes[node_num].dumpwallet(self.nodes[node_num].datadir_path / 'wallet.dump') self.log.info("More transactions") for _ in range(5): @@ -193,17 +190,13 @@ class WalletBackupTest(BitcoinTestFramework): self.restore_invalid_wallet() self.restore_nonexistent_wallet() - backup_file_0 = os.path.join(self.nodes[0].datadir, 'wallet.bak') - backup_file_1 = os.path.join(self.nodes[1].datadir, 'wallet.bak') - backup_file_2 = os.path.join(self.nodes[2].datadir, 'wallet.bak') - - self.nodes[3].restorewallet("res0", backup_file_0) - self.nodes[3].restorewallet("res1", backup_file_1) - self.nodes[3].restorewallet("res2", backup_file_2) + backup_files = [] + for node_num in range(3): + backup_files.append(self.nodes[node_num].datadir_path / 'wallet.bak') - assert os.path.exists(os.path.join(self.nodes[3].wallets_path, "res0")) - assert os.path.exists(os.path.join(self.nodes[3].wallets_path, "res1")) - assert os.path.exists(os.path.join(self.nodes[3].wallets_path, "res2")) + for idx, backup_file in enumerate(backup_files): + self.nodes[3].restorewallet(f'res{idx}', backup_file) + assert (self.nodes[3].wallets_path / f'res{idx}').exists() res0_rpc = self.nodes[3].get_wallet_rpc("res0") res1_rpc = self.nodes[3].get_wallet_rpc("res1") @@ -221,22 +214,16 @@ class WalletBackupTest(BitcoinTestFramework): self.erase_three() #start node2 with no chain - shutil.rmtree(os.path.join(self.nodes[2].blocks_path)) - shutil.rmtree(os.path.join(self.nodes[2].chain_path, 'chainstate')) + shutil.rmtree(self.nodes[2].blocks_path) + shutil.rmtree(self.nodes[2].chain_path / 'chainstate') self.start_three(["-nowallet"]) # Create new wallets for the three nodes. # We will use this empty wallets to test the 'importwallet()' RPC command below. for node_num in range(3): self.nodes[node_num].createwallet(wallet_name=self.default_wallet_name, descriptors=self.options.descriptors, load_on_startup=True) - - assert_equal(self.nodes[0].getbalance(), 0) - assert_equal(self.nodes[1].getbalance(), 0) - assert_equal(self.nodes[2].getbalance(), 0) - - self.nodes[0].importwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump')) - self.nodes[1].importwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump')) - self.nodes[2].importwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump')) + assert_equal(self.nodes[node_num].getbalance(), 0) + self.nodes[node_num].importwallet(self.nodes[node_num].datadir_path / 'wallet.dump') self.sync_blocks() diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py index 49e36b21c5..4d6e6024c5 100755 --- a/test/functional/wallet_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -33,11 +33,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 11 + self.num_nodes = 12 # Add new version after each release: self.extra_args = [ ["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to receive coins, swap wallets, etc + ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v25.0 ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v24.0.1 ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0 ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0 @@ -58,6 +59,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[ None, None, + 250000, 240001, 230000, 220000, @@ -72,20 +74,72 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): self.start_nodes() self.import_deterministic_coinbase_privkeys() - def nodes_wallet_dir(self, node): - if node.version < 170000: - return node.chain_path - return node.wallets_path + def split_version(self, node): + major = node.version // 10000 + minor = (node.version % 10000) // 100 + patch = (node.version % 100) + return (major, minor, patch) + + def major_version_equals(self, node, major): + node_major, _, _ = self.split_version(node) + return node_major == major + + def major_version_less_than(self, node, major): + node_major, _, _ = self.split_version(node) + return node_major < major + + def major_version_at_least(self, node, major): + node_major, _, _ = self.split_version(node) + return node_major >= major + + def test_v19_addmultisigaddress(self): + if not self.is_bdb_compiled(): + return + # Specific test for addmultisigaddress using v19 + # See #18075 + self.log.info("Testing 0.19 addmultisigaddress case (#18075)") + node_master = self.nodes[1] + node_v19 = self.nodes[self.num_nodes - 4] + node_v19.rpc.createwallet(wallet_name="w1_v19") + wallet = node_v19.get_wallet_rpc("w1_v19") + info = wallet.getwalletinfo() + assert info['private_keys_enabled'] + assert info['keypoolsize'] > 0 + # Use addmultisigaddress (see #18075) + address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"] + assert wallet.getaddressinfo(address_18075)["solvable"] + node_v19.unloadwallet("w1_v19") + + # Copy the 0.19 wallet to the last Bitcoin Core version and open it: + shutil.copytree( + os.path.join(node_v19.wallets_path, "w1_v19"), + os.path.join(node_master.wallets_path, "w1_v19") + ) + node_master.loadwallet("w1_v19") + wallet = node_master.get_wallet_rpc("w1_v19") + assert wallet.getaddressinfo(address_18075)["solvable"] + + # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it + node_master.unloadwallet("w1_v19") + shutil.rmtree(os.path.join(node_v19.wallets_path, "w1_v19")) + shutil.copytree( + os.path.join(node_master.wallets_path, "w1_v19"), + os.path.join(node_v19.wallets_path, "w1_v19") + ) + node_v19.loadwallet("w1_v19") + wallet = node_v19.get_wallet_rpc("w1_v19") + assert wallet.getaddressinfo(address_18075)["solvable"] def run_test(self): node_miner = self.nodes[0] node_master = self.nodes[1] - node_v19 = self.nodes[self.num_nodes - 4] - node_v18 = self.nodes[self.num_nodes - 3] + node_v21 = self.nodes[self.num_nodes - 6] node_v17 = self.nodes[self.num_nodes - 2] node_v16 = self.nodes[self.num_nodes - 1] - legacy_nodes = self.nodes[2:] + legacy_nodes = self.nodes[2:] # Nodes that support legacy wallets + legacy_only_nodes = self.nodes[-5:] # Nodes that only support legacy wallets + descriptors_nodes = self.nodes[2:-5] # Nodes that support descriptor wallets self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress()) @@ -121,24 +175,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): # Abandon transaction, but don't confirm node_master.abandontransaction(tx3_id) - # w1_v19: regular wallet, created with v0.19 - node_v19.rpc.createwallet(wallet_name="w1_v19") - wallet = node_v19.get_wallet_rpc("w1_v19") - info = wallet.getwalletinfo() - assert info['private_keys_enabled'] - assert info['keypoolsize'] > 0 - # Use addmultisigaddress (see #18075) - address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"] - assert wallet.getaddressinfo(address_18075)["solvable"] - node_v19.unloadwallet("w1_v19") - - # w1_v18: regular wallet, created with v0.18 - node_v18.rpc.createwallet(wallet_name="w1_v18") - wallet = node_v18.get_wallet_rpc("w1_v18") - info = wallet.getwalletinfo() - assert info['private_keys_enabled'] - assert info['keypoolsize'] > 0 - # w2: wallet with private keys disabled, created on master: update this # test when default wallets private keys disabled can no longer be # opened by older versions. @@ -158,9 +194,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): # Unload wallets and copy to older nodes: node_master_wallets_dir = node_master.wallets_path - node_v19_wallets_dir = node_v19.wallets_path - node_v17_wallets_dir = node_v17.wallets_path - node_v16_wallets_dir = node_v16.chain_path node_master.unloadwallet("w1") node_master.unloadwallet("w2") node_master.unloadwallet("w3") @@ -168,24 +201,35 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): for node in legacy_nodes: # Copy wallets to previous version for wallet in os.listdir(node_master_wallets_dir): - shutil.copytree( - os.path.join(node_master_wallets_dir, wallet), - os.path.join(self.nodes_wallet_dir(node), wallet) - ) - - if not self.options.descriptors: - # Descriptor wallets break compatibility, only run this test for legacy wallet - # Load modern wallet with older nodes - for node in legacy_nodes: - for wallet_name in ["w1", "w2", "w3"]: - if node.version < 170000: - # loadwallet was introduced in v0.17.0 - continue - if node.version < 180000 and wallet_name == "w3": - # Blank wallets were introduced in v0.18.0. We test the loading error below. - continue - node.loadwallet(wallet_name) - wallet = node.get_wallet_rpc(wallet_name) + dest = node.wallets_path / wallet + source = node_master_wallets_dir / wallet + if self.major_version_equals(node, 16): + # 0.16 node expect the wallet to be in the wallet dir but as a plain file rather than in directories + shutil.copyfile(source / "wallet.dat", dest) + else: + shutil.copytree(source, dest) + + self.test_v19_addmultisigaddress() + + self.log.info("Test that a wallet made on master can be opened on:") + # In descriptors wallet mode, run this test on the nodes that support descriptor wallets + # In legacy wallets mode, run this test on the nodes that support legacy wallets + for node in descriptors_nodes if self.options.descriptors else legacy_nodes: + if self.major_version_less_than(node, 17): + # loadwallet was introduced in v0.17.0 + continue + self.log.info(f"- {node.version}") + for wallet_name in ["w1", "w2", "w3"]: + if self.major_version_less_than(node, 18) and wallet_name == "w3": + # Blank wallets were introduced in v0.18.0. We test the loading error below. + continue + if self.major_version_less_than(node, 22) and wallet_name == "w1" and self.options.descriptors: + # Descriptor wallets created after 0.21 have taproot descriptors which 0.21 does not support, tested below + continue + # Also try to reopen on master after opening on old + for n in [node, node_master]: + n.loadwallet(wallet_name) + wallet = n.get_wallet_rpc(wallet_name) info = wallet.getwalletinfo() if wallet_name == "w1": assert info['private_keys_enabled'] == True @@ -208,130 +252,133 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): else: assert info['private_keys_enabled'] == True assert info['keypoolsize'] == 0 - else: - for node in legacy_nodes: - # Descriptor wallets appear to be corrupted wallets to old software - # and loadwallet is introduced in v0.17.0 - if node.version >= 170000 and node.version < 210000: - for wallet_name in ["w1", "w2", "w3"]: - assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name) - # RPC loadwallet failure causes bitcoind to exit, in addition to the RPC - # call failure, so the following test won't work: - # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3') + # Copy back to master + wallet.unloadwallet() + if n == node: + shutil.rmtree(node_master.wallets_path / wallet_name) + shutil.copytree( + n.wallets_path / wallet_name, + node_master.wallets_path / wallet_name, + ) + + # Check that descriptor wallets don't work on legacy only nodes + if self.options.descriptors: + self.log.info("Test descriptor wallet incompatibility on:") + for node in legacy_only_nodes: + # RPC loadwallet failure causes bitcoind to exit in <= 0.17, in addition to the RPC + # call failure, so the following test won't work: + # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3') + if self.major_version_less_than(node, 18): + continue + self.log.info(f"- {node.version}") + # Descriptor wallets appear to be corrupted wallets to old software + assert self.major_version_at_least(node, 18) and self.major_version_less_than(node, 21) + for wallet_name in ["w1", "w2", "w3"]: + assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name) # Instead, we stop node and try to launch it with the wallet: self.stop_node(node_v17.index) if self.options.descriptors: + self.log.info("Test descriptor wallet incompatibility with 0.17") # Descriptor wallets appear to be corrupted wallets to old software node_v17.assert_start_raises_init_error(["-wallet=w1"], "Error: wallet.dat corrupt, salvage failed") node_v17.assert_start_raises_init_error(["-wallet=w2"], "Error: wallet.dat corrupt, salvage failed") node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: wallet.dat corrupt, salvage failed") else: + self.log.info("Test blank wallet incompatibility with v17") node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core") self.start_node(node_v17.index) - if not self.options.descriptors: - # Descriptor wallets break compatibility, only run this test for legacy wallets - # Open most recent wallet in v0.16 (no loadwallet RPC) - self.restart_node(node_v16.index, extra_args=["-wallet=w2"]) - wallet = node_v16.get_wallet_rpc("w2") - info = wallet.getwalletinfo() - assert info['keypoolsize'] == 1 - - # Create upgrade wallet in v0.16 - self.restart_node(node_v16.index, extra_args=["-wallet=u1_v16"]) - wallet = node_v16.get_wallet_rpc("u1_v16") - v16_addr = wallet.getnewaddress('', "bech32") - v16_info = wallet.validateaddress(v16_addr) - v16_pubkey = v16_info['pubkey'] + # No wallet created in master can be opened in 0.16 + self.log.info("Test that wallets created in master are too new for 0.16") self.stop_node(node_v16.index) + for wallet_name in ["w1", "w2", "w3"]: + if self.options.descriptors: + node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: {wallet_name} corrupt, salvage failed") + else: + node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: Error loading {wallet_name}: Wallet requires newer version of Bitcoin Core") + + # When descriptors are enabled, w1 cannot be opened by 0.21 since it contains a taproot descriptor + if self.options.descriptors: + self.log.info("Test that 0.21 cannot open wallet containing tr() descriptors") + assert_raises_rpc_error(-1, "map::at", node_v21.loadwallet, "w1") + + self.log.info("Test that a wallet can upgrade to and downgrade from master, from:") + for node in descriptors_nodes if self.options.descriptors else legacy_nodes: + self.log.info(f"- {node.version}") + wallet_name = f"up_{node.version}" + if self.major_version_less_than(node, 17): + # createwallet is only available in 0.17+ + self.restart_node(node.index, extra_args=[f"-wallet={wallet_name}"]) + wallet_prev = node.get_wallet_rpc(wallet_name) + address = wallet_prev.getnewaddress('', "bech32") + addr_info = wallet_prev.validateaddress(address) + else: + if self.major_version_at_least(node, 21): + node.rpc.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors) + else: + node.rpc.createwallet(wallet_name=wallet_name) + wallet_prev = node.get_wallet_rpc(wallet_name) + address = wallet_prev.getnewaddress('', "bech32") + addr_info = wallet_prev.getaddressinfo(address) + + hdkeypath = addr_info["hdkeypath"].replace("'", "h") + pubkey = addr_info["pubkey"] + + # Make a backup of the wallet file + backup_path = os.path.join(self.options.tmpdir, f"{wallet_name}.dat") + wallet_prev.backupwallet(backup_path) + + # Remove the wallet from old node + if self.major_version_at_least(node, 17): + wallet_prev.unloadwallet() + else: + self.stop_node(node.index) + + # Restore the wallet to master + load_res = node_master.restorewallet(wallet_name, backup_path) - self.log.info("Test wallet upgrade path...") - # u1: regular wallet, created with v0.17 - node_v17.rpc.createwallet(wallet_name="u1_v17") - wallet = node_v17.get_wallet_rpc("u1_v17") - address = wallet.getnewaddress("bech32") - v17_info = wallet.getaddressinfo(address) - hdkeypath = v17_info["hdkeypath"].replace("'", "h") - pubkey = v17_info["pubkey"] - - if self.is_bdb_compiled(): - # Old wallets are BDB and will only work if BDB is compiled - # Copy the 0.16 wallet to the last Bitcoin Core version and open it: - shutil.copyfile( - os.path.join(node_v16_wallets_dir, "wallets/u1_v16"), - os.path.join(node_master_wallets_dir, "u1_v16") - ) - load_res = node_master.loadwallet("u1_v16") # Make sure this wallet opens with only the migration warning. See https://github.com/bitcoin/bitcoin/pull/19054 - if int(node_master.getnetworkinfo()["version"]) >= 249900: - # loadwallet#warnings (added in v25) -- only present if there is a warning + if not self.options.descriptors: # Legacy wallets will have only a deprecation warning assert_equal(load_res["warnings"], ["Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet."]) else: - # loadwallet#warning (deprecated in v25) -- always present, but empty string if no warning - assert_equal(load_res["warning"], '') - wallet = node_master.get_wallet_rpc("u1_v16") - info = wallet.getaddressinfo(v16_addr) - descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{v16_pubkey})" - assert_equal(info["desc"], descsum_create(descriptor)) + assert "warnings" not in load_res - # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it - node_master.unloadwallet("u1_v16") - os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16")) - shutil.copyfile( - os.path.join(node_master_wallets_dir, "u1_v16"), - os.path.join(node_v16_wallets_dir, "wallets/u1_v16") - ) - self.start_node(node_v16.index, extra_args=["-wallet=u1_v16"]) - wallet = node_v16.get_wallet_rpc("u1_v16") - info = wallet.validateaddress(v16_addr) - assert_equal(info, v16_info) - - # Copy the 0.17 wallet to the last Bitcoin Core version and open it: - node_v17.unloadwallet("u1_v17") - shutil.copytree( - os.path.join(node_v17_wallets_dir, "u1_v17"), - os.path.join(node_master_wallets_dir, "u1_v17") - ) - node_master.loadwallet("u1_v17") - wallet = node_master.get_wallet_rpc("u1_v17") + wallet = node_master.get_wallet_rpc(wallet_name) info = wallet.getaddressinfo(address) descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{pubkey})" assert_equal(info["desc"], descsum_create(descriptor)) - # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it - node_master.unloadwallet("u1_v17") - shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17")) - shutil.copytree( - os.path.join(node_master_wallets_dir, "u1_v17"), - os.path.join(node_v17_wallets_dir, "u1_v17") - ) - node_v17.loadwallet("u1_v17") - wallet = node_v17.get_wallet_rpc("u1_v17") - info = wallet.getaddressinfo(address) - assert_equal(info, v17_info) - - # Copy the 0.19 wallet to the last Bitcoin Core version and open it: - shutil.copytree( - os.path.join(node_v19_wallets_dir, "w1_v19"), - os.path.join(node_master_wallets_dir, "w1_v19") - ) - node_master.loadwallet("w1_v19") - wallet = node_master.get_wallet_rpc("w1_v19") - assert wallet.getaddressinfo(address_18075)["solvable"] - - # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it - node_master.unloadwallet("w1_v19") - shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19")) - shutil.copytree( - os.path.join(node_master_wallets_dir, "w1_v19"), - os.path.join(node_v19_wallets_dir, "w1_v19") - ) - node_v19.loadwallet("w1_v19") - wallet = node_v19.get_wallet_rpc("w1_v19") - assert wallet.getaddressinfo(address_18075)["solvable"] + # Make backup so the wallet can be copied back to old node + down_wallet_name = f"re_down_{node.version}" + down_backup_path = os.path.join(self.options.tmpdir, f"{down_wallet_name}.dat") + wallet.backupwallet(down_backup_path) + wallet.unloadwallet() + + # Check that no automatic upgrade broke the downgrading the wallet + if self.major_version_less_than(node, 17): + # loadwallet is only available in 0.17+ + shutil.copyfile( + down_backup_path, + node.wallets_path / down_wallet_name + ) + self.start_node(node.index, extra_args=[f"-wallet={down_wallet_name}"]) + wallet_res = node.get_wallet_rpc(down_wallet_name) + info = wallet_res.validateaddress(address) + assert_equal(info, addr_info) + else: + target_dir = node.wallets_path / down_wallet_name + os.makedirs(target_dir, exist_ok=True) + shutil.copyfile( + down_backup_path, + target_dir / "wallet.dat" + ) + node.loadwallet(down_wallet_name) + wallet_res = node.get_wallet_rpc(down_wallet_name) + info = wallet_res.getaddressinfo(address) + assert_equal(info, addr_info) if __name__ == '__main__': BackwardsCompatibilityTest().main() diff --git a/test/functional/wallet_blank.py b/test/functional/wallet_blank.py index 4836eba3b2..e646d27005 100755 --- a/test/functional/wallet_blank.py +++ b/test/functional/wallet_blank.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php. -import os from test_framework.test_framework import BitcoinTestFramework from test_framework.address import ( @@ -110,7 +109,7 @@ class WalletBlankTest(BitcoinTestFramework): assert_equal(info["descriptors"], False) assert_equal(info["blank"], True) - wallet_dump_path = os.path.join(self.nodes[0].datadir, "wallet.dump") + wallet_dump_path = self.nodes[0].datadir_path / "wallet.dump" def_wallet.dumpwallet(wallet_dump_path) wallet.importwallet(wallet_dump_path) diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index 75b507c387..8e07021e03 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -12,7 +12,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) -from test_framework.wallet_util import generate_keypair +from test_framework.wallet_util import generate_keypair, WalletUnlock EMPTY_PASSPHRASE_MSG = "Empty string given as passphrase, wallet will not be encrypted." @@ -108,24 +108,24 @@ class CreateWalletTest(BitcoinTestFramework): w4.encryptwallet('pass') assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress) - # Now set a seed and it should work. Wallet should also be encrypted - w4.walletpassphrase('pass', 60) - if self.options.descriptors: - w4.importdescriptors([{ - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/0h/*)'), - 'timestamp': 'now', - 'active': True - }, - { - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/1h/*)'), - 'timestamp': 'now', - 'active': True, - 'internal': True - }]) - else: - w4.sethdseed() - w4.getnewaddress() - w4.getrawchangeaddress() + with WalletUnlock(w4, "pass"): + # Now set a seed and it should work. Wallet should also be encrypted + if self.options.descriptors: + w4.importdescriptors([{ + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/0h/*)'), + 'timestamp': 'now', + 'active': True + }, + { + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/1h/*)'), + 'timestamp': 'now', + 'active': True, + 'internal': True + }]) + else: + w4.sethdseed() + w4.getnewaddress() + w4.getrawchangeaddress() self.log.info("Test blank creation with privkeys disabled and then encryption") self.nodes[0].createwallet(wallet_name='w5', disable_private_keys=True, blank=True) @@ -142,23 +142,23 @@ class CreateWalletTest(BitcoinTestFramework): self.nodes[0].createwallet(wallet_name='wblank', disable_private_keys=False, blank=True, passphrase='thisisapassphrase') wblank = node.get_wallet_rpc('wblank') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wblank.signmessage, "needanargument", "test") - wblank.walletpassphrase('thisisapassphrase', 60) - assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress) - assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress) + with WalletUnlock(wblank, "thisisapassphrase"): + assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress) + assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress) self.log.info('Test creating a new encrypted wallet.') # Born encrypted wallet is created (has keys) self.nodes[0].createwallet(wallet_name='w6', disable_private_keys=False, blank=False, passphrase='thisisapassphrase') w6 = node.get_wallet_rpc('w6') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", w6.signmessage, "needanargument", "test") - w6.walletpassphrase('thisisapassphrase', 60) - w6.signmessage(w6.getnewaddress('', 'legacy'), "test") - w6.keypoolrefill(1) - # There should only be 1 key for legacy, 3 for descriptors - walletinfo = w6.getwalletinfo() - keys = 4 if self.options.descriptors else 1 - assert_equal(walletinfo['keypoolsize'], keys) - assert_equal(walletinfo['keypoolsize_hd_internal'], keys) + with WalletUnlock(w6, "thisisapassphrase"): + w6.signmessage(w6.getnewaddress('', 'legacy'), "test") + w6.keypoolrefill(1) + # There should only be 1 key for legacy, 3 for descriptors + walletinfo = w6.getwalletinfo() + keys = 4 if self.options.descriptors else 1 + assert_equal(walletinfo['keypoolsize'], keys) + assert_equal(walletinfo['keypoolsize_hd_internal'], keys) # Allow empty passphrase, but there should be a warning resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='') assert_equal(resp["warnings"], [EMPTY_PASSPHRASE_MSG] if self.options.descriptors else [EMPTY_PASSPHRASE_MSG, LEGACY_WALLET_MSG]) diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py index 7a1297e65f..4c5d7192ae 100755 --- a/test/functional/wallet_crosschain.py +++ b/test/functional/wallet_crosschain.py @@ -3,8 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -import os - from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_raises_rpc_error @@ -31,13 +29,13 @@ class WalletCrossChain(BitcoinTestFramework): def run_test(self): self.log.info("Creating wallets") - node0_wallet = os.path.join(self.nodes[0].datadir, 'node0_wallet') - node0_wallet_backup = os.path.join(self.nodes[0].datadir, 'node0_wallet.bak') + node0_wallet = self.nodes[0].datadir_path / 'node0_wallet' + node0_wallet_backup = self.nodes[0].datadir_path / 'node0_wallet.bak' self.nodes[0].createwallet(node0_wallet) self.nodes[0].backupwallet(node0_wallet_backup) self.nodes[0].unloadwallet(node0_wallet) - node1_wallet = os.path.join(self.nodes[1].datadir, 'node1_wallet') - node1_wallet_backup = os.path.join(self.nodes[0].datadir, 'node1_wallet.bak') + node1_wallet = self.nodes[1].datadir_path / 'node1_wallet' + node1_wallet_backup = self.nodes[0].datadir_path / 'node1_wallet.bak' self.nodes[1].createwallet(node1_wallet) self.nodes[1].backupwallet(node1_wallet_backup) self.nodes[1].unloadwallet(node1_wallet) diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index 6f563987cc..e9321b72e2 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test descriptor wallet function.""" -import os try: import sqlite3 @@ -16,6 +15,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error ) +from test_framework.wallet_util import WalletUnlock class WalletDescriptorTest(BitcoinTestFramework): @@ -129,11 +129,10 @@ class WalletDescriptorTest(BitcoinTestFramework): # Encrypt wallet 0 send_wrpc.encryptwallet('pass') - send_wrpc.walletpassphrase('pass', 10) - addr = send_wrpc.getnewaddress() - info2 = send_wrpc.getaddressinfo(addr) - assert info1['hdmasterfingerprint'] != info2['hdmasterfingerprint'] - send_wrpc.walletlock() + with WalletUnlock(send_wrpc, "pass"): + addr = send_wrpc.getnewaddress() + info2 = send_wrpc.getaddressinfo(addr) + assert info1['hdmasterfingerprint'] != info2['hdmasterfingerprint'] assert 'hdmasterfingerprint' in send_wrpc.getaddressinfo(send_wrpc.getnewaddress()) info3 = send_wrpc.getaddressinfo(addr) assert_equal(info2['desc'], info3['desc']) @@ -143,14 +142,13 @@ class WalletDescriptorTest(BitcoinTestFramework): send_wrpc.getnewaddress() self.log.info("Test that unlock is needed when deriving only hardened keys in an encrypted wallet") - send_wrpc.walletpassphrase('pass', 10) - send_wrpc.importdescriptors([{ - "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", - "timestamp": "now", - "range": [0,10], - "active": True - }]) - send_wrpc.walletlock() + with WalletUnlock(send_wrpc, "pass"): + send_wrpc.importdescriptors([{ + "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", + "timestamp": "now", + "range": [0,10], + "active": True + }]) # Exhaust keypool of 100 for _ in range(100): send_wrpc.getnewaddress(address_type='bech32') @@ -234,7 +232,7 @@ class WalletDescriptorTest(BitcoinTestFramework): self.log.info("Test that loading descriptor wallet containing legacy key types throws error") self.nodes[0].createwallet(wallet_name="crashme", descriptors=True) self.nodes[0].unloadwallet("crashme") - wallet_db = os.path.join(self.nodes[0].wallets_path, "crashme", self.wallet_data_filename) + wallet_db = self.nodes[0].wallets_path / "crashme" / self.wallet_data_filename conn = sqlite3.connect(wallet_db) with conn: # add "cscript" entry: key type is uint160 (20 bytes), value type is CScript (zero-length here) diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index cf20ff1239..f50aae0c53 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the dumpwallet RPC.""" import datetime -import os import time from test_framework.test_framework import BitcoinTestFramework @@ -12,6 +11,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) +from test_framework.wallet_util import WalletUnlock def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): @@ -111,8 +111,8 @@ class WalletDumpTest(BitcoinTestFramework): def run_test(self): self.nodes[0].createwallet("dump") - wallet_unenc_dump = os.path.join(self.nodes[0].datadir, "wallet.unencrypted.dump") - wallet_enc_dump = os.path.join(self.nodes[0].datadir, "wallet.encrypted.dump") + wallet_unenc_dump = self.nodes[0].datadir_path / "wallet.unencrypted.dump" + wallet_enc_dump = self.nodes[0].datadir_path / "wallet.encrypted.dump" # generate 30 addresses to compare against the dump # - 10 legacy P2PKH @@ -156,7 +156,7 @@ class WalletDumpTest(BitcoinTestFramework): self.log.info('Dump unencrypted wallet') result = self.nodes[0].dumpwallet(wallet_unenc_dump) - assert_equal(result['filename'], wallet_unenc_dump) + assert_equal(result['filename'], str(wallet_unenc_dump)) found_comments, found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \ read_dump(wallet_unenc_dump, addrs, [multisig_addr], None) @@ -173,26 +173,26 @@ class WalletDumpTest(BitcoinTestFramework): # encrypt wallet, restart, unlock and dump self.nodes[0].encryptwallet('test') - self.nodes[0].walletpassphrase('test', 100) - # Should be a no-op: - self.nodes[0].keypoolrefill() - self.nodes[0].dumpwallet(wallet_enc_dump) - - found_comments, found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \ - read_dump(wallet_enc_dump, addrs, [multisig_addr], hd_master_addr_unenc) - assert '# End of dump' in found_comments # Check that file is not corrupt - assert_equal(dump_time_str, next(c for c in found_comments if c.startswith('# * Created on'))) - assert_equal(dump_best_block_1, next(c for c in found_comments if c.startswith('# * Best block'))) - assert_equal(dump_best_block_2, next(c for c in found_comments if c.startswith('# mined on'))) - assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_script_addr, 1) - assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now - assert_equal(found_addr_rsv, 90 * 2) - - # Overwriting should fail - assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump)) + with WalletUnlock(self.nodes[0], "test"): + # Should be a no-op: + self.nodes[0].keypoolrefill() + self.nodes[0].dumpwallet(wallet_enc_dump) + + found_comments, found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \ + read_dump(wallet_enc_dump, addrs, [multisig_addr], hd_master_addr_unenc) + assert '# End of dump' in found_comments # Check that file is not corrupt + assert_equal(dump_time_str, next(c for c in found_comments if c.startswith('# * Created on'))) + assert_equal(dump_best_block_1, next(c for c in found_comments if c.startswith('# * Best block'))) + assert_equal(dump_best_block_2, next(c for c in found_comments if c.startswith('# mined on'))) + assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_script_addr, 1) + assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now + assert_equal(found_addr_rsv, 90 * 2) + + # Overwriting should fail + assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump)) # Restart node with new wallet, and test importwallet self.restart_node(0) @@ -220,7 +220,7 @@ class WalletDumpTest(BitcoinTestFramework): w3.sendtoaddress(w3.getnewaddress(), 10) w3.unloadwallet() self.nodes[0].loadwallet("w3") - w3.dumpwallet(os.path.join(self.nodes[0].datadir, "w3.dump")) + w3.dumpwallet(self.nodes[0].datadir_path / "w3.dump") if __name__ == '__main__': WalletDumpTest().main() diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index 88b9ebbddd..b30634010d 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -11,6 +11,7 @@ from test_framework.util import ( assert_raises_rpc_error, assert_equal, ) +from test_framework.wallet_util import WalletUnlock class WalletEncryptionTest(BitcoinTestFramework): @@ -59,19 +60,17 @@ class WalletEncryptionTest(BitcoinTestFramework): assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) # Test walletlock - self.nodes[0].walletpassphrase(passphrase, 84600) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signmessage, address, msg) # Test passphrase changes self.nodes[0].walletpassphrasechange(passphrase, passphrase2) assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) - self.nodes[0].walletpassphrase(passphrase2, 10) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase2): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) # Test timeout bounds assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10) @@ -97,10 +96,9 @@ class WalletEncryptionTest(BitcoinTestFramework): self.nodes[0].walletpassphrasechange(passphrase2, passphrase_with_nulls) # walletpassphrasechange should not stop at null characters assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase_with_nulls.partition("\0")[0], 10) - self.nodes[0].walletpassphrase(passphrase_with_nulls, 10) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase_with_nulls): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) if __name__ == '__main__': diff --git a/test/functional/wallet_fast_rescan.py b/test/functional/wallet_fast_rescan.py index 112aa25e86..2f9c924e71 100755 --- a/test/functional/wallet_fast_rescan.py +++ b/test/functional/wallet_fast_rescan.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test that fast rescan using block filters for descriptor wallets detects top-ups correctly and finds the same transactions than the slow variant.""" -import os from typing import List from test_framework.address import address_to_scriptpubkey @@ -43,7 +42,7 @@ class WalletFastRescanTest(BitcoinTestFramework): wallet = MiniWallet(node) self.log.info("Create descriptor wallet with backup") - WALLET_BACKUP_FILENAME = os.path.join(node.datadir, 'wallet.bak') + WALLET_BACKUP_FILENAME = node.datadir_path / 'wallet.bak' node.createwallet(wallet_name='topup_test', descriptors=True) w = node.get_wallet_rpc('topup_test') fixed_key = get_generate_key() diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index b1829f42af..77611649ac 100755 --- a/test/functional/wallet_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -25,7 +25,7 @@ from test_framework.util import ( find_vout_for_address, get_fee, ) -from test_framework.wallet_util import generate_keypair +from test_framework.wallet_util import generate_keypair, WalletUnlock ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \ "Please allow other inputs to be automatically selected or include more coins manually" @@ -581,19 +581,18 @@ class RawTransactionsTest(BitcoinTestFramework): wallet.encryptwallet("test") if self.options.descriptors: - wallet.walletpassphrase('test', 10) - wallet.importdescriptors([{ - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'), - 'timestamp': 'now', - 'active': True - }, - { - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'), - 'timestamp': 'now', - 'active': True, - 'internal': True - }]) - wallet.walletlock() + with WalletUnlock(wallet, "test"): + wallet.importdescriptors([{ + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'), + 'timestamp': 'now', + 'active': True + }, + { + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'), + 'timestamp': 'now', + 'active': True, + 'internal': True + }]) # Drain the keypool. wallet.getnewaddress() @@ -619,9 +618,8 @@ class RawTransactionsTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", wallet.fundrawtransaction, rawtx) # Refill the keypool. - wallet.walletpassphrase("test", 100) - wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address - wallet.walletlock() + with WalletUnlock(wallet, "test"): + wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address assert_raises_rpc_error(-13, "walletpassphrase", wallet.sendtoaddress, self.nodes[0].getnewaddress(), 1.2) @@ -634,16 +632,16 @@ class RawTransactionsTest(BitcoinTestFramework): assert fundedTx["changepos"] != -1 # Now we need to unlock. - wallet.walletpassphrase("test", 600) - signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex']) - wallet.sendrawtransaction(signedTx['hex']) - self.generate(self.nodes[1], 1) + with WalletUnlock(wallet, "test"): + signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex']) + wallet.sendrawtransaction(signedTx['hex']) + self.generate(self.nodes[1], 1) - # Make sure funds are received at node1. - assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance()) + # Make sure funds are received at node1. + assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance()) - # Restore pre-test wallet state - wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()]) + # Restore pre-test wallet state + wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()]) wallet.unloadwallet() self.generate(self.nodes[1], 1) @@ -1063,19 +1061,19 @@ class RawTransactionsTest(BitcoinTestFramework): high_input_weight = input_weight * 2 # Funding should also work if the input weight is provided - funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}]) + funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}], fee_rate=2) signed_tx = wallet.signrawtransactionwithwallet(funded_tx["hex"]) signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx["hex"]) assert_equal(self.nodes[0].testmempoolaccept([signed_tx["hex"]])[0]["allowed"], True) assert_equal(signed_tx["complete"], True) # Reducing the weight should have a lower fee - funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": low_input_weight}]) + funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": low_input_weight}], fee_rate=2) assert_greater_than(funded_tx["fee"], funded_tx2["fee"]) # Increasing the weight should have a higher fee - funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}]) + funded_tx2 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=2) assert_greater_than(funded_tx2["fee"], funded_tx["fee"]) # The provided weight should override the calculated weight when solving data is provided - funded_tx3 = wallet.fundrawtransaction(raw_tx, solving_data={"descriptors": [desc]}, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}]) + funded_tx3 = wallet.fundrawtransaction(raw_tx, solving_data={"descriptors": [desc]}, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=2) assert_equal(funded_tx2["fee"], funded_tx3["fee"]) # The feerate should be met funded_tx4 = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": high_input_weight}], fee_rate=10) @@ -1085,8 +1083,8 @@ class RawTransactionsTest(BitcoinTestFramework): assert_fee_amount(funded_tx4["fee"], tx4_vsize, Decimal(0.0001)) # Funding with weight at csuint boundaries should not cause problems - funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 255}]) - funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 65539}]) + funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 255}], fee_rate=2) + funded_tx = wallet.fundrawtransaction(raw_tx, input_weights=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 65539}], fee_rate=2) self.nodes[2].unloadwallet("extfund") diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 62f8301c16..0f4b7cfcb1 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test Hierarchical Deterministic wallet function.""" -import os import shutil from test_framework.blocktools import COINBASE_MATURITY @@ -51,8 +50,8 @@ class WalletHDTest(BitcoinTestFramework): self.nodes[1].importprivkey(non_hd_key) # This should be enough to keep the master key and the non-HD key - self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, "hd.bak")) - #self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, "hd.dump")) + self.nodes[1].backupwallet(self.nodes[1].datadir_path / "hd.bak") + #self.nodes[1].dumpwallet(self.nodes[1].datadir_path / "hd.dump") # Derive some HD addresses and remember the last # Also send funds to each add @@ -87,11 +86,11 @@ class WalletHDTest(BitcoinTestFramework): self.stop_node(1) # we need to delete the complete chain directory # otherwise node1 would auto-recover all funds in flag the keypool keys as used - shutil.rmtree(os.path.join(self.nodes[1].blocks_path)) - shutil.rmtree(os.path.join(self.nodes[1].chain_path, "chainstate")) + shutil.rmtree(self.nodes[1].blocks_path) + shutil.rmtree(self.nodes[1].chain_path / "chainstate") shutil.copyfile( - os.path.join(self.nodes[1].datadir, "hd.bak"), - os.path.join(self.nodes[1].wallets_path, self.default_wallet_name, self.wallet_data_filename), + self.nodes[1].datadir_path / "hd.bak", + self.nodes[1].wallets_path / self.default_wallet_name / self.wallet_data_filename ) self.start_node(1) @@ -115,11 +114,11 @@ class WalletHDTest(BitcoinTestFramework): # Try a RPC based rescan self.stop_node(1) - shutil.rmtree(os.path.join(self.nodes[1].blocks_path)) - shutil.rmtree(os.path.join(self.nodes[1].chain_path, "chainstate")) + shutil.rmtree(self.nodes[1].blocks_path) + shutil.rmtree(self.nodes[1].chain_path / "chainstate") shutil.copyfile( - os.path.join(self.nodes[1].datadir, "hd.bak"), - os.path.join(self.nodes[1].wallets_path, self.default_wallet_name, self.wallet_data_filename), + self.nodes[1].datadir_path / "hd.bak", + self.nodes[1].wallets_path / self.default_wallet_name / self.wallet_data_filename ) self.start_node(1, extra_args=self.extra_args[1]) self.connect_nodes(0, 1) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index a39db3bfb8..d2341fb12e 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -9,6 +9,7 @@ from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.wallet_util import WalletUnlock class KeyPoolTest(BitcoinTestFramework): def add_options(self, parser): @@ -85,9 +86,8 @@ class KeyPoolTest(BitcoinTestFramework): assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) - nodes[0].walletpassphrase('test', 12000) - nodes[0].keypoolrefill(6) - nodes[0].walletlock() + with WalletUnlock(nodes[0], 'test'): + nodes[0].keypoolrefill(6) wi = nodes[0].getwalletinfo() if self.options.descriptors: assert_equal(wi['keypoolsize_hd_internal'], 24) @@ -131,29 +131,29 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].getnewaddress() assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getnewaddress) - nodes[0].walletpassphrase('test', 100) - nodes[0].keypoolrefill(100) - wi = nodes[0].getwalletinfo() - if self.options.descriptors: - assert_equal(wi['keypoolsize_hd_internal'], 400) - assert_equal(wi['keypoolsize'], 400) - else: - assert_equal(wi['keypoolsize_hd_internal'], 100) - assert_equal(wi['keypoolsize'], 100) - - if not self.options.descriptors: - # Check that newkeypool entirely flushes the keypool - start_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] - start_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] - # flush keypool and get new addresses - nodes[0].newkeypool() - end_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] - end_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] - # The new keypath index should be 100 more than the old one - new_index = int(start_keypath.rsplit('/', 1)[1][:-1]) + 100 - new_change_index = int(start_change_keypath.rsplit('/', 1)[1][:-1]) + 100 - assert_equal(end_keypath, "m/0'/0'/" + str(new_index) + "'") - assert_equal(end_change_keypath, "m/0'/1'/" + str(new_change_index) + "'") + with WalletUnlock(nodes[0], 'test'): + nodes[0].keypoolrefill(100) + wi = nodes[0].getwalletinfo() + if self.options.descriptors: + assert_equal(wi['keypoolsize_hd_internal'], 400) + assert_equal(wi['keypoolsize'], 400) + else: + assert_equal(wi['keypoolsize_hd_internal'], 100) + assert_equal(wi['keypoolsize'], 100) + + if not self.options.descriptors: + # Check that newkeypool entirely flushes the keypool + start_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] + start_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] + # flush keypool and get new addresses + nodes[0].newkeypool() + end_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] + end_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] + # The new keypath index should be 100 more than the old one + new_index = int(start_keypath.rsplit('/', 1)[1][:-1]) + 100 + new_change_index = int(start_change_keypath.rsplit('/', 1)[1][:-1]) + 100 + assert_equal(end_keypath, "m/0'/0'/" + str(new_index) + "'") + assert_equal(end_change_keypath, "m/0'/1'/" + str(new_change_index) + "'") # create a blank wallet nodes[0].createwallet(wallet_name='w2', blank=True, disable_private_keys=True) @@ -170,9 +170,9 @@ class KeyPoolTest(BitcoinTestFramework): else: res = w2.importmulti([{'desc': desc, 'timestamp': 'now'}]) assert_equal(res[0]['success'], True) - w1.walletpassphrase('test', 100) - res = w1.sendtoaddress(address=address, amount=0.00010000) + with WalletUnlock(w1, 'test'): + res = w1.sendtoaddress(address=address, amount=0.00010000) self.generate(nodes[0], 1) destination = addr.pop() diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 0f1c33a0c2..48180e8294 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -10,7 +10,6 @@ Two nodes. Node1 is under test. Node0 is providing transactions and generating b - Generate 110 keys (enough to drain the keypool). Store key 90 (in the initial keypool) and key 110 (beyond the initial keypool). Send funds to key 90 and key 110. - Stop node1, clear the datadir, move wallet file back into the datadir and restart node1. - connect node1 to node0. Verify that they sync and node1 receives its funds.""" -import os import shutil from test_framework.blocktools import COINBASE_MATURITY @@ -33,8 +32,8 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.skip_if_no_wallet() def run_test(self): - wallet_path = os.path.join(self.nodes[1].wallets_path, self.default_wallet_name, self.wallet_data_filename) - wallet_backup_path = os.path.join(self.nodes[1].datadir, "wallet.bak") + wallet_path = self.nodes[1].wallets_path / self.default_wallet_name / self.wallet_data_filename + wallet_backup_path = self.nodes[1].datadir_path / "wallet.bak" self.generate(self.nodes[0], COINBASE_MATURITY + 1) self.log.info("Make backup of wallet") diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index bcd71197bf..286dcb5fda 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -6,8 +6,13 @@ import random import shutil -from test_framework.address import script_to_p2sh +from test_framework.address import ( + script_to_p2sh, + key_to_p2pkh, + key_to_p2wpkh, +) from test_framework.descriptors import descsum_create +from test_framework.key import ECPubKey from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import COIN, CTransaction, CTxOut from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_script, script_to_p2wsh_script @@ -770,6 +775,58 @@ class WalletMigrationTest(BitcoinTestFramework): wallet.unloadwallet() + def test_hybrid_pubkey(self): + self.log.info("Test migration when wallet contains a hybrid pubkey") + + wallet = self.create_legacy_wallet("hybrid_keys") + + # Get the hybrid pubkey for one of the keys in the wallet + normal_pubkey = wallet.getaddressinfo(wallet.getnewaddress())["pubkey"] + first_byte = bytes.fromhex(normal_pubkey)[0] + 4 # Get the hybrid pubkey first byte + parsed_pubkey = ECPubKey() + parsed_pubkey.set(bytes.fromhex(normal_pubkey)) + parsed_pubkey.compressed = False + hybrid_pubkey_bytes = bytearray(parsed_pubkey.get_bytes()) + hybrid_pubkey_bytes[0] = first_byte # Make it hybrid + hybrid_pubkey = hybrid_pubkey_bytes.hex() + + # Import the hybrid pubkey + wallet.importpubkey(hybrid_pubkey) + p2pkh_addr = key_to_p2pkh(hybrid_pubkey) + p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr) + assert_equal(p2pkh_addr_info["iswatchonly"], True) + assert_equal(p2pkh_addr_info["ismine"], False) # Things involving hybrid pubkeys are not spendable + + # Also import the p2wpkh for the pubkey to make sure we don't migrate it + p2wpkh_addr = key_to_p2wpkh(hybrid_pubkey) + wallet.importaddress(p2wpkh_addr) + + migrate_info = wallet.migratewallet() + + # Both addresses should only appear in the watchonly wallet + p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr) + assert_equal(p2pkh_addr_info["iswatchonly"], False) + assert_equal(p2pkh_addr_info["ismine"], False) + p2wpkh_addr_info = wallet.getaddressinfo(p2wpkh_addr) + assert_equal(p2wpkh_addr_info["iswatchonly"], False) + assert_equal(p2wpkh_addr_info["ismine"], False) + + watchonly_wallet = self.nodes[0].get_wallet_rpc(migrate_info["watchonly_name"]) + watchonly_p2pkh_addr_info = watchonly_wallet.getaddressinfo(p2pkh_addr) + assert_equal(watchonly_p2pkh_addr_info["iswatchonly"], False) + assert_equal(watchonly_p2pkh_addr_info["ismine"], True) + watchonly_p2wpkh_addr_info = watchonly_wallet.getaddressinfo(p2wpkh_addr) + assert_equal(watchonly_p2wpkh_addr_info["iswatchonly"], False) + assert_equal(watchonly_p2wpkh_addr_info["ismine"], True) + + # There should only be raw or addr descriptors + for desc in watchonly_wallet.listdescriptors()["descriptors"]: + if desc["desc"].startswith("raw(") or desc["desc"].startswith("addr("): + continue + assert False, "Hybrid pubkey watchonly wallet has more than just raw() and addr()" + + wallet.unloadwallet() + def run_test(self): self.generate(self.nodes[0], 101) @@ -787,6 +844,7 @@ class WalletMigrationTest(BitcoinTestFramework): self.test_addressbook() self.test_migrate_raw_p2sh() self.test_conflict_txs() + self.test_hybrid_pubkey() if __name__ == '__main__': WalletMigrationTest().main() diff --git a/test/functional/wallet_miniscript.py b/test/functional/wallet_miniscript.py index 45f0df1c76..d174b525b3 100755 --- a/test/functional/wallet_miniscript.py +++ b/test/functional/wallet_miniscript.py @@ -22,6 +22,7 @@ TPUBS = [ "tpubD6NzVbkrYhZ4XRMcMFMMFvzVt6jaDAtjZhD7JLwdPdMm9xa76DnxYYP7w9TZGJDVFkek3ArwVsuacheqqPog8TH5iBCX1wuig8PLXim4n9a", "tpubD6NzVbkrYhZ4WsqRzDmkL82SWcu42JzUvKWzrJHQ8EC2vEHRHkXj1De93sD3biLrKd8XGnamXURGjMbYavbszVDXpjXV2cGUERucLJkE6cy", "tpubDEFLeBkKTm8aiYkySz8hXAXPVnPSfxMi7Fxhg9sejUrkwJuRWvPdLEiXjTDbhGbjLKCZUDUUibLxTnK5UP1q7qYrSnPqnNe7M8mvAW1STcc", + "tpubD6NzVbkrYhZ4WR99ygpiJvPMAJiwahjLgGywc5vJx2gUfKUfEPCrbKmQczDPJZmLcyZzRb5Ti6rfUb89S2WFyPH7FDtD6RFDA1hdgTEgEUL", ] PUBKEYS = [ "02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068", @@ -33,7 +34,7 @@ PUBKEYS = [ "0211c7b2e18b6fd330f322de087da62da92ae2ae3d0b7cec7e616479cce175f183", ] -MINISCRIPTS = [ +P2WSH_MINISCRIPTS = [ # One of two keys f"or_b(pk({TPUBS[0]}/*),s:pk({TPUBS[1]}/*))", # A script similar (same spending policy) to BOLT3's offered HTLC (with anchor outputs) @@ -44,10 +45,22 @@ MINISCRIPTS = [ f"or_i(and_b(pk({PUBKEYS[0]}),a:and_b(pk({PUBKEYS[1]}),a:and_b(pk({PUBKEYS[2]}),a:and_b(pk({PUBKEYS[3]}),s:pk({PUBKEYS[4]}))))),and_v(v:thresh(2,pkh({TPUBS[0]}/*),a:pkh({PUBKEYS[5]}),a:pkh({PUBKEYS[6]})),older(4209713)))", ] -MINISCRIPTS_PRIV = [ +DESCS = [ + *[f"wsh({ms})" for ms in P2WSH_MINISCRIPTS], + # A Taproot with one of the above scripts as the single script path. + f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{P2WSH_MINISCRIPTS[0]})", + # A Taproot with two script paths among the above scripts. + f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}})", + # A Taproot with three script paths among the above scripts. + f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')}}})", + # A Taproot with all above scripts in its tree. + f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{{{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')},{P2WSH_MINISCRIPTS[3]}}}}})", +] + +DESCS_PRIV = [ # One of two keys, of which one private key is known { - "ms": f"or_i(pk({TPRVS[0]}/*),pk({TPUBS[0]}/*))", + "desc": f"wsh(or_i(pk({TPRVS[0]}/*),pk({TPUBS[0]}/*)))", "sequence": None, "locktime": None, "sigs_count": 1, @@ -55,7 +68,7 @@ MINISCRIPTS_PRIV = [ }, # A more complex policy, that can't be satisfied through the first branch (need for a preimage) { - "ms": f"andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(2a8ce30189b2ec3200b47aeb4feaac8fcad7c0ba170389729f4898b0b7933bcb)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*)))", + "desc": f"wsh(andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(2a8ce30189b2ec3200b47aeb4feaac8fcad7c0ba170389729f4898b0b7933bcb)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*))))", "sequence": 2, "locktime": None, "sigs_count": 3, @@ -63,7 +76,7 @@ MINISCRIPTS_PRIV = [ }, # The same policy but we provide the preimage. This path will be chosen as it's a smaller witness. { - "ms": f"andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(61e33e9dbfefc45f6a194187684d278f789fd4d5e207a357e79971b6519a8b12)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*)))", + "desc": f"wsh(andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(61e33e9dbfefc45f6a194187684d278f789fd4d5e207a357e79971b6519a8b12)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*))))", "sequence": 2, "locktime": None, "sigs_count": 3, @@ -74,7 +87,7 @@ MINISCRIPTS_PRIV = [ }, # Signature with a relative timelock { - "ms": f"and_v(v:older(2),pk({TPRVS[0]}/*))", + "desc": f"wsh(and_v(v:older(2),pk({TPRVS[0]}/*)))", "sequence": 2, "locktime": None, "sigs_count": 1, @@ -82,7 +95,7 @@ MINISCRIPTS_PRIV = [ }, # Signature with an absolute timelock { - "ms": f"and_v(v:after(20),pk({TPRVS[0]}/*))", + "desc": f"wsh(and_v(v:after(20),pk({TPRVS[0]}/*)))", "sequence": None, "locktime": 20, "sigs_count": 1, @@ -90,7 +103,7 @@ MINISCRIPTS_PRIV = [ }, # Signature with both { - "ms": f"and_v(v:older(4),and_v(v:after(30),pk({TPRVS[0]}/*)))", + "desc": f"wsh(and_v(v:older(4),and_v(v:after(30),pk({TPRVS[0]}/*))))", "sequence": 4, "locktime": 30, "sigs_count": 1, @@ -98,7 +111,7 @@ MINISCRIPTS_PRIV = [ }, # We have one key on each branch; Core signs both (can't finalize) { - "ms": f"c:andor(pk({TPRVS[0]}/*),pk_k({TPUBS[0]}),and_v(v:pk({TPRVS[1]}),pk_k({TPUBS[1]})))", + "desc": f"wsh(c:andor(pk({TPRVS[0]}/*),pk_k({TPUBS[0]}),and_v(v:pk({TPRVS[1]}),pk_k({TPUBS[1]}))))", "sequence": None, "locktime": None, "sigs_count": 2, @@ -106,7 +119,7 @@ MINISCRIPTS_PRIV = [ }, # We have all the keys, wallet selects the timeout path to sign since it's smaller and sequence is set { - "ms": f"andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pk({TPRVS[1]}),older(10)))", + "desc": f"wsh(andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pk({TPRVS[1]}),older(10))))", "sequence": 10, "locktime": None, "sigs_count": 3, @@ -114,7 +127,7 @@ MINISCRIPTS_PRIV = [ }, # We have all the keys, wallet selects the primary path to sign unconditionally since nsequence wasn't set to be valid for timeout path { - "ms": f"andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pkh({TPRVS[1]}),older(10)))", + "desc": f"wsh(andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pkh({TPRVS[1]}),older(10))))", "sequence": None, "locktime": None, "sigs_count": 3, @@ -122,7 +135,7 @@ MINISCRIPTS_PRIV = [ }, # Finalizes to the smallest valid witness, regardless of sequence { - "ms": f"or_d(pk({TPRVS[0]}/*),and_v(v:pk({TPRVS[1]}),and_v(v:pk({TPRVS[2]}),older(10))))", + "desc": f"wsh(or_d(pk({TPRVS[0]}/*),and_v(v:pk({TPRVS[1]}),and_v(v:pk({TPRVS[2]}),older(10)))))", "sequence": 12, "locktime": None, "sigs_count": 3, @@ -130,7 +143,57 @@ MINISCRIPTS_PRIV = [ }, # Liquid-like federated pegin with emergency recovery privkeys { - "ms": f"or_i(and_b(pk({TPUBS[0]}/*),a:and_b(pk({TPUBS[1]}),a:and_b(pk({TPUBS[2]}),a:and_b(pk({TPUBS[3]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[4]})),older(42)))", + "desc": f"wsh(or_i(and_b(pk({TPUBS[0]}/*),a:and_b(pk({TPUBS[1]}),a:and_b(pk({TPUBS[2]}),a:and_b(pk({TPUBS[3]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[4]})),older(42))))", + "sequence": 42, + "locktime": None, + "sigs_count": 2, + "stack_size": 8, + }, + # Each leaf needs two sigs. We've got one key on each. Will sign both but can't finalize. + { + "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pk({TPRVS[1]}/*),pk({TPUBS[2]}))}})", + "sequence": None, + "locktime": None, + "sigs_count": 2, + "stack_size": None, + }, + # The same but now the two leaves are identical. Will add a single sig that is valid for both. Can't finalize. + { + "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]}))}})", + "sequence": None, + "locktime": None, + "sigs_count": 1, + "stack_size": None, + }, + # The same but we have the two necessary privkeys on one of the leaves. Also it uses a pubkey hash. + { + "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pkh({TPRVS[1]}/*),pk({TPRVS[2]}))}})", + "sequence": None, + "locktime": None, + "sigs_count": 3, + "stack_size": 5, + }, + # A key immediately or one of two keys after a timelock. If both paths are available it'll use the + # non-timelocked path because it's a smaller witness. + { + "desc": f"tr({TPUBS[0]}/*,{{pk({TPRVS[0]}/*),and_v(v:older(42),multi_a(1,{TPRVS[1]},{TPRVS[2]}))}})", + "sequence": 42, + "locktime": None, + "sigs_count": 3, + "stack_size": 3, + }, + # A key immediately or one of two keys after a timelock. If the "primary" key isn't available though it'll + # use the timelocked path. Same remark for multi_a. + { + "desc": f"tr({TPUBS[0]}/*,{{pk({TPUBS[1]}/*),and_v(v:older(42),multi_a(1,{TPRVS[0]},{TPRVS[1]}))}})", + "sequence": 42, + "locktime": None, + "sigs_count": 2, + "stack_size": 4, + }, + # Liquid-like federated pegin with emergency recovery privkeys, but in a Taproot. + { + "desc": f"tr({TPUBS[1]}/*,{{and_b(pk({TPUBS[2]}/*),a:and_b(pk({TPUBS[3]}),a:and_b(pk({TPUBS[4]}),a:and_b(pk({TPUBS[5]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[6]})),older(42))}})", "sequence": 42, "locktime": None, "sigs_count": 2, @@ -142,6 +205,7 @@ MINISCRIPTS_PRIV = [ class WalletMiniscriptTest(BitcoinTestFramework): def add_options(self, parser): self.add_wallet_options(parser, legacy=False) + self.rpc_timeout = 480 def set_test_params(self): self.num_nodes = 1 @@ -150,9 +214,9 @@ class WalletMiniscriptTest(BitcoinTestFramework): self.skip_if_no_wallet() self.skip_if_no_sqlite() - def watchonly_test(self, ms): - self.log.info(f"Importing Miniscript '{ms}'") - desc = descsum_create(f"wsh({ms})") + def watchonly_test(self, desc): + self.log.info(f"Importing descriptor '{desc}'") + desc = descsum_create(f"{desc}") assert self.ms_wo_wallet.importdescriptors( [ { @@ -166,11 +230,14 @@ class WalletMiniscriptTest(BitcoinTestFramework): )[0]["success"] self.log.info("Testing we derive new addresses for it") + addr_type = "bech32m" if desc.startswith("tr(") else "bech32" assert_equal( - self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 0)[0] + self.ms_wo_wallet.getnewaddress(address_type=addr_type), + self.funder.deriveaddresses(desc, 0)[0], ) assert_equal( - self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 1)[1] + self.ms_wo_wallet.getnewaddress(address_type=addr_type), + self.funder.deriveaddresses(desc, 1)[1], ) self.log.info("Testing we detect funds sent to one of them") @@ -183,10 +250,11 @@ class WalletMiniscriptTest(BitcoinTestFramework): assert utxo["txid"] == txid and utxo["solvable"] def signing_test( - self, ms, sequence, locktime, sigs_count, stack_size, sha256_preimages + self, desc, sequence, locktime, sigs_count, stack_size, sha256_preimages ): - self.log.info(f"Importing private Miniscript '{ms}'") - desc = descsum_create(f"wsh({ms})") + self.log.info(f"Importing private Miniscript descriptor '{desc}'") + is_taproot = desc.startswith("tr(") + desc = descsum_create(desc) res = self.ms_sig_wallet.importdescriptors( [ { @@ -201,7 +269,8 @@ class WalletMiniscriptTest(BitcoinTestFramework): assert res[0]["success"], res self.log.info("Generating an address for it and testing it detects funds") - addr = self.ms_sig_wallet.getnewaddress() + addr_type = "bech32m" if is_taproot else "bech32" + addr = self.ms_sig_wallet.getnewaddress(address_type=addr_type) txid = self.funder.sendtoaddress(addr, 0.01) self.wait_until(lambda: txid in self.funder.getrawmempool()) self.funder.generatetoaddress(1, self.funder.getnewaddress()) @@ -233,7 +302,8 @@ class WalletMiniscriptTest(BitcoinTestFramework): psbt = psbt.to_base64() res = self.ms_sig_wallet.walletprocesspsbt(psbt=psbt, finalize=False) psbtin = self.nodes[0].rpc.decodepsbt(res["psbt"])["inputs"][0] - assert len(psbtin["partial_signatures"]) == sigs_count + sigs_field_name = "taproot_script_path_sigs" if is_taproot else "partial_signatures" + assert len(psbtin[sigs_field_name]) == sigs_count res = self.ms_sig_wallet.finalizepsbt(res["psbt"]) assert res["complete"] == (stack_size is not None) @@ -290,20 +360,45 @@ class WalletMiniscriptTest(BitcoinTestFramework): assert not res["success"] and "is not satisfiable" in res["error"]["message"] # Test we can track any type of Miniscript - for ms in MINISCRIPTS: - self.watchonly_test(ms) + for desc in DESCS: + self.watchonly_test(desc) # Test we can sign for any Miniscript. - for ms in MINISCRIPTS_PRIV: + for desc in DESCS_PRIV: self.signing_test( - ms["ms"], - ms["sequence"], - ms["locktime"], - ms["sigs_count"], - ms["stack_size"], - ms.get("sha256_preimages"), + desc["desc"], + desc["sequence"], + desc["locktime"], + desc["sigs_count"], + desc["stack_size"], + desc.get("sha256_preimages"), ) + # Test we can sign for a max-size TapMiniscript. Recompute the maximum accepted size + # for a TapMiniscript (see cpp file for details). Then pad a simple pubkey check up + # to the maximum size. Make sure we can import and spend this script. + leeway_weight = (4 + 4 + 1 + 36 + 4 + 1 + 1 + 8 + 1 + 1 + 33) * 4 + 2 + max_tapmini_size = 400_000 - 3 - (1 + 65) * 1_000 - 3 - (33 + 32 * 128) - leeway_weight - 5 + padding = max_tapmini_size - 33 - 1 + ms = f"pk({TPRVS[0]}/*)" + ms = "n" * padding + ":" + ms + desc = f"tr({PUBKEYS[0]},{ms})" + self.signing_test(desc, None, None, 1, 3, None) + # This was really the maximum size, one more byte and we can't import it. + ms = "n" + ms + desc = f"tr({PUBKEYS[0]},{ms})" + res = self.ms_wo_wallet.importdescriptors( + [ + { + "desc": descsum_create(desc), + "active": False, + "timestamp": "now", + } + ] + )[0] + assert not res["success"] + assert "is not a valid descriptor function" in res["error"]["message"] + if __name__ == "__main__": WalletMiniscriptTest().main() diff --git a/test/functional/wallet_pruning.py b/test/functional/wallet_pruning.py index 06bd992da7..6e252b5406 100755 --- a/test/functional/wallet_pruning.py +++ b/test/functional/wallet_pruning.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test wallet import on pruned node.""" -import os from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.blocktools import ( @@ -74,7 +73,7 @@ class WalletPruningTest(BitcoinTestFramework): # Import wallet into pruned node self.nodes[1].createwallet(wallet_name="wallet_pruned", descriptors=False, load_on_startup=True) - self.nodes[1].importwallet(os.path.join(self.nodes[0].datadir, wallet_file)) + self.nodes[1].importwallet(self.nodes[0].datadir_path / wallet_file) # Make sure that prune node's wallet correctly accounts for balances assert_equal(self.nodes[1].getbalance(), self.nodes[0].getbalance()) @@ -93,12 +92,12 @@ class WalletPruningTest(BitcoinTestFramework): # Make sure wallet cannot be imported because of missing blocks # This will try to rescan blocks `TIMESTAMP_WINDOW` (2h) before the wallet birthheight. # There are 6 blocks an hour, so 11 blocks (excluding birthheight). - assert_raises_rpc_error(-4, f"Pruned blocks from height {wallet_birthheight - 11} required to import keys. Use RPC call getblockchaininfo to determine your pruned height.", self.nodes[1].importwallet, os.path.join(self.nodes[0].datadir, wallet_file)) + assert_raises_rpc_error(-4, f"Pruned blocks from height {wallet_birthheight - 11} required to import keys. Use RPC call getblockchaininfo to determine your pruned height.", self.nodes[1].importwallet, self.nodes[0].datadir_path / wallet_file) self.log.info("- Done") def get_birthheight(self, wallet_file): """Gets birthheight of a wallet on node0""" - with open(os.path.join(self.nodes[0].datadir, wallet_file), 'r', encoding="utf8") as f: + with open(self.nodes[0].datadir_path / wallet_file, 'r', encoding="utf8") as f: for line in f: if line.startswith('# * Best block at time of backup'): wallet_birthheight = int(line.split(' ')[9]) @@ -106,12 +105,12 @@ class WalletPruningTest(BitcoinTestFramework): def has_block(self, block_index): """Checks if the pruned node has the specific blk0000*.dat file""" - return os.path.isfile(os.path.join(self.nodes[1].blocks_path, f"blk{block_index:05}.dat")) + return (self.nodes[1].blocks_path / f"blk{block_index:05}.dat").is_file() def create_wallet(self, wallet_name, *, unload=False): """Creates and dumps a wallet on the non-pruned node0 to be later import by the pruned node""" self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=False, load_on_startup=True) - self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, wallet_name + ".dat")) + self.nodes[0].dumpwallet(self.nodes[0].datadir_path / f"{wallet_name}.dat") if (unload): self.nodes[0].unloadwallet(wallet_name) diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py index af01b9439f..86a2905c72 100755 --- a/test/functional/wallet_reorgsrestore.py +++ b/test/functional/wallet_reorgsrestore.py @@ -14,7 +14,6 @@ disconnected. """ from decimal import Decimal -import os import shutil from test_framework.test_framework import BitcoinTestFramework @@ -88,8 +87,8 @@ class ReorgsRestoreTest(BitcoinTestFramework): # Node0 wallet file is loaded on longest sync'ed node1 self.stop_node(1) - self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak')) - shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].chain_path, self.default_wallet_name, self.wallet_data_filename)) + self.nodes[0].backupwallet(self.nodes[0].datadir_path / 'wallet.bak') + shutil.copyfile(self.nodes[0].datadir_path / 'wallet.bak', self.nodes[1].chain_path / self.default_wallet_name / self.wallet_data_filename) self.start_node(1) tx_after_reorg = self.nodes[1].gettransaction(txid) # Check that normal confirmed tx is confirmed again but with different blockhash diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index c9975af225..9d64591111 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-present The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Run fuzz test targets. @@ -69,7 +69,8 @@ def main(): ) parser.add_argument( '--m_dir', - help='Merge inputs from this directory into the corpus_dir.', + action="append", + help="Merge inputs from these directories into the corpus_dir.", ) parser.add_argument( '-g', @@ -176,7 +177,7 @@ def main(): test_list=test_list_selection, src_dir=config['environment']['SRCDIR'], build_dir=config["environment"]["BUILDDIR"], - merge_dir=args.m_dir, + merge_dirs=[Path(m_dir) for m_dir in args.m_dir], ) return @@ -270,21 +271,29 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets): future.result() -def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir): - logging.info("Merge the inputs from the passed dir into the corpus_dir. Passed dir {}".format(merge_dir)) +def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dirs): + logging.info(f"Merge the inputs from the passed dir into the corpus_dir. Passed dirs {merge_dirs}") jobs = [] for t in test_list: args = [ os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'), - '-merge=1', + '-set_cover_merge=1', + # set_cover_merge is used instead of -merge=1 to reduce the overall + # size of the qa-assets git repository a bit, but more importantly, + # to cut the runtime to iterate over all fuzz inputs [0]. + # [0] https://github.com/bitcoin-core/qa-assets/issues/130#issuecomment-1761760866 '-shuffle=0', '-prefer_small=1', - '-use_value_profile=1', # Also done by oss-fuzz https://github.com/google/oss-fuzz/issues/1406#issuecomment-387790487 + '-use_value_profile=0', + # use_value_profile is enabled by oss-fuzz [0], but disabled for + # now to avoid bloating the qa-assets git repository [1]. + # [0] https://github.com/google/oss-fuzz/issues/1406#issuecomment-387790487 + # [1] https://github.com/bitcoin-core/qa-assets/issues/130#issuecomment-1749075891 os.path.join(corpus, t), - os.path.join(merge_dir, t), - ] + ] + [str(m_dir / t) for m_dir in merge_dirs] os.makedirs(os.path.join(corpus, t), exist_ok=True) - os.makedirs(os.path.join(merge_dir, t), exist_ok=True) + for m_dir in merge_dirs: + (m_dir / t).mkdir(exist_ok=True) def job(t, args): output = 'Run {} with args {}\n'.format(t, " ".join(args)) diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py index 07a5ee8691..459693102b 100755 --- a/test/get_previous_releases.py +++ b/test/get_previous_releases.py @@ -89,6 +89,15 @@ SHA256_SUMS = { "6b163cef7de4beb07b8cb3347095e0d76a584019b1891135cd1268a1f05b9d88": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-riscv64-linux-gnu.tar.gz"}, "e2f751512f3c0f00eb68ba946d9c829e6cf99422a61e8f5e0a7c109c318674d0": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-apple-darwin.tar.gz"}, "49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-linux-gnu.tar.gz"}, + + "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b": {"tag": "v25.0", "tarball": "bitcoin-25.0-aarch64-linux-gnu.tar.gz"}, + "e537c8630b05e63242d979c3004f851fd73c2a10b5b4fdbb161788427c7b3c0f": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm-linux-gnueabihf.tar.gz"}, + "3b35075d6c1209743611c705a13575be2668bc069bc6301ce78a2e1e53ebe7cc": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm64-apple-darwin.tar.gz"}, + "0c8e135a6fd297270d3b65196042d761453493a022b5ff7fb847fc911e938214": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64-linux-gnu.tar.gz"}, + "fa8af160782f5adfcea570f72b947073c1663b3e9c3cd0f82b216b609fe47573": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64le-linux-gnu.tar.gz"}, + "fe6e347a66043946920c72c9c4afca301968101e6b82fb90a63d7885ebcceb32": {"tag": "v25.0", "tarball": "bitcoin-25.0-riscv64-linux-gnu.tar.gz"}, + "5708fc639cdfc27347cccfd50db9b73b53647b36fb5f3a4a93537cbe8828c27f": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-apple-darwin.tar.gz"}, + "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-linux-gnu.tar.gz"}, } diff --git a/test/lint/lint-shell.py b/test/lint/lint-shell.py index db84ca3d39..7fb78894af 100755 --- a/test/lint/lint-shell.py +++ b/test/lint/lint-shell.py @@ -70,7 +70,7 @@ def main(): reg = re.compile(r'src/[leveldb,secp256k1,minisketch]') def should_exclude(fname: str) -> bool: - return bool(reg.match(fname)) or 'test_utxo_snapshots.sh' in fname + return bool(reg.match(fname)) # remove everything that doesn't match this regex files[:] = [file for file in files if not should_exclude(file)] |