aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml9
-rwxr-xr-xci/test/00_setup_env_native_fuzz.sh2
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_msan.sh2
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_valgrind.sh2
-rwxr-xr-xci/test/00_setup_env_native_msan.sh2
-rwxr-xr-xci/test/00_setup_env_win64.sh2
-rw-r--r--contrib/devtools/README.md4
-rwxr-xr-xcontrib/devtools/gen-manpages.py73
-rwxr-xr-xcontrib/devtools/gen-manpages.sh53
-rw-r--r--contrib/tracing/README.md31
-rwxr-xr-xcontrib/tracing/log_utxocache_flush.py21
-rwxr-xr-xcontrib/tracing/log_utxos.bt2
-rw-r--r--depends/packages/libmultiprocess.mk2
-rw-r--r--doc/managing-wallets.md6
-rw-r--r--doc/policy/packages.md15
-rw-r--r--doc/release-process.md3
-rw-r--r--doc/tracing.md33
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/bitcoin-cli.cpp22
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/fs.h22
-rw-r--r--src/index/base.h4
-rw-r--r--src/index/coinstatsindex.cpp8
-rw-r--r--src/init.cpp2
-rw-r--r--src/policy/feerate.h25
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/qt/bitcoin.cpp10
-rw-r--r--src/qt/bitcoin.h3
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/mining.cpp36
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/util.cpp10
-rw-r--r--src/rpc/util.h7
-rw-r--r--src/script/descriptor.cpp12
-rw-r--r--src/streams.h101
-rw-r--r--src/test/dbwrapper_tests.cpp4
-rw-r--r--src/test/descriptor_tests.cpp75
-rw-r--r--src/test/fs_tests.cpp24
-rw-r--r--src/test/fuzz/addition_overflow.cpp17
-rw-r--r--src/test/fuzz/script.cpp8
-rw-r--r--src/test/fuzz/script_format.cpp30
-rw-r--r--src/test/txpackage_tests.cpp11
-rw-r--r--src/test/util_tests.cpp16
-rw-r--r--src/util/overflow.h21
-rw-r--r--src/util/syscall_sandbox.cpp6
-rw-r--r--src/validation.cpp41
-rw-r--r--src/validation.h2
-rw-r--r--src/wallet/wallet.cpp3
-rwxr-xr-xtest/functional/feature_dirsymlinks.py39
-rwxr-xr-xtest/functional/rpc_help.py2
-rwxr-xr-xtest/functional/test_runner.py1
-rw-r--r--test/sanitizer_suppressions/tsan4
53 files changed, 576 insertions, 275 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 5624c97d56..9b0e2dbd2e 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -176,10 +176,10 @@ task:
QEMU_USER_CMD: "" # Disable qemu and run the test natively
task:
- name: 'Win64 [unit tests, no gui tests, no boost::process, no functional tests] [focal]'
+ name: 'Win64 [unit tests, no gui tests, no boost::process, no functional tests] [jammy]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:focal
+ image: ubuntu:jammy
env:
<< : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_win64.sh"
@@ -236,11 +236,10 @@ task:
MAKEJOBS: "-j4" # Avoid excessive memory use
task:
- name: '[fuzzer,address,undefined,integer, no depends] [focal]'
- only_if: $CIRRUS_BRANCH == $CIRRUS_DEFAULT_BRANCH || $CIRRUS_BASE_BRANCH == $CIRRUS_DEFAULT_BRANCH
+ name: '[fuzzer,address,undefined,integer, no depends] [jammy]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:focal
+ image: ubuntu:jammy
cpu: 4 # Increase CPU and memory to avoid timeout
memory: 16G
env:
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index a36063c11f..d7caec8359 100755
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8
-export DOCKER_NAME_TAG="ubuntu:20.04"
+export DOCKER_NAME_TAG="ubuntu:22.04"
export CONTAINER_NAME=ci_native_fuzz
export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libsqlite3-dev"
export NO_DEPENDS=1
diff --git a/ci/test/00_setup_env_native_fuzz_with_msan.sh b/ci/test/00_setup_env_native_fuzz_with_msan.sh
index 07332690e8..619acc4677 100755
--- a/ci/test/00_setup_env_native_fuzz_with_msan.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_msan.sh
@@ -14,7 +14,7 @@ export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
export CONTAINER_NAME="ci_native_msan"
export PACKAGES="clang-9 llvm-9 cmake"
-export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++17 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
+export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' sqlite_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,memory --with-asm=no --prefix=${DEPENDS_DIR}/x86_64-pc-linux-gnu/ CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
export USE_MEMORY_SANITIZER="true"
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
index 4657259771..a7715a6ded 100755
--- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8
-export DOCKER_NAME_TAG="ubuntu:20.04"
+export DOCKER_NAME_TAG="ubuntu:22.04"
export CONTAINER_NAME=ci_native_fuzz_valgrind
export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libsqlite3-dev valgrind"
export NO_DEPENDS=1
diff --git a/ci/test/00_setup_env_native_msan.sh b/ci/test/00_setup_env_native_msan.sh
index 557e9b1419..b8418106c6 100755
--- a/ci/test/00_setup_env_native_msan.sh
+++ b/ci/test/00_setup_env_native_msan.sh
@@ -15,7 +15,7 @@ export BDB_PREFIX="${BASE_ROOT_DIR}/db4"
export CONTAINER_NAME="ci_native_msan"
export PACKAGES="clang-9 llvm-9 cmake"
-export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++17 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' sqlite_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
+export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' sqlite_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
export BITCOIN_CONFIG="--enable-wallet --with-sanitizers=memory --with-asm=no --prefix=${DEPENDS_DIR}/x86_64-pc-linux-gnu/ CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' BDB_LIBS='-L${BDB_PREFIX}/lib -ldb_cxx-4.8' BDB_CFLAGS='-I${BDB_PREFIX}/include'"
export USE_MEMORY_SANITIZER="true"
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
index 6619852423..25aa682eb0 100755
--- a/ci/test/00_setup_env_win64.sh
+++ b/ci/test/00_setup_env_win64.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_win64
-export DOCKER_NAME_TAG=ubuntu:20.04 # Check that Focal can cross-compile to win64
+export DOCKER_NAME_TAG=ubuntu:22.04 # Check that Jammy can cross-compile to win64
export HOST=x86_64-w64-mingw32
export DPKG_ADD_ARCH="i386"
export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 wine32 file"
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index afbad096c4..79b0134adc 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -76,7 +76,7 @@ year rather than two hyphenated years.
If the file already has a copyright for `The Bitcoin Core developers`, the
script will exit.
-gen-manpages.sh
+gen-manpages.py
===============
A small script to automatically create manpages in ../../doc/man by running the release binaries with the -help option.
@@ -87,7 +87,7 @@ repostitory. To use this tool with out-of-tree builds set `BUILDDIR`. For
example:
```bash
-BUILDDIR=$PWD/build contrib/devtools/gen-manpages.sh
+BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py
```
security-check.py and test-security-check.py
diff --git a/contrib/devtools/gen-manpages.py b/contrib/devtools/gen-manpages.py
new file mode 100755
index 0000000000..05f613da38
--- /dev/null
+++ b/contrib/devtools/gen-manpages.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+import os
+import subprocess
+import sys
+import tempfile
+
+BINARIES = [
+'src/bitcoind',
+'src/bitcoin-cli',
+'src/bitcoin-tx',
+'src/bitcoin-wallet',
+'src/bitcoin-util',
+'src/qt/bitcoin-qt',
+]
+
+# Paths to external utilities.
+git = os.getenv('GIT', 'git')
+help2man = os.getenv('HELP2MAN', 'help2man')
+
+# If not otherwise specified, get top directory from git.
+topdir = os.getenv('TOPDIR')
+if not topdir:
+ r = subprocess.run([git, 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE, check=True, universal_newlines=True)
+ topdir = r.stdout.rstrip()
+
+# Get input and output directories.
+builddir = os.getenv('BUILDDIR', topdir)
+mandir = os.getenv('MANDIR', os.path.join(topdir, 'doc/man'))
+
+# Verify that all the required binaries are usable, and extract copyright
+# message in a first pass.
+copyright = None
+versions = []
+for relpath in BINARIES:
+ abspath = os.path.join(builddir, relpath)
+ try:
+ r = subprocess.run([abspath, '--version'], stdout=subprocess.PIPE, universal_newlines=True)
+ except IOError:
+ print(f'{abspath} not found or not an executable', file=sys.stderr)
+ sys.exit(1)
+ # take first line (which must contain version)
+ verstr = r.stdout.split('\n')[0]
+ # last word of line is the actual version e.g. v22.99.0-5c6b3d5b3508
+ verstr = verstr.split()[-1]
+ assert verstr.startswith('v')
+
+ # Only bitcoin-qt prints the copyright message on --version, so store it specifically.
+ if relpath == 'src/qt/bitcoin-qt':
+ copyright = r.stdout.split('\n')[1:]
+
+ versions.append((abspath, verstr))
+
+if any(verstr.endswith('-dirty') for (_, verstr) in versions):
+ print("WARNING: Binaries were built from a dirty tree.")
+ print('man pages generated from dirty binaries should NOT be committed.')
+ print('To properly generate man pages, please commit your changes (or discard them), rebuild, then run this script again.')
+ print()
+
+with tempfile.NamedTemporaryFile('w', suffix='.h2m') as footer:
+ # Create copyright footer, and write it to a temporary include file.
+ assert copyright
+ footer.write('[COPYRIGHT]\n')
+ footer.write('\n'.join(copyright).strip())
+ footer.flush()
+
+ # Call the binaries through help2man to produce a manual page for each of them.
+ for (abspath, verstr) in versions:
+ outname = os.path.join(mandir, os.path.basename(abspath) + '.1')
+ print(f'Generating {outname}…')
+ subprocess.run([help2man, '-N', '--version-string=' + verstr, '--include=' + footer.name, '-o', outname, abspath], check=True)
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh
deleted file mode 100755
index 8da6ff1204..0000000000
--- a/contrib/devtools/gen-manpages.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2016-2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C
-TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)}
-BUILDDIR=${BUILDDIR:-$TOPDIR}
-
-BINDIR=${BINDIR:-$BUILDDIR/src}
-MANDIR=${MANDIR:-$TOPDIR/doc/man}
-
-BITCOIND=${BITCOIND:-$BINDIR/bitcoind}
-BITCOINCLI=${BITCOINCLI:-$BINDIR/bitcoin-cli}
-BITCOINTX=${BITCOINTX:-$BINDIR/bitcoin-tx}
-WALLET_TOOL=${WALLET_TOOL:-$BINDIR/bitcoin-wallet}
-BITCOINUTIL=${BITCOINQT:-$BINDIR/bitcoin-util}
-BITCOINQT=${BITCOINQT:-$BINDIR/qt/bitcoin-qt}
-
-[ ! -x "$BITCOIND" ] && echo "$BITCOIND not found or not executable." && exit 1
-
-# Don't allow man pages to be generated for binaries built from a dirty tree
-DIRTY=""
-for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINUTIL $BITCOINQT; do
- VERSION_OUTPUT=$($cmd --version)
- if [[ $VERSION_OUTPUT == *"dirty"* ]]; then
- DIRTY="${DIRTY}${cmd}\n"
- fi
-done
-if [ -n "$DIRTY" ]
-then
- echo -e "WARNING: the following binaries were built from a dirty tree:\n"
- echo -e "$DIRTY"
- echo "man pages generated from dirty binaries should NOT be committed."
- echo "To properly generate man pages, please commit your changes to the above binaries, rebuild them, then run this script again."
-fi
-
-# The autodetected version git tag can screw up manpage output a little bit
-read -r -a BTCVER <<< "$($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ print $6, $7 }')"
-
-# Create a footer file with copyright content.
-# This gets autodetected fine for bitcoind if --version-string is not set,
-# but has different outcomes for bitcoin-qt and bitcoin-cli.
-echo "[COPYRIGHT]" > footer.h2m
-$BITCOIND --version | sed -n '1!p' >> footer.h2m
-
-for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $WALLET_TOOL $BITCOINUTIL $BITCOINQT; do
- cmdname="${cmd##*/}"
- help2man -N --version-string="${BTCVER[0]}" --include=footer.h2m -o "${MANDIR}/${cmdname}.1" "${cmd}"
- sed -i "s/\\\-${BTCVER[1]}//g" "${MANDIR}/${cmdname}.1"
-done
-
-rm -f footer.h2m
diff --git a/contrib/tracing/README.md b/contrib/tracing/README.md
index b71ce2f34b..a409a23ef8 100644
--- a/contrib/tracing/README.md
+++ b/contrib/tracing/README.md
@@ -237,7 +237,7 @@ Histogram of block connection times in milliseconds (ms).
### log_utxocache_flush.py
-A BCC Python script to log the cache and index flushes. Based on the
+A BCC Python script to log the UTXO cache flushes. Based on the
`utxocache:flush` tracepoint.
```bash
@@ -246,23 +246,10 @@ $ python3 contrib/tracing/log_utxocache_flush.py ./src/bitcoind
```
Logging utxocache flushes. Ctrl-C to end...
-Duration (µs) Mode Coins Count Memory Usage Prune Full Flush
-0 PERIODIC 28484 3929.87 kB False False
-1 PERIODIC 28485 3930.00 kB False False
-0 PERIODIC 28489 3930.51 kB False False
-1 PERIODIC 28490 3930.64 kB False False
-0 PERIODIC 28491 3930.77 kB False False
-0 PERIODIC 28491 3930.77 kB False False
-0 PERIODIC 28496 3931.41 kB False False
-1 PERIODIC 28496 3931.41 kB False False
-0 PERIODIC 28497 3931.54 kB False False
-1 PERIODIC 28497 3931.54 kB False False
-1 PERIODIC 28499 3931.79 kB False False
-.
-.
-.
-53788 ALWAYS 30076 4136.27 kB False False
-7463 ALWAYS 0 245.84 kB False False
+Duration (µs) Mode Coins Count Memory Usage Prune
+730451 IF_NEEDED 22990 3323.54 kB True
+637657 ALWAYS 122320 17124.80 kB False
+81349 ALWAYS 0 1383.49 kB False
```
### log_utxos.bt
@@ -275,7 +262,13 @@ uncached from the UTXO set. Based on the `utxocache:add`, `utxocache:spend` and
$ bpftrace contrib/tracing/log_utxos.bt
```
-It should produce an output similar to the following.
+This should produce an output similar to the following. If you see bpftrace
+warnings like `Lost 24 events`, the eBPF perf ring-buffer is filled faster
+than it is being read. You can increase the ring-buffer size by setting the
+ENV variable `BPFTRACE_PERF_RB_PAGES` (default 64) at a cost of higher
+memory usage. See the [bpftrace reference guide] for more information.
+
+[bpftrace reference guide]: https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md#98-bpftrace_perf_rb_pages
```bash
Attaching 4 probes...
diff --git a/contrib/tracing/log_utxocache_flush.py b/contrib/tracing/log_utxocache_flush.py
index 7f26504e7b..8c073bea0d 100755
--- a/contrib/tracing/log_utxocache_flush.py
+++ b/contrib/tracing/log_utxocache_flush.py
@@ -16,14 +16,14 @@ from bcc import BPF, USDT
# a sandboxed Linux kernel VM.
program = """
# include <uapi/linux/ptrace.h>
+
struct data_t
{
u64 duration;
u32 mode;
u64 coins_count;
u64 coins_mem_usage;
- bool is_flush_prune;
- bool is_full_flush;
+ bool is_flush_for_prune;
};
// BPF perf buffer to push the data to user space.
@@ -35,8 +35,7 @@ int trace_flush(struct pt_regs *ctx) {
bpf_usdt_readarg(2, ctx, &data.mode);
bpf_usdt_readarg(3, ctx, &data.coins_count);
bpf_usdt_readarg(4, ctx, &data.coins_mem_usage);
- bpf_usdt_readarg(5, ctx, &data.is_flush_prune);
- bpf_usdt_readarg(5, ctx, &data.is_full_flush);
+ bpf_usdt_readarg(5, ctx, &data.is_flush_for_prune);
flush.perf_submit(ctx, &data, sizeof(data));
return 0;
}
@@ -57,19 +56,17 @@ class Data(ctypes.Structure):
("mode", ctypes.c_uint32),
("coins_count", ctypes.c_uint64),
("coins_mem_usage", ctypes.c_uint64),
- ("is_flush_prune", ctypes.c_bool),
- ("is_full_flush", ctypes.c_bool)
+ ("is_flush_for_prune", ctypes.c_bool)
]
def print_event(event):
- print("%-15d %-10s %-15d %-15s %-8s %-8s" % (
+ print("%-15d %-10s %-15d %-15s %-8s" % (
event.duration,
FLUSH_MODES[event.mode],
event.coins_count,
"%.2f kB" % (event.coins_mem_usage/1000),
- event.is_flush_prune,
- event.is_full_flush
+ event.is_flush_for_prune
))
@@ -90,9 +87,9 @@ def main(bitcoind_path):
b["flush"].open_perf_buffer(handle_flush)
print("Logging utxocache flushes. Ctrl-C to end...")
- print("%-15s %-10s %-15s %-15s %-8s %-8s" % ("Duration (µs)", "Mode",
- "Coins Count", "Memory Usage",
- "Prune", "Full Flush"))
+ print("%-15s %-10s %-15s %-15s %-8s" % ("Duration (µs)", "Mode",
+ "Coins Count", "Memory Usage",
+ "Flush for Prune"))
while True:
try:
diff --git a/contrib/tracing/log_utxos.bt b/contrib/tracing/log_utxos.bt
index 0d47f3d62b..54d5010f82 100755
--- a/contrib/tracing/log_utxos.bt
+++ b/contrib/tracing/log_utxos.bt
@@ -7,7 +7,7 @@
bpftrace contrib/tracing/log_utxos.bt
This script requires a 'bitcoind' binary compiled with eBPF support and the
- 'utxochache' tracepoints. By default, it's assumed that 'bitcoind' is
+ 'utxocache' tracepoints. By default, it's assumed that 'bitcoind' is
located in './src/bitcoind'. This can be modified in the script below.
NOTE: requires bpftrace v0.12.0 or above.
diff --git a/depends/packages/libmultiprocess.mk b/depends/packages/libmultiprocess.mk
index 3e5cf5f160..40ab3c68ea 100644
--- a/depends/packages/libmultiprocess.mk
+++ b/depends/packages/libmultiprocess.mk
@@ -3,7 +3,7 @@ $(package)_version=$(native_$(package)_version)
$(package)_download_path=$(native_$(package)_download_path)
$(package)_file_name=$(native_$(package)_file_name)
$(package)_sha256_hash=$(native_$(package)_sha256_hash)
-$(package)_dependencies=native_$(package) boost capnp
+$(package)_dependencies=native_$(package) capnp
define $(package)_config_cmds
$($(package)_cmake)
diff --git a/doc/managing-wallets.md b/doc/managing-wallets.md
index aab6d131bd..6c1e13c503 100644
--- a/doc/managing-wallets.md
+++ b/doc/managing-wallets.md
@@ -12,11 +12,9 @@ In the GUI, the `Create a new wallet` button is displayed on the main screen whe
The following command, for example, creates a descriptor wallet. More information about this command may be found by running `bitcoin-cli help createwallet`.
```
-$ bitcoin-cli -named createwallet wallet_name="wallet-01" descriptors=true
+$ bitcoin-cli createwallet "wallet-01"
```
-The `descriptors` parameter can be omitted if the intention is to create a legacy wallet. For now, the default type is the legacy wallet, but that is expected to change in a future release.
-
By default, wallets are created in the `wallets` folder of the data directory, which varies by operating system, as shown below. The user can change the default by using the `-datadir` or `-walletdir` initialization parameters.
| Operating System | Default wallet directory |
@@ -54,7 +52,7 @@ Only the wallet's private key is encrypted. All other wallet information, such a
The wallet's private key can also be encrypted in the `createwallet` command via the `passphrase` argument:
```
-$ bitcoin-cli -named createwallet wallet_name="wallet-01" descriptors=true passphrase="passphrase"
+$ bitcoin-cli -named createwallet wallet_name="wallet-01" passphrase="passphrase"
```
Note that if the passphrase is lost, all the coins in the wallet will also be lost forever.
diff --git a/doc/policy/packages.md b/doc/policy/packages.md
index 07698f2af2..7f7fbe18cd 100644
--- a/doc/policy/packages.md
+++ b/doc/policy/packages.md
@@ -57,3 +57,18 @@ test accepts):
- Warning: Batched fee-bumping may be unsafe for some use cases. Users and application developers
should take caution if utilizing multi-parent packages.
+
+* Transactions in the package that have the same txid as another transaction already in the mempool
+ will be removed from the package prior to submission ("deduplication").
+
+ - *Rationale*: Node operators are free to set their mempool policies however they please, nodes
+ may receive transactions in different orders, and malicious counterparties may try to take
+ advantage of policy differences to pin or delay propagation of transactions. As such, it's
+ possible for some package transaction(s) to already be in the mempool, and there is no need to
+ repeat validation for those transactions or double-count them in fees.
+
+ - *Rationale*: We want to prevent potential censorship vectors. We should not reject entire
+ packages because we already have one of the transactions. Also, if an attacker first broadcasts
+ a competing package or transaction with a mutated witness, even though the two
+ same-txid-different-witness transactions are conflicting and cannot replace each other, the
+ honest package should still be considered for acceptance.
diff --git a/doc/release-process.md b/doc/release-process.md
index c5dba46238..5a74f72b6e 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -6,13 +6,14 @@ Release Process
### Before every release candidate
* Update translations see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#synchronising-translations).
-* Update manpages, see [gen-manpages.sh](https://github.com/bitcoin/bitcoin/blob/master/contrib/devtools/README.md#gen-manpagessh).
* Update release candidate version in `configure.ac` (`CLIENT_VERSION_RC`).
+* Update manpages (after rebuilding the binaries), see [gen-manpages.py](https://github.com/bitcoin/bitcoin/blob/master/contrib/devtools/README.md#gen-manpagespy).
### Before every major and minor release
* Update [bips.md](bips.md) to account for changes since the last release (don't forget to bump the version number on the first line).
* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_RC` to `0`).
+* Update manpages (see previous section)
* Write release notes (see "Write the release notes" below).
### Before every major release
diff --git a/doc/tracing.md b/doc/tracing.md
index 5b9ba09c2f..6ec6a6c218 100644
--- a/doc/tracing.md
+++ b/doc/tracing.md
@@ -110,23 +110,31 @@ Arguments passed:
### Context `utxocache`
+The following tracepoints cover the in-memory UTXO cache. UTXOs are, for example,
+added to and removed (spent) from the cache when we connect a new block.
+**Note**: Bitcoin Core uses temporary clones of the _main_ UTXO cache
+(`chainstate.CoinsTip()`). For example, the RPCs `generateblock` and
+`getblocktemplate` call `TestBlockValidity()`, which applies the UTXO set
+changes to a temporary cache. Similarly, mempool consistency checks, which are
+frequent on regtest, also apply the the UTXO set changes to a temporary cache.
+Changes to the _main_ UTXO cache and to temporary caches trigger the tracepoints.
+We can't tell if a temporary cache or the _main_ cache was changed.
+
#### Tracepoint `utxocache:flush`
-Is called *after* the caches and indexes are flushed depending on the mode
-`CChainState::FlushStateToDisk` is called with.
+Is called *after* the in-memory UTXO cache is flushed.
Arguments passed:
-1. Duration in microseconds as `int64`
+1. Time it took to flush the cache microseconds as `int64`
2. Flush state mode as `uint32`. It's an enumerator class with values `0`
(`NONE`), `1` (`IF_NEEDED`), `2` (`PERIODIC`), `3` (`ALWAYS`)
-3. Number of coins flushed as `uint64`
-4. Memory usage in bytes as `uint64`
-5. If the flush was pruned as `bool`
-6. If it was full flush as `bool`
+3. Cache size (number of coins) before the flush as `uint64`
+4. Cache memory usage in bytes as `uint64`
+5. If pruning caused the flush as `bool`
#### Tracepoint `utxocache:add`
-It is called when a new coin is added to the UTXO cache.
+Is called when a coin is added to a UTXO cache. This can be a temporary UTXO cache too.
Arguments passed:
1. Transaction ID (hash) as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
@@ -137,7 +145,7 @@ Arguments passed:
#### Tracepoint `utxocache:spent`
-It is called when a coin is spent from the UTXO cache.
+Is called when a coin is spent from a UTXO cache. This can be a temporary UTXO cache too.
Arguments passed:
1. Transaction ID (hash) as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
@@ -148,14 +156,17 @@ Arguments passed:
#### Tracepoint `utxocache:uncache`
-It is called when the UTXO with the given outpoint is removed from the cache.
+Is called when a coin is purposefully unloaded from a UTXO cache. This
+happens, for example, when we load an UTXO into a cache when trying to accept
+a transaction that turns out to be invalid. The loaded UTXO is uncached to avoid
+filling our UTXO cache up with irrelevant UTXOs.
Arguments passed:
1. Transaction ID (hash) as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
2. Output index as `uint32`
3. Block height the coin was uncached, as `uint32`
4. Value of the coin as `int64`
-. If the coin is a coinbase as `bool`
+5. If the coin is a coinbase as `bool`
## Adding tracepoints to Bitcoin Core
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 1763dcb562..e2e08468a7 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -294,6 +294,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/script_bitcoin_consensus.cpp \
test/fuzz/script_descriptor_cache.cpp \
test/fuzz/script_flags.cpp \
+ test/fuzz/script_format.cpp \
test/fuzz/script_interpreter.cpp \
test/fuzz/script_ops.cpp \
test/fuzz/script_sigcache.cpp \
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 6557360a71..874e38752a 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -69,8 +69,13 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-generate",
+ strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
+ "arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to "
+ "RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000",
+ DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES),
+ ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total, after filtering for quality and recency. The total number of addresses known to the node may be higher.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -606,8 +611,9 @@ public:
"Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
"Arguments:\n"
+ strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", MAX_DETAIL_LEVEL) +
- " 0 - Connection counts and local addresses\n"
- " 1 - Like 0 but with a peers listing (without address or version columns)\n"
+ " 0 - Peer counts for each reachable network as well as for block relay peers\n"
+ " and manual peers, and the list of local addresses and ports\n"
+ " 1 - Like 0 but preceded by a peers listing (without address and version columns)\n"
" 2 - Like 1 but with an address column\n"
" 3 - Like 1 but with a version column\n"
" 4 - Like 1 but with both address and version columns\n"
@@ -645,13 +651,13 @@ public:
" id Peer index, in increasing order of peer connections since node startup\n"
" address IP address and port of the peer\n"
" version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
- "* The connection counts table displays the number of peers by direction, network, and the totals\n"
- " for each, as well as two special outbound columns for block relay peers and manual peers.\n\n"
+ "* The peer counts table displays the number of peers for each reachable network as well as\n"
+ " the number of block relay peers and manual peers.\n\n"
"* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
"Examples:\n\n"
- "Connection counts and local addresses only\n"
+ "Peer counts table of reachable networks and list of local addresses\n"
"> bitcoin-cli -netinfo\n\n"
- "Compact peers listing\n"
+ "The same, preceded by a peers listing without address and version columns\n"
"> bitcoin-cli -netinfo 1\n\n"
"Full dashboard\n"
+ strprintf("> bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 65c37f182f..ff5485e2da 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -63,7 +63,7 @@ static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
strUsage += "\n"
"bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n"
"By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n"
- "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n"
+ "To change the target wallet, use the -datadir, -wallet and -regtest/-signet/-testnet arguments.\n\n"
"Usage:\n"
" bitcoin-wallet [options] <command>\n";
strUsage += "\n" + args.GetHelpMessage();
diff --git a/src/fs.h b/src/fs.h
index d2299db168..00b786453c 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -140,6 +140,28 @@ static inline path PathFromString(const std::string& string)
return std::filesystem::path(string);
#endif
}
+
+/**
+ * Create directory (and if necessary its parents), unless the leaf directory
+ * already exists or is a symlink to an existing directory.
+ * This is a temporary workaround for an issue in libstdc++ that has been fixed
+ * upstream [PR101510].
+ */
+static inline bool create_directories(const std::filesystem::path& p)
+{
+ if (std::filesystem::is_symlink(p) && std::filesystem::is_directory(p)) {
+ return false;
+ }
+ return std::filesystem::create_directories(p);
+}
+
+/**
+ * This variant is not used. Delete it to prevent it from accidentally working
+ * around the workaround. If it is needed, add a workaround in the same pattern
+ * as above.
+ */
+bool create_directories(const std::filesystem::path& p, std::error_code& ec) = delete;
+
} // namespace fs
/** Bridge operations to C stdio */
diff --git a/src/index/base.h b/src/index/base.h
index 66149686f0..c4a8215bc4 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -40,10 +40,10 @@ protected:
DB(const fs::path& path, size_t n_cache_size,
bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false);
- /// Read block locator of the chain that the txindex is in sync with.
+ /// Read block locator of the chain that the index is in sync with.
bool ReadBestBlock(CBlockLocator& locator) const;
- /// Write block locator of the chain that the txindex is in sync with.
+ /// Write block locator of the chain that the index is in sync with.
void WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator);
};
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 7d4860b20b..a1c8a5937c 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -363,6 +363,14 @@ bool CoinStatsIndex::Init()
return error("%s: Cannot read current %s state; index may be corrupted",
__func__, GetName());
}
+
+ uint256 out;
+ m_muhash.Finalize(out);
+ if (entry.muhash != out) {
+ return error("%s: Cannot read current %s state; index may be corrupted",
+ __func__, GetName());
+ }
+
m_transaction_output_count = entry.transaction_output_count;
m_bogo_size = entry.bogo_size;
m_total_amount = entry.total_amount;
diff --git a/src/init.cpp b/src/init.cpp
index 6aef1f8149..64ed868333 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -395,7 +395,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
- argsman.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-alertnotify=<cmd>", "Execute command when an alert is raised (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 188a44b73d..50fd6fd11b 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -24,35 +24,40 @@ enum class FeeEstimateMode {
};
/**
- * Fee rate in satoshis per kilobyte: CAmount / kB
+ * Fee rate in satoshis per kilovirtualbyte: CAmount / kvB
*/
class CFeeRate
{
private:
- CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes
+ /** Fee rate in sat/kvB (satoshis per 1000 virtualbytes) */
+ CAmount nSatoshisPerK;
public:
- /** Fee rate of 0 satoshis per kB */
+ /** Fee rate of 0 satoshis per kvB */
CFeeRate() : nSatoshisPerK(0) { }
template<typename I>
explicit CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
// We've previously had bugs creep in from silent double->int conversion...
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
}
- /** Constructor for a fee rate in satoshis per kvB (sat/kvB).
+
+ /**
+ * Construct a fee rate from a fee in satoshis and a vsize in vB.
*
- * Passing a num_bytes value of COIN (1e8) returns a fee rate in satoshis per vB (sat/vB),
- * e.g. (nFeePaid * 1e8 / 1e3) == (nFeePaid / 1e5),
- * where 1e5 is the ratio to convert from BTC/kvB to sat/vB.
+ * param@[in] nFeePaid The fee paid by a transaction, in satoshis
+ * param@[in] num_bytes The vsize of a transaction, in vbytes
*/
CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes);
+
/**
- * Return the fee in satoshis for the given size in bytes.
- * If the calculated fee would have fractional satoshis, then the returned fee will always be rounded up to the nearest satoshi.
+ * Return the fee in satoshis for the given vsize in vbytes.
+ * If the calculated fee would have fractional satoshis, then the
+ * returned fee will always be rounded up to the nearest satoshi.
*/
CAmount GetFee(uint32_t num_bytes) const;
+
/**
- * Return the fee in satoshis for a size of 1000 bytes
+ * Return the fee in satoshis for a vsize of 1000 vbytes
*/
CAmount GetFeePerK() const { return GetFee(1000); }
friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 1fcbc45c72..fb98fb6868 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -201,7 +201,7 @@ struct CMutableTransaction;
* - std::vector<CTxIn> vin
* - std::vector<CTxOut> vout
* - if (flags & 1):
- * - CTxWitness wit;
+ * - CScriptWitness scriptWitness; (deserialized into CTxIn)
* - uint32_t nLockTime
*/
template<typename Stream, typename TxType>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 0e5fd8b72b..d6f80e1558 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -454,6 +454,16 @@ WId BitcoinApplication::getMainWinId() const
return window->winId();
}
+bool BitcoinApplication::event(QEvent* e)
+{
+ if (e->type() == QEvent::Quit) {
+ requestShutdown();
+ return true;
+ }
+
+ return QApplication::event(e);
+}
+
static void SetupUIArgs(ArgsManager& argsman)
{
argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 0703c6dcb9..7a6aa5cdc9 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -92,6 +92,9 @@ Q_SIGNALS:
void splashFinished();
void windowShown(BitcoinGUI* window);
+protected:
+ bool event(QEvent* e) override;
+
private:
std::optional<InitExecutor> m_executor;
OptionsModel *optionsModel;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 69204e346a..9817c80cbd 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1037,7 +1037,7 @@ static RPCHelpMan getblock()
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR, "hex", "The hex"},
{RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ {RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"},
}},
}},
}},
@@ -1417,7 +1417,7 @@ static RPCHelpMan verifychain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const int check_level(request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int());
+ const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int()};
const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()};
ChainstateManager& chainman = EnsureAnyChainman(request.context);
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 0554367672..1d1ae92c58 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -217,9 +217,9 @@ static RPCHelpMan generatetodescriptor()
{
return RPCHelpMan{
"generatetodescriptor",
- "\nMine blocks immediately to a specified descriptor (before the RPC call returns)\n",
+ "Mine to a specified descriptor and return the block hashes.",
{
- {"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
+ {"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."},
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."},
{"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
},
@@ -261,18 +261,18 @@ static RPCHelpMan generate()
static RPCHelpMan generatetoaddress()
{
return RPCHelpMan{"generatetoaddress",
- "\nMine blocks immediately to a specified address (before the RPC call returns)\n",
- {
- {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
- },
- RPCResult{
- RPCResult::Type::ARR, "", "hashes of blocks generated",
- {
- {RPCResult::Type::STR_HEX, "", "blockhash"},
- }},
- RPCExamples{
+ "Mine to a specified address and return the block hashes.",
+ {
+ {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
+ {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
+ },
+ RPCResult{
+ RPCResult::Type::ARR, "", "hashes of blocks generated",
+ {
+ {RPCResult::Type::STR_HEX, "", "blockhash"},
+ }},
+ RPCExamples{
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
@@ -302,7 +302,7 @@ static RPCHelpMan generatetoaddress()
static RPCHelpMan generateblock()
{
return RPCHelpMan{"generateblock",
- "\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n",
+ "Mine a set of ordered transactions to a specified address or descriptor and return the block hash.",
{
{"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
{"transactions", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings which are either txids or raw transactions.\n"
@@ -1278,9 +1278,9 @@ static const CRPCCommand commands[] =
{ "mining", &submitheader, },
- { "generating", &generatetoaddress, },
- { "generating", &generatetodescriptor, },
- { "generating", &generateblock, },
+ { "hidden", &generatetoaddress, },
+ { "hidden", &generatetodescriptor, },
+ { "hidden", &generateblock, },
{ "util", &estimatesmartfee, },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 3d7c00edfc..4d3e3e81d4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -847,7 +847,9 @@ static RPCHelpMan setnetworkactive()
static RPCHelpMan getnodeaddresses()
{
return RPCHelpMan{"getnodeaddresses",
- "\nReturn known addresses, which can potentially be used to find new nodes in the network.\n",
+ "Return known addresses, after filtering for quality and recency.\n"
+ "These can potentially be used to find new peers in the network.\n"
+ "The total number of addresses known to the node may be higher.",
{
{"count", RPCArg::Type::NUM, RPCArg::Default{1}, "The maximum number of addresses to return. Specify 0 to return all known addresses."},
{"network", RPCArg::Type::STR, RPCArg::DefaultHint{"all networks"}, "Return only addresses of the specified network. Can be one of: " + Join(GetNetworkNames(), ", ") + "."},
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 4e14d02998..6272a7c8cf 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -542,16 +542,6 @@ static RPCHelpMan decoderawtransaction()
};
}
-static std::string GetAllOutputTypes()
-{
- std::vector<std::string> ret;
- using U = std::underlying_type<TxoutType>::type;
- for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
- ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
- }
- return Join(ret, ", ");
-}
-
static RPCHelpMan decodescript()
{
return RPCHelpMan{
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 5ef7e26ce8..7c859268be 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -21,6 +21,16 @@
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
+std::string GetAllOutputTypes()
+{
+ std::vector<std::string> ret;
+ using U = std::underlying_type<TxoutType>::type;
+ for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
+ ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
+ }
+ return Join(ret, ", ");
+}
+
void RPCTypeCheck(const UniValue& params,
const std::list<UniValueType>& typesExpected,
bool fAllowNull)
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 25ebf78fa1..89d32d4193 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -39,6 +39,13 @@ class CPubKey;
class CScript;
struct Sections;
+/**
+ * Gets all existing output types formatted for RPC help sections.
+ *
+ * @return Comma separated string representing output type names.
+ */
+std::string GetAllOutputTypes();
+
/** Wrapper for UniValue::VType, which includes typeAny:
* Used to denote don't care type. */
struct UniValueType {
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 84040bf847..798c4b3ea0 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -259,7 +259,15 @@ public:
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
- if (!arg.GetKey(m_pubkey.GetID(), key)) return false;
+ if (m_xonly) {
+ for (const auto& keyid : XOnlyPubKey(m_pubkey).GetKeyIDs()) {
+ arg.GetKey(keyid, key);
+ if (key.IsValid()) break;
+ }
+ } else {
+ arg.GetKey(m_pubkey.GetID(), key);
+ }
+ if (!key.IsValid()) return false;
ret = EncodeSecret(key);
return true;
}
@@ -1253,7 +1261,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
{
if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) {
XOnlyPubKey key{Span{script}.subspan(1, 32)};
- return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider));
+ return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider), true);
}
std::vector<std::vector<unsigned char>> data;
diff --git a/src/streams.h b/src/streams.h
index cf8b4eb96f..96b7696f72 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -9,6 +9,7 @@
#include <serialize.h>
#include <span.h>
#include <support/allocators/zeroafterfree.h>
+#include <util/overflow.h>
#include <algorithm>
#include <assert.h>
@@ -186,7 +187,7 @@ class CDataStream
protected:
using vector_type = SerializeData;
vector_type vch;
- unsigned int nReadPos{0};
+ vector_type::size_type m_read_pos{0};
int nType;
int nVersion;
@@ -229,37 +230,37 @@ public:
//
// Vector subset
//
- const_iterator begin() const { return vch.begin() + nReadPos; }
- iterator begin() { return vch.begin() + nReadPos; }
+ const_iterator begin() const { return vch.begin() + m_read_pos; }
+ iterator begin() { return vch.begin() + m_read_pos; }
const_iterator end() const { return vch.end(); }
iterator end() { return vch.end(); }
- size_type size() const { return vch.size() - nReadPos; }
- bool empty() const { return vch.size() == nReadPos; }
- void resize(size_type n, value_type c = value_type{}) { vch.resize(n + nReadPos, c); }
- void reserve(size_type n) { vch.reserve(n + nReadPos); }
- const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; }
- reference operator[](size_type pos) { return vch[pos + nReadPos]; }
- void clear() { vch.clear(); nReadPos = 0; }
- value_type* data() { return vch.data() + nReadPos; }
- const value_type* data() const { return vch.data() + nReadPos; }
+ size_type size() const { return vch.size() - m_read_pos; }
+ bool empty() const { return vch.size() == m_read_pos; }
+ void resize(size_type n, value_type c = value_type{}) { vch.resize(n + m_read_pos, c); }
+ void reserve(size_type n) { vch.reserve(n + m_read_pos); }
+ const_reference operator[](size_type pos) const { return vch[pos + m_read_pos]; }
+ reference operator[](size_type pos) { return vch[pos + m_read_pos]; }
+ void clear() { vch.clear(); m_read_pos = 0; }
+ value_type* data() { return vch.data() + m_read_pos; }
+ const value_type* data() const { return vch.data() + m_read_pos; }
inline void Compact()
{
- vch.erase(vch.begin(), vch.begin() + nReadPos);
- nReadPos = 0;
+ vch.erase(vch.begin(), vch.begin() + m_read_pos);
+ m_read_pos = 0;
}
bool Rewind(std::optional<size_type> n = std::nullopt)
{
// Total rewind if no size is passed
if (!n) {
- nReadPos = 0;
+ m_read_pos = 0;
return true;
}
// Rewind by n characters if the buffer hasn't been compacted yet
- if (*n > nReadPos)
+ if (*n > m_read_pos)
return false;
- nReadPos -= *n;
+ m_read_pos -= *n;
return true;
}
@@ -281,36 +282,32 @@ public:
if (dst.size() == 0) return;
// Read from the beginning of the buffer
- unsigned int nReadPosNext = nReadPos + dst.size();
- if (nReadPosNext > vch.size()) {
+ auto next_read_pos{CheckedAdd(m_read_pos, dst.size())};
+ if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
throw std::ios_base::failure("CDataStream::read(): end of data");
}
- memcpy(dst.data(), &vch[nReadPos], dst.size());
- if (nReadPosNext == vch.size())
- {
- nReadPos = 0;
+ memcpy(dst.data(), &vch[m_read_pos], dst.size());
+ if (next_read_pos.value() == vch.size()) {
+ m_read_pos = 0;
vch.clear();
return;
}
- nReadPos = nReadPosNext;
+ m_read_pos = next_read_pos.value();
}
- void ignore(int nSize)
+ void ignore(size_t num_ignore)
{
// Ignore from the beginning of the buffer
- if (nSize < 0) {
- throw std::ios_base::failure("CDataStream::ignore(): nSize negative");
+ auto next_read_pos{CheckedAdd(m_read_pos, num_ignore)};
+ if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
+ throw std::ios_base::failure("CDataStream::ignore(): end of data");
}
- unsigned int nReadPosNext = nReadPos + nSize;
- if (nReadPosNext >= vch.size())
- {
- if (nReadPosNext > vch.size())
- throw std::ios_base::failure("CDataStream::ignore(): end of data");
- nReadPos = 0;
+ if (next_read_pos.value() == vch.size()) {
+ m_read_pos = 0;
vch.clear();
return;
}
- nReadPos = nReadPosNext;
+ m_read_pos = next_read_pos.value();
}
void write(Span<const value_type> src)
@@ -594,7 +591,7 @@ private:
FILE *src; //!< source file
uint64_t nSrcPos; //!< how many bytes have been read from source
- uint64_t nReadPos; //!< how many bytes have been read from this
+ uint64_t m_read_pos; //!< how many bytes have been read from this
uint64_t nReadLimit; //!< up to which position we're allowed to read
uint64_t nRewind; //!< how many bytes we guarantee to rewind
std::vector<std::byte> vchBuf; //!< the buffer
@@ -604,7 +601,7 @@ protected:
bool Fill() {
unsigned int pos = nSrcPos % vchBuf.size();
unsigned int readNow = vchBuf.size() - pos;
- unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind;
+ unsigned int nAvail = vchBuf.size() - (nSrcPos - m_read_pos) - nRewind;
if (nAvail < readNow)
readNow = nAvail;
if (readNow == 0)
@@ -619,7 +616,7 @@ protected:
public:
CBufferedFile(FILE* fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn)
- : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, std::byte{0})
+ : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), m_read_pos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, std::byte{0})
{
if (nRewindIn >= nBufSize)
throw std::ios_base::failure("Rewind limit must be less than buffer size");
@@ -648,33 +645,33 @@ public:
//! check whether we're at the end of the source file
bool eof() const {
- return nReadPos == nSrcPos && feof(src);
+ return m_read_pos == nSrcPos && feof(src);
}
//! read a number of bytes
void read(Span<std::byte> dst)
{
- if (dst.size() + nReadPos > nReadLimit) {
+ if (dst.size() + m_read_pos > nReadLimit) {
throw std::ios_base::failure("Read attempted past buffer limit");
}
while (dst.size() > 0) {
- if (nReadPos == nSrcPos)
+ if (m_read_pos == nSrcPos)
Fill();
- unsigned int pos = nReadPos % vchBuf.size();
+ unsigned int pos = m_read_pos % vchBuf.size();
size_t nNow = dst.size();
if (nNow + pos > vchBuf.size())
nNow = vchBuf.size() - pos;
- if (nNow + nReadPos > nSrcPos)
- nNow = nSrcPos - nReadPos;
+ if (nNow + m_read_pos > nSrcPos)
+ nNow = nSrcPos - m_read_pos;
memcpy(dst.data(), &vchBuf[pos], nNow);
- nReadPos += nNow;
+ m_read_pos += nNow;
dst = dst.subspan(nNow);
}
}
//! return the current reading position
uint64_t GetPos() const {
- return nReadPos;
+ return m_read_pos;
}
//! rewind to a given reading position
@@ -682,22 +679,22 @@ public:
size_t bufsize = vchBuf.size();
if (nPos + bufsize < nSrcPos) {
// rewinding too far, rewind as far as possible
- nReadPos = nSrcPos - bufsize;
+ m_read_pos = nSrcPos - bufsize;
return false;
}
if (nPos > nSrcPos) {
// can't go this far forward, go as far as possible
- nReadPos = nSrcPos;
+ m_read_pos = nSrcPos;
return false;
}
- nReadPos = nPos;
+ m_read_pos = nPos;
return true;
}
//! prevent reading beyond a certain position
//! no argument removes the limit
bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {
- if (nPos < nReadPos)
+ if (nPos < m_read_pos)
return false;
nReadLimit = nPos;
return true;
@@ -714,12 +711,12 @@ public:
void FindByte(uint8_t ch)
{
while (true) {
- if (nReadPos == nSrcPos)
+ if (m_read_pos == nSrcPos)
Fill();
- if (vchBuf[nReadPos % vchBuf.size()] == std::byte{ch}) {
+ if (vchBuf[m_read_pos % vchBuf.size()] == std::byte{ch}) {
break;
}
- nReadPos++;
+ m_read_pos++;
}
}
};
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 2f95cc27a3..fc89fe1450 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -203,7 +203,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
fs::path ph = m_args.GetDataDirBase() / "existing_data_no_obfuscate";
- create_directories(ph);
+ fs::create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
@@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
fs::path ph = m_args.GetDataDirBase() / "existing_data_reindex";
- create_directories(ph);
+ fs::create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 5a3e382c3f..55a9a78159 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -43,6 +43,8 @@ constexpr int UNSOLVABLE = 4; // This descriptor is not expected to be solvable
constexpr int SIGNABLE = 8; // We can sign with this descriptor (this is not true when actual BIP32 derivation is used, as that's not integrated in our signing code)
constexpr int DERIVE_HARDENED = 16; // The final derivation is hardened, i.e. ends with *' or *h
constexpr int MIXED_PUBKEYS = 32;
+constexpr int XONLY_KEYS = 64; // X-only pubkeys are in use (and thus inferring/caching may swap parity of pubkeys/keyids)
+constexpr int MISSING_PRIVKEYS = 128; // Not all private keys are available, so ToPrivateString will fail.
/** Compare two descriptors. If only one of them has a checksum, the checksum is ignored. */
bool EqualDescriptor(std::string a, std::string b)
@@ -88,6 +90,42 @@ static size_t CountXpubs(const std::string& desc)
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
+std::set<CPubKey> GetKeyData(const FlatSigningProvider& provider, int flags) {
+ std::set<CPubKey> ret;
+ for (const auto& [_, pubkey] : provider.pubkeys) {
+ if (flags & XONLY_KEYS) {
+ unsigned char bytes[33];
+ BOOST_CHECK_EQUAL(pubkey.size(), 33);
+ std::copy(pubkey.begin(), pubkey.end(), bytes);
+ bytes[0] = 0x02;
+ CPubKey norm_pubkey{bytes};
+ ret.insert(norm_pubkey);
+ } else {
+ ret.insert(pubkey);
+ }
+ }
+ return ret;
+}
+
+std::set<std::pair<CPubKey, KeyOriginInfo>> GetKeyOriginData(const FlatSigningProvider& provider, int flags) {
+ std::set<std::pair<CPubKey, KeyOriginInfo>> ret;
+ for (const auto& [_, data] : provider.origins) {
+ if (flags & XONLY_KEYS) {
+ unsigned char bytes[33];
+ BOOST_CHECK_EQUAL(data.first.size(), 33);
+ std::copy(data.first.begin(), data.first.end(), bytes);
+ bytes[0] = 0x02;
+ CPubKey norm_pubkey{bytes};
+ KeyOriginInfo norm_origin = data.second;
+ std::fill(std::begin(norm_origin.fingerprint), std::end(norm_origin.fingerprint), 0); // fingerprints don't necessarily match.
+ ret.emplace(norm_pubkey, norm_origin);
+ } else {
+ ret.insert(data);
+ }
+ }
+ return ret;
+}
+
void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false)
{
@@ -127,13 +165,15 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
BOOST_CHECK(EqualDescriptor(pub, pub2));
// Check that both can be serialized with private key back to the private version, but not without private key.
- std::string prv1;
- BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1));
- BOOST_CHECK(EqualDescriptor(prv, prv1));
- BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1));
- BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1));
- BOOST_CHECK(EqualDescriptor(prv, prv1));
- BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));
+ if (!(flags & MISSING_PRIVKEYS)) {
+ std::string prv1;
+ BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1));
+ BOOST_CHECK(EqualDescriptor(prv, prv1));
+ BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1));
+ BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1));
+ BOOST_CHECK(EqualDescriptor(prv, prv1));
+ BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));
+ }
// Check that private can produce the normalized descriptors
std::string norm1;
@@ -177,9 +217,9 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
// Try to expand again using cached data, and compare.
BOOST_CHECK(parse_pub->ExpandFromCache(i, desc_cache, spks_cached, script_provider_cached));
BOOST_CHECK(spks == spks_cached);
- BOOST_CHECK(script_provider.pubkeys == script_provider_cached.pubkeys);
+ BOOST_CHECK(GetKeyData(script_provider, flags) == GetKeyData(script_provider_cached, flags));
BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);
- BOOST_CHECK(script_provider.origins == script_provider_cached.origins);
+ BOOST_CHECK(GetKeyOriginData(script_provider, flags) == GetKeyOriginData(script_provider_cached, flags));
// Check whether keys are in the cache
const auto& der_xpub_cache = desc_cache.GetCachedDerivedExtPubKeys();
@@ -255,9 +295,9 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
// Try again but use the cache from expanding i. That cache won't have the pubkeys for i + 1, but will have the parent xpub for derivation.
BOOST_CHECK(parse_pub->ExpandFromCache(i + 1, desc_cache, spk1_from_cache, script_provider_cached1));
BOOST_CHECK(spks1 == spk1_from_cache);
- BOOST_CHECK(script_provider1.pubkeys == script_provider_cached1.pubkeys);
+ BOOST_CHECK(GetKeyData(script_provider1, flags) == GetKeyData(script_provider_cached1, flags));
BOOST_CHECK(script_provider1.scripts == script_provider_cached1.scripts);
- BOOST_CHECK(script_provider1.origins == script_provider_cached1.origins);
+ BOOST_CHECK(GetKeyOriginData(script_provider1, flags) == GetKeyOriginData(script_provider_cached1, flags));
}
// For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.
@@ -269,7 +309,12 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
CMutableTransaction spend;
spend.vin.resize(1);
spend.vout.resize(1);
- BOOST_CHECK_MESSAGE(SignSignature(Merge(keys_priv, script_provider), spks[n], spend, 0, 1, SIGHASH_ALL), prv);
+ std::vector<CTxOut> utxos(1);
+ PrecomputedTransactionData txdata;
+ txdata.Init(spend, std::move(utxos), /* force */ true);
+ MutableTransactionSignatureCreator creator(&spend, 0, CAmount{0}, &txdata, SIGHASH_DEFAULT);
+ SignatureData sigdata;
+ BOOST_CHECK_MESSAGE(ProduceSignature(Merge(keys_priv, script_provider), creator, spks[n], sigdata), prv);
}
/* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */
@@ -281,7 +326,7 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
BOOST_CHECK_EQUAL(spks_inferred.size(), 1U);
BOOST_CHECK(spks_inferred[0] == spks[n]);
BOOST_CHECK_EQUAL(IsSolvable(provider_inferred, spks_inferred[0]), !(flags & UNSOLVABLE));
- BOOST_CHECK(provider_inferred.origins == script_provider.origins);
+ BOOST_CHECK(GetKeyOriginData(provider_inferred, flags) == GetKeyOriginData(script_provider, flags));
}
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
@@ -335,6 +380,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}});
Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M);
CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey
CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin
CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin
@@ -354,6 +400,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", XONLY_KEYS | SIGNABLE | MISSING_PRIVKEYS, {{"51201497ae16f30dacb88523ed9301bff17773b609e8a90518a3f96ea328a47d1500"}}, OutputType::BECH32M);
// Versions with BIP32 derivations
Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt);
@@ -362,6 +409,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}});
+ Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}});
// Mixed xpubs and const pubkeys
Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32,{{0},{}});
// Mixed range xpubs and const pubkeys
@@ -380,6 +428,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT);
+ Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M);
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 4fb2c23d98..313064b294 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -152,4 +152,28 @@ BOOST_AUTO_TEST_CASE(rename)
fs::remove(path2);
}
+#ifndef WIN32
+BOOST_AUTO_TEST_CASE(create_directories)
+{
+ // Test fs::create_directories workaround.
+ const fs::path tmpfolder{m_args.GetDataDirBase()};
+
+ const fs::path dir{GetUniquePath(tmpfolder)};
+ fs::create_directory(dir);
+ BOOST_CHECK(fs::exists(dir));
+ BOOST_CHECK(fs::is_directory(dir));
+ BOOST_CHECK(!fs::create_directories(dir));
+
+ const fs::path symlink{GetUniquePath(tmpfolder)};
+ fs::create_directory_symlink(dir, symlink);
+ BOOST_CHECK(fs::exists(symlink));
+ BOOST_CHECK(fs::is_symlink(symlink));
+ BOOST_CHECK(fs::is_directory(symlink));
+ BOOST_CHECK(!fs::create_directories(symlink));
+
+ fs::remove(symlink);
+ fs::remove(dir);
+}
+#endif // WIN32
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/addition_overflow.cpp b/src/test/fuzz/addition_overflow.cpp
index cfad41659e..372c1a370e 100644
--- a/src/test/fuzz/addition_overflow.cpp
+++ b/src/test/fuzz/addition_overflow.cpp
@@ -26,6 +26,12 @@ void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider)
const T i = fuzzed_data_provider.ConsumeIntegral<T>();
const T j = fuzzed_data_provider.ConsumeIntegral<T>();
const bool is_addition_overflow_custom = AdditionOverflow(i, j);
+ const auto maybe_add{CheckedAdd(i, j)};
+ const auto sat_add{SaturatingAdd(i, j)};
+ assert(is_addition_overflow_custom == !maybe_add.has_value());
+ assert(is_addition_overflow_custom == AdditionOverflow(j, i));
+ assert(maybe_add == CheckedAdd(j, i));
+ assert(sat_add == SaturatingAdd(j, i));
#if defined(HAVE_BUILTIN_ADD_OVERFLOW)
T result_builtin;
const bool is_addition_overflow_builtin = __builtin_add_overflow(i, j, &result_builtin);
@@ -33,11 +39,14 @@ void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider)
if (!is_addition_overflow_custom) {
assert(i + j == result_builtin);
}
-#else
- if (!is_addition_overflow_custom) {
- (void)(i + j);
- }
#endif
+ if (is_addition_overflow_custom) {
+ assert(sat_add == std::numeric_limits<T>::min() || sat_add == std::numeric_limits<T>::max());
+ } else {
+ const auto add{i + j};
+ assert(add == maybe_add.value());
+ assert(add == sat_add);
+ }
}
} // namespace
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 14a59912db..fdcd0da37d 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -167,12 +167,4 @@ FUZZ_TARGET_INIT(script, initialize_script)
Assert(dest == GetScriptForDestination(tx_destination_2));
}
}
-
- (void)FormatScript(script);
- (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool());
-
- UniValue o1(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool());
- UniValue o3(UniValue::VOBJ);
- ScriptToUniv(script, o3);
}
diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp
new file mode 100644
index 0000000000..2fa893f812
--- /dev/null
+++ b/src/test/fuzz/script_format.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) 2019-2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <core_io.h>
+#include <script/script.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <univalue.h>
+
+void initialize_script_format()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+FUZZ_TARGET_INIT(script_format, initialize_script_format)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const CScript script{ConsumeScript(fuzzed_data_provider)};
+
+ (void)FormatScript(script);
+ (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool());
+
+ UniValue o1(UniValue::VOBJ);
+ ScriptPubKeyToUniv(script, o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool());
+ UniValue o3(UniValue::VOBJ);
+ ScriptToUniv(script, o3);
+}
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index 560efb6b42..6013080dc6 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -413,6 +413,17 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child2->GetHash())));
BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_child2->GetWitnessHash())));
+
+ // Deduplication should work when wtxid != txid. Submit package with the already-in-mempool
+ // transactions again, which should not fail.
+ const auto submit_segwit_dedup = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
+ {ptx_parent, ptx_child1}, /*test_accept=*/ false);
+ BOOST_CHECK_MESSAGE(submit_segwit_dedup.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << submit_segwit_dedup.m_state.GetRejectReason());
+ auto it_parent_dup = submit_segwit_dedup.m_tx_results.find(ptx_parent->GetWitnessHash());
+ auto it_child_dup = submit_segwit_dedup.m_tx_results.find(ptx_child1->GetWitnessHash());
+ BOOST_CHECK(it_parent_dup->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
+ BOOST_CHECK(it_child_dup->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
}
// Try submitting Package1{child2, grandchild} where child2 is same-txid-different-witness as
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index d2638e221a..9f78215de2 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1474,9 +1474,17 @@ static void TestAddMatrixOverflow()
constexpr T MAXI{std::numeric_limits<T>::max()};
BOOST_CHECK(!CheckedAdd(T{1}, MAXI));
BOOST_CHECK(!CheckedAdd(MAXI, MAXI));
+ BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{1}, MAXI));
+ BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(MAXI, MAXI));
+
BOOST_CHECK_EQUAL(0, CheckedAdd(T{0}, T{0}).value());
BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{0}, MAXI).value());
BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{1}, MAXI - 1).value());
+ BOOST_CHECK_EQUAL(MAXI - 1, CheckedAdd(T{1}, MAXI - 2).value());
+ BOOST_CHECK_EQUAL(0, SaturatingAdd(T{0}, T{0}));
+ BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{0}, MAXI));
+ BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{1}, MAXI - 1));
+ BOOST_CHECK_EQUAL(MAXI - 1, SaturatingAdd(T{1}, MAXI - 2));
}
/* Check for overflow or underflow */
@@ -1488,9 +1496,17 @@ static void TestAddMatrix()
constexpr T MAXI{std::numeric_limits<T>::max()};
BOOST_CHECK(!CheckedAdd(T{-1}, MINI));
BOOST_CHECK(!CheckedAdd(MINI, MINI));
+ BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{-1}, MINI));
+ BOOST_CHECK_EQUAL(MINI, SaturatingAdd(MINI, MINI));
+
BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{0}, MINI).value());
BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{-1}, MINI + 1).value());
BOOST_CHECK_EQUAL(-1, CheckedAdd(MINI, MAXI).value());
+ BOOST_CHECK_EQUAL(MINI + 1, CheckedAdd(T{-1}, MINI + 2).value());
+ BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{0}, MINI));
+ BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{-1}, MINI + 1));
+ BOOST_CHECK_EQUAL(MINI + 1, SaturatingAdd(T{-1}, MINI + 2));
+ BOOST_CHECK_EQUAL(-1, SaturatingAdd(MINI, MAXI));
}
BOOST_AUTO_TEST_CASE(util_overflow)
diff --git a/src/util/overflow.h b/src/util/overflow.h
index 5982af8d04..6b7dd1e8fd 100644
--- a/src/util/overflow.h
+++ b/src/util/overflow.h
@@ -6,13 +6,14 @@
#define BITCOIN_UTIL_OVERFLOW_H
#include <limits>
+#include <optional>
#include <type_traits>
template <class T>
[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept
{
static_assert(std::is_integral<T>::value, "Integral required.");
- if (std::numeric_limits<T>::is_signed) {
+ if constexpr (std::numeric_limits<T>::is_signed) {
return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
(i < 0 && j < std::numeric_limits<T>::min() - i);
}
@@ -28,4 +29,22 @@ template <class T>
return i + j;
}
+template <class T>
+[[nodiscard]] T SaturatingAdd(const T i, const T j) noexcept
+{
+ if constexpr (std::numeric_limits<T>::is_signed) {
+ if (i > 0 && j > std::numeric_limits<T>::max() - i) {
+ return std::numeric_limits<T>::max();
+ }
+ if (i < 0 && j < std::numeric_limits<T>::min() - i) {
+ return std::numeric_limits<T>::min();
+ }
+ } else {
+ if (std::numeric_limits<T>::max() - i < j) {
+ return std::numeric_limits<T>::max();
+ }
+ }
+ return i + j;
+}
+
#endif // BITCOIN_UTIL_OVERFLOW_H
diff --git a/src/util/syscall_sandbox.cpp b/src/util/syscall_sandbox.cpp
index f513dba598..f2a9cf664d 100644
--- a/src/util/syscall_sandbox.cpp
+++ b/src/util/syscall_sandbox.cpp
@@ -68,6 +68,10 @@ bool g_syscall_sandbox_log_violation_before_terminating{false};
#define __NR_copy_file_range 326
#endif
+#ifndef __NR_rseq
+#define __NR_rseq 334
+#endif
+
// This list of syscalls in LINUX_SYSCALLS is only used to map syscall numbers to syscall names in
// order to be able to print user friendly error messages which include the syscall name in addition
// to the syscall number.
@@ -327,6 +331,7 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_request_key, "request_key"},
{__NR_restart_syscall, "restart_syscall"},
{__NR_rmdir, "rmdir"},
+ {__NR_rseq, "rseq"},
{__NR_rt_sigaction, "rt_sigaction"},
{__NR_rt_sigpending, "rt_sigpending"},
{__NR_rt_sigprocmask, "rt_sigprocmask"},
@@ -723,6 +728,7 @@ public:
allowed_syscalls.insert(__NR_fork); // create a child process
allowed_syscalls.insert(__NR_tgkill); // send a signal to a thread
allowed_syscalls.insert(__NR_wait4); // wait for process to change state, BSD style
+ allowed_syscalls.insert(__NR_rseq); // register restartable sequence for thread
}
void AllowScheduling()
diff --git a/src/validation.cpp b/src/validation.cpp
index 12d29c8d5e..035b5783c3 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -915,12 +915,15 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
TxValidationState& state = ws.m_state;
CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
- // It's possible that the replacement pays more fees than its direct conflicts but not more
- // than all conflicts (i.e. the direct conflicts have high-fee descendants). However, if the
- // replacement doesn't pay more fees than its direct conflicts, then we can be sure it's not
- // more economically rational to mine. Before we go digging through the mempool for all
- // transactions that would need to be removed (direct conflicts and all descendants), check
- // that the replacement transaction pays more than its direct conflicts.
+ // The replacement transaction must have a higher feerate than its direct conflicts.
+ // - The motivation for this check is to ensure that the replacement transaction is preferable for
+ // block-inclusion, compared to what would be removed from the mempool.
+ // - This logic predates ancestor feerate-based transaction selection, which is why it doesn't
+ // consider feerates of descendants.
+ // - Note: Ancestor feerate-based transaction selection has made this comparison insufficient to
+ // guarantee that this is incentive-compatible for miners, because it is possible for a
+ // descendant transaction of a direct conflict to pay a higher feerate than the transaction that
+ // might replace them, under these rules.
if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
@@ -1318,7 +1321,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// we know is that the inputs aren't available.
if (m_pool.exists(GenTxid::Wtxid(wtxid))) {
// Exact transaction already exists in the mempool.
- auto iter = m_pool.GetIter(wtxid);
+ auto iter = m_pool.GetIter(txid);
assert(iter != std::nullopt);
results.emplace(wtxid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee()));
} else if (m_pool.exists(GenTxid::Txid(txid))) {
@@ -1923,7 +1926,10 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
{
AssertLockHeld(cs_main);
assert(pindex);
- assert(*pindex->phashBlock == block.GetHash());
+
+ uint256 block_hash{block.GetHash()};
+ assert(*pindex->phashBlock == block_hash);
+
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
@@ -1957,7 +1963,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
- if (block.GetHash() == m_params.GetConsensus().hashGenesisBlock) {
+ if (block_hash == m_params.GetConsensus().hashGenesisBlock) {
if (!fJustCheck)
view.SetBestBlock(pindex->GetBlockHash());
return true;
@@ -2214,12 +2220,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
TRACE6(validation, block_connected,
- block.GetHash().data(),
+ block_hash.data(),
pindex->nHeight,
block.vtx.size(),
nInputs,
nSigOpsCost,
- GetTimeMicros() - nTimeStart // in microseconds (µs)
+ nTime5 - nTimeStart // in microseconds (µs)
);
return true;
@@ -2370,14 +2376,13 @@ bool CChainState::FlushStateToDisk(
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
full_flush_completed = true;
+ TRACE5(utxocache, flush,
+ (int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
+ (u_int32_t)mode,
+ (u_int64_t)coins_count,
+ (u_int64_t)coins_mem_usage,
+ (bool)fFlushForPrune);
}
- TRACE6(utxocache, flush,
- (int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
- (u_int32_t)mode,
- (u_int64_t)coins_count,
- (u_int64_t)coins_mem_usage,
- (bool)fFlushForPrune,
- (bool)fDoFullFlush);
}
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
diff --git a/src/validation.h b/src/validation.h
index 968f62aa9a..7766d77a88 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -90,7 +90,7 @@ static const int DEFAULT_STOPATHEIGHT = 0;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ActiveChain().Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
static const signed int DEFAULT_CHECKBLOCKS = 6;
-static const unsigned int DEFAULT_CHECKLEVEL = 3;
+static constexpr int DEFAULT_CHECKLEVEL{3};
// Require that user allocate at least 550 MiB for block & undo files (blk???.dat and rev???.dat)
// At 1MB per block, 288 blocks = 288MB.
// Add 15% for Undo data = 331MB
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7e694d1987..6549a2146b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1027,7 +1027,8 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
if (!fill_wtx(wtx, ins.second)) {
return false;
}
- // If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn.
+ // If wallet doesn't have a chain (e.g when using bitcoin-wallet tool),
+ // don't bother to update txn.
if (HaveChain()) {
bool active;
auto lookup_block = [&](const uint256& hash, int& height, TxState& state) {
diff --git a/test/functional/feature_dirsymlinks.py b/test/functional/feature_dirsymlinks.py
new file mode 100755
index 0000000000..85c8e27600
--- /dev/null
+++ b/test/functional/feature_dirsymlinks.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test successful startup with symlinked directories.
+"""
+
+import os
+import sys
+
+from test_framework.test_framework import BitcoinTestFramework, SkipTest
+
+
+def rename_and_link(*, from_name, to_name):
+ os.rename(from_name, to_name)
+ os.symlink(to_name, from_name)
+ assert os.path.islink(from_name) and os.path.isdir(from_name)
+
+class SymlinkTest(BitcoinTestFramework):
+ def skip_test_if_missing_module(self):
+ if sys.platform == 'win32':
+ raise SkipTest("Symlinks test skipped on Windows")
+
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.stop_node(0)
+
+ rename_and_link(from_name=os.path.join(self.nodes[0].datadir, self.chain, "blocks"),
+ to_name=os.path.join(self.nodes[0].datadir, self.chain, "newblocks"))
+ rename_and_link(from_name=os.path.join(self.nodes[0].datadir, self.chain, "chainstate"),
+ to_name=os.path.join(self.nodes[0].datadir, self.chain, "newchainstate"))
+
+ self.start_node(0)
+
+
+if __name__ == '__main__':
+ SymlinkTest().main()
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index ccb380e25b..3b6413d4a6 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -100,7 +100,7 @@ class HelpRpcTest(BitcoinTestFramework):
# command titles
titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')]
- components = ['Blockchain', 'Control', 'Generating', 'Mining', 'Network', 'Rawtransactions', 'Util']
+ components = ['Blockchain', 'Control', 'Mining', 'Network', 'Rawtransactions', 'Util']
if self.is_wallet_compiled():
components.append('Wallet')
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 1a0d62aa8f..516e8be638 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -321,6 +321,7 @@ BASE_SCRIPTS = [
'rpc_getdescriptorinfo.py',
'rpc_mempool_entry_fee_fields_deprecation.py',
'rpc_help.py',
+ 'feature_dirsymlinks.py',
'feature_help.py',
'feature_shutdown.py',
'p2p_ibd_txrelay.py',
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index ed8c75d617..26f3fdc7af 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -22,10 +22,6 @@ deadlock:sync_tests::potential_deadlock_detected
race:src/qt/test/*
deadlock:src/qt/test/*
-# Race in src/test/main.cpp
-# Can be removed once upgraded to boost test 1.74 in depends
-race:validation_chainstatemanager_tests
-
# External libraries
deadlock:libdb
race:libzmq