aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml5
-rw-r--r--CONTRIBUTING.md25
-rw-r--r--build-aux/m4/ax_boost_chrono.m4118
-rw-r--r--build_msvc/README.md2
-rw-r--r--build_msvc/bitcoin_config.h9
-rw-r--r--ci/test/00_setup_env_native_asan.sh2
-rw-r--r--ci/test/00_setup_env_native_fuzz.sh2
-rw-r--r--ci/test/00_setup_env_native_fuzz_with_valgrind.sh18
-rw-r--r--ci/test/00_setup_env_native_tsan.sh2
-rw-r--r--ci/test/00_setup_env_native_valgrind.sh4
-rwxr-xr-xci/test/05_before_script.sh2
-rwxr-xr-xci/test/06_script_b.sh2
-rw-r--r--configure.ac67
-rw-r--r--depends/packages/boost.mk2
-rw-r--r--doc/build-osx.md1
-rw-r--r--doc/build-unix.md2
-rw-r--r--doc/developer-notes.md4
-rw-r--r--doc/release-notes-15437.md53
-rw-r--r--doc/release-notes-15954.md4
-rw-r--r--doc/release-notes-17056.md4
-rw-r--r--doc/release-notes-17410.md5
-rw-r--r--doc/release-notes-17437.md5
-rw-r--r--doc/release-notes-17578.md13
-rw-r--r--doc/release-notes.md87
-rw-r--r--doc/release-notes/release-notes-0.19.1.md115
-rw-r--r--src/Makefile.am7
-rw-r--r--src/Makefile.test.include254
-rw-r--r--src/addrman.cpp10
-rw-r--r--src/addrman.h11
-rw-r--r--src/bench/ccoins_caching.cpp42
-rw-r--r--src/bench/examples.cpp2
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/blockencodings.h140
-rw-r--r--src/compressor.h10
-rw-r--r--src/consensus/validation.h61
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/index/base.h2
-rw-r--r--src/indirectmap.h2
-rw-r--r--src/init.cpp56
-rw-r--r--src/interfaces/wallet.cpp17
-rw-r--r--src/interfaces/wallet.h9
-rw-r--r--src/logging/timer.h2
-rw-r--r--src/memusage.h2
-rw-r--r--src/miner.cpp3
-rw-r--r--src/net.cpp30
-rw-r--r--src/net.h21
-rw-r--r--src/net_permissions.h4
-rw-r--r--src/net_processing.cpp39
-rw-r--r--src/netaddress.cpp7
-rw-r--r--src/netaddress.h2
-rw-r--r--src/node/psbt.cpp31
-rw-r--r--src/node/transaction.cpp3
-rw-r--r--src/outputtype.cpp14
-rw-r--r--src/prevector.h9
-rw-r--r--src/qt/guiutil.cpp4
-rw-r--r--src/qt/guiutil.h4
-rw-r--r--src/qt/peertablemodel.cpp4
-rw-r--r--src/qt/rpcconsole.cpp6
-rw-r--r--src/qt/sendcoinsdialog.cpp1
-rw-r--r--src/qt/signverifymessagedialog.cpp112
-rw-r--r--src/randomenv.cpp19
-rw-r--r--src/reverselock.h34
-rw-r--r--src/rpc/blockchain.cpp627
-rw-r--r--src/rpc/mining.cpp224
-rw-r--r--src/rpc/misc.cpp139
-rw-r--r--src/rpc/net.cpp272
-rw-r--r--src/rpc/rawtransaction.cpp554
-rw-r--r--src/rpc/rawtransaction_util.cpp56
-rw-r--r--src/rpc/rawtransaction_util.h2
-rw-r--r--src/rpc/server.cpp28
-rw-r--r--src/rpc/util.cpp115
-rw-r--r--src/rpc/util.h149
-rw-r--r--src/scheduler.cpp58
-rw-r--r--src/scheduler.h33
-rw-r--r--src/script/descriptor.cpp49
-rw-r--r--src/script/descriptor.h5
-rw-r--r--src/script/sign.cpp51
-rw-r--r--src/script/sign.h4
-rw-r--r--src/script/signingprovider.h46
-rw-r--r--src/serialize.h93
-rw-r--r--src/sync.cpp19
-rw-r--r--src/sync.h39
-rw-r--r--src/test/blockfilter_index_tests.cpp2
-rw-r--r--src/test/bswap_tests.cpp20
-rw-r--r--src/test/checkqueue_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp74
-rw-r--r--src/test/fuzz/bloom_filter.cpp80
-rw-r--r--src/test/fuzz/float.cpp42
-rw-r--r--src/test/fuzz/hex.cpp16
-rw-r--r--src/test/fuzz/integer.cpp94
-rw-r--r--src/test/fuzz/key.cpp309
-rw-r--r--src/test/fuzz/key_io.cpp48
-rw-r--r--src/test/fuzz/locale.cpp96
-rw-r--r--src/test/fuzz/netaddress.cpp123
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp47
-rw-r--r--src/test/fuzz/process_message.cpp98
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp50
-rw-r--r--src/test/fuzz/script.cpp33
-rw-r--r--src/test/fuzz/script_ops.cpp67
-rw-r--r--src/test/fuzz/scriptnum_ops.cpp137
-rw-r--r--src/test/fuzz/strprintf.cpp47
-rw-r--r--src/test/fuzz/transaction.cpp24
-rw-r--r--src/test/fuzz/util.h57
-rw-r--r--src/test/reverselock_tests.cpp45
-rw-r--r--src/test/scheduler_tests.cpp43
-rw-r--r--src/test/transaction_tests.cpp59
-rw-r--r--src/test/txindex_tests.cpp4
-rw-r--r--src/test/util/setup_common.cpp8
-rw-r--r--src/test/util/transaction_utils.cpp32
-rw-r--r--src/test/util/transaction_utils.h10
-rw-r--r--src/test/util_tests.cpp120
-rw-r--r--src/test/validation_block_tests.cpp2
-rw-r--r--src/test/validation_flush_tests.cpp26
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/message.cpp92
-rw-r--r--src/util/message.h76
-rw-r--r--src/util/moneystr.cpp10
-rw-r--r--src/util/moneystr.h2
-rw-r--r--src/util/time.cpp37
-rw-r--r--src/util/time.h4
-rw-r--r--src/util/validation.cpp25
-rw-r--r--src/util/validation.h18
-rw-r--r--src/validation.cpp45
-rw-r--r--src/validationinterface.cpp3
-rw-r--r--src/wallet/db.cpp6
-rw-r--r--src/wallet/db.h4
-rw-r--r--src/wallet/feebumper.cpp1
-rw-r--r--src/wallet/psbtwallet.cpp77
-rw-r--r--src/wallet/psbtwallet.h32
-rw-r--r--src/wallet/rpcdump.cpp45
-rw-r--r--src/wallet/rpcwallet.cpp830
-rw-r--r--src/wallet/scriptpubkeyman.cpp96
-rw-r--r--src/wallet/scriptpubkeyman.h42
-rw-r--r--src/wallet/test/coinselector_tests.cpp13
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp7
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp43
-rw-r--r--src/wallet/wallet.cpp236
-rw-r--r--src/wallet/wallet.h60
-rw-r--r--src/zmq/zmqrpc.cpp17
-rw-r--r--test/README.md2
-rw-r--r--test/functional/README.md5
-rwxr-xr-xtest/functional/feature_asmap.py106
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py16
-rwxr-xr-xtest/functional/feature_block.py6
-rwxr-xr-xtest/functional/feature_cltv.py4
-rwxr-xr-xtest/functional/feature_csv_activation.py79
-rwxr-xr-xtest/functional/feature_dersig.py5
-rwxr-xr-xtest/functional/feature_fee_estimation.py8
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py5
-rwxr-xr-xtest/functional/feature_nulldummy.py5
-rwxr-xr-xtest/functional/feature_segwit.py1
-rwxr-xr-xtest/functional/mempool_expiry.py100
-rwxr-xr-xtest/functional/mempool_packages.py32
-rwxr-xr-xtest/functional/p2p_invalid_block.py2
-rwxr-xr-xtest/functional/p2p_permissions.py10
-rwxr-xr-xtest/functional/p2p_segwit.py6
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py2
-rwxr-xr-xtest/functional/rpc_getdescriptorinfo.py65
-rwxr-xr-xtest/functional/rpc_psbt.py22
-rwxr-xr-xtest/functional/test_runner.py3
-rwxr-xr-xtest/functional/wallet_address_types.py2
-rwxr-xr-xtest/functional/wallet_avoidreuse.py2
-rwxr-xr-xtest/functional/wallet_backup.py8
-rwxr-xr-xtest/functional/wallet_bumpfee.py108
-rwxr-xr-xtest/functional/wallet_createwallet.py2
-rwxr-xr-xtest/functional/wallet_listsinceblock.py10
-rwxr-xr-xtest/fuzz/test_runner.py85
-rwxr-xr-xtest/lint/extended-lint-cppcheck.sh2
-rwxr-xr-xtest/lint/lint-includes.sh1
-rwxr-xr-xtest/lint/lint-locale-dependence.sh1
-rwxr-xr-xtest/lint/lint-shell.sh2
-rw-r--r--test/lint/lint-spelling.ignore-words.txt2
-rwxr-xr-xtest/lint/lint-submodule.sh20
176 files changed, 5592 insertions, 2881 deletions
diff --git a/.travis.yml b/.travis.yml
index c5b8c476b7..9a111c03ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -135,6 +135,11 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_native_fuzz.sh"
- stage: test
+ name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, fuzzers under valgrind]'
+ env: >-
+ FILE_ENV="./ci/test/00_setup_env_native_fuzz_with_valgrind.sh"
+
+ - stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_nowallet.sh"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index af4a1121e4..525958147a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -309,6 +309,31 @@ about:
when someone else is asking for feedback on their code, and universe balances out.
+Backporting
+-----------
+
+Security and bug fixes can be backported from `master` to release
+branches.
+If the backport is non-trivial, it may be appropriate to open an
+additional PR, to backport the change, only after the original PR
+has been merged.
+Otherwise, backports will be done in batches and
+the maintainers will use the proper `Needs backport (...)` labels
+when needed (the original author does not need to worry).
+
+A backport should contain the following metadata in the commit body:
+
+```
+Github-Pull: #<PR number>
+Rebased-From: <commit hash of the original commit>
+```
+
+Have a look at [an example backport PR](
+https://github.com/bitcoin/bitcoin/pull/16189).
+
+Also see the [backport.py script](
+https://github.com/bitcoin-core/bitcoin-maintainer-tools#backport).
+
Release Policy
--------------
diff --git a/build-aux/m4/ax_boost_chrono.m4 b/build-aux/m4/ax_boost_chrono.m4
deleted file mode 100644
index 4cd3b86041..0000000000
--- a/build-aux/m4/ax_boost_chrono.m4
+++ /dev/null
@@ -1,118 +0,0 @@
-# ===========================================================================
-# https://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_BOOST_CHRONO
-#
-# DESCRIPTION
-#
-# Test for Chrono library from the Boost C++ libraries. The macro requires
-# a preceding call to AX_BOOST_BASE. Further documentation is available at
-# <http://randspringer.de/boost/index.html>.
-#
-# This macro calls:
-#
-# AC_SUBST(BOOST_CHRONO_LIB)
-#
-# And sets:
-#
-# HAVE_BOOST_CHRONO
-#
-# LICENSE
-#
-# Copyright (c) 2012 Xiyue Deng <manphiz@gmail.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 5
-
-AC_DEFUN([AX_BOOST_CHRONO],
-[
- AC_ARG_WITH([boost-chrono],
- AS_HELP_STRING([--with-boost-chrono@<:@=special-lib@:>@],
- [use the Chrono library from boost - it is possible to specify a certain library for the linker
- e.g. --with-boost-chrono=boost_chrono-gcc-mt ]),
- [
- if test "$withval" = "no"; then
- want_boost="no"
- elif test "$withval" = "yes"; then
- want_boost="yes"
- ax_boost_user_chrono_lib=""
- else
- want_boost="yes"
- ax_boost_user_chrono_lib="$withval"
- fi
- ],
- [want_boost="yes"]
- )
-
- if test "x$want_boost" = "xyes"; then
- AC_REQUIRE([AC_PROG_CC])
- AC_REQUIRE([AC_CANONICAL_BUILD])
- CPPFLAGS_SAVED="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- export CPPFLAGS
-
- LDFLAGS_SAVED="$LDFLAGS"
- LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
- export LDFLAGS
-
- AC_CACHE_CHECK(whether the Boost::Chrono library is available,
- ax_cv_boost_chrono,
- [AC_LANG_PUSH([C++])
- CXXFLAGS_SAVE=$CXXFLAGS
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/chrono.hpp>]],
- [[boost::chrono::system_clock::time_point* time = new boost::chrono::system_clock::time_point; delete time;]])],
- ax_cv_boost_chrono=yes, ax_cv_boost_chrono=no)
- CXXFLAGS=$CXXFLAGS_SAVE
- AC_LANG_POP([C++])
- ])
- if test "x$ax_cv_boost_chrono" = "xyes"; then
- AC_SUBST(BOOST_CPPFLAGS)
-
- AC_DEFINE(HAVE_BOOST_CHRONO,,[define if the Boost::Chrono library is available])
- BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
-
- LDFLAGS_SAVE=$LDFLAGS
- if test "x$ax_boost_user_chrono_lib" = "x"; then
- for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break],
- [link_chrono="no"])
- done
- if test "x$link_chrono" != "xyes"; then
- for libextension in `ls $BOOSTLIBDIR/boost_chrono*.dll* $BOOSTLIBDIR/boost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_chrono.*\)\.dll.*$;\1;' -e 's;^\(boost_chrono.*\)\.a.*$;\1;'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break],
- [link_chrono="no"])
- done
- fi
-
- else
- for ax_lib in $ax_boost_user_chrono_lib boost_chrono-$ax_boost_user_chrono_lib; do
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break],
- [link_chrono="no"])
- done
-
- fi
- if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the Boost::Chrono library!)
- fi
- if test "x$link_chrono" = "xno"; then
- AC_MSG_ERROR(Could not link against $ax_lib !)
- fi
- fi
-
- CPPFLAGS="$CPPFLAGS_SAVED"
- LDFLAGS="$LDFLAGS_SAVED"
- fi
-])
diff --git a/build_msvc/README.md b/build_msvc/README.md
index e5aaf57abf..d4e710d55b 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -13,6 +13,7 @@ The minimal steps required to build Bitcoin Core with the msbuild toolchain are
```
vcpkg install --triplet x64-windows-static berkeleydb boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent[thread] rapidcheck zeromq double-conversion
+vcpkg integrate install
py -3 build_msvc\msvc-autogen.py
msbuild /m build_msvc\bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
```
@@ -56,6 +57,7 @@ The instructions below use `vcpkg` to install the dependencies.
```
PS >.\vcpkg install --triplet x64-windows-static $(Get-Content -Path build_msvc\vcpkg-packages.txt).split()
+PS >.\vcpkg integrate install
```
- Use Python to generate `*.vcxproj` from Makefile
diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h
index 3178f2a3d8..ea304dc9fc 100644
--- a/build_msvc/bitcoin_config.h
+++ b/build_msvc/bitcoin_config.h
@@ -47,9 +47,6 @@
/* define if the Boost library is available */
#define HAVE_BOOST /**/
-/* define if the Boost::Chrono library is available */
-#define HAVE_BOOST_CHRONO /**/
-
/* define if the Boost::Filesystem library is available */
#define HAVE_BOOST_FILESYSTEM /**/
@@ -258,12 +255,6 @@
/* Define if the visibility attribute is supported. */
#define HAVE_VISIBILITY_ATTRIBUTE 1
-/* Define this symbol if boost sleep works */
-/* #undef HAVE_WORKING_BOOST_SLEEP */
-
-/* Define this symbol if boost sleep_for works */
-#define HAVE_WORKING_BOOST_SLEEP_FOR 1
-
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index 24c043cb52..d5f39daaf5 100644
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_asan
-export PACKAGES="clang-8 llvm-8 python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
+export PACKAGES="clang-8 llvm-8 python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
# Use clang-8 instead of default clang (which is clang-6 on Bionic) to avoid spurious segfaults when running on ppc64le
export NO_DEPENDS=1
export GOAL="install"
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index af93dd1d68..a739ad50d2 100644
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_fuzz
-export PACKAGES="clang-8 llvm-8 python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev"
+export PACKAGES="clang-8 llvm-8 python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev"
export NO_DEPENDS=1
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
new file mode 100644
index 0000000000..fabb3affa4
--- /dev/null
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export CONTAINER_NAME=ci_native_fuzz_valgrind
+export PACKAGES="clang-8 llvm-8 python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev valgrind"
+export NO_DEPENDS=1
+export RUN_UNIT_TESTS=false
+export RUN_FUNCTIONAL_TESTS=false
+export RUN_FUZZ_TESTS=true
+export FUZZ_TESTS_CONFIG="--valgrind"
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang-8 CXX=clang++-8"
+# Use clang-8, instead of default clang on bionic, which is clang-6 and does not come with libfuzzer on aarch64
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 37884128fe..4d3f345ca6 100644
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_tsan
export DOCKER_NAME_TAG=ubuntu:16.04
-export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
+export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
export NO_DEPENDS=1
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index 156a83590c..f112aa87f9 100644
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -7,10 +7,10 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_valgrind
-export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev"
+export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev"
export USE_VALGRIND=1
export NO_DEPENDS=1
-export TEST_RUNNER_EXTRA="p2p_segwit.py" # Only run one test for now. TODO enable all and bump timeouts
+export TEST_RUNNER_EXTRA="--exclude feature_abortnode,feature_block,rpc_bind" # Excluded for now
export RUN_FUNCTIONAL_TESTS=true
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index 1cb5c30901..933f4cea91 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -26,7 +26,7 @@ if [[ $HOST = *-mingw32 ]]; then
fi
if [ -z "$NO_DEPENDS" ]; then
if [[ $DOCKER_NAME_TAG == centos* ]]; then
- # CentOS has problems building the depends if the config shell is not explicitely set
+ # CentOS has problems building the depends if the config shell is not explicitly set
# (i.e. for libevent a Makefile with an empty SHELL variable is generated, leading to
# an error as the first command is executed)
SHELL_OPTS="CONFIG_SHELL=/bin/bash"
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 537493a710..3b32513353 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -36,6 +36,6 @@ fi
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
BEGIN_FOLD fuzz-tests
- DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
+ DOCKER_EXEC test/fuzz/test_runner.py ${FUZZ_TESTS_CONFIG} -l DEBUG ${DIR_FUZZ_IN}
END_FOLD
fi
diff --git a/configure.ac b/configure.ac
index d0fcf48713..1c0f39d7f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -262,6 +262,13 @@ AC_ARG_ENABLE([gprof],
[enable_gprof=$enableval],
[enable_gprof=no])
+dnl Pass compiler & liner flags that make builds deterministic
+AC_ARG_ENABLE([determinism],
+ [AS_HELP_STRING([--enable-determinism],
+ [Enable compilation flags that make builds deterministic (default is no)])],
+ [enable_determinism=$enableval],
+ [enable_determinism=no])
+
dnl Turn warnings into errors
AC_ARG_ENABLE([werror],
[AS_HELP_STRING([--enable-werror],
@@ -764,6 +771,12 @@ if test x$TARGET_OS = xdarwin; then
AX_CHECK_LINK_FLAG([[-Wl,-bind_at_load]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-bind_at_load"])
fi
+if test x$enable_determinism = xyes; then
+ if test x$TARGET_OS = xwindows; then
+ AX_CHECK_LINK_FLAG([[-Wl,--no-insert-timestamp]], [LDFLAGS="$LDFLAGS -Wl,--no-insert-timestamp"])
+ fi
+fi
+
AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h])
dnl FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS
@@ -1126,7 +1139,6 @@ fi
AX_BOOST_SYSTEM
AX_BOOST_FILESYSTEM
AX_BOOST_THREAD
-AX_BOOST_CHRONO
dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic
dnl counter implementations. In 1.63 and later the std::atomic approach is default.
@@ -1193,7 +1205,7 @@ fi
if test x$use_boost = xyes; then
-BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB"
+BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB"
dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums
@@ -1231,57 +1243,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
LIBS="$TEMP_LIBS"
CPPFLAGS="$TEMP_CPPFLAGS"
-dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however
-dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if
-dnl a working version is available, else fall back to sleep. sleep was removed
-dnl after 1.56.
-dnl If neither is available, abort.
-TEMP_LIBS="$LIBS"
-LIBS="$BOOST_LIBS $LIBS"
-TEMP_CPPFLAGS="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #include <boost/thread/thread.hpp>
- #include <boost/version.hpp>
- ]],[[
- #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200)
- boost::this_thread::sleep_for(boost::chrono::milliseconds(0));
- #else
- choke me
- #endif
- ]])],
- [boost_sleep=yes;
- AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])],
- [boost_sleep=no])
-LIBS="$TEMP_LIBS"
-CPPFLAGS="$TEMP_CPPFLAGS"
-
-if test x$boost_sleep != xyes; then
-TEMP_LIBS="$LIBS"
-LIBS="$BOOST_LIBS $LIBS"
-TEMP_CPPFLAGS="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #include <boost/version.hpp>
- #include <boost/thread.hpp>
- #include <boost/date_time/posix_time/posix_time_types.hpp>
- ]],[[
- #if BOOST_VERSION <= 105600
- boost::this_thread::sleep(boost::posix_time::milliseconds(0));
- #else
- choke me
- #endif
- ]])],
- [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])],
- [boost_sleep=no])
-LIBS="$TEMP_LIBS"
-CPPFLAGS="$TEMP_CPPFLAGS"
-fi
-
-if test x$boost_sleep != xyes; then
- AC_MSG_ERROR(No working boost sleep implementation found.)
-fi
-
fi
if test x$use_pkgconfig = xyes; then
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index cd0e70fb1c..cbe4fe4d97 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -22,7 +22,7 @@ $(package)_config_opts_armv7a_android=address-model=32
$(package)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=clang-darwin
-$(package)_config_libraries=chrono,filesystem,system,thread,test
+$(package)_config_libraries=filesystem,system,thread,test
$(package)_cxxflags=-std=c++11 -fvisibility=hidden
$(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_android=-fPIC
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 86b5c5b602..7b76117c8b 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -22,6 +22,7 @@ Then install [Homebrew](https://brew.sh).
brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode
```
+If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
See [dependencies.md](dependencies.md) for a complete overview.
If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG:
diff --git a/doc/build-unix.md b/doc/build-unix.md
index e799e709fa..6b51db5f55 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -80,7 +80,7 @@ Build requirements:
Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies:
- sudo apt-get install libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev
+ sudo apt-get install libevent-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev
BerkeleyDB is required for the wallet.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index a82ecee7d8..207c4ba7c6 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -288,6 +288,7 @@ $ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
$ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
--show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite
$ valgrind -v --leak-check=full src/bitcoind -printtoconsole
+$ ./test/functional/test_runner.py --valgrind
```
### Compiling for test coverage
@@ -1089,7 +1090,8 @@ A few guidelines for introducing and reviewing new RPC interfaces:
new RPC is replacing a deprecated RPC, to avoid both RPCs confusingly
showing up in the command list.
-- Use *invalid* bech32 addresses for `RPCExamples` help documentation.
+- Use *invalid* bech32 addresses (e.g. the constant `EXAMPLE_ADDRESS`) for
+ `RPCExamples` help documentation.
- *Rationale*: Prevent accidental transactions by users and encourage the use
of bech32 addresses by default.
diff --git a/doc/release-notes-15437.md b/doc/release-notes-15437.md
deleted file mode 100644
index 6614207757..0000000000
--- a/doc/release-notes-15437.md
+++ /dev/null
@@ -1,53 +0,0 @@
-P2P and network changes
------------------------
-
-#### Removal of reject network messages from Bitcoin Core (BIP61)
-
-The command line option to enable BIP61 (`-enablebip61`) has been removed.
-
-This feature has been disabled by default since Bitcoin Core version 0.18.0.
-Nodes on the network can not generally be trusted to send valid ("reject")
-messages, so this should only ever be used when connected to a trusted node.
-Please use the recommended alternatives if you rely on this deprecated feature:
-
-* Testing or debugging of implementations of the Bitcoin P2P network protocol
- should be done by inspecting the log messages that are produced by a recent
- version of Bitcoin Core. Bitcoin Core logs debug messages
- (`-debug=<category>`) to a stream (`-printtoconsole`) or to a file
- (`-debuglogfile=<debug.log>`).
-
-* Testing the validity of a block can be achieved by specific RPCs:
- - `submitblock`
- - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with
- potentially invalid POW
-
-* Testing the validity of a transaction can be achieved by specific RPCs:
- - `sendrawtransaction`
- - `testmempoolaccept`
-
-* Wallets should not use the absence of "reject" messages to indicate a
- transaction has propagated the network, nor should wallets use "reject"
- messages to set transaction fees. Wallets should rather use fee estimation
- to determine transaction fees and set replace-by-fee if desired. Thus, they
- could wait until the transaction has confirmed (taking into account the fee
- target they set (compare the RPC `estimatesmartfee`)) or listen for the
- transaction announcement by other network peers to check for propagation.
-
-The removal of BIP61 REJECT message support also has the following minor RPC
-and logging implications:
-
-* `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
- code when a transaction is not accepted to the mempool. They still return the
- verbal reject reason.
-
-* Log messages that previously reported the REJECT code when a transaction was
- not accepted to the mempool now no longer report the REJECT code. The reason
- for rejection is still reported.
-
-Updated RPCs
-------------
-
-- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
- code when a transaction is not accepted to the mempool. See the Section
- _Removal of reject network messages from Bitcoin Core (BIP61)_ for details on
- the removal of BIP61 REJECT message support.
diff --git a/doc/release-notes-15954.md b/doc/release-notes-15954.md
deleted file mode 100644
index f4d2c5688c..0000000000
--- a/doc/release-notes-15954.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Configuration option changes
------------------------------
-
-Importing blocks upon startup via the `bootstrap.dat` file no longer occurs by default. The file must now be specified with `-loadblock=<file>`.
diff --git a/doc/release-notes-17056.md b/doc/release-notes-17056.md
deleted file mode 100644
index 23d5a8c8cd..0000000000
--- a/doc/release-notes-17056.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Low-level RPC Changes
-===
-
-- A new descriptor type `sortedmulti(...)` has been added to support multisig scripts where the public keys are sorted lexicographically in the resulting script.
diff --git a/doc/release-notes-17410.md b/doc/release-notes-17410.md
deleted file mode 100644
index 08ed353889..0000000000
--- a/doc/release-notes-17410.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Command-line options
---------------------
-
-- The `-debug=db` logging category has been renamed to `-debug=walletdb`, to distinguish it from `coindb`.
- `-debug=db` has been deprecated and will be removed in the next major release.
diff --git a/doc/release-notes-17437.md b/doc/release-notes-17437.md
deleted file mode 100644
index 3edfd00a38..0000000000
--- a/doc/release-notes-17437.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Low-level RPC Changes
-===
-
-- The RPC gettransaction, listtransactions and listsinceblock responses now also
-includes the height of the block that contains the wallet transaction, if any.
diff --git a/doc/release-notes-17578.md b/doc/release-notes-17578.md
deleted file mode 100644
index 664d17fd78..0000000000
--- a/doc/release-notes-17578.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Deprecated or removed RPCs
---------------------------
-
-- RPC `getaddressinfo` changes:
-
- - the `label` field has been deprecated in favor of the `labels` field and
- will be removed in 0.21. It can be re-enabled in the interim by launching
- with `-deprecatedrpc=label`.
-
- - the `labels` behavior of returning an array of JSON objects containing name
- and purpose key/value pairs has been deprecated in favor of an array of
- label names and will be removed in 0.21. The previous behavior can be
- re-enabled in the interim by launching with `-deprecatedrpc=labelspurpose`.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 99ca53c597..22d5767b7b 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -62,6 +62,64 @@ distribution provides binaries for the RISC-V platform.
Notable changes
===============
+P2P and network changes
+-----------------------
+
+#### Removal of reject network messages from Bitcoin Core (BIP61)
+
+The command line option to enable BIP61 (`-enablebip61`) has been removed.
+
+This feature has been disabled by default since Bitcoin Core version 0.18.0.
+Nodes on the network can not generally be trusted to send valid ("reject")
+messages, so this should only ever be used when connected to a trusted node.
+Please use the recommended alternatives if you rely on this deprecated feature:
+
+* Testing or debugging of implementations of the Bitcoin P2P network protocol
+ should be done by inspecting the log messages that are produced by a recent
+ version of Bitcoin Core. Bitcoin Core logs debug messages
+ (`-debug=<category>`) to a stream (`-printtoconsole`) or to a file
+ (`-debuglogfile=<debug.log>`).
+
+* Testing the validity of a block can be achieved by specific RPCs:
+ - `submitblock`
+ - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with
+ potentially invalid POW
+
+* Testing the validity of a transaction can be achieved by specific RPCs:
+ - `sendrawtransaction`
+ - `testmempoolaccept`
+
+* Wallets should not use the absence of "reject" messages to indicate a
+ transaction has propagated the network, nor should wallets use "reject"
+ messages to set transaction fees. Wallets should rather use fee estimation
+ to determine transaction fees and set replace-by-fee if desired. Thus, they
+ could wait until the transaction has confirmed (taking into account the fee
+ target they set (compare the RPC `estimatesmartfee`)) or listen for the
+ transaction announcement by other network peers to check for propagation.
+
+The removal of BIP61 REJECT message support also has the following minor RPC
+and logging implications:
+
+* `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
+ code when a transaction is not accepted to the mempool. They still return the
+ verbal reject reason.
+
+* Log messages that previously reported the REJECT code when a transaction was
+ not accepted to the mempool now no longer report the REJECT code. The reason
+ for rejection is still reported.
+
+Updated RPCs
+------------
+
+- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
+ code when a transaction is not accepted to the mempool. See the Section
+ _Removal of reject network messages from Bitcoin Core (BIP61)_ for details on
+ the removal of BIP61 REJECT message support.
+
+- A new descriptor type `sortedmulti(...)` has been added to support multisig scripts where the public keys are sorted lexicographically in the resulting script.
+
+- `walletprocesspsbt` and `walletcreatefundedpsbt` now include BIP 32 derivation paths by default for public keys if we know them. This can be disabled by setting `bip32derivs` to `false`.
+
Build System
------------
@@ -81,14 +139,21 @@ New settings
- RPC Whitelist system. It can give certain RPC users permissions to only some RPC calls.
It can be set with two command line arguments (`rpcwhitelist` and `rpcwhitelistdefault`). (#12763)
+- A new `-asmap` configuration option has been added to enable IP-to-ASN mapping
+ for bucketing of the network peers to diversify the network connections. The
+ legacy /16 prefix mapping remains the default. See [issue
+ #16599](https://github.com/bitcoin/bitcoin/issues/16599), [PR
+ #16702](https://github.com/bitcoin/bitcoin/pull/16702), and the `bitcoind
+ help` for more information. This option is experimental and subject to changes
+ or removal in future releases.
+
Updated settings
----------------
-Updated RPCs
-------------
+Importing blocks upon startup via the `bootstrap.dat` file no longer occurs by default. The file must now be specified with `-loadblock=<file>`.
-Note: some low-level RPC changes mainly useful for testing are described in the
-Low-level Changes section below.
+- The `-debug=db` logging category has been renamed to `-debug=walletdb`, to distinguish it from `coindb`.
+ `-debug=db` has been deprecated and will be removed in the next major release.
GUI changes
-----------
@@ -101,6 +166,20 @@ Wallet
- The wallet now by default uses bech32 addresses when using RPC, and creates native segwit change outputs.
- The way that output trust was computed has been fixed in #16766, which impacts confirmed/unconfirmed balance status and coin selection.
+- The RPC gettransaction, listtransactions and listsinceblock responses now also
+includes the height of the block that contains the wallet transaction, if any.
+
+- RPC `getaddressinfo` changes:
+
+ - the `label` field has been deprecated in favor of the `labels` field and
+ will be removed in 0.21. It can be re-enabled in the interim by launching
+ with `-deprecatedrpc=label`.
+
+ - the `labels` behavior of returning an array of JSON objects containing name
+ and purpose key/value pairs has been deprecated in favor of an array of
+ label names and will be removed in 0.21. The previous behavior can be
+ re-enabled in the interim by launching with `-deprecatedrpc=labelspurpose`.
+
Low-level changes
=================
diff --git a/doc/release-notes/release-notes-0.19.1.md b/doc/release-notes/release-notes-0.19.1.md
new file mode 100644
index 0000000000..5746bebb0d
--- /dev/null
+++ b/doc/release-notes/release-notes-0.19.1.md
@@ -0,0 +1,115 @@
+0.19.1 Release Notes
+===============================
+
+Bitcoin Core version 0.19.1 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.19.1/>
+
+This minor release includes various bug fixes and performance
+improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the datadir needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems using
+the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not recommended
+to use Bitcoin Core on unsupported systems.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+as frequently tested on them.
+
+From Bitcoin Core 0.17.0 onwards, macOS versions earlier than 10.10 are no
+longer supported, as Bitcoin Core is now built using Qt 5.9.x which requires
+macOS 10.10+. Additionally, Bitcoin Core does not yet change appearance when
+macOS "dark mode" is activated.
+
+In addition to previously supported CPU platforms, this release's pre-compiled
+distribution provides binaries for the RISC-V platform.
+
+0.19.1 change log
+=================
+
+### Wallet
+- #17643 Fix origfee return for bumpfee with feerate arg (instagibbs)
+- #16963 Fix `unique_ptr` usage in boost::signals2 (promag)
+- #17258 Fix issue with conflicted mempool tx in listsinceblock (adamjonas, mchrostowski)
+- #17924 Bug: IsUsedDestination shouldn't use key id as script id for ScriptHash (instagibbs)
+- #17621 IsUsedDestination should count any known single-key address (instagibbs)
+- #17843 Reset reused transactions cache (fjahr)
+
+### RPC and other APIs
+- #17687 cli: Fix fatal leveldb error when specifying -blockfilterindex=basic twice (brakmic)
+- #17728 require second argument only for scantxoutset start action (achow101)
+- #17445 zmq: Fix due to invalid argument and multiple notifiers (promag)
+- #17524 psbt: handle unspendable psbts (achow101)
+- #17156 psbt: check that various indexes and amounts are within bounds (achow101)
+
+### GUI
+- #17427 Fix missing qRegisterMetaType for `size_t` (hebasto)
+- #17695 disable File-\>CreateWallet during startup (fanquake)
+- #17634 Fix comparison function signature (hebasto)
+- #18062 Fix unintialized WalletView::progressDialog (promag)
+
+### Tests and QA
+- #17416 Appveyor improvement - text file for vcpkg package list (sipsorcery)
+- #17488 fix "bitcoind already running" warnings on macOS (fanquake)
+- #17980 add missing #include to fix compiler errors (kallewoof)
+
+### Platform support
+- #17736 Update msvc build for Visual Studio 2019 v16.4 (sipsorcery)
+- #17364 Updates to appveyor config for VS2019 and Qt5.9.8 + msvc project fixes (sipsorcery)
+- #17887 bug-fix macos: give free bytes to `F_PREALLOCATE` (kallewoof)
+
+### Miscellaneous
+- #17897 init: Stop indexes on shutdown after ChainStateFlushed callback (jimpo)
+- #17450 util: Add missing headers to util/fees.cpp (hebasto)
+- #17654 Unbreak build with Boost 1.72.0 (jbeich)
+- #17857 scripts: Fix symbol-check & security-check argument passing (fanquake)
+- #17762 Log to net category for exceptions in ProcessMessages (laanwj)
+- #18100 Update univalue subtree (MarcoFalke)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Aaron Clauson
+- Adam Jonas
+- Andrew Chow
+- Fabian Jahr
+- fanquake
+- Gregory Sanders
+- Harris
+- Hennadii Stepanov
+- Jan Beich
+- Jim Posen
+- João Barbosa
+- Karl-Johan Alm
+- Luke Dashjr
+- MarcoFalke
+- Michael Chrostowski
+- Russell Yanofsky
+- Wladimir J. van der Laan
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/src/Makefile.am b/src/Makefile.am
index 6edd5e75b7..6fc6d5b5a3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -178,7 +178,6 @@ BITCOIN_CORE_H = \
random.h \
randomenv.h \
reverse_iterator.h \
- reverselock.h \
rpc/blockchain.h \
rpc/client.h \
rpc/protocol.h \
@@ -220,6 +219,7 @@ BITCOIN_CORE_H = \
util/system.h \
util/macros.h \
util/memory.h \
+ util/message.h \
util/moneystr.h \
util/rbf.h \
util/settings.h \
@@ -228,7 +228,6 @@ BITCOIN_CORE_H = \
util/time.h \
util/translation.h \
util/url.h \
- util/validation.h \
util/vector.h \
validation.h \
validationinterface.h \
@@ -242,7 +241,6 @@ BITCOIN_CORE_H = \
wallet/fees.h \
wallet/ismine.h \
wallet/load.h \
- wallet/psbtwallet.h \
wallet/rpcwallet.h \
wallet/scriptpubkeyman.h \
wallet/wallet.h \
@@ -350,7 +348,6 @@ libbitcoin_wallet_a_SOURCES = \
wallet/feebumper.cpp \
wallet/fees.cpp \
wallet/load.cpp \
- wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/scriptpubkeyman.cpp \
@@ -517,6 +514,7 @@ libbitcoin_util_a_SOURCES = \
util/error.cpp \
util/fees.cpp \
util/system.cpp \
+ util/message.cpp \
util/moneystr.cpp \
util/rbf.cpp \
util/settings.cpp \
@@ -526,7 +524,6 @@ libbitcoin_util_a_SOURCES = \
util/string.cpp \
util/time.cpp \
util/url.cpp \
- util/validation.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index c76f30de8e..5a2ecff651 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -2,7 +2,6 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
FUZZ_TARGETS = \
test/fuzz/addr_info_deserialize \
test/fuzz/address_deserialize \
@@ -22,6 +21,7 @@ FUZZ_TARGETS = \
test/fuzz/blocktransactions_deserialize \
test/fuzz/blocktransactionsrequest_deserialize \
test/fuzz/blockundo_deserialize \
+ test/fuzz/bloom_filter \
test/fuzz/bloomfilter_deserialize \
test/fuzz/coins_deserialize \
test/fuzz/decode_tx \
@@ -30,14 +30,20 @@ FUZZ_TARGETS = \
test/fuzz/eval_script \
test/fuzz/fee_rate_deserialize \
test/fuzz/flat_file_pos_deserialize \
+ test/fuzz/float \
test/fuzz/hex \
test/fuzz/integer \
test/fuzz/inv_deserialize \
+ test/fuzz/key \
+ test/fuzz/key_io \
test/fuzz/key_origin_info_deserialize \
+ test/fuzz/locale \
test/fuzz/merkle_block_deserialize \
test/fuzz/messageheader_deserialize \
test/fuzz/netaddr_deserialize \
+ test/fuzz/netaddress \
test/fuzz/out_point_deserialize \
+ test/fuzz/p2p_transport_deserializer \
test/fuzz/parse_hd_keypath \
test/fuzz/parse_iso8601 \
test/fuzz/parse_numbers \
@@ -46,13 +52,41 @@ FUZZ_TARGETS = \
test/fuzz/partial_merkle_tree_deserialize \
test/fuzz/partially_signed_transaction_deserialize \
test/fuzz/prefilled_transaction_deserialize \
+ test/fuzz/process_message \
+ test/fuzz/process_message_addr \
+ test/fuzz/process_message_block \
+ test/fuzz/process_message_blocktxn \
+ test/fuzz/process_message_cmpctblock \
+ test/fuzz/process_message_feefilter \
+ test/fuzz/process_message_filteradd \
+ test/fuzz/process_message_filterclear \
+ test/fuzz/process_message_filterload \
+ test/fuzz/process_message_getaddr \
+ test/fuzz/process_message_getblocks \
+ test/fuzz/process_message_getblocktxn \
+ test/fuzz/process_message_getdata \
+ test/fuzz/process_message_getheaders \
+ test/fuzz/process_message_headers \
+ test/fuzz/process_message_inv \
+ test/fuzz/process_message_mempool \
+ test/fuzz/process_message_notfound \
+ test/fuzz/process_message_ping \
+ test/fuzz/process_message_pong \
+ test/fuzz/process_message_sendcmpct \
+ test/fuzz/process_message_sendheaders \
+ test/fuzz/process_message_tx \
+ test/fuzz/process_message_verack \
+ test/fuzz/process_message_version \
test/fuzz/psbt \
test/fuzz/psbt_input_deserialize \
test/fuzz/psbt_output_deserialize \
test/fuzz/pub_key_deserialize \
+ test/fuzz/rolling_bloom_filter \
test/fuzz/script \
test/fuzz/script_deserialize \
test/fuzz/script_flags \
+ test/fuzz/script_ops \
+ test/fuzz/scriptnum_ops \
test/fuzz/service_deserialize \
test/fuzz/spanparsing \
test/fuzz/strprintf \
@@ -96,7 +130,8 @@ BITCOIN_TEST_SUITE = \
FUZZ_SUITE = \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h \
- test/fuzz/FuzzedDataProvider.h
+ test/fuzz/FuzzedDataProvider.h \
+ test/fuzz/util.h
FUZZ_SUITE_LD_COMMON = \
$(LIBBITCOIN_SERVER) \
@@ -209,7 +244,8 @@ BITCOIN_TESTS += \
wallet/test/wallet_crypto_tests.cpp \
wallet/test/coinselector_tests.cpp \
wallet/test/init_tests.cpp \
- wallet/test/ismine_tests.cpp
+ wallet/test/ismine_tests.cpp \
+ wallet/test/scriptpubkeyman_tests.cpp
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
@@ -346,6 +382,12 @@ test_fuzz_blockundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blockundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_blockundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bloom_filter_SOURCES = $(FUZZ_SUITE) test/fuzz/bloom_filter.cpp
+
test_fuzz_bloomfilter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOOMFILTER_DESERIALIZE=1
test_fuzz_bloomfilter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -394,6 +436,12 @@ test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_flat_file_pos_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_float_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_float_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_float_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_float_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_float_SOURCES = $(FUZZ_SUITE) test/fuzz/float.cpp
+
test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -412,12 +460,30 @@ test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_SOURCES = $(FUZZ_SUITE) test/fuzz/key.cpp
+
+test_fuzz_key_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_key_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_key_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_key_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_io_SOURCES = $(FUZZ_SUITE) test/fuzz/key_io.cpp
+
test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1
test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_key_origin_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_locale_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_locale_SOURCES = $(FUZZ_SUITE) test/fuzz/locale.cpp
+
test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1
test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -436,12 +502,24 @@ test_fuzz_netaddr_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_netaddr_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_netaddr_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_netaddress_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_netaddress_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_netaddress_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_netaddress_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_netaddress_SOURCES = $(FUZZ_SUITE) test/fuzz/netaddress.cpp
+
test_fuzz_out_point_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DOUT_POINT_DESERIALIZE=1
test_fuzz_out_point_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_out_point_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_out_point_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_out_point_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_p2p_transport_deserializer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_p2p_transport_deserializer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_p2p_transport_deserializer_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_p2p_transport_deserializer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_p2p_transport_deserializer_SOURCES = $(FUZZ_SUITE) test/fuzz/p2p_transport_deserializer.cpp
+
test_fuzz_parse_hd_keypath_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_hd_keypath_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_hd_keypath_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -490,6 +568,156 @@ test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_prefilled_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_process_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_process_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_addr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=addr
+test_fuzz_process_message_addr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_addr_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_addr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_addr_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=block
+test_fuzz_process_message_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_block_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_block_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_blocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=blocktxn
+test_fuzz_process_message_blocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_blocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_blocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_blocktxn_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_cmpctblock_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=cmpctblock
+test_fuzz_process_message_cmpctblock_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_cmpctblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_cmpctblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_cmpctblock_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_feefilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=feefilter
+test_fuzz_process_message_feefilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_feefilter_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_feefilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_feefilter_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_filteradd_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filteradd
+test_fuzz_process_message_filteradd_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_filteradd_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_filteradd_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filteradd_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_filterclear_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterclear
+test_fuzz_process_message_filterclear_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_filterclear_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_filterclear_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterclear_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_filterload_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterload
+test_fuzz_process_message_filterload_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_filterload_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_filterload_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterload_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_getaddr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getaddr
+test_fuzz_process_message_getaddr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_getaddr_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_getaddr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getaddr_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_getblocks_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocks
+test_fuzz_process_message_getblocks_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_getblocks_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_getblocks_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocks_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_getblocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocktxn
+test_fuzz_process_message_getblocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_getblocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_getblocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocktxn_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_getdata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getdata
+test_fuzz_process_message_getdata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_getdata_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_getdata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getdata_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_getheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getheaders
+test_fuzz_process_message_getheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_getheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_getheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getheaders_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_headers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=headers
+test_fuzz_process_message_headers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_headers_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_headers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_headers_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_inv_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=inv
+test_fuzz_process_message_inv_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_inv_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_inv_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_inv_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_mempool_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=mempool
+test_fuzz_process_message_mempool_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_mempool_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_mempool_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_mempool_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_notfound_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=notfound
+test_fuzz_process_message_notfound_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_notfound_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_notfound_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_notfound_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_ping_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=ping
+test_fuzz_process_message_ping_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_ping_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_ping_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_ping_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_pong_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=pong
+test_fuzz_process_message_pong_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_pong_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_pong_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_pong_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_sendcmpct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendcmpct
+test_fuzz_process_message_sendcmpct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_sendcmpct_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_sendcmpct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendcmpct_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_sendheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendheaders
+test_fuzz_process_message_sendheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_sendheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_sendheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendheaders_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=tx
+test_fuzz_process_message_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_tx_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_verack_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=verack
+test_fuzz_process_message_verack_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_verack_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_verack_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_verack_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
+test_fuzz_process_message_version_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=version
+test_fuzz_process_message_version_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_message_version_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_message_version_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_version_SOURCES = $(FUZZ_SUITE) test/fuzz/process_message.cpp
+
test_fuzz_psbt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_psbt_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -514,6 +742,12 @@ test_fuzz_pub_key_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_pub_key_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_pub_key_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
+test_fuzz_rolling_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_rolling_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_rolling_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_rolling_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rolling_bloom_filter_SOURCES = $(FUZZ_SUITE) test/fuzz/rolling_bloom_filter.cpp
+
test_fuzz_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -532,6 +766,18 @@ test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_flags_SOURCES = $(FUZZ_SUITE) test/fuzz/script_flags.cpp
+test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_ops_SOURCES = $(FUZZ_SUITE) test/fuzz/script_ops.cpp
+
+test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_scriptnum_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_scriptnum_ops_SOURCES = $(FUZZ_SUITE) test/fuzz/scriptnum_ops.cpp
+
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -639,7 +885,7 @@ endif
%.cpp.test: %.cpp
@echo Running tests: `cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $<
- $(AM_V_at)$(TEST_BINARY) -l test_suite -t "`cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $<.log 2>&1 || (cat $<.log && false)
+ $(AM_V_at)$(TEST_BINARY) --catch_system_errors=no -l test_suite -t "`cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $<.log 2>&1 || (cat $<.log && false)
%.json.h: %.json
@$(MKDIR_P) $(@D)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 121ae4bf7e..2f8a3a0bd5 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -6,8 +6,8 @@
#include <addrman.h>
#include <hash.h>
-#include <serialize.h>
#include <logging.h>
+#include <serialize.h>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -15,7 +15,7 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asma
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
uint32_t mapped_as = GetMappedAS(asmap);
- LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i.\n", ToStringIP(), mapped_as, tried_bucket);
+ LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket);
return tried_bucket;
}
@@ -26,7 +26,7 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std:
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT;
uint32_t mapped_as = GetMappedAS(asmap);
- LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i.\n", ToStringIP(), mapped_as, new_bucket);
+ LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket);
return new_bucket;
}
@@ -630,12 +630,12 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
FILE *filestr = fsbridge::fopen(path, "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
- LogPrintf("Failed to open asmap file from disk.\n");
+ LogPrintf("Failed to open asmap file from disk\n");
return bits;
}
fseek(filestr, 0, SEEK_END);
int length = ftell(filestr);
- LogPrintf("Opened asmap file %s (%d bytes) from disk.\n", path, length);
+ LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
fseek(filestr, 0, SEEK_SET);
char cur_byte;
for (int i = 0; i < length; ++i) {
diff --git a/src/addrman.h b/src/addrman.h
index 5901611bee..8e82020df0 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,23 +6,22 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
+#include <clientversion.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
#include <sync.h>
#include <timedata.h>
#include <util/system.h>
-#include <clientversion.h>
+#include <fs.h>
+#include <hash.h>
+#include <iostream>
#include <map>
#include <set>
#include <stdint.h>
-#include <vector>
-#include <iostream>
#include <streams.h>
-#include <fs.h>
-#include <hash.h>
-
+#include <vector>
/**
* Extended statistics about a CAddress
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index c313029ea8..e9dd40293f 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -6,47 +6,10 @@
#include <coins.h>
#include <policy/policy.h>
#include <script/signingprovider.h>
+#include <test/util/transaction_utils.h>
#include <vector>
-// FIXME: Dedup with SetupDummyInputs in test/transaction_tests.cpp.
-//
-// Helper: create two dummy transactions, each with
-// two outputs. The first has 11 and 50 COIN outputs
-// paid to a TX_PUBKEY, the second 21 and 22 COIN outputs
-// paid to a TX_PUBKEYHASH.
-//
-static std::vector<CMutableTransaction>
-SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
-{
- std::vector<CMutableTransaction> dummyTransactions;
- dummyTransactions.resize(2);
-
- // Add some keys to the keystore:
- CKey key[4];
- for (int i = 0; i < 4; i++) {
- key[i].MakeNewKey(i % 2);
- keystoreRet.AddKey(key[i]);
- }
-
- // Create some dummy input transactions
- dummyTransactions[0].vout.resize(2);
- dummyTransactions[0].vout[0].nValue = 11 * COIN;
- dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- dummyTransactions[0].vout[1].nValue = 50 * COIN;
- dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- AddCoins(coinsRet, CTransaction(dummyTransactions[0]), 0);
-
- dummyTransactions[1].vout.resize(2);
- dummyTransactions[1].vout[0].nValue = 21 * COIN;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
- dummyTransactions[1].vout[1].nValue = 22 * COIN;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
- AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
-
- return dummyTransactions;
-}
-
// Microbenchmark for simple accesses to a CCoinsViewCache database. Note from
// laanwj, "replicating the actual usage patterns of the client is hard though,
// many times micro-benchmarks of the database showed completely different
@@ -58,7 +21,8 @@ static void CCoinsCaching(benchmark::State& state)
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
- std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
+ std::vector<CMutableTransaction> dummyTransactions =
+ SetupDummyInputs(keystore, coins, {11 * COIN, 50 * COIN, 21 * COIN, 22 * COIN});
CMutableTransaction t1;
t1.vin.resize(3);
diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp
index 60a4fbf0ba..a2fdab5609 100644
--- a/src/bench/examples.cpp
+++ b/src/bench/examples.cpp
@@ -10,7 +10,7 @@
static void Sleep100ms(benchmark::State& state)
{
while (state.KeepRunning()) {
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index c085095a2b..6982eaab61 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -524,7 +524,7 @@ static int CommandLineRPC(int argc, char *argv[])
}
catch (const CConnectionFailed&) {
if (fWait)
- MilliSleep(1000);
+ UninterruptibleSleep(std::chrono::milliseconds{1000});
else
throw;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 4b5cea4849..e284dce0d5 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -29,7 +29,7 @@ static void WaitForShutdown(NodeContext& node)
{
while (!ShutdownRequested())
{
- MilliSleep(200);
+ UninterruptibleSleep(std::chrono::milliseconds{200});
}
Interrupt(node);
}
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 55ed8989bb..be50166cfc 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -10,18 +10,29 @@
class CTxMemPool;
-// Dumb helper to handle CTransaction compression at serialize-time
-struct TransactionCompressor {
-private:
- CTransactionRef& tx;
-public:
- explicit TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {}
+// Transaction compression schemes for compact block relay can be introduced by writing
+// an actual formatter here.
+using TransactionCompression = DefaultFormatter;
- ADD_SERIALIZE_METHODS;
+class DifferenceFormatter
+{
+ uint64_t m_shift = 0;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(tx); //TODO: Compress tx encoding
+public:
+ template<typename Stream, typename I>
+ void Ser(Stream& s, I v)
+ {
+ if (v < m_shift || v >= std::numeric_limits<uint64_t>::max()) throw std::ios_base::failure("differential value overflow");
+ WriteCompactSize(s, v - m_shift);
+ m_shift = uint64_t(v) + 1;
+ }
+ template<typename Stream, typename I>
+ void Unser(Stream& s, I& v)
+ {
+ uint64_t n = ReadCompactSize(s);
+ m_shift += n;
+ if (m_shift < n || m_shift >= std::numeric_limits<uint64_t>::max() || m_shift < std::numeric_limits<I>::min() || m_shift > std::numeric_limits<I>::max()) throw std::ios_base::failure("differential value overflow");
+ v = I(m_shift++);
}
};
@@ -31,39 +42,9 @@ public:
uint256 blockhash;
std::vector<uint16_t> indexes;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(blockhash);
- uint64_t indexes_size = (uint64_t)indexes.size();
- READWRITE(COMPACTSIZE(indexes_size));
- if (ser_action.ForRead()) {
- size_t i = 0;
- while (indexes.size() < indexes_size) {
- indexes.resize(std::min((uint64_t)(1000 + indexes.size()), indexes_size));
- for (; i < indexes.size(); i++) {
- uint64_t index = 0;
- READWRITE(COMPACTSIZE(index));
- if (index > std::numeric_limits<uint16_t>::max())
- throw std::ios_base::failure("index overflowed 16 bits");
- indexes[i] = index;
- }
- }
-
- int32_t offset = 0;
- for (size_t j = 0; j < indexes.size(); j++) {
- if (int32_t(indexes[j]) + offset > std::numeric_limits<uint16_t>::max())
- throw std::ios_base::failure("indexes overflowed 16 bits");
- indexes[j] = indexes[j] + offset;
- offset = int32_t(indexes[j]) + 1;
- }
- } else {
- for (size_t i = 0; i < indexes.size(); i++) {
- uint64_t index = indexes[i] - (i == 0 ? 0 : (indexes[i - 1] + 1));
- READWRITE(COMPACTSIZE(index));
- }
- }
+ SERIALIZE_METHODS(BlockTransactionsRequest, obj)
+ {
+ READWRITE(obj.blockhash, Using<VectorFormatter<DifferenceFormatter>>(obj.indexes));
}
};
@@ -77,24 +58,9 @@ public:
explicit BlockTransactions(const BlockTransactionsRequest& req) :
blockhash(req.blockhash), txn(req.indexes.size()) {}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(blockhash);
- uint64_t txn_size = (uint64_t)txn.size();
- READWRITE(COMPACTSIZE(txn_size));
- if (ser_action.ForRead()) {
- size_t i = 0;
- while (txn.size() < txn_size) {
- txn.resize(std::min((uint64_t)(1000 + txn.size()), txn_size));
- for (; i < txn.size(); i++)
- READWRITE(TransactionCompressor(txn[i]));
- }
- } else {
- for (size_t i = 0; i < txn.size(); i++)
- READWRITE(TransactionCompressor(txn[i]));
- }
+ SERIALIZE_METHODS(BlockTransactions, obj)
+ {
+ READWRITE(obj.blockhash, Using<VectorFormatter<TransactionCompression>>(obj.txn));
}
};
@@ -105,17 +71,7 @@ struct PrefilledTransaction {
uint16_t index;
CTransactionRef tx;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- uint64_t idx = index;
- READWRITE(COMPACTSIZE(idx));
- if (idx > std::numeric_limits<uint16_t>::max())
- throw std::ios_base::failure("index overflowed 16-bits");
- index = idx;
- READWRITE(TransactionCompressor(tx));
- }
+ SERIALIZE_METHODS(PrefilledTransaction, obj) { READWRITE(COMPACTSIZE(obj.index), Using<TransactionCompression>(obj.tx)); }
};
typedef enum ReadStatus_t
@@ -153,43 +109,15 @@ public:
size_t BlockTxCount() const { return shorttxids.size() + prefilledtxn.size(); }
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(header);
- READWRITE(nonce);
-
- uint64_t shorttxids_size = (uint64_t)shorttxids.size();
- READWRITE(COMPACTSIZE(shorttxids_size));
+ SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj)
+ {
+ READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
if (ser_action.ForRead()) {
- size_t i = 0;
- while (shorttxids.size() < shorttxids_size) {
- shorttxids.resize(std::min((uint64_t)(1000 + shorttxids.size()), shorttxids_size));
- for (; i < shorttxids.size(); i++) {
- uint32_t lsb = 0; uint16_t msb = 0;
- READWRITE(lsb);
- READWRITE(msb);
- shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
- static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids serialization assumes 6-byte shorttxids");
- }
- }
- } else {
- for (size_t i = 0; i < shorttxids.size(); i++) {
- uint32_t lsb = shorttxids[i] & 0xffffffff;
- uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
- READWRITE(lsb);
- READWRITE(msb);
+ if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) {
+ throw std::ios_base::failure("indexes overflowed 16 bits");
}
+ obj.FillShortTxIDSelector();
}
-
- READWRITE(prefilledtxn);
-
- if (BlockTxCount() > std::numeric_limits<uint16_t>::max())
- throw std::ios_base::failure("indexes overflowed 16 bits");
-
- if (ser_action.ForRead())
- FillShortTxIDSelector();
}
};
diff --git a/src/compressor.h b/src/compressor.h
index 7bb60d311e..223603e7e9 100644
--- a/src/compressor.h
+++ b/src/compressor.h
@@ -15,7 +15,17 @@ bool CompressScript(const CScript& script, std::vector<unsigned char> &out);
unsigned int GetSpecialScriptSize(unsigned int nSize);
bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out);
+/**
+ * Compress amount.
+ *
+ * nAmount is of type uint64_t and thus cannot be negative. If you're passing in
+ * a CAmount (int64_t), make sure to properly handle the case where the amount
+ * is negative before calling CompressAmount(...).
+ *
+ * @pre Function defined only for 0 <= nAmount <= MAX_MONEY.
+ */
uint64_t CompressAmount(uint64_t nAmount);
+
uint64_t DecompressAmount(uint64_t nAmount);
/** Compact serializer for scripts.
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 8a3abb31f4..a79e7b9d12 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -16,7 +16,7 @@
* provider of the transaction should be banned/ignored/disconnected/etc.
*/
enum class TxValidationResult {
- TX_RESULT_UNSET, //!< initial value. Tx has not yet been rejected
+ TX_RESULT_UNSET = 0, //!< initial value. Tx has not yet been rejected
TX_CONSENSUS, //!< invalid by consensus rules
/**
* Invalid by a change to consensus rules more recent than SegWit.
@@ -50,7 +50,7 @@ enum class TxValidationResult {
* useful for some other use-cases.
*/
enum class BlockValidationResult {
- BLOCK_RESULT_UNSET, //!< initial value. Block has not yet been rejected
+ BLOCK_RESULT_UNSET = 0, //!< initial value. Block has not yet been rejected
BLOCK_CONSENSUS, //!< invalid by consensus rules (excluding any below reasons)
/**
* Invalid by a change to consensus rules more recent than SegWit.
@@ -71,31 +71,31 @@ enum class BlockValidationResult {
-/** Base class for capturing information about block/transaction validation. This is subclassed
+/** Template for capturing information about block/transaction validation. This is instantiated
* by TxValidationState and BlockValidationState for validation information on transactions
* and blocks respectively. */
+template <typename Result>
class ValidationState {
private:
enum mode_state {
MODE_VALID, //!< everything ok
MODE_INVALID, //!< network rule violation (DoS value may be set)
MODE_ERROR, //!< run-time error
- } m_mode;
+ } m_mode{MODE_VALID};
+ Result m_result{};
std::string m_reject_reason;
std::string m_debug_message;
-protected:
- void Invalid(const std::string &reject_reason="",
+public:
+ bool Invalid(Result result,
+ const std::string &reject_reason="",
const std::string &debug_message="")
{
+ m_result = result;
m_reject_reason = reject_reason;
m_debug_message = debug_message;
if (m_mode != MODE_ERROR) m_mode = MODE_INVALID;
+ return false;
}
-public:
- // ValidationState is abstract. Have a pure virtual destructor.
- virtual ~ValidationState() = 0;
-
- ValidationState() : m_mode(MODE_VALID) {}
bool Error(const std::string& reject_reason)
{
if (m_mode == MODE_VALID)
@@ -106,40 +106,25 @@ public:
bool IsValid() const { return m_mode == MODE_VALID; }
bool IsInvalid() const { return m_mode == MODE_INVALID; }
bool IsError() const { return m_mode == MODE_ERROR; }
+ Result GetResult() const { return m_result; }
std::string GetRejectReason() const { return m_reject_reason; }
std::string GetDebugMessage() const { return m_debug_message; }
-};
+ std::string ToString() const
+ {
+ if (IsValid()) {
+ return "Valid";
+ }
-inline ValidationState::~ValidationState() {};
+ if (!m_debug_message.empty()) {
+ return m_reject_reason + ", " + m_debug_message;
+ }
-class TxValidationState : public ValidationState {
-private:
- TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET;
-public:
- bool Invalid(TxValidationResult result,
- const std::string &reject_reason="",
- const std::string &debug_message="")
- {
- m_result = result;
- ValidationState::Invalid(reject_reason, debug_message);
- return false;
+ return m_reject_reason;
}
- TxValidationResult GetResult() const { return m_result; }
};
-class BlockValidationState : public ValidationState {
-private:
- BlockValidationResult m_result = BlockValidationResult::BLOCK_RESULT_UNSET;
-public:
- bool Invalid(BlockValidationResult result,
- const std::string &reject_reason="",
- const std::string &debug_message="") {
- m_result = result;
- ValidationState::Invalid(reject_reason, debug_message);
- return false;
- }
- BlockValidationResult GetResult() const { return m_result; }
-};
+class TxValidationState : public ValidationState<TxValidationResult> {};
+class BlockValidationState : public ValidationState<BlockValidationResult> {};
// These implement the weight = (stripped_size * 4) + witness_size formula,
// using only serialization with and without witness data. As witness_size
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index ff75789223..4d49736140 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -174,7 +174,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
/* Deter brute-forcing
If this results in a DoS the user really
shouldn't have their RPC port exposed. */
- MilliSleep(250);
+ UninterruptibleSleep(std::chrono::milliseconds{250});
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 0e13b85806..11d73b7c9a 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -236,7 +236,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
hreq->GetPeer().ToString());
- hreq->WriteReply(HTTP_BADMETHOD);
+ hreq->WriteReply(HTTP_BAD_METHOD);
return;
}
@@ -268,10 +268,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
item.release(); /* if true, queue took ownership */
else {
LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
- item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
+ item->req->WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Work queue depth exceeded");
}
} else {
- hreq->WriteReply(HTTP_NOTFOUND);
+ hreq->WriteReply(HTTP_NOT_FOUND);
}
}
@@ -519,7 +519,7 @@ HTTPRequest::~HTTPRequest()
if (!replySent) {
// Keep track of whether reply was sent to avoid request leaks
LogPrintf("%s: Unhandled request\n", __func__);
- WriteReply(HTTP_INTERNAL, "Unhandled request");
+ WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Unhandled request");
}
// evhttpd cleans up the request, as long as a reply was sent.
}
diff --git a/src/index/base.cpp b/src/index/base.cpp
index dcb8e99fc1..3f3b301246 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -270,7 +270,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
Commit();
}
-bool BaseIndex::BlockUntilSyncedToCurrentChain()
+bool BaseIndex::BlockUntilSyncedToCurrentChain() const
{
AssertLockNotHeld(cs_main);
diff --git a/src/index/base.h b/src/index/base.h
index d0088d9c9a..fb30aa7a0e 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -97,7 +97,7 @@ public:
/// sync once and only needs to process blocks in the ValidationInterface
/// queue. If the index is catching up from far behind, this method does
/// not block and immediately returns false.
- bool BlockUntilSyncedToCurrentChain();
+ bool BlockUntilSyncedToCurrentChain() const;
void Interrupt();
diff --git a/src/indirectmap.h b/src/indirectmap.h
index 76da4a6bd5..417d500bd4 100644
--- a/src/indirectmap.h
+++ b/src/indirectmap.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_INDIRECTMAP_H
#define BITCOIN_INDIRECTMAP_H
+#include <map>
+
template <class T>
struct DereferencingComparator { bool operator()(const T a, const T b) const { return *a < *b; } };
diff --git a/src/init.cpp b/src/init.cpp
index 24a1fd27db..a637aac4d2 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -47,12 +47,11 @@
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
+#include <util/asmap.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
-#include <util/validation.h>
-#include <util/asmap.h>
#include <validation.h>
#include <hash.h>
@@ -207,6 +206,7 @@ void Shutdown(NodeContext& node)
// After everything has been shut down, but before things get flushed, stop the
// CScheduler/checkqueue threadGroup
+ if (node.scheduler) node.scheduler->stop();
threadGroup.interrupt_all();
threadGroup.join_all();
@@ -409,6 +409,7 @@ void SetupServerArgs()
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
@@ -437,7 +438,6 @@ void SetupServerArgs()
gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
- gArgs.AddArg("-asmap=<file>", "Specify asn mapping used for bucketing of the peers. Path should be relative to the -datadir path.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -450,7 +450,7 @@ void SetupServerArgs()
gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. "
"Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), "
"noban (do not ban for misbehavior), "
- "forcerelay (relay even non-standard transactions), "
+ "forcerelay (relay transactions that are already in the mempool; implies relay), "
"relay (relay even in -blocksonly mode), "
"and mempool (allow requesting BIP35 mempool contents). "
"Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -527,7 +527,7 @@ void SetupServerArgs()
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool or violate local relay policy. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
@@ -710,7 +710,7 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
// scan for better chains in the block chain database, that are not yet connected in the active best chain
BlockValidationState state;
if (!ActivateBestChain(state, chainparams)) {
- LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state));
+ LogPrintf("Failed to connect best block (%s)\n", state.ToString());
StartShutdown();
return;
}
@@ -1419,6 +1419,31 @@ bool AppInitMain(NodeContext& node)
return InitError(ResolveErrMsg("externalip", strAddr));
}
+ // Read asmap file if configured
+ if (gArgs.IsArgSet("-asmap")) {
+ fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", ""));
+ if (asmap_path.empty()) {
+ asmap_path = DEFAULT_ASMAP_FILENAME;
+ }
+ if (!asmap_path.is_absolute()) {
+ asmap_path = GetDataDir() / asmap_path;
+ }
+ if (!fs::exists(asmap_path)) {
+ InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path));
+ return false;
+ }
+ std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
+ if (asmap.size() == 0) {
+ InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path));
+ return false;
+ }
+ const uint256 asmap_version = SerializeHash(asmap);
+ node.connman->SetAsmap(std::move(asmap));
+ LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString());
+ } else {
+ LogPrintf("Using /16 prefix for IP bucketing\n");
+ }
+
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create();
@@ -1826,25 +1851,6 @@ bool AppInitMain(NodeContext& node)
return false;
}
- // Read asmap file if configured
- if (gArgs.IsArgSet("-asmap")) {
- std::string asmap_file = gArgs.GetArg("-asmap", "");
- if (asmap_file.empty()) {
- asmap_file = DEFAULT_ASMAP_FILENAME;
- }
- const fs::path asmap_path = GetDataDir() / asmap_file;
- std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
- if (asmap.size() == 0) {
- InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path));
- return false;
- }
- const uint256 asmap_version = SerializeHash(asmap);
- node.connman->SetAsmap(std::move(asmap));
- LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString());
- } else {
- LogPrintf("Using /16 prefix for IP bucketing.\n");
- }
-
// ********************************************************* Step 13: finished
SetRPCWarmupFinished();
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index baea71d0bb..01ade56b2a 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -19,7 +19,6 @@
#include <wallet/fees.h>
#include <wallet/ismine.h>
#include <wallet/load.h>
-#include <wallet/psbtwallet.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
@@ -119,19 +118,15 @@ public:
}
bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
{
- std::unique_ptr<SigningProvider> provider = m_wallet->GetSigningProvider(script);
+ std::unique_ptr<SigningProvider> provider = m_wallet->GetSolvingProvider(script);
if (provider) {
return provider->GetPubKey(address, pub_key);
}
return false;
}
- bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) override
+ SigningResult signMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) override
{
- std::unique_ptr<SigningProvider> provider = m_wallet->GetSigningProvider(script);
- if (provider) {
- return provider->GetKey(address, key);
- }
- return false;
+ return m_wallet->SignMessage(message, pkhash, str_sig);
}
bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
bool haveWatchOnly() override
@@ -361,9 +356,9 @@ public:
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
- bool bip32derivs = false) override
+ bool bip32derivs = false) const override
{
- return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs);
+ return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs);
}
WalletBalances getBalances() override
{
@@ -468,7 +463,7 @@ public:
}
unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
- bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
+ bool canGetAddresses() const override { return m_wallet->CanGetAddresses(); }
bool IsWalletFlagSet(uint64_t flag) override { return m_wallet->IsWalletFlagSet(flag); }
OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
OutputType getDefaultChangeType() override { return m_wallet->m_default_change_type; }
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index d4280e8091..9476c9f77f 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -10,6 +10,7 @@
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
#include <ui_interface.h> // For ChangeType
+#include <util/message.h>
#include <functional>
#include <map>
@@ -84,8 +85,8 @@ public:
//! Get public key.
virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
- //! Get private key.
- virtual bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) = 0;
+ //! Sign message
+ virtual SigningResult signMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) = 0;
//! Return whether wallet has private key.
virtual bool isSpendable(const CTxDestination& dest) = 0;
@@ -196,7 +197,7 @@ public:
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
- bool bip32derivs = false) = 0;
+ bool bip32derivs = false) const = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
@@ -246,7 +247,7 @@ public:
virtual bool hdEnabled() = 0;
// Return whether the wallet is blank.
- virtual bool canGetAddresses() = 0;
+ virtual bool canGetAddresses() const = 0;
// check if a certain wallet flag is set.
virtual bool IsWalletFlagSet(uint64_t flag) = 0;
diff --git a/src/logging/timer.h b/src/logging/timer.h
index 45bfc4aa65..2b27c71080 100644
--- a/src/logging/timer.h
+++ b/src/logging/timer.h
@@ -85,7 +85,7 @@ private:
const std::string m_title{};
//! Forwarded on to LogPrint if specified - has the effect of only
- //! outputing the timing log when a particular debug= category is specified.
+ //! outputting the timing log when a particular debug= category is specified.
const BCLog::LogFlags m_log_category{};
};
diff --git a/src/memusage.h b/src/memusage.h
index 3ae9face15..24eb450465 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -6,9 +6,11 @@
#define BITCOIN_MEMUSAGE_H
#include <indirectmap.h>
+#include <prevector.h>
#include <stdlib.h>
+#include <cassert>
#include <map>
#include <memory>
#include <set>
diff --git a/src/miner.cpp b/src/miner.cpp
index 6f4e10b6ed..61d27d17c1 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -20,7 +20,6 @@
#include <timedata.h>
#include <util/moneystr.h>
#include <util/system.h>
-#include <util/validation.h>
#include <algorithm>
#include <utility>
@@ -167,7 +166,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
BlockValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
- throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
+ throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
int64_t nTime2 = GetTimeMicros();
diff --git a/src/net.cpp b/src/net.cpp
index 18fe95e675..d156450394 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -555,9 +555,9 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
}
// Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
- stats.dPingTime = (((double)nPingUsecTime) / 1e6);
- stats.dMinPing = (((double)nMinPingUsecTime) / 1e6);
- stats.dPingWait = (((double)nPingUsecWait) / 1e6);
+ stats.m_ping_usec = nPingUsecTime;
+ stats.m_min_ping_usec = nMinPingUsecTime;
+ stats.m_ping_wait_usec = nPingUsecWait;
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -718,6 +718,19 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta
return msg;
}
+void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) {
+ // create dbl-sha256 checksum
+ uint256 hash = Hash(msg.data.begin(), msg.data.end());
+
+ // create header
+ CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), msg.data.size());
+ memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE);
+
+ // serialize header
+ header.reserve(CMessageHeader::HEADER_SIZE);
+ CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0, hdr};
+}
+
size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend)
{
auto it = pnode->vSendMsg.begin();
@@ -2705,6 +2718,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
}
m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer());
}
CNode::~CNode()
@@ -2720,16 +2734,12 @@ bool CConnman::NodeFullyConnected(const CNode* pnode)
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
size_t nMessageSize = msg.data.size();
- size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE;
LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command), nMessageSize, pnode->GetId());
+ // make sure we use the appropriate network transport format
std::vector<unsigned char> serializedHeader;
- serializedHeader.reserve(CMessageHeader::HEADER_SIZE);
- uint256 hash = Hash(msg.data.data(), msg.data.data() + nMessageSize);
- CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), nMessageSize);
- memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE);
-
- CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, serializedHeader, 0, hdr};
+ pnode->m_serializer->prepareForTransport(msg, serializedHeader);
+ size_t nTotalSize = nMessageSize + serializedHeader.size();
size_t nBytesSent = 0;
{
diff --git a/src/net.h b/src/net.h
index 819947658a..975d7f15d7 100644
--- a/src/net.h
+++ b/src/net.h
@@ -596,9 +596,9 @@ public:
mapMsgCmdSize mapRecvBytesPerMsgCmd;
NetPermissionFlags m_permissionFlags;
bool m_legacyWhitelisted;
- double dPingTime;
- double dPingWait;
- double dMinPing;
+ int64_t m_ping_usec;
+ int64_t m_ping_wait_usec;
+ int64_t m_min_ping_usec;
CAmount minFeeFilter;
// Our address, as reported by the peer
std::string addrLocal;
@@ -703,12 +703,27 @@ public:
CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, int64_t time) override;
};
+/** The TransportSerializer prepares messages for the network transport
+ */
+class TransportSerializer {
+public:
+ // prepare message for transport (header construction, error-correction computation, payload encryption, etc.)
+ virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) = 0;
+ virtual ~TransportSerializer() {}
+};
+
+class V1TransportSerializer : public TransportSerializer {
+public:
+ void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) override;
+};
+
/** Information about a peer */
class CNode
{
friend class CConnman;
public:
std::unique_ptr<TransportDeserializer> m_deserializer;
+ std::unique_ptr<TransportSerializer> m_serializer;
// socket
std::atomic<ServiceFlags> nServices{NODE_NONE};
diff --git a/src/net_permissions.h b/src/net_permissions.h
index a06d2f544d..ad74848347 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -15,7 +15,7 @@ enum NetPermissionFlags
PF_BLOOMFILTER = (1U << 1),
// Relay and accept transactions from this peer, even if -blocksonly is true
PF_RELAY = (1U << 3),
- // Always relay transactions from this peer, even if already in mempool or rejected from policy
+ // Always relay transactions from this peer, even if already in mempool
// Keep parameter interaction: forcerelay implies relay
PF_FORCERELAY = (1U << 2) | PF_RELAY,
// Can't be banned for misbehavior
@@ -59,4 +59,4 @@ public:
CSubNet m_subnet;
};
-#endif // BITCOIN_NET_PERMISSIONS_H \ No newline at end of file
+#endif // BITCOIN_NET_PERMISSIONS_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index cf4aee0647..e4df9fb90e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -26,7 +26,6 @@
#include <txmempool.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <util/validation.h>
#include <memory>
#include <typeinfo>
@@ -987,15 +986,6 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
}
/**
- * Returns true if the given validation state result may result in a peer
- * banning/disconnecting us. We use this to determine which unaccepted
- * transactions from a whitelisted peer that we can safely relay.
- */
-static bool TxRelayMayResultInDisconnect(const TxValidationState& state) {
- return state.GetResult() == TxValidationResult::TX_CONSENSUS;
-}
-
-/**
* Potentially ban a node based on the contents of a BlockValidationState object
*
* @param[in] via_compact_block this bool is passed in because net_processing should
@@ -1064,10 +1054,9 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
* Potentially ban a node based on the contents of a TxValidationState object
*
* @return Returns true if the peer was punished (probably disconnected)
- *
- * Changes here may need to be reflected in TxRelayMayResultInDisconnect().
*/
-static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "") {
+static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "")
+{
switch (state.GetResult()) {
case TxValidationResult::TX_RESULT_UNSET:
break;
@@ -1095,11 +1084,6 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
}
-
-
-
-
-
//////////////////////////////////////////////////////////////////////////////
//
// blockchain -> download logic notification
@@ -1447,7 +1431,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (need_activate_chain) {
BlockValidationState state;
if (!ActivateBestChain(state, Params(), a_recent_block)) {
- LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
+ LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -1924,7 +1908,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
}
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
+bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -2357,7 +2341,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
BlockValidationState state;
if (!ActivateBestChain(state, Params(), a_recent_block)) {
- LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
+ LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -2615,14 +2599,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pfrom->HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
- // if they were already in the mempool or rejected from it due
- // to policy, allowing the node to function as a gateway for
+ // if they were already in the mempool,
+ // allowing the node to function as a gateway for
// nodes hidden behind it.
- //
- // Never relay transactions that might result in being
- // disconnected (or banned).
- if (state.IsInvalid() && TxRelayMayResultInDisconnect(state)) {
- LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
+ if (!mempool.exists(tx.GetHash())) {
+ LogPrintf("Not relaying non-mempool transaction %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
} else {
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
RelayTransaction(tx.GetHash(), *connman);
@@ -2654,7 +2635,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
{
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
- FormatStateMessage(state));
+ state.ToString());
MaybePunishNodeForTx(pfrom->GetId(), state);
}
return true;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 1cac57a817..228caf74a9 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -210,6 +210,11 @@ bool CNetAddr::IsRFC7343() const
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20);
}
+bool CNetAddr::IsHeNet() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70);
+}
+
/**
* @returns Whether or not this is a dummy address that maps an onion address
* into IPv6.
@@ -516,7 +521,7 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
} else if (IsTor()) {
nStartByte = 6;
nBits = 4;
- } else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) {
+ } else if (IsHeNet()) {
// for he.net, use /36 groups
nBits = 36;
} else {
diff --git a/src/netaddress.h b/src/netaddress.h
index b300b709f3..b7381c1eb4 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -45,7 +45,6 @@ class CNetAddr
*/
void SetRaw(Network network, const uint8_t *data);
- public:
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
@@ -66,6 +65,7 @@ class CNetAddr
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
+ bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
bool IsTor() const;
bool IsLocal() const;
bool IsRoutable() const;
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index 8678b33cf3..5b16035f7d 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -18,9 +18,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTAnalysis result;
bool calc_fee = true;
- bool all_final = true;
- bool only_missing_sigs = true;
- bool only_missing_final = false;
+
CAmount in_amt = 0;
result.inputs.resize(psbtx.tx->vin.size());
@@ -29,6 +27,9 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
PSBTInputAnalysis& input_analysis = result.inputs[i];
+ // We set next role here and ratchet backwards as required
+ input_analysis.next = PSBTRole::EXTRACTOR;
+
// Check for a UTXO
CTxOut utxo;
if (psbtx.GetInputUTXO(utxo, i)) {
@@ -57,7 +58,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Check if it is final
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
input_analysis.is_final = false;
- all_final = false;
// Figure out what is missing
SignatureData outdata;
@@ -74,11 +74,9 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
input_analysis.next = PSBTRole::SIGNER;
} else {
- only_missing_sigs = false;
input_analysis.next = PSBTRole::UPDATER;
}
} else {
- only_missing_final = true;
input_analysis.next = PSBTRole::FINALIZER;
}
} else if (!utxo.IsNull()){
@@ -86,10 +84,14 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
}
}
- if (all_final) {
- only_missing_sigs = false;
- result.next = PSBTRole::EXTRACTOR;
+ // Calculate next role for PSBT by grabbing "minimum" PSBTInput next role
+ result.next = PSBTRole::EXTRACTOR;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInputAnalysis& input_analysis = result.inputs[i];
+ result.next = std::min(result.next, input_analysis.next);
}
+ assert(result.next > PSBTRole::CREATOR);
+
if (calc_fee) {
// Get the output amount
CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
@@ -139,17 +141,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
result.estimated_feerate = feerate;
}
- if (only_missing_sigs) {
- result.next = PSBTRole::SIGNER;
- } else if (only_missing_final) {
- result.next = PSBTRole::FINALIZER;
- } else if (all_final) {
- result.next = PSBTRole::EXTRACTOR;
- } else {
- result.next = PSBTRole::UPDATER;
- }
- } else {
- result.next = PSBTRole::UPDATER;
}
return result;
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 1bb9b88d00..201406ce3b 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -7,7 +7,6 @@
#include <net.h>
#include <net_processing.h>
#include <node/context.h>
-#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <node/transaction.h>
@@ -41,7 +40,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
TxValidationState state;
if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx),
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
- err_string = FormatStateMessage(state);
+ err_string = state.ToString();
if (state.IsInvalid()) {
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
return TransactionError::MISSING_INPUTS;
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 567eecb5c9..71b5cba01c 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -82,30 +82,22 @@ CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore,
{
// Add script to keystore
keystore.AddCScript(script);
- ScriptHash sh(script);
// Note that scripts over 520 bytes are not yet supported.
switch (type) {
case OutputType::LEGACY:
- keystore.AddCScript(GetScriptForDestination(sh));
- return sh;
+ return ScriptHash(script);
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
CTxDestination witdest = WitnessV0ScriptHash(script);
CScript witprog = GetScriptForDestination(witdest);
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(keystore, witprog)) {
- // Since the wsh is invalid, add and return the sh instead.
- keystore.AddCScript(GetScriptForDestination(sh));
- return sh;
- }
+ if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
keystore.AddCScript(witprog);
if (type == OutputType::BECH32) {
return witdest;
} else {
- ScriptHash sh_w = ScriptHash(witprog);
- keystore.AddCScript(GetScriptForDestination(sh_w));
- return sh_w;
+ return ScriptHash(witprog);
}
}
default: assert(false);
diff --git a/src/prevector.h b/src/prevector.h
index 09debedc4f..6d690e7f96 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -424,15 +424,20 @@ public:
return first;
}
- void push_back(const T& value) {
+ template<typename... Args>
+ void emplace_back(Args&&... args) {
size_type new_size = size() + 1;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
- new(item_ptr(size())) T(value);
+ new(item_ptr(size())) T(std::forward<Args>(args)...);
_size++;
}
+ void push_back(const T& value) {
+ emplace_back(value);
+ }
+
void pop_back() {
erase(end() - 1, end());
}
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 911322092c..98dde1656a 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -773,9 +773,9 @@ QString formatServicesStr(quint64 mask)
return QObject::tr("None");
}
-QString formatPingTime(double dPingTime)
+QString formatPingTime(int64_t ping_usec)
{
- return (dPingTime == std::numeric_limits<int64_t>::max()/1e6 || dPingTime == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10));
+ return (ping_usec == std::numeric_limits<int64_t>::max() || ping_usec == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(ping_usec / 1000), 10));
}
QString formatTimeOffset(int64_t nTimeOffset)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 05e73cc5f0..e571262443 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -202,8 +202,8 @@ namespace GUIUtil
/* Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(quint64 mask);
- /* Format a CNodeCombinedStats.dPingTime into a user-readable string or display N/A, if 0*/
- QString formatPingTime(double dPingTime);
+ /* Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0*/
+ QString formatPingTime(int64_t ping_usec);
/* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */
QString formatTimeOffset(int64_t nTimeOffset);
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index a497f58b16..a1fc791536 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -32,7 +32,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
case PeerTableModel::Subversion:
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
case PeerTableModel::Ping:
- return pLeft->dMinPing < pRight->dMinPing;
+ return pLeft->m_min_ping_usec < pRight->m_min_ping_usec;
case PeerTableModel::Sent:
return pLeft->nSendBytes < pRight->nSendBytes;
case PeerTableModel::Received:
@@ -161,7 +161,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
case Subversion:
return QString::fromStdString(rec->nodeStats.cleanSubVer);
case Ping:
- return GUIUtil::formatPingTime(rec->nodeStats.dMinPing);
+ return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec);
case Sent:
return GUIUtil::formatBytes(rec->nodeStats.nSendBytes);
case Received:
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index e1f783b0e5..b82ab9ffe8 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1109,9 +1109,9 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
- ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
- ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
- ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing));
+ ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_usec));
+ ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_wait_usec));
+ ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion)));
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index cc01aafb23..4ddee513a1 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -26,7 +26,6 @@
#include <ui_interface.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
-#include <wallet/psbtwallet.h>
#include <wallet/wallet.h>
#include <QFontMetrics>
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 5f2836cc75..4552753bf6 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -11,7 +11,7 @@
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <util/validation.h> // For strMessageMagic
+#include <util/message.h> // For MessageSign(), MessageVerify()
#include <wallet/wallet.h>
#include <vector>
@@ -133,30 +133,34 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
return;
}
- CKey key;
- if (!model->wallet().getPrivKey(GetScriptForDestination(destination), CKeyID(*pkhash), key))
- {
- ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
- return;
+ const std::string& message = ui->messageIn_SM->document()->toPlainText().toStdString();
+ std::string signature;
+ SigningResult res = model->wallet().signMessage(message, *pkhash, signature);
+
+ QString error;
+ switch (res) {
+ case SigningResult::OK:
+ error = tr("No error");
+ break;
+ case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
+ error = tr("Private key for the entered address is not available.");
+ break;
+ case SigningResult::SIGNING_FAILED:
+ error = tr("Message signing failed.");
+ break;
+ // no default case, so the compiler can warn about missing cases
}
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << ui->messageIn_SM->document()->toPlainText().toStdString();
-
- std::vector<unsigned char> vchSig;
- if (!key.SignCompact(ss.GetHash(), vchSig))
- {
+ if (res != SigningResult::OK) {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signing failed.") + QString("</nobr>"));
+ ui->statusLabel_SM->setText(QString("<nobr>") + error + QString("</nobr>"));
return;
}
ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }");
ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>"));
- ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(vchSig.data(), vchSig.size())));
+ ui->signatureOut_SM->setText(QString::fromStdString(signature));
}
void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked()
@@ -189,51 +193,57 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
{
- CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString());
- if (!IsValidDestination(destination)) {
+ const std::string& address = ui->addressIn_VM->text().toStdString();
+ const std::string& signature = ui->signatureIn_VM->text().toStdString();
+ const std::string& message = ui->messageIn_VM->document()->toPlainText().toStdString();
+
+ const auto result = MessageVerify(address, signature, message);
+
+ if (result == MessageVerificationResult::OK) {
+ ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }");
+ } else {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
- return;
}
- if (!boost::get<PKHash>(&destination)) {
+
+ switch (result) {
+ case MessageVerificationResult::OK:
+ ui->statusLabel_VM->setText(
+ QString("<nobr>") + tr("Message verified.") + QString("</nobr>")
+ );
+ return;
+ case MessageVerificationResult::ERR_INVALID_ADDRESS:
+ ui->statusLabel_VM->setText(
+ tr("The entered address is invalid.") + QString(" ") +
+ tr("Please check the address and try again.")
+ );
+ return;
+ case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
ui->addressIn_VM->setValid(false);
- ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
+ ui->statusLabel_VM->setText(
+ tr("The entered address does not refer to a key.") + QString(" ") +
+ tr("Please check the address and try again.")
+ );
return;
- }
-
- bool fInvalid = false;
- std::vector<unsigned char> vchSig = DecodeBase64(ui->signatureIn_VM->text().toStdString().c_str(), &fInvalid);
-
- if (fInvalid)
- {
+ case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
ui->signatureIn_VM->setValid(false);
- ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_VM->setText(tr("The signature could not be decoded.") + QString(" ") + tr("Please check the signature and try again."));
+ ui->statusLabel_VM->setText(
+ tr("The signature could not be decoded.") + QString(" ") +
+ tr("Please check the signature and try again.")
+ );
return;
- }
-
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << ui->messageIn_VM->document()->toPlainText().toStdString();
-
- CPubKey pubkey;
- if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
- {
+ case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
ui->signatureIn_VM->setValid(false);
- ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_VM->setText(tr("The signature did not match the message digest.") + QString(" ") + tr("Please check the signature and try again."));
+ ui->statusLabel_VM->setText(
+ tr("The signature did not match the message digest.") + QString(" ") +
+ tr("Please check the signature and try again.")
+ );
return;
- }
-
- if (!(CTxDestination(PKHash(pubkey)) == destination)) {
- ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
+ case MessageVerificationResult::ERR_NOT_SIGNED:
+ ui->statusLabel_VM->setText(
+ QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")
+ );
return;
}
-
- ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }");
- ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verified.") + QString("</nobr>"));
}
void SignVerifyMessageDialog::on_clearButton_VM_clicked()
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index 6992c720ff..8b3d478529 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -38,11 +38,6 @@
#include <sys/utsname.h>
#include <unistd.h>
#endif
-#ifdef __MACH__
-#include <mach/clock.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#endif
#if HAVE_DECL_GETIFADDRS
#include <ifaddrs.h>
#endif
@@ -237,8 +232,6 @@ void RandAddDynamicEnv(CSHA512& hasher)
GetSystemTimeAsFileTime(&ftime);
hasher << ftime;
#else
-# ifndef __MACH__
- // On non-MacOS systems, use various clock_gettime() calls.
struct timespec ts = {};
# ifdef CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -252,18 +245,6 @@ void RandAddDynamicEnv(CSHA512& hasher)
clock_gettime(CLOCK_BOOTTIME, &ts);
hasher << ts;
# endif
-# else
- // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC,
- // and clock_get_time for CALENDAR_CLOCK as a replacement for CLOCK_REALTIME.
- hasher << mach_absolute_time();
- // From https://gist.github.com/jbenet/1087739
- clock_serv_t cclock;
- mach_timespec_t mts = {};
- if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) {
- hasher << mts;
- mach_port_deallocate(mach_task_self(), cclock);
- }
-# endif
// gettimeofday is available on all UNIX systems, but only has microsecond precision.
struct timeval tv = {};
gettimeofday(&tv, nullptr);
diff --git a/src/reverselock.h b/src/reverselock.h
deleted file mode 100644
index 9d9cc9fd77..0000000000
--- a/src/reverselock.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2015-2016 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_REVERSELOCK_H
-#define BITCOIN_REVERSELOCK_H
-
-/**
- * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
- */
-template<typename Lock>
-class reverse_lock
-{
-public:
-
- explicit reverse_lock(Lock& _lock) : lock(_lock) {
- _lock.unlock();
- _lock.swap(templock);
- }
-
- ~reverse_lock() {
- templock.lock();
- templock.swap(lock);
- }
-
-private:
- reverse_lock(reverse_lock const&);
- reverse_lock& operator=(reverse_lock const&);
-
- Lock& lock;
- Lock templock;
-};
-
-#endif // BITCOIN_REVERSELOCK_H
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 9b06aba22b..133d1b809f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -31,7 +31,6 @@
#include <undo.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -178,8 +177,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
"The genesis block has height 0.\n",
{},
RPCResult{
- "n (numeric) The current block count\n"
- },
+ RPCResult::Type::NUM, "", "The current block count"},
RPCExamples{
HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
@@ -196,8 +194,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
"\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
RPCResult{
- "\"hex\" (string) the block hash, hex-encoded\n"
- },
+ RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"},
RPCExamples{
HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
@@ -227,11 +224,11 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
- "{ (json object)\n"
- " \"hash\" : { (string) The blockhash\n"
- " \"height\" : { (numeric) Block height\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
+ {RPCResult::Type::NUM, "height", "Block height"},
+ }},
RPCExamples{
HelpExampleCli("waitfornewblock", "1000")
+ HelpExampleRpc("waitfornewblock", "1000")
@@ -267,11 +264,11 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
- "{ (json object)\n"
- " \"hash\" : { (string) The blockhash\n"
- " \"height\" : { (numeric) Block height\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
+ {RPCResult::Type::NUM, "height", "Block height"},
+ }},
RPCExamples{
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
@@ -311,11 +308,11 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
- "{ (json object)\n"
- " \"hash\" : { (string) The blockhash\n"
- " \"height\" : { (numeric) Block height\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
+ {RPCResult::Type::NUM, "height", "Block height"},
+ }},
RPCExamples{
HelpExampleCli("waitforblockheight", "\"100\", 1000")
+ HelpExampleRpc("waitforblockheight", "\"100\", 1000")
@@ -365,8 +362,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
{},
RPCResult{
- "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
- },
+ RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
RPCExamples{
HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
@@ -377,37 +373,35 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
return GetDifficulty(::ChainActive().Tip());
}
-static std::string EntryDescriptionString()
-{
- return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
- " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n"
- " size will be completely removed in v0.20.\n"
- " \"weight\" : n, (numeric) transaction weight as defined in BIP 141.\n"
- " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
- " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n"
- " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
- " \"height\" : n, (numeric) block height when transaction entered pool\n"
- " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
- " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n"
- " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n"
- " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n"
- " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n"
- " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n"
- " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
- " \"fees\" : {\n"
- " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
- " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n"
- " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n"
- " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n"
- " }\n"
- " \"depends\" : [ (json array) unconfirmed transactions used as inputs for this transaction\n"
- " \"transactionid\", (string) parent transaction id\n"
- " ... ]\n"
- " \"spentby\" : [ (json array) unconfirmed transactions spending outputs from this transaction\n"
- " \"transactionid\", (string) child transaction id\n"
- " ... ]\n"
- " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n";
-}
+static std::vector<RPCResult> MempoolEntryDescription() { return {
+ RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
+ RPCResult{RPCResult::Type::NUM, "size", "(DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n"
+ "size will be completely removed in v0.20."},
+ RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "fee", "transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)"},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", "transaction fee with fee deltas used for mining priority (DEPRECATED)"},
+ RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
+ RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
+ RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
+ RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", "modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)"},
+ RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
+ RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", "modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)"},
+ RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
+ RPCResult{RPCResult::Type::OBJ, "fees", "",
+ {
+ RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT},
+ RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT},
+ }},
+ RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
+ {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
+ RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
+ {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
+ RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
+};}
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
{
@@ -506,17 +500,17 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
{
{"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
},
- RPCResult{"for verbose = false",
- "[ (json array of string)\n"
- " \"transactionid\" (string) The transaction id\n"
- " ,...\n"
- "]\n"
- "\nResult: (for verbose = true):\n"
- "{ (json object)\n"
- " \"transactionid\" : { (json object)\n"
- + EntryDescriptionString()
- + " }, ...\n"
- "}\n"
+ {
+ RPCResult{"for verbose = false",
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "", "The transaction id"},
+ }},
+ RPCResult{"for verbose = true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()},
+ }},
},
RPCExamples{
HelpExampleCli("getrawmempool", "true")
@@ -541,18 +535,10 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
},
{
RPCResult{"for verbose = false",
- "[ (json array of strings)\n"
- " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
- " ,...\n"
- "]\n"
- },
+ RPCResult::Type::ARR, "", "",
+ {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
RPCResult{"for verbose = true",
- "{ (json object)\n"
- " \"transactionid\" : { (json object)\n"
- + EntryDescriptionString()
- + " }, ...\n"
- "}\n"
- },
+ RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()},
},
RPCExamples{
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
@@ -609,18 +595,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
},
{
RPCResult{"for verbose = false",
- "[ (json array of strings)\n"
- " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
- " ,...\n"
- "]\n"
- },
+ RPCResult::Type::ARR, "", "",
+ {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
RPCResult{"for verbose = true",
- "{ (json object)\n"
- " \"transactionid\" : { (json object)\n"
- + EntryDescriptionString()
- + " }, ...\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()},
+ }},
},
RPCExamples{
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
@@ -675,10 +656,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
},
RPCResult{
- "{ (json object)\n"
- + EntryDescriptionString()
- + "}\n"
- },
+ RPCResult::Type::OBJ_DYN, "", "", MempoolEntryDescription()},
RPCExamples{
HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
@@ -709,8 +687,7 @@ static UniValue getblockhash(const JSONRPCRequest& request)
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
},
RPCResult{
- "\"hash\" (string) The block hash\n"
- },
+ RPCResult::Type::STR_HEX, "", "The block hash"},
RPCExamples{
HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
@@ -738,27 +715,26 @@ static UniValue getblockheader(const JSONRPCRequest& request)
},
{
RPCResult{"for verbose = true",
- "{\n"
- " \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
- " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
- " \"height\" : n, (numeric) The block height or index\n"
- " \"version\" : n, (numeric) The block version\n"
- " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n"
- " \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
- " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"nonce\" : n, (numeric) The nonce\n"
- " \"bits\" : \"1d00ffff\", (string) The bits\n"
- " \"difficulty\" : x.xxx, (numeric) The difficulty\n"
- " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n"
- " \"nTx\" : n, (numeric) The number of transactions in the block.\n"
- " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
- " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
+ {RPCResult::Type::NUM, "height", "The block height or index"},
+ {RPCResult::Type::NUM, "version", "The block version"},
+ {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
+ {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
+ {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "nonce", "The nonce"},
+ {RPCResult::Type::STR_HEX, "bits", "The bits"},
+ {RPCResult::Type::NUM, "difficulty", "The difficulty"},
+ {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
+ {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ }},
RPCResult{"for verbose=false",
- "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
- },
+ RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
},
RPCExamples{
HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
@@ -840,44 +816,45 @@ static UniValue getblock(const JSONRPCRequest& request)
},
{
RPCResult{"for verbosity = 0",
- "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
- },
+ RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
RPCResult{"for verbosity = 1",
- "{\n"
- " \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
- " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
- " \"size\" : n, (numeric) The block size\n"
- " \"strippedsize\" : n, (numeric) The block size excluding witness data\n"
- " \"weight\" : n (numeric) The block weight as defined in BIP 141\n"
- " \"height\" : n, (numeric) The block height or index\n"
- " \"version\" : n, (numeric) The block version\n"
- " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n"
- " \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
- " \"tx\" : [ (array of string) The transaction ids\n"
- " \"transactionid\" (string) The transaction id\n"
- " ,...\n"
- " ],\n"
- " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"nonce\" : n, (numeric) The nonce\n"
- " \"bits\" : \"1d00ffff\", (string) The bits\n"
- " \"difficulty\" : x.xxx, (numeric) The difficulty\n"
- " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n"
- " \"nTx\" : n, (numeric) The number of transactions in the block.\n"
- " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
- " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
+ {RPCResult::Type::NUM, "size", "The block size"},
+ {RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},
+ {RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},
+ {RPCResult::Type::NUM, "height", "The block height or index"},
+ {RPCResult::Type::NUM, "version", "The block version"},
+ {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
+ {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
+ {RPCResult::Type::ARR, "tx", "The transaction ids",
+ {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
+ {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "nonce", "The nonce"},
+ {RPCResult::Type::STR_HEX, "bits", "The bits"},
+ {RPCResult::Type::NUM, "difficulty", "The difficulty"},
+ {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"},
+ {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ }},
RPCResult{"for verbosity = 2",
- "{\n"
- " ..., Same output as verbosity = 1.\n"
- " \"tx\" : [ (array of Objects) The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result.\n"
- " ,...\n"
- " ],\n"
- " ,... Same output as verbosity = 1.\n"
- "}\n"
- },
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
+ {RPCResult::Type::ARR, "tx", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
+ }},
+ }},
+ {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
+ }},
+ },
RPCExamples{
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
@@ -928,8 +905,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
},
RPCResult{
- "n (numeric) Height of the last block pruned.\n"
- },
+ RPCResult::Type::NUM, "", "Height of the last block pruned"},
RPCExamples{
HelpExampleCli("pruneblockchain", "1000")
+ HelpExampleRpc("pruneblockchain", "1000")
@@ -983,17 +959,17 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
"Note this call may take some time.\n",
{},
RPCResult{
- "{\n"
- " \"height\" : n, (numeric) The current block height (index)\n"
- " \"bestblock\" : \"hex\", (string) The hash of the block at the tip of the chain\n"
- " \"transactions\" : n, (numeric) The number of transactions with unspent outputs\n"
- " \"txouts\" : n, (numeric) The number of unspent transaction outputs\n"
- " \"bogosize\" : n, (numeric) A meaningless metric for UTXO set size\n"
- " \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
- " \"disk_size\" : n, (numeric) The estimated size of the chainstate on disk\n"
- " \"total_amount\" : x.xxx (numeric) The total amount\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "height", "The current block height (index)"},
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
+ {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
+ {RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"},
+ {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
+ {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
+ }},
RPCExamples{
HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
@@ -1031,23 +1007,22 @@ UniValue gettxout(const JSONRPCRequest& request)
{"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
},
RPCResult{
- "{\n"
- " \"bestblock\" : \"hash\", (string) The hash of the block at the tip of the chain\n"
- " \"confirmations\" : n, (numeric) The number of confirmations\n"
- " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n"
- " \"scriptPubKey\" : { (json object)\n"
- " \"asm\" : \"code\", (string) \n"
- " \"hex\" : \"hex\", (string) \n"
- " \"reqSigs\" : n, (numeric) Number of required signatures\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n"
- " \"addresses\" : [ (array of string) array of bitcoin addresses\n"
- " \"address\" (string) bitcoin address\n"
- " ,...\n"
- " ]\n"
- " },\n"
- " \"coinbase\" : true|false (boolean) Coinbase or not\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR_HEX, "asm", ""},
+ {RPCResult::Type::STR_HEX, "hex", ""},
+ {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
+ {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
+ {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses",
+ {{RPCResult::Type::STR, "address", "bitcoin address"}}},
+ }},
+ {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
+ }},
RPCExamples{
"\nGet unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
@@ -1112,8 +1087,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
{"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."},
},
RPCResult{
- "true|false (boolean) Verified or not\n"
- },
+ RPCResult::Type::BOOL, "", "Verified or not"},
RPCExamples{
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
@@ -1203,45 +1177,49 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
"Returns an object containing various state info regarding blockchain processing.\n",
{},
RPCResult{
- "{\n"
- " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n"
- " \"blocks\" : xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n"
- " \"headers\" : xxxxxx, (numeric) the current number of headers we have validated\n"
- " \"bestblockhash\" : \"...\", (string) the hash of the currently best block\n"
- " \"difficulty\" : xxxxxx, (numeric) the current difficulty\n"
- " \"mediantime\" : xxxxxx, (numeric) median time for the current best block\n"
- " \"verificationprogress\" : xxxx, (numeric) estimate of verification progress [0..1]\n"
- " \"initialblockdownload\" : xxxx, (boolean) (debug information) estimate of whether this node is in Initial Block Download mode.\n"
- " \"chainwork\" : \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
- " \"size_on_disk\" : xxxxxx, (numeric) the estimated size of the block and undo files on disk\n"
- " \"pruned\" : xx, (boolean) if the blocks are subject to pruning\n"
- " \"pruneheight\" : xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
- " \"automatic_pruning\" : xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
- " \"prune_target_size\" : xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
- " \"softforks\" : { (json object) status of softforks\n"
- " \"xxxx\" : { (string) name of the softfork\n"
- " \"type\" : \"xxxx\", (string) one of \"buried\", \"bip9\"\n"
- " \"bip9\": { (json object) status of bip9 softforks (only for \"bip9\" type)\n"
- " \"status\" : \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
- " \"bit\" : xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
- " \"start_time\" : xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\" : xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
- " \"since\" : xx, (numeric) height of the first block to which the status applies\n"
- " \"statistics\" : { (json object) numeric statistics about BIP9 signalling for a softfork\n"
- " \"period\" : xx, (numeric) the length in blocks of the BIP9 signalling period \n"
- " \"threshold\" : xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
- " \"elapsed\" : xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
- " \"count\" : xx, (numeric) the number of blocks with the version bit set in the current period \n"
- " \"possible\" : xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
- " }\n"
- " },\n"
- " \"height\" : \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n"
- " \"active\" : xx, (boolean) true if the rules are enforced for the mempool and the next block\n"
- " }\n"
- " }\n"
- " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"},
+ {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
+ {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
+ {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
+ {RPCResult::Type::NUM, "difficulty", "the current difficulty"},
+ {RPCResult::Type::NUM, "mediantime", "median time for the current best block"},
+ {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
+ {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
+ {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
+ {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
+ {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
+ {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"},
+ {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"},
+ {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"},
+ {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks",
+ {
+ {RPCResult::Type::OBJ, "xxxx", "name of the softfork",
+ {
+ {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
+ {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)",
+ {
+ {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
+ {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"},
+ {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
+ {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
+ {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
+ {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork",
+ {
+ {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
+ {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"},
+ {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
+ {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
+ {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"},
+ }},
+ }},
+ {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
+ }},
+ }},
+ {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ }},
RPCExamples{
HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "")
@@ -1316,27 +1294,20 @@ static UniValue getchaintips(const JSONRPCRequest& request)
" including the main chain as well as orphaned branches.\n",
{},
RPCResult{
- "[\n"
- " {\n"
- " \"height\" : xxxx, (numeric) height of the chain tip\n"
- " \"hash\" : \"xxxx\", (string) block hash of the tip\n"
- " \"branchlen\" : 0 (numeric) zero for main chain\n"
- " \"status\" : \"active\" (string) \"active\" for the main chain\n"
- " },\n"
- " {\n"
- " \"height\" : xxxx,\n"
- " \"hash\" : \"xxxx\",\n"
- " \"branchlen\" : 1 (numeric) length of branch connecting the tip to the main chain\n"
- " \"status\" : \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n"
- " }\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {{RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "height", "height of the chain tip"},
+ {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"},
+ {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"},
+ {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n"
"Possible values for status:\n"
"1. \"invalid\" This branch contains at least one invalid block\n"
"2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n"
"3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n"
"4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n"
- "5. \"active\" This is the tip of the active main chain, which is certainly valid\n"
- },
+ "5. \"active\" This is the tip of the active main chain, which is certainly valid"},
+ }}}},
RPCExamples{
HelpExampleCli("getchaintips", "")
+ HelpExampleRpc("getchaintips", "")
@@ -1436,16 +1407,16 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
"\nReturns details on the active state of the TX memory pool.\n",
{},
RPCResult{
- "{\n"
- " \"loaded\" : true|false (boolean) True if the mempool is fully loaded\n"
- " \"size\" : xxxxx, (numeric) Current tx count\n"
- " \"bytes\" : xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
- " \"usage\" : xxxxx, (numeric) Total memory usage for the mempool\n"
- " \"maxmempool\" : xxxxx, (numeric) Maximum memory usage for the mempool\n"
- " \"mempoolminfee\" : xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n"
- " \"minrelaytxfee\" : xxxxx (numeric) Current minimum relay fee for transactions\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"},
+ {RPCResult::Type::NUM, "size", "Current tx count"},
+ {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
+ {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
+ {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
+ {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
+ {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
+ }},
RPCExamples{
HelpExampleCli("getmempoolinfo", "")
+ HelpExampleRpc("getmempoolinfo", "")
@@ -1486,7 +1457,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
}
return NullUniValue;
@@ -1524,7 +1495,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
}
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
}
return NullUniValue;
@@ -1561,7 +1532,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
ActivateBestChain(state, Params());
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
+ throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
}
return NullUniValue;
@@ -1576,17 +1547,17 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
{"blockhash", RPCArg::Type::STR_HEX, /* default */ "chain tip", "The hash of the block that ends the window."},
},
RPCResult{
- "{\n"
- " \"time\" : xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"txcount\" : xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
- " \"window_final_block_hash\" : \"...\", (string) The hash of the final block in the window.\n"
- " \"window_final_block_height\" : xxxxx, (numeric) The height of the final block in the window.\n"
- " \"window_block_count\" : xxxxx, (numeric) Size of the window in number of blocks.\n"
- " \"window_tx_count\" : xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n"
- " \"window_interval\" : xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n"
- " \"txrate\" : x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"},
+ {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
+ {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
+ {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
+ {RPCResult::Type::NUM, "window_tx_count", "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"},
+ {RPCResult::Type::NUM, "window_interval", "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
+ {RPCResult::Type::NUM, "txrate", "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"},
+ }},
RPCExamples{
HelpExampleCli("getchaintxstats", "")
+ HelpExampleRpc("getchaintxstats", "2016")
@@ -1715,44 +1686,45 @@ static UniValue getblockstats(const JSONRPCRequest& request)
"stats"},
},
RPCResult{
- "{ (json object)\n"
- " \"avgfee\" : xxxxx, (numeric) Average fee in the block\n"
- " \"avgfeerate\" : xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n"
- " \"avgtxsize\" : xxxxx, (numeric) Average transaction size\n"
- " \"blockhash\" : xxxxx, (string) The block hash (to check for potential reorgs)\n"
- " \"feerate_percentiles\" : [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n"
- " \"10th_percentile_feerate\", (numeric) The 10th percentile feerate\n"
- " \"25th_percentile_feerate\", (numeric) The 25th percentile feerate\n"
- " \"50th_percentile_feerate\", (numeric) The 50th percentile feerate\n"
- " \"75th_percentile_feerate\", (numeric) The 75th percentile feerate\n"
- " \"90th_percentile_feerate\", (numeric) The 90th percentile feerate\n"
- " ],\n"
- " \"height\" : xxxxx, (numeric) The height of the block\n"
- " \"ins\" : xxxxx, (numeric) The number of inputs (excluding coinbase)\n"
- " \"maxfee\" : xxxxx, (numeric) Maximum fee in the block\n"
- " \"maxfeerate\" : xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n"
- " \"maxtxsize\" : xxxxx, (numeric) Maximum transaction size\n"
- " \"medianfee\" : xxxxx, (numeric) Truncated median fee in the block\n"
- " \"mediantime\" : xxxxx, (numeric) The block median time past\n"
- " \"mediantxsize\" : xxxxx, (numeric) Truncated median transaction size\n"
- " \"minfee\" : xxxxx, (numeric) Minimum fee in the block\n"
- " \"minfeerate\" : xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n"
- " \"mintxsize\" : xxxxx, (numeric) Minimum transaction size\n"
- " \"outs\" : xxxxx, (numeric) The number of outputs\n"
- " \"subsidy\" : xxxxx, (numeric) The block subsidy\n"
- " \"swtotal_size\" : xxxxx, (numeric) Total size of all segwit transactions\n"
- " \"swtotal_weight\" : xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n"
- " \"swtxs\" : xxxxx, (numeric) The number of segwit transactions\n"
- " \"time\" : xxxxx, (numeric) The block time\n"
- " \"total_out\" : xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n"
- " \"total_size\" : xxxxx, (numeric) Total size of all non-coinbase transactions\n"
- " \"total_weight\" : xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n"
- " \"totalfee\" : xxxxx, (numeric) The fee total\n"
- " \"txs\" : xxxxx, (numeric) The number of transactions (excluding coinbase)\n"
- " \"utxo_increase\" : xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n"
- " \"utxo_size_inc\" : xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "avgfee", "Average fee in the block"},
+ {RPCResult::Type::NUM, "avgfeerate", "Average feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "avgtxsize", "Average transaction size"},
+ {RPCResult::Type::STR_HEX, "blockhash", "The block hash (to check for potential reorgs)"},
+ {RPCResult::Type::ARR_FIXED, "feerate_percentiles", "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
+ {
+ {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
+ {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
+ {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},
+ {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
+ {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
+ }},
+ {RPCResult::Type::NUM, "height", "The height of the block"},
+ {RPCResult::Type::NUM, "ins", "The number of inputs (excluding coinbase)"},
+ {RPCResult::Type::NUM, "maxfee", "Maximum fee in the block"},
+ {RPCResult::Type::NUM, "maxfeerate", "Maximum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "maxtxsize", "Maximum transaction size"},
+ {RPCResult::Type::NUM, "medianfee", "Truncated median fee in the block"},
+ {RPCResult::Type::NUM, "mediantime", "The block median time past"},
+ {RPCResult::Type::NUM, "mediantxsize", "Truncated median transaction size"},
+ {RPCResult::Type::NUM, "minfee", "Minimum fee in the block"},
+ {RPCResult::Type::NUM, "minfeerate", "Minimum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "mintxsize", "Minimum transaction size"},
+ {RPCResult::Type::NUM, "outs", "The number of outputs"},
+ {RPCResult::Type::NUM, "subsidy", "The block subsidy"},
+ {RPCResult::Type::NUM, "swtotal_size", "Total size of all segwit transactions"},
+ {RPCResult::Type::NUM, "swtotal_weight", "Total weight of all segwit transactions divided by segwit scale factor (4)"},
+ {RPCResult::Type::NUM, "swtxs", "The number of segwit transactions"},
+ {RPCResult::Type::NUM, "time", "The block time"},
+ {RPCResult::Type::NUM, "total_out", "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
+ {RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"},
+ {RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions divided by segwit scale factor (4)"},
+ {RPCResult::Type::NUM, "totalfee", "The fee total"},
+ {RPCResult::Type::NUM, "txs", "The number of transactions (excluding coinbase)"},
+ {RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"},
+ {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
+ }},
RPCExamples{
HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
@@ -2075,24 +2047,26 @@ UniValue scantxoutset(const JSONRPCRequest& request)
"[scanobjects,...]"},
},
RPCResult{
- "{\n"
- " \"success\" : true|false, (boolean) Whether the scan was completed\n"
- " \"txouts\" : n, (numeric) The number of unspent transaction outputs scanned\n"
- " \"height\" : n, (numeric) The current block height (index)\n"
- " \"bestblock\" : \"hex\", (string) The hash of the block at the tip of the chain\n"
- " \"unspents\" : [\n"
- " {\n"
- " \"txid\" : \"hash\", (string) The transaction id\n"
- " \"vout\" : n, (numeric) The vout value\n"
- " \"scriptPubKey\" : \"script\", (string) The script key\n"
- " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
- " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
- " \"height\" : n, (numeric) Height of the unspent transaction output\n"
- " }\n"
- " ,...],\n"
- " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
- "]\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
+ {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
+ {RPCResult::Type::NUM, "height", "The current block height (index)"},
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::ARR, "unspents", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
+ {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
+ {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
+ }},
+ }},
+ {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
+ }},
RPCExamples{""},
}.Check(request);
@@ -2198,11 +2172,11 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
{"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
},
RPCResult{
- "{\n"
- " \"filter\" : (string) the hex-encoded filter data\n"
- " \"header\" : (string) the hex-encoded filter header\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"},
+ {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"},
+ }},
RPCExamples{
HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
}
@@ -2283,12 +2257,13 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
"path to the output file. If relative, will be prefixed by datadir."},
},
RPCResult{
- "{\n"
- " \"coins_written\" : n, (numeric) the number of coins written in the snapshot\n"
- " \"base_hash\" : \"...\", (string) the hash of the base of the snapshot\n"
- " \"base_height\" : n, (string) the height of the base of the snapshot\n"
- " \"path\" : \"...\" (string) the absolute path that the snapshot was written to\n"
- "]\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"},
+ {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"},
+ {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
+ {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"},
+ }
},
RPCExamples{
HelpExampleCli("dumptxoutset", "utxo.dat")
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index ab5d830b2a..9245d09b89 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -28,7 +28,6 @@
#include <util/fees.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -90,8 +89,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
{"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."},
},
RPCResult{
- "x (numeric) Hashes per second estimated\n"
- },
+ RPCResult::Type::NUM, "", "Hashes per second estimated"},
RPCExamples{
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
@@ -154,7 +152,11 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
{"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
},
RPCResult{
- "[ blockhashes ] (json array) hashes of blocks generated\n"},
+ RPCResult::Type::ARR, "", "hashes of blocks generated",
+ {
+ {RPCResult::Type::STR_HEX, "", "blockhash"},
+ }
+ },
RPCExamples{
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
}
@@ -196,8 +198,10 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
},
RPCResult{
- "[ blockhashes ] (json array) hashes of blocks generated\n"
- },
+ RPCResult::Type::ARR, "", "hashes of blocks generated",
+ {
+ {RPCResult::Type::STR_HEX, "", "blockhash"},
+ }},
RPCExamples{
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
@@ -230,17 +234,17 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
- "{\n"
- " \"blocks\" : nnn, (numeric) The current block\n"
- " \"currentblockweight\" : nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n"
- " \"currentblocktx\" : nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n"
- " \"difficulty\" : xxx.xxxxx (numeric) The current difficulty\n"
- " \"networkhashps\" : nnn, (numeric) The network hashes per second\n"
- " \"pooledtx\" : n (numeric) The size of the mempool\n"
- " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n"
- " \"warnings\" : \"...\" (string) any network and blockchain warnings\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "blocks", "The current block"},
+ {RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"},
+ {RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"},
+ {RPCResult::Type::NUM, "difficulty", "The current difficulty"},
+ {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"},
+ {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
+ {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"},
+ {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ }},
RPCExamples{
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
@@ -278,8 +282,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
" considers the transaction as it would have paid a higher (or lower) fee."},
},
RPCResult{
- "true (boolean) Returns true\n"
- },
+ RPCResult::Type::BOOL, "", "Returns true"},
RPCExamples{
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
@@ -307,7 +310,7 @@ static UniValue BIP22ValidationResult(const BlockValidationState& state)
return NullUniValue;
if (state.IsError())
- throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
+ throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
if (state.IsInvalid())
{
std::string strRejectReason = state.GetRejectReason();
@@ -356,48 +359,58 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
"\"template_request\""},
},
RPCResult{
- "{\n"
- " \"version\" : n, (numeric) The preferred block version\n"
- " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n"
- " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n"
- " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n"
- " ,...\n"
- " },\n"
- " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n"
- " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
- " \"transactions\" : [ (json array) contents of non-coinbase transactions that should be included in the next block\n"
- " {\n"
- " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
- " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n"
- " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n"
- " \"depends\" : [ (json array) array of numbers \n"
- " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
- " ,...\n"
- " ],\n"
- " \"fee\" : n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
- " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
- " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n"
- " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n"
- " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
- " \"target\" : \"xxxx\", (string) The hash target\n"
- " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
- " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
- " ,...\n"
- " ],\n"
- " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n"
- " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
- " \"sizelimit\" : n, (numeric) limit of block size\n"
- " \"weightlimit\" : n, (numeric) limit of block weight\n"
- " \"curtime\" : ttt, (numeric) current timestamp in " + UNIX_EPOCH_TIME + "\n"
- " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n"
- " \"height\" : n (numeric) The height of the next block\n"
- "}\n"
- },
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "version", "The preferred block version"},
+ {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
+ {
+ {RPCResult::Type::STR, "", "rulename"},
+ }},
+ {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
+ {
+ {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
+ }},
+ {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
+ {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
+ {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
+ {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
+ {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
+ {RPCResult::Type::ARR, "depends", "array of numbers",
+ {
+ {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
+ }},
+ {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"},
+ {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"},
+ {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
+ }},
+ }},
+ {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
+ {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::STR, "target", "The hash target"},
+ {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
+ {
+ {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
+ }},
+ {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
+ {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
+ {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
+ {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
+ {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::STR, "bits", "compressed target of next block"},
+ {RPCResult::Type::NUM, "height", "The height of the next block"},
+ }},
RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
@@ -800,8 +813,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
{"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"},
},
RPCResult{
- "None"
- },
+ RPCResult::Type::NONE, "", "None"},
RPCExamples{
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
@@ -823,7 +835,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
ProcessNewBlockHeaders({h}, state, Params());
if (state.IsValid()) return NullUniValue;
if (state.IsError()) {
- throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
+ throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
}
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
}
@@ -848,17 +860,19 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
RPCResult{
- "{\n"
- " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n"
- " \"errors\" : [ str... ] (json array of strings, optional) Errors encountered during processing\n"
- " \"blocks\" : n (numeric) block number where estimate was found\n"
- "}\n"
- "\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"},
+ {RPCResult::Type::ARR, "errors", "Errors encountered during processing",
+ {
+ {RPCResult::Type::STR, "", "error"},
+ }},
+ {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n"
"The request target will be clamped between 2 and the highest target\n"
"fee estimation is able to return based on how long it has been running.\n"
"An error is returned if not enough transactions and blocks\n"
- "have been observed to make an estimate for any number of blocks.\n"
- },
+ "have been observed to make an estimate for any number of blocks."},
+ }},
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},
@@ -908,36 +922,40 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
" lower buckets."},
},
RPCResult{
- "{ (json object) Results are returned for any horizon which tracks blocks up to the confirmation target\n"
- " \"short\" : { (json object, optional) estimate for short time horizon\n"
- " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n"
- " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n"
- " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n"
- " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n"
- " \"startrange\" : x.x, (numeric) start of feerate range\n"
- " \"endrange\" : x.x, (numeric) end of feerate range\n"
- " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n"
- " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n"
- " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n"
- " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n"
- " },\n"
- " \"fail\" : { (json object, optional) information about the highest range of feerates to fail to meet the threshold\n"
- " ...\n"
- " },\n"
- " \"errors\" : [ (json array, optional) Errors encountered during processing\n"
- " \"str\", (string)\n"
- " ...\n"
- " ],\n"
- " },\n"
- " \"medium\" : { (json object, optional) estimate for medium time horizon\n"
- " ...\n"
- " },\n"
- " \"long\" : { (json object, optional) estimate for long time horizon\n"
- " ...\n"
- " },\n"
- "}\n"
- "\n"
- },
+ RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
+ {
+ {RPCResult::Type::OBJ, "short", /* optional */ true, "estimate for short time horizon",
+ {
+ {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
+ {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
+ {RPCResult::Type::OBJ, "pass", /* optional */ true, "information about the lowest range of feerates to succeed in meeting the threshold",
+ {
+ {RPCResult::Type::NUM, "startrange", "start of feerate range"},
+ {RPCResult::Type::NUM, "endrange", "end of feerate range"},
+ {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
+ {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
+ {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
+ {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
+ }},
+ {RPCResult::Type::OBJ, "fail", /* optional */ true, "information about the highest range of feerates to fail to meet the threshold",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing",
+ {
+ {RPCResult::Type::STR, "error", ""},
+ }},
+ }},
+ {RPCResult::Type::OBJ, "medium", /* optional */ true, "estimate for medium time horizon",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::OBJ, "long", /* optional */ true, "estimate for long time horizon",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ }},
RPCExamples{
HelpExampleCli("estimaterawfee", "6 0.9")
},
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 2b4ee62c71..8357183934 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -13,9 +13,9 @@
#include <scheduler.h>
#include <script/descriptor.h>
#include <util/check.h>
+#include <util/message.h> // For MessageSign(), MessageVerify()
#include <util/strencodings.h>
#include <util/system.h>
-#include <util/validation.h>
#include <stdint.h>
#include <tuple>
@@ -33,19 +33,20 @@ static UniValue validateaddress(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
},
RPCResult{
- "{\n"
- " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
- " \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
- " \"isscript\" : true|false, (boolean) If the key is a script\n"
- " \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
- " \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
- " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."},
+ {RPCResult::Type::STR, "address", "The bitcoin address validated"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"},
+ {RPCResult::Type::BOOL, "isscript", "If the key is a script"},
+ {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"},
+ {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"},
+ {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"},
+ }
},
RPCExamples{
- HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
- + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ HelpExampleCli("validateaddress", EXAMPLE_ADDRESS) +
+ HelpExampleRpc("validateaddress", EXAMPLE_ADDRESS)
},
}.Check(request);
@@ -82,11 +83,12 @@ static UniValue createmultisig(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "{\n"
- " \"address\" : \"multisigaddress\", (string) The value of the new multisig address.\n"
- " \"redeemScript\" : \"script\" (string) The string value of the hex-encoded redemption script.\n"
- " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The value of the new multisig address."},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
+ {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ }
},
RPCExamples{
"\nCreate a multisig address from 2 public keys\n"
@@ -141,13 +143,14 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
},
RPCResult{
- "{\n"
- " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
- " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
- " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
- " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
- " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
+ {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
+ {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
+ {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
+ {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"},
+ }
},
RPCExamples{
"Analyse a descriptor\n" +
@@ -189,7 +192,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
},
RPCResult{
- "[ address ] (json array) the derived addresses\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "address", "the derived addresses"},
+ }
},
RPCExamples{
"First three native segwit receive addresses\n" +
@@ -258,7 +264,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
},
RPCResult{
- "true|false (boolean) If the signature is verified or not.\n"
+ RPCResult::Type::BOOL, "", "If the signature is verified or not."
},
RPCExamples{
"\nUnlock the wallet for 30 seconds\n"
@@ -278,31 +284,21 @@ static UniValue verifymessage(const JSONRPCRequest& request)
std::string strSign = request.params[1].get_str();
std::string strMessage = request.params[2].get_str();
- CTxDestination destination = DecodeDestination(strAddress);
- if (!IsValidDestination(destination)) {
+ switch (MessageVerify(strAddress, strSign, strMessage)) {
+ case MessageVerificationResult::ERR_INVALID_ADDRESS:
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
- }
-
- const PKHash *pkhash = boost::get<PKHash>(&destination);
- if (!pkhash) {
+ case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
- }
-
- bool fInvalid = false;
- std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
-
- if (fInvalid)
+ case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
-
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << strMessage;
-
- CPubKey pubkey;
- if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
+ case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
+ case MessageVerificationResult::ERR_NOT_SIGNED:
return false;
+ case MessageVerificationResult::OK:
+ return true;
+ }
- return (pubkey.GetID() == *pkhash);
+ return false;
}
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
@@ -314,7 +310,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
- "\"signature\" (string) The signature of the message encoded in base 64\n"
+ RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
},
RPCExamples{
"\nCreate the signature\n"
@@ -334,15 +330,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << strMessage;
+ std::string signature;
- std::vector<unsigned char> vchSig;
- if (!key.SignCompact(ss.GetHash(), vchSig))
+ if (!MessageSign(key, strMessage, signature)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
+ }
- return EncodeBase64(vchSig.data(), vchSig.size());
+ return signature;
}
static UniValue setmocktime(const JSONRPCRequest& request)
@@ -357,8 +351,9 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCExamples{""},
}.Check(request);
- if (!Params().MineBlocksOnDemand())
- throw std::runtime_error("setmocktime for regression testing (-regtest mode) only");
+ if (!Params().IsMockableChain()) {
+ throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only");
+ }
// For now, don't change mocktime if we're in the middle of validation, as
// this could have an effect on mempool time-based eviction, as well as
@@ -398,7 +393,7 @@ static UniValue mockscheduler(const JSONRPCRequest& request)
// protect against null pointer dereference
CHECK_NONFATAL(g_rpc_node);
CHECK_NONFATAL(g_rpc_node->scheduler);
- g_rpc_node->scheduler->MockForward(boost::chrono::seconds(delta_seconds));
+ g_rpc_node->scheduler->MockForward(std::chrono::seconds(delta_seconds));
return NullUniValue;
}
@@ -449,19 +444,21 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
},
{
RPCResult{"mode \"stats\"",
- "{\n"
- " \"locked\" : { (json object) Information about locked memory manager\n"
- " \"used\" : xxxxx, (numeric) Number of bytes used\n"
- " \"free\" : xxxxx, (numeric) Number of bytes available in current arenas\n"
- " \"total\" : xxxxxxx, (numeric) Total number of bytes managed\n"
- " \"locked\" : xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
- " \"chunks_used\" : xxxxx, (numeric) Number allocated chunks\n"
- " \"chunks_free\" : xxxxx, (numeric) Number unused chunks\n"
- " }\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "locked", "Information about locked memory manager",
+ {
+ {RPCResult::Type::NUM, "used", "Number of bytes used"},
+ {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"},
+ {RPCResult::Type::NUM, "total", "Total number of bytes managed"},
+ {RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."},
+ {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"},
+ {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"},
+ }},
+ }
},
RPCResult{"mode \"mallocinfo\"",
- "\"<malloc version=\"1\">...\"\n"
+ RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\""
},
},
RPCExamples{
@@ -528,10 +525,10 @@ UniValue logging(const JSONRPCRequest& request)
}},
},
RPCResult{
- "{ (json object where keys are the logging categories, and values indicates its status\n"
- " \"category\" : true|false, (boolean) if being debug logged or not. false:inactive, true:active\n"
- " ...\n"
- "}\n"
+ RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status",
+ {
+ {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"},
+ }
},
RPCExamples{
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 92542539df..eeb617f849 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -33,7 +33,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
"\nReturns the number of connections to other nodes.\n",
{},
RPCResult{
- "n (numeric) The connection count\n"
+ RPCResult::Type::NUM, "", "The connection count"
},
RPCExamples{
HelpExampleCli("getconnectioncount", "")
@@ -77,57 +77,60 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
"\nReturns data about each connected network node as a json array of objects.\n",
{},
RPCResult{
- "[\n"
- " {\n"
- " \"id\" : n, (numeric) Peer index\n"
- " \"addr\" : \"host:port\", (string) The IP address and port of the peer\n"
- " \"addrbind\" : \"ip:port\", (string) Bind address of the connection to the peer\n"
- " \"addrlocal\" : \"ip:port\", (string) Local address as reported by the peer\n"
- " \"mapped_as\" : \"mapped_as\", (string) The AS in the BGP route to the peer used for diversifying peer selection\n"
- " \"services\" : \"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
- " \"servicesnames\" : [ (json array) the services offered, in human-readable form\n"
- " \"SERVICE_NAME\", (string) the service name if it is recognised\n"
- " ...\n"
- " ],\n"
- " \"relaytxes\" : true|false, (boolean) Whether peer has asked us to relay transactions to it\n"
- " \"lastsend\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n"
- " \"lastrecv\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n"
- " \"bytessent\" : n, (numeric) The total bytes sent\n"
- " \"bytesrecv\" : n, (numeric) The total bytes received\n"
- " \"conntime\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n"
- " \"timeoffset\" : ttt, (numeric) The time offset in seconds\n"
- " \"pingtime\" : n, (numeric) ping time (if available)\n"
- " \"minping\" : n, (numeric) minimum observed ping time (if any at all)\n"
- " \"pingwait\" : n, (numeric) ping wait (if non-zero)\n"
- " \"version\" : v, (numeric) The peer version, such as 70001\n"
- " \"subver\" : \"/Satoshi:0.8.5/\", (string) The string version\n"
- " \"inbound\" : true|false, (boolean) Inbound (true) or Outbound (false)\n"
- " \"addnode\" : true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
- " \"startingheight\" : n, (numeric) The starting height (block) of the peer\n"
- " \"banscore\" : n, (numeric) The ban score\n"
- " \"synced_headers\" : n, (numeric) The last header we have in common with this peer\n"
- " \"synced_blocks\" : n, (numeric) The last block we have in common with this peer\n"
- " \"inflight\" : [\n"
- " n, (numeric) The heights of blocks we're currently asking from this peer\n"
- " ...\n"
- " ],\n"
- " \"whitelisted\" : true|false, (boolean) Whether the peer is whitelisted\n"
- " \"minfeefilter\" : n, (numeric) The minimum fee rate for transactions this peer accepts\n"
- " \"bytessent_per_msg\" : {\n"
- " \"msg\" : n, (numeric) The total bytes sent aggregated by message type\n"
- " When a message type is not listed in this json object, the bytes sent are 0.\n"
- " Only known message types can appear as keys in the object.\n"
- " ...\n"
- " },\n"
- " \"bytesrecv_per_msg\" : {\n"
- " \"msg\" : n, (numeric) The total bytes received aggregated by message type\n"
- " When a message type is not listed in this json object, the bytes received are 0.\n"
- " Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'.\n"
- " ...\n"
- " }\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {
+ {RPCResult::Type::NUM, "id", "Peer index"},
+ {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
+ {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
+ {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
+ {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
+ "peer selection (only available if the asmap config flag is set)"},
+ {RPCResult::Type::STR_HEX, "services", "The services offered"},
+ {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
+ {
+ {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
+ }},
+ {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"},
+ {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
+ {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
+ {RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
+ {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
+ {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
+ {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
+ {RPCResult::Type::NUM, "pingtime", "ping time (if available)"},
+ {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"},
+ {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"},
+ {RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
+ {RPCResult::Type::STR, "subver", "The string version"},
+ {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
+ {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"},
+ {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"},
+ {RPCResult::Type::NUM, "banscore", "The ban score"},
+ {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
+ {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"},
+ {RPCResult::Type::ARR, "inflight", "",
+ {
+ {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"},
+ }},
+ {RPCResult::Type::BOOL, "whitelisted", "Whether the peer is whitelisted"},
+ {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
+ {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
+ {
+ {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n"
+ "When a message type is not listed in this json object, the bytes sent are 0.\n"
+ "Only known message types can appear as keys in the object."}
+ }},
+ {RPCResult::Type::OBJ, "bytesrecv_per_msg", "",
+ {
+ {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n"
+ "When a message type is not listed in this json object, the bytes received are 0.\n"
+ "Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."}
+ }},
+ }},
+ }},
},
RPCExamples{
HelpExampleCli("getpeerinfo", "")
@@ -165,12 +168,15 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
obj.pushKV("timeoffset", stats.nTimeOffset);
- if (stats.dPingTime > 0.0)
- obj.pushKV("pingtime", stats.dPingTime);
- if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6)
- obj.pushKV("minping", stats.dMinPing);
- if (stats.dPingWait > 0.0)
- obj.pushKV("pingwait", stats.dPingWait);
+ if (stats.m_ping_usec > 0) {
+ obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6);
+ }
+ if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
+ obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
+ }
+ if (stats.m_ping_wait_usec > 0) {
+ obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6);
+ }
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
// corrupting or modifying the JSON output by putting special characters in
@@ -320,19 +326,22 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
},
RPCResult{
- "[\n"
- " {\n"
- " \"addednode\" : \"192.168.0.201\", (string) The node IP address or name (as provided to addnode)\n"
- " \"connected\" : true|false, (boolean) If connected\n"
- " \"addresses\" : [ (list of objects) Only when connected = true\n"
- " {\n"
- " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server IP and port we're connected to\n"
- " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n"
- " }\n"
- " ]\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "addednode", "The node IP address or name (as provided to addnode)"},
+ {RPCResult::Type::BOOL, "connected", "If connected"},
+ {RPCResult::Type::ARR, "addresses", "Only when connected = true",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The bitcoin server IP and port we're connected to"},
+ {RPCResult::Type::STR, "connected", "connection, inbound or outbound"},
+ }},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
@@ -386,20 +395,21 @@ static UniValue getnettotals(const JSONRPCRequest& request)
"and current time.\n",
{},
RPCResult{
- "{\n"
- " \"totalbytesrecv\" : n, (numeric) Total bytes received\n"
- " \"totalbytessent\" : n, (numeric) Total bytes sent\n"
- " \"timemillis\" : t, (numeric) Current UNIX time in milliseconds\n"
- " \"uploadtarget\" : \n"
- " {\n"
- " \"timeframe\" : n, (numeric) Length of the measuring timeframe in seconds\n"
- " \"target\" : n, (numeric) Target in bytes\n"
- " \"target_reached\" : true|false, (boolean) True if target is reached\n"
- " \"serve_historical_blocks\" : true|false, (boolean) True if serving historical blocks\n"
- " \"bytes_left_in_cycle\" : t, (numeric) Bytes left in current time cycle\n"
- " \"time_left_in_cycle\" : t (numeric) Seconds left in current time cycle\n"
- " }\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
+ {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
+ {RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"},
+ {RPCResult::Type::OBJ, "uploadtarget", "",
+ {
+ {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
+ {RPCResult::Type::NUM, "target", "Target in bytes"},
+ {RPCResult::Type::BOOL, "target_reached", "True if target is reached"},
+ {RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"},
+ {RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"},
+ {RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getnettotals", "")
@@ -452,41 +462,44 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
"Returns an object containing various state info regarding P2P networking.\n",
{},
RPCResult{
- "{ (json object)\n"
- " \"version\" : xxxxx, (numeric) the server version\n"
- " \"subversion\" : \"str\", (string) the server subversion string\n"
- " \"protocolversion\" : xxxxx, (numeric) the protocol version\n"
- " \"localservices\" : \"hex\", (string) the services we offer to the network\n"
- " \"localservicesnames\" : [ (json array) the services we offer to the network, in human-readable form\n"
- " \"SERVICE_NAME\", (string) the service name\n"
- " ...\n"
- " ],\n"
- " \"localrelay\" : true|false, (boolean) true if transaction relay is requested from peers\n"
- " \"timeoffset\" : xxxxx, (numeric) the time offset\n"
- " \"connections\" : xxxxx, (numeric) the number of connections\n"
- " \"networkactive\" : true|false, (boolean) whether p2p networking is enabled\n"
- " \"networks\" : [ (json array) information per network\n"
- " { (json object)\n"
- " \"name\" : \"str\", (string) network (ipv4, ipv6 or onion)\n"
- " \"limited\" : true|false, (boolean) is the network limited using -onlynet?\n"
- " \"reachable\" : true|false, (boolean) is the network reachable?\n"
- " \"proxy\" : \"str\" (string) (\"host:port\") the proxy that is used for this network, or empty if none\n"
- " \"proxy_randomize_credentials\" : true|false, (boolean) Whether randomized credentials are used\n"
- " },\n"
- " ...\n"
- " ],\n"
- " \"relayfee\" : x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n"
- " \"incrementalfee\" : x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n"
- " \"localaddresses\" : [ (json array) list of local addresses\n"
- " { (json object)\n"
- " \"address\" : \"xxxx\", (string) network address\n"
- " \"port\" : xxx, (numeric) network port\n"
- " \"score\" : xxx (numeric) relative score\n"
- " },\n"
- " ...\n"
- " ],\n"
- " \"warnings\" : \"str\", (string) any network and blockchain warnings\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "version", "the server version"},
+ {RPCResult::Type::STR, "subversion", "the server subversion string"},
+ {RPCResult::Type::NUM, "protocolversion", "the protocol version"},
+ {RPCResult::Type::STR_HEX, "localservices", "the services we offer to the network"},
+ {RPCResult::Type::ARR, "localservicesnames", "the services we offer to the network, in human-readable form",
+ {
+ {RPCResult::Type::STR, "SERVICE_NAME", "the service name"},
+ }},
+ {RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"},
+ {RPCResult::Type::NUM, "timeoffset", "the time offset"},
+ {RPCResult::Type::NUM, "connections", "the number of connections"},
+ {RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"},
+ {RPCResult::Type::ARR, "networks", "information per network",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"},
+ {RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
+ {RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
+ {RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
+ {RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"},
+ }},
+ }},
+ {RPCResult::Type::NUM, "relayfee", "minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::NUM, "incrementalfee", "minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::ARR, "localaddresses", "list of local addresses",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "network address"},
+ {RPCResult::Type::NUM, "port", "network port"},
+ {RPCResult::Type::NUM, "score", "relative score"},
+ }},
+ }},
+ {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ }
},
RPCExamples{
HelpExampleCli("getnetworkinfo", "")
@@ -693,15 +706,16 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
{"count", RPCArg::Type::NUM, /* default */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."},
},
RPCResult{
- "[\n"
- " {\n"
- " \"time\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of when the node was last seen\n"
- " \"services\" : n, (numeric) The services offered\n"
- " \"address\" : \"host\", (string) The address of the node\n"
- " \"port\" : n (numeric) The port of the node\n"
- " }\n"
- " ,....\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"},
+ {RPCResult::Type::NUM, "services", "The services offered"},
+ {RPCResult::Type::STR, "address", "The address of the node"},
+ {RPCResult::Type::NUM, "port", "The port of the node"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getnodeaddresses", "8")
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index cd1c657c26..361cf08086 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -94,54 +94,62 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
},
{
RPCResult{"if verbose is not set or set to false",
- "\"data\" (string) The serialized, hex-encoded data for 'txid'\n"
+ RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'"
},
RPCResult{"if verbose is set to true",
- "{\n"
- " \"in_active_chain\" : b, (boolean) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n"
- " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n"
- " \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
- " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
- " \"size\" : n, (numeric) The serialized transaction size\n"
- " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
- " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n"
- " \"version\" : n, (numeric) The version\n"
- " \"locktime\" : ttt, (numeric) The lock time\n"
- " \"vin\" : [ (array of json objects)\n"
- " {\n"
- " \"txid\" : \"id\", (string) The transaction id\n"
- " \"vout\" : n, (numeric) \n"
- " \"scriptSig\" : { (json object) The script\n"
- " \"asm\" : \"asm\", (string) asm\n"
- " \"hex\" : \"hex\" (string) hex\n"
- " },\n"
- " \"sequence\" : n (numeric) The script sequence number\n"
- " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"vout\" : [ (array of json objects)\n"
- " {\n"
- " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
- " \"n\" : n, (numeric) index\n"
- " \"scriptPubKey\" : { (json object)\n"
- " \"asm\" : \"asm\", (string) the asm\n"
- " \"hex\" : \"hex\", (string) the hex\n"
- " \"reqSigs\" : n, (numeric) The required sigs\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " \"addresses\" : [ (json array of string)\n"
- " \"address\" (string) bitcoin address\n"
- " ,...\n"
- " ]\n"
- " }\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"blockhash\" : \"hash\", (string) the block hash\n"
- " \"confirmations\" : n, (numeric) The confirmations\n"
- " \"blocktime\" : ttt (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n"
- " \"time\" : ttt, (numeric) Same as \"blocktime\"\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "in_active_chain", "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"},
+ {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"},
+ {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},
+ {RPCResult::Type::NUM, "size", "The serialized transaction size"},
+ {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"},
+ {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4-3 and vsize*4)"},
+ {RPCResult::Type::NUM, "version", "The version"},
+ {RPCResult::Type::NUM_TIME, "locktime", "The lock time"},
+ {RPCResult::Type::ARR, "vin", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::STR, "vout", ""},
+ {RPCResult::Type::OBJ, "scriptSig", "The script",
+ {
+ {RPCResult::Type::STR, "asm", "asm"},
+ {RPCResult::Type::STR_HEX, "hex", "hex"},
+ }},
+ {RPCResult::Type::NUM, "sequence", "The script sequence number"},
+ {RPCResult::Type::ARR, "txinwitness", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::ARR, "vout", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT},
+ {RPCResult::Type::NUM, "n", "index"},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR, "hex", "the hex"},
+ {RPCResult::Type::NUM, "reqSigs", "The required sigs"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ {RPCResult::Type::ARR, "addresses", "",
+ {
+ {RPCResult::Type::STR, "address", "bitcoin address"},
+ }},
+ }},
+ }},
+ }},
+ {RPCResult::Type::STR_HEX, "blockhash", "the block hash"},
+ {RPCResult::Type::NUM, "confirmations", "The confirmations"},
+ {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "time", "Same as \"blocktime\""},
+ }
},
},
RPCExamples{
@@ -230,7 +238,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"},
},
RPCResult{
- "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
+ RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
},
RPCExamples{""},
}.Check(request);
@@ -315,7 +323,10 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
{"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"},
},
RPCResult{
- "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof can not be validated."},
+ }
},
RPCExamples{""},
}.Check(request);
@@ -390,7 +401,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
- "\"transaction\" (string) hex string of the transaction\n"
+ RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction"
},
RPCExamples{
HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
@@ -432,45 +443,53 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
},
},
RPCResult{
- "{\n"
- " \"txid\" : \"id\", (string) The transaction id\n"
- " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
- " \"size\" : n, (numeric) The transaction size\n"
- " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
- " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n"
- " \"version\" : n, (numeric) The version\n"
- " \"locktime\" : ttt, (numeric) The lock time\n"
- " \"vin\" : [ (array of json objects)\n"
- " {\n"
- " \"txid\" : \"id\", (string) The transaction id\n"
- " \"vout\" : n, (numeric) The output number\n"
- " \"scriptSig\" : { (json object) The script\n"
- " \"asm\" : \"asm\", (string) asm\n"
- " \"hex\" : \"hex\" (string) hex\n"
- " },\n"
- " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
- " \"sequence\" : n (numeric) The script sequence number\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"vout\" : [ (array of json objects)\n"
- " {\n"
- " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
- " \"n\" : n, (numeric) index\n"
- " \"scriptPubKey\" : { (json object)\n"
- " \"asm\" : \"asm\", (string) the asm\n"
- " \"hex\" : \"hex\", (string) the hex\n"
- " \"reqSigs\" : n, (numeric) The required sigs\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " \"addresses\" : [ (json array of string)\n"
- " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n"
- " ,...\n"
- " ]\n"
- " }\n"
- " }\n"
- " ,...\n"
- " ],\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},
+ {RPCResult::Type::NUM, "size", "The transaction size"},
+ {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"},
+ {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4 - 3 and vsize*4)"},
+ {RPCResult::Type::NUM, "version", "The version"},
+ {RPCResult::Type::NUM_TIME, "locktime", "The lock time"},
+ {RPCResult::Type::ARR, "vin", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::NUM, "vout", "The output number"},
+ {RPCResult::Type::OBJ, "scriptSig", "The script",
+ {
+ {RPCResult::Type::STR, "asm", "asm"},
+ {RPCResult::Type::STR_HEX, "hex", "hex"},
+ }},
+ {RPCResult::Type::ARR, "txinwitness", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
+ }},
+ {RPCResult::Type::NUM, "sequence", "The script sequence number"},
+ }},
+ }},
+ {RPCResult::Type::ARR, "vout", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT},
+ {RPCResult::Type::NUM, "n", "index"},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR_HEX, "hex", "the hex"},
+ {RPCResult::Type::NUM, "reqSigs", "The required sigs"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ {RPCResult::Type::ARR, "addresses", "",
+ {
+ {RPCResult::Type::STR, "address", "bitcoin address"},
+ }},
+ }},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
@@ -513,26 +532,29 @@ static UniValue decodescript(const JSONRPCRequest& request)
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
},
RPCResult{
- "{\n"
- " \"asm\" : \"asm\", (string) Script public key\n"
- " \"type\" : \"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n"
- " \"reqSigs\" : n, (numeric) The required signatures\n"
- " \"addresses\" : [ (json array of string)\n"
- " \"address\" (string) bitcoin address\n"
- " ,...\n"
- " ],\n"
- " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
- " \"segwit\" : { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n"
- " \"asm\" : \"str\", (string) String representation of the script public key\n"
- " \"hex\" : \"hexstr\", (string) Hex string of the script public key\n"
- " \"type\" : \"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n"
- " \"reqSigs\" : n, (numeric) The required signatures (always 1)\n"
- " \"addresses\" : [ (json array of string) (always length 1)\n"
- " \"address\" (string) segwit address\n"
- " ,...\n"
- " ],\n"
- " \"p2sh-segwit\":\"str\" (string) address of the P2SH script wrapping this witness redeem script.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "asm", "Script public key"},
+ {RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"},
+ {RPCResult::Type::NUM, "reqSigs", "The required signatures"},
+ {RPCResult::Type::ARR, "addresses", "",
+ {
+ {RPCResult::Type::STR, "address", "bitcoin address"},
+ }},
+ {RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
+ {RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
+ {
+ {RPCResult::Type::STR, "asm", "String representation of the script public key"},
+ {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
+ {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
+ {RPCResult::Type::NUM, "reqSigs", "The required signatures (always 1)"},
+ {RPCResult::Type::ARR, "addresses", "(always length 1)",
+ {
+ {RPCResult::Type::STR, "address", "segwit address"},
+ }},
+ {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("decodescript", "\"hexstring\"")
@@ -607,7 +629,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
},
},
RPCResult{
- "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n"
+ RPCResult::Type::STR, "", "The hex-encoded raw transaction with signature(s)"
},
RPCExamples{
HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')")
@@ -715,20 +737,22 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
},
},
RPCResult{
- "{\n"
- " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
- " {\n"
- " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
- " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
- " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
- " \"sequence\" : n, (numeric) Script sequence number\n"
- " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
+ {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
+ {RPCResult::Type::NUM, "sequence", "Script sequence number"},
+ {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
@@ -784,7 +808,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"/kB.\nSet to 0 to accept any fee rate.\n"},
},
RPCResult{
- "\"hex\" (string) The transaction hash in hex\n"
+ RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
},
RPCExamples{
"\nCreate a transaction\n"
@@ -846,14 +870,16 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
},
RPCResult{
- "[ (json array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
- " Length is exactly one for now.\n"
- " {\n"
- " \"txid\" (string) The transaction hash in hex\n"
- " \"allowed\" (boolean) If the mempool allows this tx to be inserted\n"
- " \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n"
- " }\n"
- "]\n"
+ RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
+ "Length is exactly one for now.",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
+ {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
+ {RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
+ }},
+ }
},
RPCExamples{
"\nCreate a transaction\n"
@@ -950,92 +976,108 @@ UniValue decodepsbt(const JSONRPCRequest& request)
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
},
RPCResult{
- "{\n"
- " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
- " ... The layout is the same as the output of decoderawtransaction.\n"
- " },\n"
- " \"unknown\" : { (json object) The unknown global fields\n"
- " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
- " ...\n"
- " },\n"
- " \"inputs\" : [ (array of json objects)\n"
- " {\n"
- " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n"
- " ...\n"
- " },\n"
- " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n"
- " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
- " \"scriptPubKey\" : { (json object)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " \"address\" : \"address\" (string) Bitcoin address if there is one\n"
- " }\n"
- " },\n"
- " \"partial_signatures\" : { (json object, optional)\n"
- " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n"
- " ,...\n"
- " }\n"
- " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n"
- " \"redeem_script\" : { (json object, optional)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " }\n"
- " \"witness_script\" : { (json object, optional)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " }\n"
- " \"bip32_derivs\" : { (json object, optional)\n"
- " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n"
- " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
- " \"path\" : \"path\", (string) The path\n"
- " }\n"
- " ,...\n"
- " }\n"
- " \"final_scriptsig\" : { (json object, optional)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " }\n"
- " \"final_scriptwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
- " \"unknown\" : { (json object) The unknown global fields\n"
- " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
- " ...\n"
- " },\n"
- " }\n"
- " ,...\n"
- " ]\n"
- " \"outputs\" : [ (array of json objects)\n"
- " {\n"
- " \"redeem_script\" : { (json object, optional)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " }\n"
- " \"witness_script\" : { (json object, optional)\n"
- " \"asm\" : \"asm\", (string) The asm\n"
- " \"hex\" : \"hex\", (string) The hex\n"
- " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
- " }\n"
- " \"bip32_derivs\" : [ (array of json objects, optional)\n"
- " {\n"
- " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n"
- " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
- " \"path\" : \"path\", (string) The path\n"
- " }\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"unknown\" : { (json object) The unknown global fields\n"
- " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
- " ...\n"
- " },\n"
- " }\n"
- " ,...\n"
- " ]\n"
- " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "tx", "The decoded network-serialized unsigned transaction.",
+ {
+ {RPCResult::Type::ELISION, "", "The layout is the same as the output of decoderawtransaction."},
+ }},
+ {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {
+ {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
+ }},
+ {RPCResult::Type::ARR, "inputs", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "non_witness_utxo", /* optional */ true, "Decoded network transaction for non-witness UTXOs",
+ {
+ {RPCResult::Type::ELISION, "",""},
+ }},
+ {RPCResult::Type::OBJ, "witness_utxo", /* optional */ true, "Transaction output for witness UTXOs",
+ {
+ {RPCResult::Type::NUM, "amount", "The value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ {RPCResult::Type::STR, "address"," Bitcoin address if there is one"},
+ }},
+ }},
+ {RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "pubkey", "The public key and signature that corresponds to it."},
+ }},
+ {RPCResult::Type::STR, "sighash", /* optional */ true, "The sighash type to be used"},
+ {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ }},
+ {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ }},
+ {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "",
+ {
+ {RPCResult::Type::OBJ, "pubkey", /* optional */ true, "The public key with the derivation path as the value.",
+ {
+ {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
+ {RPCResult::Type::STR, "path", "The path"},
+ }},
+ }},
+ {RPCResult::Type::OBJ, "final_scriptsig", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR, "hex", "The hex"},
+ }},
+ {RPCResult::Type::ARR, "final_scriptwitness", "",
+ {
+ {RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
+ }},
+ {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {
+ {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::ARR, "outputs", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ }},
+ {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ }},
+ {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "pubkey", "The public key this path corresponds to"},
+ {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
+ {RPCResult::Type::STR, "path", "The path"},
+ }},
+ }},
+ {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {
+ {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."},
+ }
},
RPCExamples{
HelpExampleCli("decodepsbt", "\"psbt\"")
@@ -1244,7 +1286,7 @@ UniValue combinepsbt(const JSONRPCRequest& request)
},
},
RPCResult{
- " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ RPCResult::Type::STR, "", "The base64-encoded partially signed transaction"
},
RPCExamples{
HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')")
@@ -1292,11 +1334,12 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
" extract and return the complete transaction in normal network serialization instead of the PSBT."},
},
RPCResult{
- "{ (json object)\n"
- " \"psbt\" : \"str\", (string) The base64-encoded partially signed transaction if not extracted\n"
- " \"hex\" : \"hex\", (string) The hex-encoded network transaction if extracted\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction if not extracted"},
+ {RPCResult::Type::STR_HEX, "hex", "The hex-encoded network transaction if extracted"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ }
},
RPCExamples{
HelpExampleCli("finalizepsbt", "\"psbt\"")
@@ -1374,7 +1417,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
- " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
},
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
@@ -1431,7 +1474,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
},
},
RPCResult{
- " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
},
RPCExamples{
"\nCreate a transaction\n"
@@ -1495,7 +1538,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
}},
},
RPCResult {
- " \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
+ RPCResult::Type::STR, "", "The base64-encoded partially signed transaction with inputs updated"
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
@@ -1580,7 +1623,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
}}
},
RPCResult {
- " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ RPCResult::Type::STR, "", "The base64-encoded partially signed transaction"
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
@@ -1669,31 +1712,36 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
},
RPCResult {
- "{\n"
- " \"inputs\" : [ (array of json objects)\n"
- " {\n"
- " \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n"
- " \"is_final\" : true|false (boolean) Whether the input is finalized\n"
- " \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n"
- " \"pubkeys\" : [ (array, optional)\n"
- " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n"
- " ]\n"
- " \"signatures\" : [ (array, optional)\n"
- " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n"
- " ]\n"
- " \"redeemscript\" : \"hash\" (string, optional) Hash160 of the redeemScript that is missing\n"
- " \"witnessscript\" : \"hash\" (string, optional) SHA256 of the witnessScript that is missing\n"
- " }\n"
- " \"next\" : \"role\" (string, optional) Role of the next person that this input needs to go to\n"
- " }\n"
- " ,...\n"
- " ]\n"
- " \"estimated_vsize\" : vsize (numeric, optional) Estimated vsize of the final signed transaction\n"
- " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n"
- " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n"
- " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n"
- " \"error\" : \"error\" (string) Error message if there is one\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "inputs", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "has_utxo", "Whether a UTXO is provided"},
+ {RPCResult::Type::BOOL, "is_final", "Whether the input is finalized"},
+ {RPCResult::Type::OBJ, "missing", /* optional */ true, "Things that are missing that are required to complete this input",
+ {
+ {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing"},
+ }},
+ {RPCResult::Type::ARR, "signatures", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose signature is missing"},
+ }},
+ {RPCResult::Type::STR_HEX, "redeemscript", /* optional */ true, "Hash160 of the redeemScript that is missing"},
+ {RPCResult::Type::STR_HEX, "witnessscript", /* optional */ true, "SHA256 of the witnessScript that is missing"},
+ }},
+ {RPCResult::Type::STR, "next", /* optional */ true, "Role of the next person that this input needs to go to"},
+ }},
+ }},
+ {RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"},
+ {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled"},
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"},
+ {RPCResult::Type::STR, "next", "Role of the next person that this psbt needs to go to"},
+ {RPCResult::Type::STR, "error", "Error message if there is one"},
+ }
},
RPCExamples {
HelpExampleCli("analyzepsbt", "\"psbt\"")
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 40334883c5..54baec6c6f 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -272,55 +272,27 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
{
int nHashType = ParseSighashString(hashType);
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
-
// Script verification errors
- UniValue vErrors(UniValue::VARR);
-
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(mtx);
- // Sign what we can:
- for (unsigned int i = 0; i < mtx.vin.size(); i++) {
- CTxIn& txin = mtx.vin[i];
- auto coin = coins.find(txin.prevout);
- if (coin == coins.end() || coin->second.IsSpent()) {
- TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
- continue;
- }
- const CScript& prevPubKey = coin->second.out.scriptPubKey;
- const CAmount& amount = coin->second.out.nValue;
-
- SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
- // Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
- }
-
- UpdateInput(txin, sigdata);
+ std::map<int, std::string> input_errors;
- // amount must be specified for valid segwit signature
- if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
- }
+ bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
+ SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
+}
- ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
- if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
- // Unable to sign input and verification failed (possible attempt to partially sign).
- TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
- } else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
- // Verification failed (possibly due to insufficient signatures).
- TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
- } else {
- TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
- }
+void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result)
+{
+ // Make errors UniValue
+ UniValue vErrors(UniValue::VARR);
+ for (const auto& err_pair : input_errors) {
+ if (err_pair.second == "Missing amount") {
+ // This particular error needs to be an exception for some reason
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
}
+ TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second);
}
- bool fComplete = vErrors.empty();
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
- result.pushKV("complete", fComplete);
+ result.pushKV("complete", complete);
if (!vErrors.empty()) {
if (result.exists("errors")) {
vErrors.push_backV(result["errors"].getValues());
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 4750fd64ed..436db5dc60 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -6,6 +6,7 @@
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
#include <map>
+#include <string>
class FillableSigningProvider;
class UniValue;
@@ -24,6 +25,7 @@ class SigningProvider;
* @param result JSON object where signed transaction results accumulate
*/
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
+void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result);
/**
* Parse a prevtxs UniValue array and get the map of coins from it
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index b62490ed29..2893aa0e60 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -137,7 +137,7 @@ UniValue help(const JSONRPCRequest& jsonRequest)
{"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
},
RPCResult{
- "\"text\" (string) The help text\n"
+ RPCResult::Type::STR, "", "The help text"
},
RPCExamples{""},
}.ToString()
@@ -169,7 +169,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
// this reply will get back to the client.
StartShutdown();
if (jsonRequest.params[0].isNum()) {
- MilliSleep(jsonRequest.params[0].get_int());
+ UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
}
return PACKAGE_NAME " stopping";
}
@@ -180,7 +180,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
"\nReturns the total uptime of the server.\n",
{},
RPCResult{
- "ttt (numeric) The number of seconds that the server has been running\n"
+ RPCResult::Type::NUM, "", "The number of seconds that the server has been running"
},
RPCExamples{
HelpExampleCli("uptime", "")
@@ -197,16 +197,18 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
"\nReturns details of the RPC server.\n",
{},
RPCResult{
- "{\n"
- " \"active_commands\" (json array) All active commands\n"
- " [\n"
- " { (json object) Information about an active command\n"
- " \"method\" (string) The name of the RPC command \n"
- " \"duration\" (numeric) The running time in microseconds\n"
- " },...\n"
- " ],\n"
- " \"logpath\" : \"xxx\" (string) The complete file path to the debug log\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "active_commands", "All active commands",
+ {
+ {RPCResult::Type::OBJ, "", "Information about an active command",
+ {
+ {RPCResult::Type::STR, "method", "The name of the RPC command"},
+ {RPCResult::Type::NUM, "duration", "The running time in microseconds"},
+ }},
+ }},
+ {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"},
+ }
},
RPCExamples{
HelpExampleCli("getrpcinfo", "")
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 78586c22f9..a4e67c8da8 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -14,6 +14,7 @@
#include <tuple>
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
+const std::string EXAMPLE_ADDRESS = "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"";
void RPCTypeCheck(const UniValue& params,
const std::list<UniValueType>& typesExpected,
@@ -311,20 +312,9 @@ struct Sections {
}
/**
- * Serializing RPCArgs depends on the outer type. Only arrays and
- * dictionaries can be nested in json. The top-level outer type is "named
- * arguments", a mix between a dictionary and arrays.
- */
- enum class OuterType {
- ARR,
- OBJ,
- NAMED_ARG, // Only set on first recursion
- };
-
- /**
* Recursive helper to translate an RPCArg into sections
*/
- void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
+ void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
{
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
@@ -337,7 +327,7 @@ struct Sections {
case RPCArg::Type::AMOUNT:
case RPCArg::Type::RANGE:
case RPCArg::Type::BOOL: {
- if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
+ if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
@@ -350,7 +340,7 @@ struct Sections {
}
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
- const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
+ const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
@@ -358,20 +348,20 @@ struct Sections {
if (arg.m_type != RPCArg::Type::OBJ) {
PushSection({indent_next + "...", ""});
}
- PushSection({indent + "}" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
+ PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
case RPCArg::Type::ARR: {
auto left = indent;
left += push_name ? "\"" + arg.m_name + "\": " : "";
left += "[";
- const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
+ const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
PushSection({left, right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::ARR);
}
PushSection({indent_next + "...", ""});
- PushSection({indent + "]" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""});
+ PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
@@ -443,7 +433,9 @@ std::string RPCResults::ToDescriptionString() const
} else {
result += "\nResult (" + r.m_cond + "):\n";
}
- result += r.m_result;
+ Sections sections;
+ r.ToSections(sections);
+ result += sections.ToString();
}
return result;
}
@@ -590,6 +582,93 @@ std::string RPCArg::ToDescriptionString() const
return ret;
}
+void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
+{
+ // Indentation
+ const std::string indent(current_indent, ' ');
+ const std::string indent_next(current_indent + 2, ' ');
+
+ // Elements in a JSON structure (dictionary or array) are separated by a comma
+ const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
+
+ // The key name if recursed into an dictionary
+ const std::string maybe_key{
+ outer_type == OuterType::OBJ ?
+ "\"" + this->m_key_name + "\" : " :
+ ""};
+
+ // Format description with type
+ const auto Description = [&](const std::string& type) {
+ return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
+ (this->m_description.empty() ? "" : " " + this->m_description);
+ };
+
+ switch (m_type) {
+ case Type::ELISION: {
+ // If the inner result is empty, use three dots for elision
+ sections.PushSection({indent_next + "...", m_description});
+ return;
+ }
+ case Type::NONE: {
+ sections.PushSection({indent + "None", Description("json null")});
+ return;
+ }
+ case Type::STR: {
+ sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
+ return;
+ }
+ case Type::STR_AMOUNT: {
+ sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
+ return;
+ }
+ case Type::STR_HEX: {
+ sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
+ return;
+ }
+ case Type::NUM: {
+ sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
+ return;
+ }
+ case Type::NUM_TIME: {
+ sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
+ return;
+ }
+ case Type::BOOL: {
+ sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
+ return;
+ }
+ case Type::ARR_FIXED:
+ case Type::ARR: {
+ sections.PushSection({indent + maybe_key + "[", Description("json array")});
+ for (const auto& i : m_inner) {
+ i.ToSections(sections, OuterType::ARR, current_indent + 2);
+ }
+ if (m_type == Type::ARR) {
+ sections.PushSection({indent_next + "...", ""});
+ }
+ sections.PushSection({indent + "]" + maybe_separator, ""});
+ return;
+ }
+ case Type::OBJ_DYN:
+ case Type::OBJ: {
+ sections.PushSection({indent + maybe_key + "{", Description("json object")});
+ for (const auto& i : m_inner) {
+ i.ToSections(sections, OuterType::OBJ, current_indent + 2);
+ }
+ if (m_type == Type::OBJ_DYN) {
+ // If the dictionary keys are dynamic, use three dots for continuation
+ sections.PushSection({indent_next + "...", ""});
+ }
+ sections.PushSection({indent + "}" + maybe_separator, ""});
+ return;
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+
+ CHECK_NONFATAL(false);
+}
+
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 065a992a88..b5eebf0915 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -28,9 +28,16 @@
*/
extern const std::string UNIX_EPOCH_TIME;
+/**
+ * Example bech32 address used in multiple RPCExamples. The address is intentionally
+ * invalid to prevent accidental transactions by users.
+ */
+extern const std::string EXAMPLE_ADDRESS;
+
class FillableSigningProvider;
class CPubKey;
class CScript;
+struct Sections;
/** Wrapper for UniValue::VType, which includes typeAny:
* Used to denote don't care type. */
@@ -95,6 +102,16 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl
/** Returns, given services flags, a list of humanly readable (known) network services */
UniValue GetServicesNames(ServiceFlags services);
+/**
+ * Serializing JSON objects depends on the outer type. Only arrays and
+ * dictionaries can be nested in json. The top-level outer type is "NONE".
+ */
+enum class OuterType {
+ ARR,
+ OBJ,
+ NONE, // Only set on first recursion
+};
+
struct RPCArg {
enum class Type {
OBJ,
@@ -134,37 +151,37 @@ struct RPCArg {
const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description.
RPCArg(
- const std::string& name,
- const Type& type,
- const Fallback& fallback,
- const std::string& description,
- const std::string& oneline_description = "",
- const std::vector<std::string>& type_str = {})
- : m_name{name},
- m_type{type},
- m_fallback{fallback},
- m_description{description},
- m_oneline_description{oneline_description},
- m_type_str{type_str}
+ const std::string name,
+ const Type type,
+ const Fallback fallback,
+ const std::string description,
+ const std::string oneline_description = "",
+ const std::vector<std::string> type_str = {})
+ : m_name{std::move(name)},
+ m_type{std::move(type)},
+ m_fallback{std::move(fallback)},
+ m_description{std::move(description)},
+ m_oneline_description{std::move(oneline_description)},
+ m_type_str{std::move(type_str)}
{
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
}
RPCArg(
- const std::string& name,
- const Type& type,
- const Fallback& fallback,
- const std::string& description,
- const std::vector<RPCArg>& inner,
- const std::string& oneline_description = "",
- const std::vector<std::string>& type_str = {})
- : m_name{name},
- m_type{type},
- m_inner{inner},
- m_fallback{fallback},
- m_description{description},
- m_oneline_description{oneline_description},
- m_type_str{type_str}
+ const std::string name,
+ const Type type,
+ const Fallback fallback,
+ const std::string description,
+ const std::vector<RPCArg> inner,
+ const std::string oneline_description = "",
+ const std::vector<std::string> type_str = {})
+ : m_name{std::move(name)},
+ m_type{std::move(type)},
+ m_inner{std::move(inner)},
+ m_fallback{std::move(fallback)},
+ m_description{std::move(description)},
+ m_oneline_description{std::move(oneline_description)},
+ m_type_str{std::move(type_str)}
{
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ);
}
@@ -189,21 +206,85 @@ struct RPCArg {
};
struct RPCResult {
+ enum class Type {
+ OBJ,
+ ARR,
+ STR,
+ NUM,
+ BOOL,
+ NONE,
+ STR_AMOUNT, //!< Special string to represent a floating point amount
+ STR_HEX, //!< Special string with only hex chars
+ OBJ_DYN, //!< Special dictionary with keys that are not literals
+ ARR_FIXED, //!< Special array that has a fixed number of entries
+ NUM_TIME, //!< Special numeric to denote unix epoch time
+ ELISION, //!< Special type to denote elision (...)
+ };
+
+ const Type m_type;
+ const std::string m_key_name; //!< Only used for dicts
+ const std::vector<RPCResult> m_inner; //!< Only used for arrays or dicts
+ const bool m_optional;
+ const std::string m_description;
const std::string m_cond;
- const std::string m_result;
- explicit RPCResult(std::string result)
- : m_cond{}, m_result{std::move(result)}
+ RPCResult(
+ const std::string cond,
+ const Type type,
+ const std::string m_key_name,
+ const bool optional,
+ const std::string description,
+ const std::vector<RPCResult> inner = {})
+ : m_type{std::move(type)},
+ m_key_name{std::move(m_key_name)},
+ m_inner{std::move(inner)},
+ m_optional{optional},
+ m_description{std::move(description)},
+ m_cond{std::move(cond)}
{
- CHECK_NONFATAL(!m_result.empty());
+ CHECK_NONFATAL(!m_cond.empty());
+ const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
+ CHECK_NONFATAL(inner_needed != inner.empty());
}
- RPCResult(std::string cond, std::string result)
- : m_cond{std::move(cond)}, m_result{std::move(result)}
+ RPCResult(
+ const std::string cond,
+ const Type type,
+ const std::string m_key_name,
+ const std::string description,
+ const std::vector<RPCResult> inner = {})
+ : RPCResult{cond, type, m_key_name, false, description, inner} {}
+
+ RPCResult(
+ const Type type,
+ const std::string m_key_name,
+ const bool optional,
+ const std::string description,
+ const std::vector<RPCResult> inner = {})
+ : m_type{std::move(type)},
+ m_key_name{std::move(m_key_name)},
+ m_inner{std::move(inner)},
+ m_optional{optional},
+ m_description{std::move(description)},
+ m_cond{}
{
- CHECK_NONFATAL(!m_cond.empty());
- CHECK_NONFATAL(!m_result.empty());
+ const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
+ CHECK_NONFATAL(inner_needed != inner.empty());
}
+
+ RPCResult(
+ const Type type,
+ const std::string m_key_name,
+ const std::string description,
+ const std::vector<RPCResult> inner = {})
+ : RPCResult{type, m_key_name, false, description, inner} {}
+
+ /** Append the sections of the result. */
+ void ToSections(Sections& sections, OuterType outer_type = OuterType::NONE, const int current_indent = 0) const;
+ /** Return the type string of the result when it is in an object (dict). */
+ std::string ToStringObj() const;
+ /** Return the description string, including the result type. */
+ std::string ToDescriptionString() const;
};
struct RPCResults {
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 72cca89d99..7cb7754fde 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -5,7 +5,6 @@
#include <scheduler.h>
#include <random.h>
-#include <reverselock.h>
#include <assert.h>
#include <utility>
@@ -20,18 +19,9 @@ CScheduler::~CScheduler()
}
-#if BOOST_VERSION < 105000
-static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t)
-{
- // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t,
- // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then.
- return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast<boost::chrono::milliseconds>(t.time_since_epoch()).count());
-}
-#endif
-
void CScheduler::serviceQueue()
{
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ WAIT_LOCK(newTaskMutex, lock);
++nThreadsServicingQueue;
// newTaskMutex is locked throughout this loop EXCEPT
@@ -40,7 +30,7 @@ void CScheduler::serviceQueue()
while (!shouldStop()) {
try {
if (!shouldStop() && taskQueue.empty()) {
- reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
+ REVERSE_LOCK(lock);
}
while (!shouldStop() && taskQueue.empty()) {
// Wait until there is something to do.
@@ -50,21 +40,13 @@ void CScheduler::serviceQueue()
// Wait until either there is a new task, or until
// the time of the first item on the queue:
-// wait_until needs boost 1.50 or later; older versions have timed_wait:
-#if BOOST_VERSION < 105000
- while (!shouldStop() && !taskQueue.empty() &&
- newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) {
- // Keep waiting until timeout
- }
-#else
- // Some boost versions have a conflicting overload of wait_until that returns void.
- // Explicitly use a template here to avoid hitting that overload.
while (!shouldStop() && !taskQueue.empty()) {
- boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
- if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout)
+ std::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
+ if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) {
break; // Exit loop after timeout, it means we reached the time of the event
+ }
}
-#endif
+
// If there are multiple threads, the queue can empty while we're waiting (another
// thread may service the task we were waiting on).
if (shouldStop() || taskQueue.empty())
@@ -76,7 +58,7 @@ void CScheduler::serviceQueue()
{
// Unlock before calling f, so it can reschedule itself or another task
// without deadlocking:
- reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
+ REVERSE_LOCK(lock);
f();
}
} catch (...) {
@@ -91,7 +73,7 @@ void CScheduler::serviceQueue()
void CScheduler::stop(bool drain)
{
{
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ LOCK(newTaskMutex);
if (drain)
stopWhenEmpty = true;
else
@@ -100,10 +82,10 @@ void CScheduler::stop(bool drain)
newTaskScheduled.notify_all();
}
-void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t)
+void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t)
{
{
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ LOCK(newTaskMutex);
taskQueue.insert(std::make_pair(t, f));
}
newTaskScheduled.notify_one();
@@ -111,18 +93,18 @@ void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::t
void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds)
{
- schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds));
+ schedule(f, std::chrono::system_clock::now() + std::chrono::milliseconds(deltaMilliSeconds));
}
-void CScheduler::MockForward(boost::chrono::seconds delta_seconds)
+void CScheduler::MockForward(std::chrono::seconds delta_seconds)
{
- assert(delta_seconds.count() > 0 && delta_seconds < boost::chrono::hours{1});
+ assert(delta_seconds.count() > 0 && delta_seconds < std::chrono::hours{1});
{
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ LOCK(newTaskMutex);
// use temp_queue to maintain updated schedule
- std::multimap<boost::chrono::system_clock::time_point, Function> temp_queue;
+ std::multimap<std::chrono::system_clock::time_point, Function> temp_queue;
for (const auto& element : taskQueue) {
temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second);
@@ -147,10 +129,10 @@ void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds
scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds);
}
-size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
- boost::chrono::system_clock::time_point &last) const
+size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first,
+ std::chrono::system_clock::time_point &last) const
{
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ LOCK(newTaskMutex);
size_t result = taskQueue.size();
if (!taskQueue.empty()) {
first = taskQueue.begin()->first;
@@ -160,7 +142,7 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
}
bool CScheduler::AreThreadsServicingQueue() const {
- boost::unique_lock<boost::mutex> lock(newTaskMutex);
+ LOCK(newTaskMutex);
return nThreadsServicingQueue;
}
@@ -174,7 +156,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
if (m_are_callbacks_running) return;
if (m_callbacks_pending.empty()) return;
}
- m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this));
+ m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this), std::chrono::system_clock::now());
}
void SingleThreadedSchedulerClient::ProcessQueue() {
diff --git a/src/scheduler.h b/src/scheduler.h
index d18be0ea5e..4d5aa3068e 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -7,11 +7,12 @@
//
// NOTE:
-// boost::thread / boost::chrono should be ported to std::thread / std::chrono
+// boost::thread should be ported to std::thread
// when we support C++11.
//
-#include <boost/chrono/chrono.hpp>
-#include <boost/thread.hpp>
+#include <condition_variable>
+#include <functional>
+#include <list>
#include <map>
#include <sync.h>
@@ -27,8 +28,8 @@
// s->scheduleFromNow(std::bind(Class::func, this, argument), 3);
// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s));
//
-// ... then at program shutdown, clean up the thread running serviceQueue:
-// t->interrupt();
+// ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue:
+// s->stop();
// t->join();
// delete t;
// delete s; // Must be done after thread is interrupted/joined.
@@ -43,7 +44,7 @@ public:
typedef std::function<void()> Function;
// Call func at/after time t
- void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());
+ void schedule(Function f, std::chrono::system_clock::time_point t);
// Convenience method: call f once deltaMilliSeconds from now
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);
@@ -60,7 +61,7 @@ public:
* Iterates through items on taskQueue and reschedules them
* to be delta_seconds sooner.
*/
- void MockForward(boost::chrono::seconds delta_seconds);
+ void MockForward(std::chrono::seconds delta_seconds);
// To keep things as simple as possible, there is no unschedule.
@@ -75,20 +76,20 @@ public:
// Returns number of tasks waiting to be serviced,
// and first and last task times
- size_t getQueueInfo(boost::chrono::system_clock::time_point &first,
- boost::chrono::system_clock::time_point &last) const;
+ size_t getQueueInfo(std::chrono::system_clock::time_point &first,
+ std::chrono::system_clock::time_point &last) const;
// Returns true if there are threads actively running in serviceQueue()
bool AreThreadsServicingQueue() const;
private:
- std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue;
- boost::condition_variable newTaskScheduled;
- mutable boost::mutex newTaskMutex;
- int nThreadsServicingQueue;
- bool stopRequested;
- bool stopWhenEmpty;
- bool shouldStop() const { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }
+ mutable Mutex newTaskMutex;
+ std::condition_variable newTaskScheduled;
+ std::multimap<std::chrono::system_clock::time_point, Function> taskQueue GUARDED_BY(newTaskMutex);
+ int nThreadsServicingQueue GUARDED_BY(newTaskMutex);
+ bool stopRequested GUARDED_BY(newTaskMutex);
+ bool stopWhenEmpty GUARDED_BY(newTaskMutex);
+ bool shouldStop() const EXCLUSIVE_LOCKS_REQUIRED(newTaskMutex) { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }
};
/**
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 773d6a55c4..b1d9a5bda7 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -339,14 +339,15 @@ class DescriptorImpl : public Descriptor
{
//! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
+ //! The string name of the descriptor function.
+ const std::string m_name;
+
+protected:
//! The sub-descriptor argument (nullptr for everything but SH and WSH).
//! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
//! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
- //! The string name of the descriptor function.
- const std::string m_name;
-protected:
//! Return a serialization of anything except pubkey and script arguments, to be prepended to those.
virtual std::string ToStringExtra() const { return ""; }
@@ -364,7 +365,7 @@ protected:
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_subdescriptor_arg(std::move(script)), m_name(name) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_arg(std::move(script)) {}
bool IsSolvable() const override
{
@@ -500,6 +501,8 @@ public:
out = Merge(out, subprovider);
}
}
+
+ Optional<OutputType> GetOutputType() const override { return nullopt; }
};
/** A parsed addr(A) descriptor. */
@@ -512,6 +515,19 @@ protected:
public:
AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
+
+ Optional<OutputType> GetOutputType() const override
+ {
+ switch (m_destination.which()) {
+ case 1 /* PKHash */:
+ case 2 /* ScriptHash */: return OutputType::LEGACY;
+ case 3 /* WitnessV0ScriptHash */:
+ case 4 /* WitnessV0KeyHash */:
+ case 5 /* WitnessUnknown */: return OutputType::BECH32;
+ case 0 /* CNoDestination */:
+ default: return nullopt;
+ }
+ }
};
/** A parsed raw(H) descriptor. */
@@ -524,6 +540,21 @@ protected:
public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
+
+ Optional<OutputType> GetOutputType() const override
+ {
+ CTxDestination dest;
+ ExtractDestination(m_script, dest);
+ switch (dest.which()) {
+ case 1 /* PKHash */:
+ case 2 /* ScriptHash */: return OutputType::LEGACY;
+ case 3 /* WitnessV0ScriptHash */:
+ case 4 /* WitnessV0KeyHash */:
+ case 5 /* WitnessUnknown */: return OutputType::BECH32;
+ case 0 /* CNoDestination */:
+ default: return nullopt;
+ }
+ }
};
/** A parsed pk(P) descriptor. */
@@ -547,6 +578,7 @@ protected:
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {}
+ Optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
};
/** A parsed wpkh(P) descriptor. */
@@ -561,6 +593,7 @@ protected:
}
public:
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {}
+ Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
};
/** A parsed combo(P) descriptor. */
@@ -612,6 +645,13 @@ protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(ScriptHash(*script))); }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
+
+ Optional<OutputType> GetOutputType() const override
+ {
+ assert(m_subdescriptor_arg);
+ if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
+ return OutputType::LEGACY;
+ }
};
/** A parsed wsh(...) descriptor. */
@@ -621,6 +661,7 @@ protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
public:
WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
+ Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
};
////////////////////////////////////////////////////////////////////////////
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index a5a41d78dd..58b920c681 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_SCRIPT_DESCRIPTOR_H
#define BITCOIN_SCRIPT_DESCRIPTOR_H
+#include <optional.h>
+#include <outputtype.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
@@ -71,6 +73,9 @@ struct Descriptor {
* @param[out] out Any private keys available for the specified `pos`.
*/
virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
+
+ /** @return The OutputType of the scriptPubKey(s) produced by this descriptor. Or nullopt if indeterminate (multiple or none) */
+ virtual Optional<OutputType> GetOutputType() const = 0;
};
/** Parse a `descriptor` string. Included private keys are put in `out`.
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 58eae3ce96..fe8292fe57 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -465,3 +465,54 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
}
return false;
}
+
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, std::string>& input_errors)
+{
+ bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mtx);
+ // Sign what we can:
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ input_errors[i] = "Input not found or already spent";
+ continue;
+ }
+ const CScript& prevPubKey = coin->second.out.scriptPubKey;
+ const CAmount& amount = coin->second.out.nValue;
+
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
+ // Only sign SIGHASH_SINGLE if there's a corresponding output:
+ if (!fHashSingle || (i < mtx.vout.size())) {
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ }
+
+ UpdateInput(txin, sigdata);
+
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ input_errors[i] = "Missing amount";
+ continue;
+ }
+
+ ScriptError serror = SCRIPT_ERR_OK;
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
+ if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
+ // Unable to sign input and verification failed (possible attempt to partially sign).
+ input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
+ } else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
+ // Verification failed (possibly due to insufficient signatures).
+ input_errors[i] = "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)";
+ } else {
+ input_errors[i] = ScriptErrorString(serror);
+ }
+ } else {
+ // If this input succeeds, make sure there is no error set for it
+ input_errors.erase(i);
+ }
+ }
+ return input_errors.empty();
+}
diff --git a/src/script/sign.h b/src/script/sign.h
index 033c9ba19e..f03af0713f 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
+#include <coins.h>
#include <hash.h>
#include <pubkey.h>
#include <script/interpreter.h>
@@ -168,4 +169,7 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script);
/** Check whether a scriptPubKey is known to be segwit. */
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
+/** Sign the CMutableTransaction */
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors);
+
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 6ad20480a7..76f31d2f6f 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -66,7 +66,53 @@ protected:
using KeyMap = std::map<CKeyID, CKey>;
using ScriptMap = std::map<CScriptID, CScript>;
+ /**
+ * Map of key id to unencrypted private keys known by the signing provider.
+ * Map may be empty if the provider has another source of keys, like an
+ * encrypted store.
+ */
KeyMap mapKeys GUARDED_BY(cs_KeyStore);
+
+ /**
+ * Map of script id to scripts known by the signing provider.
+ *
+ * This map originally just held P2SH redeemScripts, and was used by wallet
+ * code to look up script ids referenced in "OP_HASH160 <script id>
+ * OP_EQUAL" P2SH outputs. Later in 605e8473a7d it was extended to hold
+ * P2WSH witnessScripts as well, and used to look up nested scripts
+ * referenced in "OP_0 <script hash>" P2WSH outputs. Later in commits
+ * f4691ab3a9d and 248f3a76a82, it was extended once again to hold segwit
+ * "OP_0 <key or script hash>" scriptPubKeys, in order to give the wallet a
+ * way to distinguish between segwit outputs that it generated addresses for
+ * and wanted to receive payments from, and segwit outputs that it never
+ * generated addresses for, but it could spend just because of having keys.
+ * (Before segwit activation it was also important to not treat segwit
+ * outputs to arbitrary wallet keys as payments, because these could be
+ * spent by anyone without even needing to sign with the keys.)
+ *
+ * Some of the scripts stored in mapScripts are memory-only and
+ * intentionally not saved to disk. Specifically, scripts added by
+ * ImplicitlyLearnRelatedKeyScripts(pubkey) calls are not written to disk so
+ * future wallet code can have flexibility to be more selective about what
+ * transaction outputs it recognizes as payments, instead of having to treat
+ * all outputs spending to keys it knows as payments. By contrast,
+ * mapScripts entries added by AddCScript(script),
+ * LearnRelatedScripts(pubkey, type), and LearnAllRelatedScripts(pubkey)
+ * calls are saved because they are all intentionally used to receive
+ * payments.
+ *
+ * The FillableSigningProvider::mapScripts script map should not be confused
+ * with LegacyScriptPubKeyMan::setWatchOnly script set. The two collections
+ * can hold the same scripts, but they serve different purposes. The
+ * setWatchOnly script set is intended to expand the set of outputs the
+ * wallet considers payments. Every output with a script it contains is
+ * considered to belong to the wallet, regardless of whether the script is
+ * solvable or signable. By contrast, the scripts in mapScripts are only
+ * used for solving, and to restrict which outputs are considered payments
+ * by the wallet. An output with a script in mapScripts, unlike
+ * setWatchOnly, is not automatically considered to belong to the wallet if
+ * it can't be solved and signed for.
+ */
ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
diff --git a/src/serialize.h b/src/serialize.h
index cee7225bcb..5045cb3c7f 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -9,6 +9,7 @@
#include <compat/endian.h>
#include <algorithm>
+#include <cstring>
#include <ios>
#include <limits>
#include <map>
@@ -139,27 +140,31 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
}
inline uint64_t ser_double_to_uint64(double x)
{
- union { double x; uint64_t y; } tmp;
- tmp.x = x;
- return tmp.y;
+ uint64_t tmp;
+ std::memcpy(&tmp, &x, sizeof(x));
+ static_assert(sizeof(tmp) == sizeof(x), "double and uint64_t assumed to have the same size");
+ return tmp;
}
inline uint32_t ser_float_to_uint32(float x)
{
- union { float x; uint32_t y; } tmp;
- tmp.x = x;
- return tmp.y;
+ uint32_t tmp;
+ std::memcpy(&tmp, &x, sizeof(x));
+ static_assert(sizeof(tmp) == sizeof(x), "float and uint32_t assumed to have the same size");
+ return tmp;
}
inline double ser_uint64_to_double(uint64_t y)
{
- union { double x; uint64_t y; } tmp;
- tmp.y = y;
- return tmp.x;
+ double tmp;
+ std::memcpy(&tmp, &y, sizeof(y));
+ static_assert(sizeof(tmp) == sizeof(y), "double and uint64_t assumed to have the same size");
+ return tmp;
}
inline float ser_uint32_to_float(uint32_t y)
{
- union { float x; uint32_t y; } tmp;
- tmp.y = y;
- return tmp.x;
+ float tmp;
+ std::memcpy(&tmp, &y, sizeof(y));
+ static_assert(sizeof(tmp) == sizeof(y), "float and uint32_t assumed to have the same size");
+ return tmp;
}
@@ -495,7 +500,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&
#define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
-#define COMPACTSIZE(obj) CCompactSize(REF(obj))
+#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj)
#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj))
/** Serialization wrapper class for integers in VarInt format. */
@@ -513,6 +518,28 @@ struct VarIntFormatter
}
};
+template<int Bytes>
+struct CustomUintFormatter
+{
+ static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range");
+ static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes));
+
+ template <typename Stream, typename I> void Ser(Stream& s, I v)
+ {
+ if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
+ uint64_t raw = htole64(v);
+ s.write((const char*)&raw, Bytes);
+ }
+
+ template <typename Stream, typename I> void Unser(Stream& s, I& v)
+ {
+ static_assert(std::numeric_limits<I>::max() >= MAX && std::numeric_limits<I>::min() <= 0, "CustomUintFormatter type too small");
+ uint64_t raw = 0;
+ s.read((char*)&raw, Bytes);
+ v = le64toh(raw);
+ }
+};
+
/** Serialization wrapper class for big-endian integers.
*
* Use this wrapper around integer types that are stored in memory in native
@@ -547,21 +574,26 @@ public:
}
};
-class CCompactSize
+/** Formatter for integers in CompactSize format. */
+struct CompactSizeFormatter
{
-protected:
- uint64_t &n;
-public:
- explicit CCompactSize(uint64_t& nIn) : n(nIn) { }
-
- template<typename Stream>
- void Serialize(Stream &s) const {
- WriteCompactSize<Stream>(s, n);
+ template<typename Stream, typename I>
+ void Unser(Stream& s, I& v)
+ {
+ uint64_t n = ReadCompactSize<Stream>(s);
+ if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) {
+ throw std::ios_base::failure("CompactSize exceeds limit of type");
+ }
+ v = n;
}
- template<typename Stream>
- void Unserialize(Stream& s) {
- n = ReadCompactSize<Stream>(s);
+ template<typename Stream, typename I>
+ void Ser(Stream& s, I v)
+ {
+ static_assert(std::is_unsigned<I>::value, "CompactSize only supported for unsigned integers");
+ static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max(), "CompactSize only supports 64-bit integers and below");
+
+ WriteCompactSize<Stream>(s, v);
}
};
@@ -608,7 +640,7 @@ BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); }
* as a vector of VarInt-encoded integers.
*
* V is not required to be an std::vector type. It works for any class that
- * exposes a value_type, size, reserve, push_back, and const iterators.
+ * exposes a value_type, size, reserve, emplace_back, back, and const iterators.
*/
template<class Formatter>
struct VectorFormatter
@@ -616,15 +648,17 @@ struct VectorFormatter
template<typename Stream, typename V>
void Ser(Stream& s, const V& v)
{
+ Formatter formatter;
WriteCompactSize(s, v.size());
for (const typename V::value_type& elem : v) {
- s << Using<Formatter>(elem);
+ formatter.Ser(s, elem);
}
}
template<typename Stream, typename V>
void Unser(Stream& s, V& v)
{
+ Formatter formatter;
v.clear();
size_t size = ReadCompactSize(s);
size_t allocated = 0;
@@ -636,9 +670,8 @@ struct VectorFormatter
allocated = std::min(size, allocated + MAX_VECTOR_ALLOCATE / sizeof(typename V::value_type));
v.reserve(allocated);
while (v.size() < allocated) {
- typename V::value_type val;
- s >> Using<Formatter>(val);
- v.push_back(std::move(val));
+ v.emplace_back();
+ formatter.Unser(s, v.back());
}
}
};
diff --git a/src/sync.cpp b/src/sync.cpp
index 924e7b5bb0..71657a7439 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -13,7 +13,7 @@
#include <util/strencodings.h>
#include <util/threadnames.h>
-
+#include <system_error>
#include <map>
#include <set>
@@ -60,6 +60,11 @@ struct CLockLocation {
mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
}
+ std::string Name() const
+ {
+ return mutexName;
+ }
+
private:
bool fTry;
std::string mutexName;
@@ -155,6 +160,18 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
}
+void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
+{
+ if (!g_lockstack.empty()) {
+ const auto& lastlock = g_lockstack.back();
+ if (lastlock.first == cs) {
+ lockname = lastlock.second.Name();
+ return;
+ }
+ }
+ throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname));
+}
+
void LeaveCritical()
{
pop_lock();
diff --git a/src/sync.h b/src/sync.h
index 0cdbb59c70..204734c273 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -50,6 +50,7 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();
+void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
@@ -64,6 +65,7 @@ extern bool g_debug_lockorder_abort;
#else
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
+void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
@@ -171,8 +173,45 @@ public:
{
return Base::owns_lock();
}
+
+protected:
+ // needed for reverse_lock
+ UniqueLock() { }
+
+public:
+ /**
+ * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
+ */
+ class reverse_lock {
+ public:
+ explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
+ CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
+ lock.unlock();
+ LeaveCritical();
+ lock.swap(templock);
+ }
+
+ ~reverse_lock() {
+ templock.swap(lock);
+ EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex());
+ lock.lock();
+ }
+
+ private:
+ reverse_lock(reverse_lock const&);
+ reverse_lock& operator=(reverse_lock const&);
+
+ UniqueLock& lock;
+ UniqueLock templock;
+ std::string lockname;
+ const std::string file;
+ const int line;
+ };
+ friend class reverse_lock;
};
+#define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
+
template<typename MutexArg>
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 79e18cd2c0..5e52dc268f 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
int64_t time_start = GetTimeMillis();
while (!filter_index.BlockUntilSyncedToCurrentChain()) {
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
// Check that filter index has all blocks that were in the chain before it started.
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index d5e2344a8b..0b4bfdb019 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -11,16 +11,16 @@ BOOST_FIXTURE_TEST_SUITE(bswap_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(bswap_tests)
{
- // Sibling in bitcoin/src/qt/test/compattests.cpp
- uint16_t u1 = 0x1234;
- uint32_t u2 = 0x56789abc;
- uint64_t u3 = 0xdef0123456789abc;
- uint16_t e1 = 0x3412;
- uint32_t e2 = 0xbc9a7856;
- uint64_t e3 = 0xbc9a78563412f0de;
- BOOST_CHECK(bswap_16(u1) == e1);
- BOOST_CHECK(bswap_32(u2) == e2);
- BOOST_CHECK(bswap_64(u3) == e3);
+ // Sibling in bitcoin/src/qt/test/compattests.cpp
+ uint16_t u1 = 0x1234;
+ uint32_t u2 = 0x56789abc;
+ uint64_t u3 = 0xdef0123456789abc;
+ uint16_t e1 = 0x3412;
+ uint32_t e2 = 0xbc9a7856;
+ uint64_t e3 = 0xbc9a78563412f0de;
+ BOOST_CHECK(bswap_16(u1) == e1);
+ BOOST_CHECK(bswap_32(u2) == e2);
+ BOOST_CHECK(bswap_64(u3) == e3);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 482fe3772c..a9628e85f9 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
CCheckQueueControl<FakeCheck> control(queue.get());
// While sleeping, no other thread should execute to this point
auto observed = ++nThreads;
- MilliSleep(10);
+ UninterruptibleSleep(std::chrono::milliseconds{10});
fails += observed != nThreads;
});
}
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index bcce8854e3..e99aee724e 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -62,7 +62,7 @@ std::string UseHInsteadOfApostrophe(const std::string& desc)
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
-void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
+void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const 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)
{
FlatSigningProvider keys_priv, keys_pub;
@@ -86,6 +86,10 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
BOOST_CHECK(parse_priv);
BOOST_CHECK(parse_pub);
+ // Check that the correct OutputType is inferred
+ BOOST_CHECK(parse_priv->GetOutputType() == type);
+ BOOST_CHECK(parse_pub->GetOutputType() == type);
+
// Check private keys are extracted from the private version but not the public one.
BOOST_CHECK(keys_priv.keys.size());
BOOST_CHECK(!keys_pub.keys.size());
@@ -181,29 +185,29 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
-void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
+void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const Optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
{
bool found_apostrophes_in_prv = false;
bool found_apostrophes_in_pub = false;
// Do not replace apostrophes with 'h' in prv and pub
- DoCheck(prv, pub, flags, scripts, paths);
+ DoCheck(prv, pub, flags, scripts, type, paths);
// Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv
if (prv.find('\'') != std::string::npos) {
found_apostrophes_in_prv = true;
- DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
+ DoCheck(prv, pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
}
// Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub
if (pub.find('\'') != std::string::npos) {
found_apostrophes_in_pub = true;
- DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
}
// Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both
if (found_apostrophes_in_prv && found_apostrophes_in_pub) {
- DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
}
}
@@ -214,50 +218,50 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(descriptor_test)
{
// Basic single-key compressed
- Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
- Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}});
- Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, {{1,0x80000002UL,3,0x80000004UL}});
- Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}});
- Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
+ Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, nullopt);
+ Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, nullopt);
+ Check("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)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
+ Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
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
// Basic single-key uncompressed
- Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
- Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}});
- Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
+ Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, nullopt);
+ Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, nullopt);
+ Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY);
CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
// Some unconventional single-key constructions
- Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}});
- Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}});
- Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}});
- Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}});
- Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}});
- Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}});
+ Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY);
+ Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY);
+ Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}}, OutputType::BECH32);
+ Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
+ Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
+ Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
// Versions with BIP32 derivations
- Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}});
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, {{0}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, {{0xFFFFFFFFUL,0}});
- Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, {{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/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
- Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, {{0}, {1}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, nullopt);
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, nullopt, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}});
+ Check("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/*'))", RANGE | 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/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, nullopt, {{0}, {1}});
CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "Key path value 2147483648 is out of range"); // BIP 32 path element overflow
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "Key path value '1aa' is not a valid uint32"); // Path is not valid uint
// Multisig constructions
- Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
- Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
- Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
- Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, {{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/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, {{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)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}});
+ Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
+ Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
+ Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, 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/*'))", HARDENED | RANGE, {{"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)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT);
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
@@ -280,8 +284,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have wsh within wsh"); // Cannot embed P2WSH inside P2WSH
// Checksums
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum
CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
new file mode 100644
index 0000000000..d1112f8e62
--- /dev/null
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -0,0 +1,80 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bloom.h>
+#include <optional.h>
+#include <primitives/transaction.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <uint256.h>
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ CBloomFilter bloom_filter{
+ fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 10000000),
+ 1.0 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max()),
+ fuzzed_data_provider.ConsumeIntegral<unsigned int>(),
+ static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))};
+ while (fuzzed_data_provider.remaining_bytes() > 0) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 6)) {
+ case 0: {
+ const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ (void)bloom_filter.contains(b);
+ bloom_filter.insert(b);
+ const bool present = bloom_filter.contains(b);
+ assert(present);
+ break;
+ }
+ case 1: {
+ const Optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ if (!out_point) {
+ break;
+ }
+ (void)bloom_filter.contains(*out_point);
+ bloom_filter.insert(*out_point);
+ const bool present = bloom_filter.contains(*out_point);
+ assert(present);
+ break;
+ }
+ case 2: {
+ const Optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+ if (!u256) {
+ break;
+ }
+ (void)bloom_filter.contains(*u256);
+ bloom_filter.insert(*u256);
+ const bool present = bloom_filter.contains(*u256);
+ assert(present);
+ break;
+ }
+ case 3:
+ bloom_filter.clear();
+ break;
+ case 4:
+ bloom_filter.reset(fuzzed_data_provider.ConsumeIntegral<unsigned int>());
+ break;
+ case 5: {
+ const Optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!mut_tx) {
+ break;
+ }
+ const CTransaction tx{*mut_tx};
+ (void)bloom_filter.IsRelevantAndUpdate(tx);
+ break;
+ }
+ case 6:
+ bloom_filter.UpdateEmptyFull();
+ break;
+ }
+ (void)bloom_filter.IsWithinSizeConstraints();
+ }
+}
diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp
new file mode 100644
index 0000000000..a24bae5b35
--- /dev/null
+++ b/src/test/fuzz/float.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <memusage.h>
+#include <serialize.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <version.h>
+
+#include <cassert>
+#include <cstdint>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ {
+ const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>();
+ (void)memusage::DynamicUsage(d);
+ assert(ser_uint64_to_double(ser_double_to_uint64(d)) == d);
+
+ CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
+ stream << d;
+ double d_deserialized;
+ stream >> d_deserialized;
+ assert(d == d_deserialized);
+ }
+
+ {
+ const float f = fuzzed_data_provider.ConsumeFloatingPoint<float>();
+ (void)memusage::DynamicUsage(f);
+ assert(ser_uint32_to_float(ser_float_to_uint32(f)) == f);
+
+ CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
+ stream << f;
+ float f_deserialized;
+ stream >> f_deserialized;
+ assert(f == f_deserialized);
+ }
+}
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index 54693180be..2de6100d7b 100644
--- a/src/test/fuzz/hex.cpp
+++ b/src/test/fuzz/hex.cpp
@@ -2,8 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <core_io.h>
+#include <primitives/block.h>
+#include <rpc/util.h>
#include <test/fuzz/fuzz.h>
-
+#include <uint256.h>
+#include <univalue.h>
#include <util/strencodings.h>
#include <cassert>
@@ -19,4 +23,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
if (IsHex(random_hex_string)) {
assert(ToLower(random_hex_string) == hex_data);
}
+ (void)IsHexNumber(random_hex_string);
+ uint256 result;
+ (void)ParseHashStr(random_hex_string, result);
+ (void)uint256S(random_hex_string);
+ try {
+ (void)HexToPubKey(random_hex_string);
+ } catch (const UniValue&) {
+ }
+ CBlockHeader block_header;
+ (void)DecodeHexBlockHeader(block_header, random_hex_string);
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 723938bcdb..350d3d3331 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <amount.h>
#include <arith_uint256.h>
#include <compressor.h>
#include <consensus/merkle.h>
@@ -18,12 +19,15 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <uint256.h>
+#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/time.h>
+#include <version.h>
#include <cassert>
#include <limits>
@@ -53,10 +57,18 @@ void test_one_input(const std::vector<uint8_t>& buffer)
// We cannot assume a specific value of std::is_signed<char>::value:
// ConsumeIntegral<char>() instead of casting from {u,}int8_t.
const char ch = fuzzed_data_provider.ConsumeIntegral<char>();
+ const bool b = fuzzed_data_provider.ConsumeBool();
const Consensus::Params& consensus_params = Params().GetConsensus();
(void)CheckProofOfWork(u256, u32, consensus_params);
- (void)CompressAmount(u64);
+ if (u64 <= MAX_MONEY) {
+ const uint64_t compressed_money_amount = CompressAmount(u64);
+ assert(u64 == DecompressAmount(compressed_money_amount));
+ static const uint64_t compressed_money_amount_max = CompressAmount(MAX_MONEY - 1);
+ assert(compressed_money_amount <= compressed_money_amount_max);
+ } else {
+ (void)CompressAmount(u64);
+ }
static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000"));
static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
const std::vector<uint256> v256{u256, u256_min, u256_max};
@@ -65,11 +77,19 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)DecompressAmount(u64);
(void)FormatISO8601Date(i64);
(void)FormatISO8601DateTime(i64);
+ // FormatMoney(i) not defined when i == std::numeric_limits<int64_t>::min()
+ if (i64 != std::numeric_limits<int64_t>::min()) {
+ int64_t parsed_money;
+ if (ParseMoney(FormatMoney(i64), parsed_money)) {
+ assert(parsed_money == i64);
+ }
+ }
(void)GetSizeOfCompactSize(u64);
(void)GetSpecialScriptSize(u32);
// (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs
// (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs
(void)HexDigit(ch);
+ (void)MoneyRange(i64);
(void)i64tostr(i64);
(void)IsDigit(ch);
(void)IsSpace(ch);
@@ -95,6 +115,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)SipHashUint256(u64, u64, u256);
(void)SipHashUint256Extra(u64, u64, u256, u32);
(void)ToLower(ch);
+ (void)ToUpper(ch);
+ // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
+ if (i64 != std::numeric_limits<int64_t>::min()) {
+ int64_t parsed_money;
+ if (ParseMoney(ValueFromAmount(i64).getValStr(), parsed_money)) {
+ assert(parsed_money == i64);
+ }
+ }
const arith_uint256 au256 = UintToArith256(u256);
assert(ArithToUint256(au256) == u256);
@@ -124,4 +152,68 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)GetScriptForDestination(destination);
(void)IsValidDestination(destination);
}
+
+ {
+ CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
+
+ uint256 deserialized_u256;
+ stream << u256;
+ stream >> deserialized_u256;
+ assert(u256 == deserialized_u256 && stream.empty());
+
+ uint160 deserialized_u160;
+ stream << u160;
+ stream >> deserialized_u160;
+ assert(u160 == deserialized_u160 && stream.empty());
+
+ uint64_t deserialized_u64;
+ stream << u64;
+ stream >> deserialized_u64;
+ assert(u64 == deserialized_u64 && stream.empty());
+
+ int64_t deserialized_i64;
+ stream << i64;
+ stream >> deserialized_i64;
+ assert(i64 == deserialized_i64 && stream.empty());
+
+ uint32_t deserialized_u32;
+ stream << u32;
+ stream >> deserialized_u32;
+ assert(u32 == deserialized_u32 && stream.empty());
+
+ int32_t deserialized_i32;
+ stream << i32;
+ stream >> deserialized_i32;
+ assert(i32 == deserialized_i32 && stream.empty());
+
+ uint16_t deserialized_u16;
+ stream << u16;
+ stream >> deserialized_u16;
+ assert(u16 == deserialized_u16 && stream.empty());
+
+ int16_t deserialized_i16;
+ stream << i16;
+ stream >> deserialized_i16;
+ assert(i16 == deserialized_i16 && stream.empty());
+
+ uint8_t deserialized_u8;
+ stream << u8;
+ stream >> deserialized_u8;
+ assert(u8 == deserialized_u8 && stream.empty());
+
+ int8_t deserialized_i8;
+ stream << i8;
+ stream >> deserialized_i8;
+ assert(i8 == deserialized_i8 && stream.empty());
+
+ char deserialized_ch;
+ stream << ch;
+ stream >> deserialized_ch;
+ assert(ch == deserialized_ch && stream.empty());
+
+ bool deserialized_b;
+ stream << b;
+ stream >> deserialized_b;
+ assert(b == deserialized_b && stream.empty());
+ }
}
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
new file mode 100644
index 0000000000..1919a5f881
--- /dev/null
+++ b/src/test/fuzz/key.cpp
@@ -0,0 +1,309 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <key.h>
+#include <key_io.h>
+#include <outputtype.h>
+#include <policy/policy.h>
+#include <pubkey.h>
+#include <rpc/util.h>
+#include <script/keyorigin.h>
+#include <script/script.h>
+#include <script/sign.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
+#include <streams.h>
+#include <test/fuzz/fuzz.h>
+#include <util/memory.h>
+#include <util/strencodings.h>
+
+#include <cassert>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const CKey key = [&] {
+ CKey k;
+ k.Set(buffer.begin(), buffer.end(), true);
+ return k;
+ }();
+ if (!key.IsValid()) {
+ return;
+ }
+
+ {
+ assert(key.begin() + key.size() == key.end());
+ assert(key.IsCompressed());
+ assert(key.size() == 32);
+ assert(DecodeSecret(EncodeSecret(key)) == key);
+ }
+
+ {
+ CKey invalid_key;
+ assert(!(invalid_key == key));
+ assert(!invalid_key.IsCompressed());
+ assert(!invalid_key.IsValid());
+ assert(invalid_key.size() == 0);
+ }
+
+ {
+ CKey uncompressed_key;
+ uncompressed_key.Set(buffer.begin(), buffer.end(), false);
+ assert(!(uncompressed_key == key));
+ assert(!uncompressed_key.IsCompressed());
+ assert(key.size() == 32);
+ assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end());
+ assert(uncompressed_key.IsValid());
+ }
+
+ {
+ CKey copied_key;
+ copied_key.Set(key.begin(), key.end(), key.IsCompressed());
+ assert(copied_key == key);
+ }
+
+ {
+ CKey negated_key = key;
+ negated_key.Negate();
+ assert(negated_key.IsValid());
+ assert(!(negated_key == key));
+
+ negated_key.Negate();
+ assert(negated_key == key);
+ }
+
+ const uint256 random_uint256 = Hash(buffer.begin(), buffer.end());
+
+ {
+ CKey child_key;
+ ChainCode child_chaincode;
+ const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
+ assert(ok);
+ assert(child_key.IsValid());
+ assert(!(child_key == key));
+ assert(child_chaincode != random_uint256);
+ }
+
+ const CPubKey pubkey = key.GetPubKey();
+
+ {
+ assert(pubkey.size() == 33);
+ assert(key.VerifyPubKey(pubkey));
+ assert(pubkey.GetHash() != random_uint256);
+ assert(pubkey.begin() + pubkey.size() == pubkey.end());
+ assert(pubkey.data() == pubkey.begin());
+ assert(pubkey.IsCompressed());
+ assert(pubkey.IsValid());
+ assert(pubkey.IsFullyValid());
+ assert(HexToPubKey(HexStr(pubkey.begin(), pubkey.end())) == pubkey);
+ assert(GetAllDestinationsForKey(pubkey).size() == 3);
+ }
+
+ {
+ CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
+ pubkey.Serialize(data_stream);
+
+ CPubKey pubkey_deserialized;
+ pubkey_deserialized.Unserialize(data_stream);
+ assert(pubkey_deserialized == pubkey);
+ }
+
+ {
+ const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
+ assert(!tx_pubkey_script.IsPayToScriptHash());
+ assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
+ assert(!tx_pubkey_script.IsPushOnly());
+ assert(!tx_pubkey_script.IsUnspendable());
+ assert(tx_pubkey_script.HasValidOps());
+ assert(tx_pubkey_script.size() == 35);
+
+ const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
+ assert(!tx_multisig_script.IsPayToScriptHash());
+ assert(!tx_multisig_script.IsPayToWitnessScriptHash());
+ assert(!tx_multisig_script.IsPushOnly());
+ assert(!tx_multisig_script.IsUnspendable());
+ assert(tx_multisig_script.HasValidOps());
+ assert(tx_multisig_script.size() == 37);
+
+ FillableSigningProvider fillable_signing_provider;
+ assert(IsSolvable(fillable_signing_provider, tx_pubkey_script));
+ assert(IsSolvable(fillable_signing_provider, tx_multisig_script));
+ assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
+ assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
+ assert(fillable_signing_provider.GetKeys().size() == 0);
+ assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
+
+ const bool ok_add_key = fillable_signing_provider.AddKey(key);
+ assert(ok_add_key);
+ assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
+
+ FillableSigningProvider fillable_signing_provider_pub;
+ assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
+
+ const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
+ assert(ok_add_key_pubkey);
+ assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
+
+ txnouttype which_type_tx_pubkey;
+ const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey);
+ assert(is_standard_tx_pubkey);
+ assert(which_type_tx_pubkey == txnouttype::TX_PUBKEY);
+
+ txnouttype which_type_tx_multisig;
+ const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig);
+ assert(is_standard_tx_multisig);
+ assert(which_type_tx_multisig == txnouttype::TX_MULTISIG);
+
+ std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
+ const txnouttype outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
+ assert(outtype_tx_pubkey == txnouttype::TX_PUBKEY);
+ assert(v_solutions_ret_tx_pubkey.size() == 1);
+ assert(v_solutions_ret_tx_pubkey[0].size() == 33);
+
+ std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
+ const txnouttype outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
+ assert(outtype_tx_multisig == txnouttype::TX_MULTISIG);
+ assert(v_solutions_ret_tx_multisig.size() == 3);
+ assert(v_solutions_ret_tx_multisig[0].size() == 1);
+ assert(v_solutions_ret_tx_multisig[1].size() == 33);
+ assert(v_solutions_ret_tx_multisig[2].size() == 1);
+
+ OutputType output_type{};
+ const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
+ assert(output_type == OutputType::LEGACY);
+ assert(IsValidDestination(tx_destination));
+ assert(CTxDestination{PKHash{pubkey}} == tx_destination);
+
+ const CScript script_for_destination = GetScriptForDestination(tx_destination);
+ assert(script_for_destination.size() == 25);
+
+ const std::string destination_address = EncodeDestination(tx_destination);
+ assert(DecodeDestination(destination_address) == tx_destination);
+
+ const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
+ assert(pubkey_from_address_string == pubkey);
+
+ CKeyID key_id = pubkey.GetID();
+ assert(!key_id.IsNull());
+ assert(key_id == CKeyID{key_id});
+ assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
+
+ CPubKey pubkey_out;
+ const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
+ assert(ok_get_pubkey);
+
+ CKey key_out;
+ const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
+ assert(ok_get_key);
+ assert(fillable_signing_provider.GetKeys().size() == 1);
+ assert(fillable_signing_provider.HaveKey(key_id));
+
+ KeyOriginInfo key_origin_info;
+ const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
+ assert(!ok_get_key_origin);
+ }
+
+ {
+ const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
+ assert(CPubKey::ValidSize(vch_pubkey));
+ assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
+
+ const CPubKey pubkey_ctor_1{vch_pubkey};
+ assert(pubkey == pubkey_ctor_1);
+
+ const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
+ assert(pubkey == pubkey_ctor_2);
+
+ CPubKey pubkey_set;
+ pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
+ assert(pubkey == pubkey_set);
+ }
+
+ {
+ const CPubKey invalid_pubkey{};
+ assert(!invalid_pubkey.IsValid());
+ assert(!invalid_pubkey.IsFullyValid());
+ assert(!(pubkey == invalid_pubkey));
+ assert(pubkey != invalid_pubkey);
+ assert(pubkey < invalid_pubkey);
+ }
+
+ {
+ // Cover CPubKey's operator[](unsigned int pos)
+ unsigned int sum = 0;
+ for (size_t i = 0; i < pubkey.size(); ++i) {
+ sum += pubkey[i];
+ }
+ assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
+ }
+
+ {
+ CPubKey decompressed_pubkey = pubkey;
+ assert(decompressed_pubkey.IsCompressed());
+
+ const bool ok = decompressed_pubkey.Decompress();
+ assert(ok);
+ assert(!decompressed_pubkey.IsCompressed());
+ assert(decompressed_pubkey.size() == 65);
+ }
+
+ {
+ std::vector<unsigned char> vch_sig;
+ const bool ok = key.Sign(random_uint256, vch_sig, false);
+ assert(ok);
+ assert(pubkey.Verify(random_uint256, vch_sig));
+ assert(CPubKey::CheckLowS(vch_sig));
+
+ const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
+ assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
+ assert(!CPubKey::CheckLowS(vch_invalid_sig));
+ }
+
+ {
+ std::vector<unsigned char> vch_compact_sig;
+ const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
+ assert(ok_sign_compact);
+
+ CPubKey recover_pubkey;
+ const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
+ assert(ok_recover_compact);
+ assert(recover_pubkey == pubkey);
+ }
+
+ {
+ CPubKey child_pubkey;
+ ChainCode child_chaincode;
+ const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
+ assert(ok);
+ assert(child_pubkey != pubkey);
+ assert(child_pubkey.IsCompressed());
+ assert(child_pubkey.IsFullyValid());
+ assert(child_pubkey.IsValid());
+ assert(child_pubkey.size() == 33);
+ assert(child_chaincode != random_uint256);
+ }
+
+ const CPrivKey priv_key = key.GetPrivKey();
+
+ {
+ for (const bool skip_check : {true, false}) {
+ CKey loaded_key;
+ const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
+ assert(ok);
+ assert(key == loaded_key);
+ }
+ }
+}
diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp
new file mode 100644
index 0000000000..f44628b0f8
--- /dev/null
+++ b/src/test/fuzz/key_io.cpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <key_io.h>
+#include <rpc/util.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
+#include <test/fuzz/fuzz.h>
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string random_string(buffer.begin(), buffer.end());
+
+ const CKey key = DecodeSecret(random_string);
+ if (key.IsValid()) {
+ assert(key == DecodeSecret(EncodeSecret(key)));
+ }
+
+ const CExtKey ext_key = DecodeExtKey(random_string);
+ if (ext_key.key.size() == 32) {
+ assert(ext_key == DecodeExtKey(EncodeExtKey(ext_key)));
+ }
+
+ const CExtPubKey ext_pub_key = DecodeExtPubKey(random_string);
+ if (ext_pub_key.pubkey.size() == CPubKey::COMPRESSED_SIZE) {
+ assert(ext_pub_key == DecodeExtPubKey(EncodeExtPubKey(ext_pub_key)));
+ }
+
+ const CTxDestination tx_destination = DecodeDestination(random_string);
+ (void)DescribeAddress(tx_destination);
+ (void)GetKeyForDestination(/* store */ {}, tx_destination);
+ (void)GetScriptForDestination(tx_destination);
+ (void)IsValidDestination(tx_destination);
+
+ (void)IsValidDestinationString(random_string);
+}
diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp
new file mode 100644
index 0000000000..c8288123e8
--- /dev/null
+++ b/src/test/fuzz/locale.cpp
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+
+#include <cassert>
+#include <clocale>
+#include <cstdint>
+#include <locale>
+#include <string>
+#include <vector>
+
+namespace {
+const std::string locale_identifiers[] = {
+ "C", "C.UTF-8", "aa_DJ", "aa_DJ.ISO-8859-1", "aa_DJ.UTF-8", "aa_ER", "aa_ER.UTF-8", "aa_ET", "aa_ET.UTF-8", "af_ZA", "af_ZA.ISO-8859-1", "af_ZA.UTF-8", "agr_PE", "agr_PE.UTF-8", "ak_GH", "ak_GH.UTF-8", "am_ET", "am_ET.UTF-8", "an_ES", "an_ES.ISO-8859-15", "an_ES.UTF-8", "anp_IN", "anp_IN.UTF-8", "ar_AE", "ar_AE.ISO-8859-6", "ar_AE.UTF-8", "ar_BH", "ar_BH.ISO-8859-6", "ar_BH.UTF-8", "ar_DZ", "ar_DZ.ISO-8859-6", "ar_DZ.UTF-8", "ar_EG", "ar_EG.ISO-8859-6", "ar_EG.UTF-8", "ar_IN", "ar_IN.UTF-8", "ar_IQ", "ar_IQ.ISO-8859-6", "ar_IQ.UTF-8", "ar_JO", "ar_JO.ISO-8859-6", "ar_JO.UTF-8", "ar_KW", "ar_KW.ISO-8859-6", "ar_KW.UTF-8", "ar_LB", "ar_LB.ISO-8859-6", "ar_LB.UTF-8", "ar_LY", "ar_LY.ISO-8859-6", "ar_LY.UTF-8", "ar_MA", "ar_MA.ISO-8859-6", "ar_MA.UTF-8", "ar_OM", "ar_OM.ISO-8859-6", "ar_OM.UTF-8", "ar_QA", "ar_QA.ISO-8859-6", "ar_QA.UTF-8", "ar_SA", "ar_SA.ISO-8859-6", "ar_SA.UTF-8", "ar_SD", "ar_SD.ISO-8859-6", "ar_SD.UTF-8", "ar_SS", "ar_SS.UTF-8", "ar_SY", "ar_SY.ISO-8859-6", "ar_SY.UTF-8", "ar_TN", "ar_TN.ISO-8859-6", "ar_TN.UTF-8", "ar_YE", "ar_YE.ISO-8859-6", "ar_YE.UTF-8", "as_IN", "as_IN.UTF-8", "ast_ES", "ast_ES.ISO-8859-15", "ast_ES.UTF-8", "ayc_PE", "ayc_PE.UTF-8", "az_AZ", "az_AZ.UTF-8", "az_IR", "az_IR.UTF-8", "be_BY", "be_BY.CP1251", "be_BY.UTF-8", "bem_ZM", "bem_ZM.UTF-8", "ber_DZ", "ber_DZ.UTF-8", "ber_MA", "ber_MA.UTF-8", "bg_BG", "bg_BG.CP1251", "bg_BG.UTF-8", "bho_IN", "bho_IN.UTF-8", "bho_NP", "bho_NP.UTF-8", "bi_VU", "bi_VU.UTF-8", "bn_BD", "bn_BD.UTF-8", "bn_IN", "bn_IN.UTF-8", "bo_CN", "bo_CN.UTF-8", "bo_IN", "bo_IN.UTF-8", "br_FR", "br_FR.ISO-8859-1", "br_FR.UTF-8", "brx_IN", "brx_IN.UTF-8", "bs_BA", "bs_BA.ISO-8859-2", "bs_BA.UTF-8", "byn_ER", "byn_ER.UTF-8", "ca_AD", "ca_AD.ISO-8859-15", "ca_AD.UTF-8", "ca_ES", "ca_ES.ISO-8859-1", "ca_ES.UTF-8", "ca_FR", "ca_FR.ISO-8859-15", "ca_FR.UTF-8", "ca_IT", "ca_IT.ISO-8859-15", "ca_IT.UTF-8", "ce_RU", "ce_RU.UTF-8", "chr_US", "chr_US.UTF-8", "ckb_IQ", "ckb_IQ.UTF-8", "cmn_TW", "cmn_TW.UTF-8", "crh_UA", "crh_UA.UTF-8", "csb_PL", "csb_PL.UTF-8", "cs_CZ", "cs_CZ.ISO-8859-2", "cs_CZ.UTF-8", "cv_RU", "cv_RU.UTF-8", "cy_GB", "cy_GB.ISO-8859-14", "cy_GB.UTF-8", "da_DK", "da_DK.ISO-8859-1", "da_DK.UTF-8", "de_AT", "de_AT.ISO-8859-1", "de_AT.UTF-8", "de_BE", "de_BE.ISO-8859-1", "de_BE.UTF-8", "de_CH", "de_CH.ISO-8859-1", "de_CH.UTF-8", "de_DE", "de_DE.ISO-8859-1", "de_DE.UTF-8", "de_IT", "de_IT.ISO-8859-1", "de_IT.UTF-8", "de_LU", "de_LU.ISO-8859-1", "de_LU.UTF-8", "doi_IN", "doi_IN.UTF-8", "dv_MV", "dv_MV.UTF-8", "dz_BT", "dz_BT.UTF-8", "el_CY", "el_CY.ISO-8859-7", "el_CY.UTF-8", "el_GR", "el_GR.ISO-8859-7", "el_GR.UTF-8", "en_AG", "en_AG.UTF-8", "en_AU", "en_AU.ISO-8859-1", "en_AU.UTF-8", "en_BW", "en_BW.ISO-8859-1", "en_BW.UTF-8", "en_CA", "en_CA.ISO-8859-1", "en_CA.UTF-8", "en_DK", "en_DK.ISO-8859-1", "en_DK.ISO-8859-15", "en_DK.UTF-8", "en_GB", "en_GB.ISO-8859-1", "en_GB.ISO-8859-15", "en_GB.UTF-8", "en_HK", "en_HK.ISO-8859-1", "en_HK.UTF-8", "en_IE", "en_IE.ISO-8859-1", "en_IE.UTF-8", "en_IL", "en_IL.UTF-8", "en_IN", "en_IN.UTF-8", "en_NG", "en_NG.UTF-8", "en_NZ", "en_NZ.ISO-8859-1", "en_NZ.UTF-8", "en_PH", "en_PH.ISO-8859-1", "en_PH.UTF-8", "en_SG", "en_SG.ISO-8859-1", "en_SG.UTF-8", "en_US", "en_US.ISO-8859-1", "en_US.ISO-8859-15", "en_US.UTF-8", "en_ZA", "en_ZA.ISO-8859-1", "en_ZA.UTF-8", "en_ZM", "en_ZM.UTF-8", "en_ZW", "en_ZW.ISO-8859-1", "en_ZW.UTF-8", "es_AR", "es_AR.ISO-8859-1", "es_AR.UTF-8", "es_BO", "es_BO.ISO-8859-1", "es_BO.UTF-8", "es_CL", "es_CL.ISO-8859-1", "es_CL.UTF-8", "es_CO", "es_CO.ISO-8859-1", "es_CO.UTF-8", "es_CR", "es_CR.ISO-8859-1", "es_CR.UTF-8", "es_CU", "es_CU.UTF-8", "es_DO", "es_DO.ISO-8859-1", "es_DO.UTF-8", "es_EC", "es_EC.ISO-8859-1", "es_EC.UTF-8", "es_ES", "es_ES.ISO-8859-1", "es_ES.UTF-8", "es_GT", "es_GT.ISO-8859-1", "es_GT.UTF-8", "es_HN", "es_HN.ISO-8859-1", "es_HN.UTF-8", "es_MX", "es_MX.ISO-8859-1", "es_MX.UTF-8", "es_NI", "es_NI.ISO-8859-1", "es_NI.UTF-8", "es_PA", "es_PA.ISO-8859-1", "es_PA.UTF-8", "es_PE", "es_PE.ISO-8859-1", "es_PE.UTF-8", "es_PR", "es_PR.ISO-8859-1", "es_PR.UTF-8", "es_PY", "es_PY.ISO-8859-1", "es_PY.UTF-8", "es_SV", "es_SV.ISO-8859-1", "es_SV.UTF-8", "es_US", "es_US.ISO-8859-1", "es_US.UTF-8", "es_UY", "es_UY.ISO-8859-1", "es_UY.UTF-8", "es_VE", "es_VE.ISO-8859-1", "es_VE.UTF-8", "et_EE", "et_EE.ISO-8859-1", "et_EE.ISO-8859-15", "et_EE.UTF-8", "eu_ES", "eu_ES.ISO-8859-1", "eu_ES.UTF-8", "eu_FR", "eu_FR.ISO-8859-1", "eu_FR.UTF-8", "fa_IR", "fa_IR.UTF-8", "ff_SN", "ff_SN.UTF-8", "fi_FI", "fi_FI.ISO-8859-1", "fi_FI.UTF-8", "fil_PH", "fil_PH.UTF-8", "fo_FO", "fo_FO.ISO-8859-1", "fo_FO.UTF-8", "fr_BE", "fr_BE.ISO-8859-1", "fr_BE.UTF-8", "fr_CA", "fr_CA.ISO-8859-1", "fr_CA.UTF-8", "fr_CH", "fr_CH.ISO-8859-1", "fr_CH.UTF-8", "fr_FR", "fr_FR.ISO-8859-1", "fr_FR.UTF-8", "fr_LU", "fr_LU.ISO-8859-1", "fr_LU.UTF-8", "fur_IT", "fur_IT.UTF-8", "fy_DE", "fy_DE.UTF-8", "fy_NL", "fy_NL.UTF-8", "ga_IE", "ga_IE.ISO-8859-1", "ga_IE.UTF-8", "gd_GB", "gd_GB.ISO-8859-15", "gd_GB.UTF-8", "gez_ER", "gez_ER.UTF-8", "gez_ET", "gez_ET.UTF-8", "gl_ES", "gl_ES.ISO-8859-1", "gl_ES.UTF-8", "gu_IN", "gu_IN.UTF-8", "gv_GB", "gv_GB.ISO-8859-1", "gv_GB.UTF-8", "hak_TW", "hak_TW.UTF-8", "ha_NG", "ha_NG.UTF-8", "he_IL", "he_IL.ISO-8859-8", "he_IL.UTF-8", "hif_FJ", "hif_FJ.UTF-8", "hi_IN", "hi_IN.UTF-8", "hne_IN", "hne_IN.UTF-8", "hr_HR", "hr_HR.ISO-8859-2", "hr_HR.UTF-8", "hsb_DE", "hsb_DE.ISO-8859-2", "hsb_DE.UTF-8", "ht_HT", "ht_HT.UTF-8", "hu_HU", "hu_HU.ISO-8859-2", "hu_HU.UTF-8", "hy_AM", "hy_AM.ARMSCII-8", "hy_AM.UTF-8", "ia_FR", "ia_FR.UTF-8", "id_ID", "id_ID.ISO-8859-1", "id_ID.UTF-8", "ig_NG", "ig_NG.UTF-8", "ik_CA", "ik_CA.UTF-8", "is_IS", "is_IS.ISO-8859-1", "is_IS.UTF-8", "it_CH", "it_CH.ISO-8859-1", "it_CH.UTF-8", "it_IT", "it_IT.ISO-8859-1", "it_IT.UTF-8", "iu_CA", "iu_CA.UTF-8", "kab_DZ", "kab_DZ.UTF-8", "ka_GE", "ka_GE.GEORGIAN-PS", "ka_GE.UTF-8", "kk_KZ", "kk_KZ.PT154", "kk_KZ.RK1048", "kk_KZ.UTF-8", "kl_GL", "kl_GL.ISO-8859-1", "kl_GL.UTF-8", "km_KH", "km_KH.UTF-8", "kn_IN", "kn_IN.UTF-8", "kok_IN", "kok_IN.UTF-8", "ks_IN", "ks_IN.UTF-8", "ku_TR", "ku_TR.ISO-8859-9", "ku_TR.UTF-8", "kw_GB", "kw_GB.ISO-8859-1", "kw_GB.UTF-8", "ky_KG", "ky_KG.UTF-8", "lb_LU", "lb_LU.UTF-8", "lg_UG", "lg_UG.ISO-8859-10", "lg_UG.UTF-8", "li_BE", "li_BE.UTF-8", "lij_IT", "lij_IT.UTF-8", "li_NL", "li_NL.UTF-8", "ln_CD", "ln_CD.UTF-8", "lo_LA", "lo_LA.UTF-8", "lt_LT", "lt_LT.ISO-8859-13", "lt_LT.UTF-8", "lv_LV", "lv_LV.ISO-8859-13", "lv_LV.UTF-8", "lzh_TW", "lzh_TW.UTF-8", "mag_IN", "mag_IN.UTF-8", "mai_IN", "mai_IN.UTF-8", "mai_NP", "mai_NP.UTF-8", "mfe_MU", "mfe_MU.UTF-8", "mg_MG", "mg_MG.ISO-8859-15", "mg_MG.UTF-8", "mhr_RU", "mhr_RU.UTF-8", "mi_NZ", "mi_NZ.ISO-8859-13", "mi_NZ.UTF-8", "miq_NI", "miq_NI.UTF-8", "mjw_IN", "mjw_IN.UTF-8", "mk_MK", "mk_MK.ISO-8859-5", "mk_MK.UTF-8", "ml_IN", "ml_IN.UTF-8", "mni_IN", "mni_IN.UTF-8", "mn_MN", "mn_MN.UTF-8", "mr_IN", "mr_IN.UTF-8", "ms_MY", "ms_MY.ISO-8859-1", "ms_MY.UTF-8", "mt_MT", "mt_MT.ISO-8859-3", "mt_MT.UTF-8", "my_MM", "my_MM.UTF-8", "nan_TW", "nan_TW.UTF-8", "nb_NO", "nb_NO.ISO-8859-1", "nb_NO.UTF-8", "nds_DE", "nds_DE.UTF-8", "nds_NL", "nds_NL.UTF-8", "ne_NP", "ne_NP.UTF-8", "nhn_MX", "nhn_MX.UTF-8", "niu_NU", "niu_NU.UTF-8", "niu_NZ", "niu_NZ.UTF-8", "nl_AW", "nl_AW.UTF-8", "nl_BE", "nl_BE.ISO-8859-1", "nl_BE.UTF-8", "nl_NL", "nl_NL.ISO-8859-1", "nl_NL.UTF-8", "nn_NO", "nn_NO.ISO-8859-1", "nn_NO.UTF-8", "nr_ZA", "nr_ZA.UTF-8", "nso_ZA", "nso_ZA.UTF-8", "oc_FR", "oc_FR.ISO-8859-1", "oc_FR.UTF-8", "om_ET", "om_ET.UTF-8", "om_KE", "om_KE.ISO-8859-1", "om_KE.UTF-8", "or_IN", "or_IN.UTF-8", "os_RU", "os_RU.UTF-8", "pa_IN", "pa_IN.UTF-8", "pap_AW", "pap_AW.UTF-8", "pap_CW", "pap_CW.UTF-8", "pa_PK", "pa_PK.UTF-8", "pl_PL", "pl_PL.ISO-8859-2", "pl_PL.UTF-8", "ps_AF", "ps_AF.UTF-8", "pt_BR", "pt_BR.ISO-8859-1", "pt_BR.UTF-8", "pt_PT", "pt_PT.ISO-8859-1", "pt_PT.UTF-8", "quz_PE", "quz_PE.UTF-8", "raj_IN", "raj_IN.UTF-8", "ro_RO", "ro_RO.ISO-8859-2", "ro_RO.UTF-8", "ru_RU", "ru_RU.CP1251", "ru_RU.ISO-8859-5", "ru_RU.KOI8-R", "ru_RU.UTF-8", "ru_UA", "ru_UA.KOI8-U", "ru_UA.UTF-8", "rw_RW", "rw_RW.UTF-8", "sa_IN", "sa_IN.UTF-8", "sat_IN", "sat_IN.UTF-8", "sc_IT", "sc_IT.UTF-8", "sd_IN", "sd_IN.UTF-8", "sd_PK", "sd_PK.UTF-8", "se_NO", "se_NO.UTF-8", "sgs_LT", "sgs_LT.UTF-8", "shn_MM", "shn_MM.UTF-8", "shs_CA", "shs_CA.UTF-8", "sid_ET", "sid_ET.UTF-8", "si_LK", "si_LK.UTF-8", "sk_SK", "sk_SK.ISO-8859-2", "sk_SK.UTF-8", "sl_SI", "sl_SI.ISO-8859-2", "sl_SI.UTF-8", "sm_WS", "sm_WS.UTF-8", "so_DJ", "so_DJ.ISO-8859-1", "so_DJ.UTF-8", "so_ET", "so_ET.UTF-8", "so_KE", "so_KE.ISO-8859-1", "so_KE.UTF-8", "so_SO", "so_SO.ISO-8859-1", "so_SO.UTF-8", "sq_AL", "sq_AL.ISO-8859-1", "sq_AL.UTF-8", "sq_MK", "sq_MK.UTF-8", "sr_ME", "sr_ME.UTF-8", "sr_RS", "sr_RS.UTF-8", "ss_ZA", "ss_ZA.UTF-8", "st_ZA", "st_ZA.ISO-8859-1", "st_ZA.UTF-8", "sv_FI", "sv_FI.ISO-8859-1", "sv_FI.UTF-8", "sv_SE", "sv_SE.ISO-8859-1", "sv_SE.ISO-8859-15", "sv_SE.UTF-8", "sw_KE", "sw_KE.UTF-8", "sw_TZ", "sw_TZ.UTF-8", "szl_PL", "szl_PL.UTF-8", "ta_IN", "ta_IN.UTF-8", "ta_LK", "ta_LK.UTF-8", "te_IN", "te_IN.UTF-8", "tg_TJ", "tg_TJ.KOI8-T", "tg_TJ.UTF-8", "the_NP", "the_NP.UTF-8", "th_TH", "th_TH.TIS-620", "th_TH.UTF-8", "ti_ER", "ti_ER.UTF-8", "ti_ET", "ti_ET.UTF-8", "tig_ER", "tig_ER.UTF-8", "tk_TM", "tk_TM.UTF-8", "tl_PH", "tl_PH.ISO-8859-1", "tl_PH.UTF-8", "tn_ZA", "tn_ZA.UTF-8", "to_TO", "to_TO.UTF-8", "tpi_PG", "tpi_PG.UTF-8", "tr_CY", "tr_CY.ISO-8859-9", "tr_CY.UTF-8", "tr_TR", "tr_TR.ISO-8859-9", "tr_TR.UTF-8", "ts_ZA", "ts_ZA.UTF-8", "tt_RU", "tt_RU.UTF-8", "ug_CN", "ug_CN.UTF-8", "uk_UA", "uk_UA.KOI8-U", "uk_UA.UTF-8", "unm_US", "unm_US.UTF-8", "ur_IN", "ur_IN.UTF-8", "ur_PK", "ur_PK.UTF-8", "uz_UZ", "uz_UZ.ISO-8859-1", "uz_UZ.UTF-8", "ve_ZA", "ve_ZA.UTF-8", "vi_VN", "vi_VN.UTF-8", "wa_BE", "wa_BE.ISO-8859-1", "wa_BE.UTF-8", "wae_CH", "wae_CH.UTF-8", "wal_ET", "wal_ET.UTF-8", "wo_SN", "wo_SN.UTF-8", "xh_ZA", "xh_ZA.ISO-8859-1", "xh_ZA.UTF-8", "yi_US", "yi_US.CP1255", "yi_US.UTF-8", "yo_NG", "yo_NG.UTF-8", "yue_HK", "yue_HK.UTF-8", "yuw_PG", "yuw_PG.UTF-8", "zh_CN", "zh_CN.GB18030", "zh_CN.GB2312", "zh_CN.GBK", "zh_CN.UTF-8", "zh_HK", "zh_HK.BIG5-HKSCS", "zh_HK.UTF-8", "zh_SG", "zh_SG.GB2312", "zh_SG.GBK", "zh_SG.UTF-8", "zh_TW", "zh_TW.BIG5", "zh_TW.EUC-TW", "zh_TW.UTF-8", "zu_ZA", "zu_ZA.ISO-8859-1", "zu_ZA.UTF-8"
+};
+
+std::string ConsumeLocaleIdentifier(FuzzedDataProvider& fuzzed_data_provider)
+{
+ return fuzzed_data_provider.PickValueInArray<std::string>(locale_identifiers);
+}
+
+bool IsAvailableLocale(const std::string& locale_identifier)
+{
+ try {
+ (void)std::locale(locale_identifier);
+ } catch (const std::runtime_error&) {
+ return false;
+ }
+ return true;
+}
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::string locale_identifier = ConsumeLocaleIdentifier(fuzzed_data_provider);
+ if (!IsAvailableLocale(locale_identifier)) {
+ return;
+ }
+ const char* c_locale = std::setlocale(LC_ALL, "C");
+ assert(c_locale != nullptr);
+
+ const std::string random_string = fuzzed_data_provider.ConsumeRandomLengthString(5);
+ int32_t parseint32_out_without_locale;
+ const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale);
+ int64_t parseint64_out_without_locale;
+ const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale);
+ const int64_t atoi64_without_locale = atoi64(random_string);
+ const int atoi_without_locale = atoi(random_string);
+ const int64_t atoi64c_without_locale = atoi64(random_string.c_str());
+ const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const std::string i64tostr_without_locale = i64tostr(random_int64);
+ const int32_t random_int32 = fuzzed_data_provider.ConsumeIntegral<int32_t>();
+ const std::string itostr_without_locale = itostr(random_int32);
+ const std::string strprintf_int_without_locale = strprintf("%d", random_int64);
+ const double random_double = fuzzed_data_provider.ConsumeFloatingPoint<double>();
+ const std::string strprintf_double_without_locale = strprintf("%f", random_double);
+
+ const char* new_locale = std::setlocale(LC_ALL, locale_identifier.c_str());
+ assert(new_locale != nullptr);
+
+ int32_t parseint32_out_with_locale;
+ const bool parseint32_with_locale = ParseInt32(random_string, &parseint32_out_with_locale);
+ assert(parseint32_without_locale == parseint32_with_locale);
+ if (parseint32_without_locale) {
+ assert(parseint32_out_without_locale == parseint32_out_with_locale);
+ }
+ int64_t parseint64_out_with_locale;
+ const bool parseint64_with_locale = ParseInt64(random_string, &parseint64_out_with_locale);
+ assert(parseint64_without_locale == parseint64_with_locale);
+ if (parseint64_without_locale) {
+ assert(parseint64_out_without_locale == parseint64_out_with_locale);
+ }
+ const int64_t atoi64_with_locale = atoi64(random_string);
+ assert(atoi64_without_locale == atoi64_with_locale);
+ const int64_t atoi64c_with_locale = atoi64(random_string.c_str());
+ assert(atoi64c_without_locale == atoi64c_with_locale);
+ const int atoi_with_locale = atoi(random_string);
+ assert(atoi_without_locale == atoi_with_locale);
+ const std::string i64tostr_with_locale = i64tostr(random_int64);
+ assert(i64tostr_without_locale == i64tostr_with_locale);
+ const std::string itostr_with_locale = itostr(random_int32);
+ assert(itostr_without_locale == itostr_with_locale);
+ const std::string strprintf_int_with_locale = strprintf("%d", random_int64);
+ assert(strprintf_int_without_locale == strprintf_int_with_locale);
+ const std::string strprintf_double_with_locale = strprintf("%f", random_double);
+ assert(strprintf_double_without_locale == strprintf_double_with_locale);
+
+ const std::locale current_cpp_locale;
+ assert(current_cpp_locale == std::locale::classic());
+}
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
new file mode 100644
index 0000000000..7d87ebc214
--- /dev/null
+++ b/src/test/fuzz/netaddress.cpp
@@ -0,0 +1,123 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <netaddress.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+
+#include <cassert>
+#include <cstdint>
+#include <netinet/in.h>
+#include <vector>
+
+namespace {
+CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
+ if (network == Network::NET_IPV4) {
+ const in_addr v4_addr = {
+ .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ return CNetAddr{v4_addr};
+ } else if (network == Network::NET_IPV6) {
+ if (fuzzed_data_provider.remaining_bytes() < 16) {
+ return CNetAddr{};
+ }
+ in6_addr v6_addr = {};
+ memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
+ return CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+ } else if (network == Network::NET_INTERNAL) {
+ CNetAddr net_addr;
+ net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
+ return net_addr;
+ } else if (network == Network::NET_ONION) {
+ CNetAddr net_addr;
+ net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
+ return net_addr;
+ } else {
+ assert(false);
+ }
+}
+}; // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider);
+ for (int i = 0; i < 15; ++i) {
+ (void)net_addr.GetByte(i);
+ }
+ (void)net_addr.GetHash();
+ (void)net_addr.GetNetClass();
+ if (net_addr.GetNetwork() == Network::NET_IPV4) {
+ assert(net_addr.IsIPv4());
+ }
+ if (net_addr.GetNetwork() == Network::NET_IPV6) {
+ assert(net_addr.IsIPv6());
+ }
+ if (net_addr.GetNetwork() == Network::NET_ONION) {
+ assert(net_addr.IsTor());
+ }
+ if (net_addr.GetNetwork() == Network::NET_INTERNAL) {
+ assert(net_addr.IsInternal());
+ }
+ if (net_addr.GetNetwork() == Network::NET_UNROUTABLE) {
+ assert(!net_addr.IsRoutable());
+ }
+ (void)net_addr.IsBindAny();
+ if (net_addr.IsInternal()) {
+ assert(net_addr.GetNetwork() == Network::NET_INTERNAL);
+ }
+ if (net_addr.IsIPv4()) {
+ assert(net_addr.GetNetwork() == Network::NET_IPV4 || net_addr.GetNetwork() == Network::NET_UNROUTABLE);
+ }
+ if (net_addr.IsIPv6()) {
+ assert(net_addr.GetNetwork() == Network::NET_IPV6 || net_addr.GetNetwork() == Network::NET_UNROUTABLE);
+ }
+ (void)net_addr.IsLocal();
+ if (net_addr.IsRFC1918() || net_addr.IsRFC2544() || net_addr.IsRFC6598() || net_addr.IsRFC5737() || net_addr.IsRFC3927()) {
+ assert(net_addr.IsIPv4());
+ }
+ (void)net_addr.IsRFC2544();
+ if (net_addr.IsRFC3849() || net_addr.IsRFC3964() || net_addr.IsRFC4380() || net_addr.IsRFC4843() || net_addr.IsRFC7343() || net_addr.IsRFC4862() || net_addr.IsRFC6052() || net_addr.IsRFC6145()) {
+ assert(net_addr.IsIPv6());
+ }
+ (void)net_addr.IsRFC3927();
+ (void)net_addr.IsRFC3964();
+ if (net_addr.IsRFC4193()) {
+ assert(net_addr.GetNetwork() == Network::NET_ONION || net_addr.GetNetwork() == Network::NET_INTERNAL || net_addr.GetNetwork() == Network::NET_UNROUTABLE);
+ }
+ (void)net_addr.IsRFC4380();
+ (void)net_addr.IsRFC4843();
+ (void)net_addr.IsRFC4862();
+ (void)net_addr.IsRFC5737();
+ (void)net_addr.IsRFC6052();
+ (void)net_addr.IsRFC6145();
+ (void)net_addr.IsRFC6598();
+ (void)net_addr.IsRFC7343();
+ if (!net_addr.IsRoutable()) {
+ assert(net_addr.GetNetwork() == Network::NET_UNROUTABLE || net_addr.GetNetwork() == Network::NET_INTERNAL);
+ }
+ if (net_addr.IsTor()) {
+ assert(net_addr.GetNetwork() == Network::NET_ONION);
+ }
+ (void)net_addr.IsValid();
+ (void)net_addr.ToString();
+ (void)net_addr.ToStringIP();
+
+ const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ (void)sub_net.IsValid();
+ (void)sub_net.ToString();
+
+ const CService service{net_addr, fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
+ (void)service.GetKey();
+ (void)service.GetPort();
+ (void)service.ToString();
+ (void)service.ToStringIPPort();
+ (void)service.ToStringPort();
+
+ const CNetAddr other_net_addr = ConsumeNetAddr(fuzzed_data_provider);
+ (void)net_addr.GetReachabilityFrom(&other_net_addr);
+ (void)sub_net.Match(other_net_addr);
+}
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
new file mode 100644
index 0000000000..57393fed45
--- /dev/null
+++ b/src/test/fuzz/p2p_transport_deserializer.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2019 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 <net.h>
+#include <protocol.h>
+#include <test/fuzz/fuzz.h>
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+void initialize()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ V1TransportDeserializer deserializer{Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION};
+ const char* pch = (const char*)buffer.data();
+ size_t n_bytes = buffer.size();
+ while (n_bytes > 0) {
+ const int handled = deserializer.Read(pch, n_bytes);
+ if (handled < 0) {
+ break;
+ }
+ pch += handled;
+ n_bytes -= handled;
+ if (deserializer.Complete()) {
+ const int64_t m_time = std::numeric_limits<int64_t>::max();
+ const CNetMessage msg = deserializer.GetMessage(Params().MessageStart(), m_time);
+ assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
+ assert(msg.m_raw_message_size <= buffer.size());
+ assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
+ assert(msg.m_time == m_time);
+ if (msg.m_valid_header) {
+ assert(msg.m_valid_netmagic);
+ }
+ if (!msg.m_valid_netmagic) {
+ assert(!msg.m_valid_header);
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
new file mode 100644
index 0000000000..934f741068
--- /dev/null
+++ b/src/test/fuzz/process_message.cpp
@@ -0,0 +1,98 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <banman.h>
+#include <chainparams.h>
+#include <consensus/consensus.h>
+#include <net.h>
+#include <net_processing.h>
+#include <protocol.h>
+#include <scheduler.h>
+#include <script/script.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/util/mining.h>
+#include <test/util/setup_common.h>
+#include <util/memory.h>
+#include <validationinterface.h>
+#include <version.h>
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstdint>
+#include <iosfwd>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
+
+namespace {
+
+#ifdef MESSAGE_TYPE
+#define TO_STRING_(s) #s
+#define TO_STRING(s) TO_STRING_(s)
+const std::string LIMIT_TO_MESSAGE_TYPE{TO_STRING(MESSAGE_TYPE)};
+#else
+const std::string LIMIT_TO_MESSAGE_TYPE;
+#endif
+
+const std::map<std::string, std::set<std::string>> EXPECTED_DESERIALIZATION_EXCEPTIONS = {
+ {"CDataStream::read(): end of data: iostream error", {"addr", "block", "blocktxn", "cmpctblock", "feefilter", "filteradd", "filterload", "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv", "notfound", "ping", "sendcmpct", "tx"}},
+ {"CompactSize exceeds limit of type: iostream error", {"cmpctblock"}},
+ {"differential value overflow: iostream error", {"getblocktxn"}},
+ {"index overflowed 16 bits: iostream error", {"getblocktxn"}},
+ {"index overflowed 16-bits: iostream error", {"cmpctblock"}},
+ {"indexes overflowed 16 bits: iostream error", {"getblocktxn"}},
+ {"non-canonical ReadCompactSize(): iostream error", {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload", "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv", "notfound", "tx"}},
+ {"ReadCompactSize(): size too large: iostream error", {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload", "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv", "notfound", "tx"}},
+ {"Superfluous witness record: iostream error", {"block", "blocktxn", "cmpctblock", "tx"}},
+ {"Unknown transaction optional data: iostream error", {"block", "blocktxn", "cmpctblock", "tx"}},
+};
+
+const RegTestingSetup* g_setup;
+} // namespace
+
+void initialize()
+{
+ static RegTestingSetup setup{};
+ g_setup = &setup;
+
+ for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+ MineBlock(g_setup->m_node, CScript() << OP_TRUE);
+ }
+ SyncWithValidationInterfaceQueue();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
+ if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
+ return;
+ }
+ CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
+ CNode p2p_node{0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false};
+ p2p_node.fSuccessfullyConnected = true;
+ p2p_node.nVersion = PROTOCOL_VERSION;
+ p2p_node.SetSendVersion(PROTOCOL_VERSION);
+ g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+ try {
+ (void)ProcessMessage(&p2p_node, random_message_type, random_bytes_data_stream, GetTimeMillis(), Params(), g_setup->m_node.connman.get(), g_setup->m_node.banman.get(), std::atomic<bool>{false});
+ } catch (const std::ios_base::failure& e) {
+ const std::string exception_message{e.what()};
+ const auto p = EXPECTED_DESERIALIZATION_EXCEPTIONS.find(exception_message);
+ if (p == EXPECTED_DESERIALIZATION_EXCEPTIONS.cend() || p->second.count(random_message_type) == 0) {
+ std::cout << "Unexpected exception when processing message type \"" << random_message_type << "\": " << exception_message << std::endl;
+ assert(false);
+ }
+ }
+ SyncWithValidationInterfaceQueue();
+}
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
new file mode 100644
index 0000000000..3b37321977
--- /dev/null
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bloom.h>
+#include <optional.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <uint256.h>
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ CRollingBloomFilter rolling_bloom_filter{
+ fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 1000),
+ 0.999 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max())};
+ while (fuzzed_data_provider.remaining_bytes() > 0) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) {
+ case 0: {
+ const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ (void)rolling_bloom_filter.contains(b);
+ rolling_bloom_filter.insert(b);
+ const bool present = rolling_bloom_filter.contains(b);
+ assert(present);
+ break;
+ }
+ case 1: {
+ const Optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+ if (!u256) {
+ break;
+ }
+ (void)rolling_bloom_filter.contains(*u256);
+ rolling_bloom_filter.insert(*u256);
+ const bool present = rolling_bloom_filter.contains(*u256);
+ assert(present);
+ break;
+ }
+ case 2:
+ rolling_bloom_filter.reset();
+ break;
+ }
+ }
+}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 0469e87de6..0d18784302 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -15,12 +15,15 @@
#include <script/standard.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
+#include <univalue.h>
#include <util/memory.h>
void initialize()
{
// Fuzzers using pubkey must hold an ECCVerifyHandle.
static const auto verify_handle = MakeUnique<ECCVerifyHandle>();
+
+ SelectParams(CBaseChainParams::REGTEST);
}
void test_one_input(const std::vector<uint8_t>& buffer)
@@ -28,7 +31,22 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const CScript script(buffer.begin(), buffer.end());
std::vector<unsigned char> compressed;
- (void)CompressScript(script, compressed);
+ if (CompressScript(script, compressed)) {
+ const unsigned int size = compressed[0];
+ compressed.erase(compressed.begin());
+ assert(size >= 0 && size <= 5);
+ CScript decompressed_script;
+ const bool ok = DecompressScript(decompressed_script, size, compressed);
+ assert(ok);
+ assert(script == decompressed_script);
+ }
+
+ for (unsigned int size = 0; size < 6; ++size) {
+ std::vector<unsigned char> vch(GetSpecialScriptSize(size), 0x00);
+ vch.insert(vch.end(), buffer.begin(), buffer.end());
+ CScript decompressed_script;
+ (void)DecompressScript(decompressed_script, size, vch);
+ }
CTxDestination address;
(void)ExtractDestination(script, address);
@@ -61,4 +79,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)script.IsPushOnly();
(void)script.IsUnspendable();
(void)script.GetSigOpCount(/* fAccurate= */ false);
+
+ (void)FormatScript(script);
+ (void)ScriptToAsmStr(script, false);
+ (void)ScriptToAsmStr(script, true);
+
+ UniValue o1(UniValue::VOBJ);
+ ScriptPubKeyToUniv(script, o1, true);
+ UniValue o2(UniValue::VOBJ);
+ ScriptPubKeyToUniv(script, o2, false);
+ UniValue o3(UniValue::VOBJ);
+ ScriptToUniv(script, o3, true);
+ UniValue o4(UniValue::VOBJ);
+ ScriptToUniv(script, o4, false);
}
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
new file mode 100644
index 0000000000..0cd129ba7a
--- /dev/null
+++ b/src/test/fuzz/script_ops.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <script/script.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ CScript script = ConsumeScript(fuzzed_data_provider);
+ while (fuzzed_data_provider.remaining_bytes() > 0) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
+ case 0:
+ script += ConsumeScript(fuzzed_data_provider);
+ break;
+ case 1:
+ script = script + ConsumeScript(fuzzed_data_provider);
+ break;
+ case 2:
+ script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ break;
+ case 3:
+ script << ConsumeOpcodeType(fuzzed_data_provider);
+ break;
+ case 4:
+ script << ConsumeScriptNum(fuzzed_data_provider);
+ break;
+ case 5:
+ script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ break;
+ case 6:
+ script.clear();
+ break;
+ case 7: {
+ (void)script.GetSigOpCount(false);
+ (void)script.GetSigOpCount(true);
+ (void)script.GetSigOpCount(script);
+ (void)script.HasValidOps();
+ (void)script.IsPayToScriptHash();
+ (void)script.IsPayToWitnessScriptHash();
+ (void)script.IsPushOnly();
+ (void)script.IsUnspendable();
+ {
+ CScript::const_iterator pc = script.begin();
+ opcodetype opcode;
+ (void)script.GetOp(pc, opcode);
+ std::vector<uint8_t> data;
+ (void)script.GetOp(pc, opcode, data);
+ (void)script.IsPushOnly(pc);
+ }
+ {
+ int version;
+ std::vector<uint8_t> program;
+ (void)script.IsWitnessProgram(version, program);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
new file mode 100644
index 0000000000..db44bb9e19
--- /dev/null
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -0,0 +1,137 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <script/script.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+namespace {
+bool IsValidAddition(const CScriptNum& lhs, const CScriptNum& rhs)
+{
+ return rhs == 0 || (rhs > 0 && lhs <= CScriptNum{std::numeric_limits<int64_t>::max()} - rhs) || (rhs < 0 && lhs >= CScriptNum{std::numeric_limits<int64_t>::min()} - rhs);
+}
+
+bool IsValidSubtraction(const CScriptNum& lhs, const CScriptNum& rhs)
+{
+ return rhs == 0 || (rhs > 0 && lhs >= CScriptNum{std::numeric_limits<int64_t>::min()} + rhs) || (rhs < 0 && lhs <= CScriptNum{std::numeric_limits<int64_t>::max()} + rhs);
+}
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ CScriptNum script_num = ConsumeScriptNum(fuzzed_data_provider);
+ while (fuzzed_data_provider.remaining_bytes() > 0) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) {
+ case 0: {
+ const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ assert((script_num == i) != (script_num != i));
+ assert((script_num <= i) != script_num > i);
+ assert((script_num >= i) != (script_num < i));
+ // Avoid signed integer overflow:
+ // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long'
+ if (IsValidAddition(script_num, CScriptNum{i})) {
+ assert((script_num + i) - i == script_num);
+ }
+ // Avoid signed integer overflow:
+ // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long'
+ if (IsValidSubtraction(script_num, CScriptNum{i})) {
+ assert((script_num - i) + i == script_num);
+ }
+ break;
+ }
+ case 1: {
+ const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+ assert((script_num == random_script_num) != (script_num != random_script_num));
+ assert((script_num <= random_script_num) != (script_num > random_script_num));
+ assert((script_num >= random_script_num) != (script_num < random_script_num));
+ // Avoid signed integer overflow:
+ // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long'
+ if (IsValidAddition(script_num, random_script_num)) {
+ assert((script_num + random_script_num) - random_script_num == script_num);
+ }
+ // Avoid signed integer overflow:
+ // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long'
+ if (IsValidSubtraction(script_num, random_script_num)) {
+ assert((script_num - random_script_num) + random_script_num == script_num);
+ }
+ break;
+ }
+ case 2: {
+ const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+ if (!IsValidAddition(script_num, random_script_num)) {
+ // Avoid assertion failure:
+ // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
+ break;
+ }
+ script_num += random_script_num;
+ break;
+ }
+ case 3: {
+ const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+ if (!IsValidSubtraction(script_num, random_script_num)) {
+ // Avoid assertion failure:
+ // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
+ break;
+ }
+ script_num -= random_script_num;
+ break;
+ }
+ case 4:
+ script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ break;
+ case 5:
+ script_num = script_num & ConsumeScriptNum(fuzzed_data_provider);
+ break;
+ case 6:
+ script_num &= ConsumeScriptNum(fuzzed_data_provider);
+ break;
+ case 7:
+ if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) {
+ // Avoid assertion failure:
+ // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed.
+ break;
+ }
+ script_num = -script_num;
+ break;
+ case 8:
+ script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ break;
+ case 9: {
+ const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ if (!IsValidAddition(script_num, CScriptNum{random_integer})) {
+ // Avoid assertion failure:
+ // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
+ break;
+ }
+ script_num += random_integer;
+ break;
+ }
+ case 10: {
+ const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) {
+ // Avoid assertion failure:
+ // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
+ break;
+ }
+ script_num -= random_integer;
+ break;
+ }
+ case 11:
+ script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ break;
+ }
+ // Avoid negation failure:
+ // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
+ if (script_num != CScriptNum{std::numeric_limits<int64_t>::min()}) {
+ (void)script_num.getvch();
+ }
+ }
+}
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index 0de21f0e7c..d5be1070bd 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -8,7 +8,6 @@
#include <util/strencodings.h>
#include <algorithm>
-#include <cassert>
#include <cstdint>
#include <string>
#include <vector>
@@ -27,7 +26,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
// * strprintf("%.222222200000000$", 1.1);
//
// Upstream bug report: https://github.com/c42f/tinyformat/issues/70
- if (format_string.find("%") != std::string::npos && digits_in_format_specifier >= 7) {
+ if (format_string.find('%') != std::string::npos && digits_in_format_specifier >= 7) {
return;
}
@@ -35,7 +34,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
// * strprintf("%1$*1$*", -11111111);
//
// Upstream bug report: https://github.com/c42f/tinyformat/issues/70
- if (format_string.find("%") != std::string::npos && format_string.find("$") != std::string::npos && format_string.find("*") != std::string::npos && digits_in_format_specifier > 0) {
+ if (format_string.find('%') != std::string::npos && format_string.find('$') != std::string::npos && format_string.find('*') != std::string::npos && digits_in_format_specifier > 0) {
return;
}
@@ -96,7 +95,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
try {
- switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 13)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) {
case 0:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
break;
@@ -115,32 +114,52 @@ void test_one_input(const std::vector<uint8_t>& buffer)
case 5:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
break;
- case 6:
+ }
+ } catch (const tinyformat::format_error&) {
+ }
+
+ if (format_string.find('%') != std::string::npos && format_string.find('c') != std::string::npos) {
+ // Avoid triggering the following:
+ // * strprintf("%c", 1.31783e+38);
+ // tinyformat.h:244:36: runtime error: 1.31783e+38 is outside the range of representable values of type 'char'
+ return;
+ }
+
+ if (format_string.find('%') != std::string::npos && format_string.find('*') != std::string::npos) {
+ // Avoid triggering the following:
+ // * strprintf("%*", -2.33527e+38);
+ // tinyformat.h:283:65: runtime error: -2.33527e+38 is outside the range of representable values of type 'int'
+ // * strprintf("%*", -2147483648);
+ // tinyformat.h:763:25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
+ return;
+ }
+
+ try {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
+ case 0:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
break;
- case 7:
+ case 1:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
break;
- case 8:
+ case 2:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
break;
- case 9:
+ case 3:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
break;
- case 10:
+ case 4:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
break;
- case 11:
+ case 5:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
break;
- case 12:
+ case 6:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
break;
- case 13:
+ case 7:
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
break;
- default:
- assert(false);
}
} catch (const tinyformat::format_error&) {
}
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index fefafda36b..1ec69cc23d 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chainparams.h>
#include <coins.h>
#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
@@ -13,12 +14,18 @@
#include <primitives/transaction.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
+#include <univalue.h>
#include <util/rbf.h>
#include <validation.h>
#include <version.h>
#include <cassert>
+void initialize()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
void test_one_input(const std::vector<uint8_t>& buffer)
{
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
@@ -85,4 +92,21 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)IsStandardTx(tx, reason);
(void)RecursiveDynamicUsage(tx);
(void)SignalsOptInRBF(tx);
+
+ CCoinsView coins_view;
+ const CCoinsViewCache coins_view_cache(&coins_view);
+ (void)AreInputsStandard(tx, coins_view_cache);
+ (void)IsWitnessStandard(tx, coins_view_cache);
+
+ UniValue u(UniValue::VOBJ);
+ // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
+ bool skip_tx_to_univ = false;
+ for (const CTxOut& txout : tx.vout) {
+ if (txout.nValue == std::numeric_limits<int64_t>::min()) {
+ skip_tx_to_univ = true;
+ }
+ }
+ if (!skip_tx_to_univ) {
+ TxToUniv(tx, /* hashBlock */ {}, u);
+ }
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
new file mode 100644
index 0000000000..2f4aa9ad2b
--- /dev/null
+++ b/src/test/fuzz/util.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2009-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_FUZZ_UTIL_H
+#define BITCOIN_TEST_FUZZ_UTIL_H
+
+#include <attributes.h>
+#include <optional.h>
+#include <script/script.h>
+#include <serialize.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <version.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, size_t max_length = 4096) noexcept
+{
+ const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
+ return {s.begin(), s.end()};
+}
+
+template <typename T>
+NODISCARD inline Optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, size_t max_length = 4096) noexcept
+{
+ const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
+ CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
+ T obj;
+ try {
+ ds >> obj;
+ } catch (const std::ios_base::failure&) {
+ return nullopt;
+ }
+ return obj;
+}
+
+NODISCARD inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
+}
+
+NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ return {b.begin(), b.end()};
+}
+
+NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
+}
+
+#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 532fe143ae..4e51b8c02a 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <reverselock.h>
+#include <sync.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -11,21 +11,50 @@ BOOST_FIXTURE_TEST_SUITE(reverselock_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(reverselock_basics)
{
- boost::mutex mutex;
- boost::unique_lock<boost::mutex> lock(mutex);
+ Mutex mutex;
+ WAIT_LOCK(mutex, lock);
BOOST_CHECK(lock.owns_lock());
{
- reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
+ REVERSE_LOCK(lock);
BOOST_CHECK(!lock.owns_lock());
}
BOOST_CHECK(lock.owns_lock());
}
+BOOST_AUTO_TEST_CASE(reverselock_multiple)
+{
+ Mutex mutex2;
+ Mutex mutex;
+ WAIT_LOCK(mutex2, lock2);
+ WAIT_LOCK(mutex, lock);
+
+ // Make sure undoing two locks succeeds
+ {
+ REVERSE_LOCK(lock);
+ BOOST_CHECK(!lock.owns_lock());
+ REVERSE_LOCK(lock2);
+ BOOST_CHECK(!lock2.owns_lock());
+ }
+ BOOST_CHECK(lock.owns_lock());
+ BOOST_CHECK(lock2.owns_lock());
+}
+
BOOST_AUTO_TEST_CASE(reverselock_errors)
{
- boost::mutex mutex;
- boost::unique_lock<boost::mutex> lock(mutex);
+ Mutex mutex2;
+ Mutex mutex;
+ WAIT_LOCK(mutex2, lock2);
+ WAIT_LOCK(mutex, lock);
+
+#ifdef DEBUG_LOCKORDER
+ // Make sure trying to reverse lock a previous lock fails
+ try {
+ REVERSE_LOCK(lock2);
+ BOOST_CHECK(false); // REVERSE_LOCK(lock2) succeeded
+ } catch(...) { }
+ BOOST_CHECK(lock2.owns_lock());
+#endif
// Make sure trying to reverse lock an unlocked lock fails
lock.unlock();
@@ -34,7 +63,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
bool failed = false;
try {
- reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
+ REVERSE_LOCK(lock);
} catch(...) {
failed = true;
}
@@ -49,7 +78,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
lock.lock();
BOOST_CHECK(lock.owns_lock());
{
- reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
+ REVERSE_LOCK(lock);
BOOST_CHECK(!lock.owns_lock());
}
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index a6cb34cf28..7d26840b73 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -4,37 +4,26 @@
#include <random.h>
#include <scheduler.h>
+#include <util/time.h>
#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(scheduler_tests)
-static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, boost::chrono::system_clock::time_point rescheduleTime)
+static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
{
{
boost::unique_lock<boost::mutex> lock(mutex);
counter += delta;
}
- boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min();
+ std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
if (rescheduleTime != noTime) {
CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
s.schedule(f, rescheduleTime);
}
}
-static void MicroSleep(uint64_t n)
-{
-#if defined(HAVE_WORKING_BOOST_SLEEP_FOR)
- boost::this_thread::sleep_for(boost::chrono::microseconds(n));
-#elif defined(HAVE_WORKING_BOOST_SLEEP)
- boost::this_thread::sleep(boost::posix_time::microseconds(n));
-#else
- //should never get here
- #error missing boost sleep implementation
-#endif
-}
-
BOOST_AUTO_TEST_CASE(manythreads)
{
// Stress test: hundreds of microsecond-scheduled tasks,
@@ -56,15 +45,15 @@ BOOST_AUTO_TEST_CASE(manythreads)
auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]
- boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now();
- boost::chrono::system_clock::time_point now = start;
- boost::chrono::system_clock::time_point first, last;
+ std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
+ std::chrono::system_clock::time_point now = start;
+ std::chrono::system_clock::time_point first, last;
size_t nTasks = microTasks.getQueueInfo(first, last);
BOOST_CHECK(nTasks == 0);
for (int i = 0; i < 100; ++i) {
- boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng));
- boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng));
+ std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
+ std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
@@ -81,15 +70,15 @@ BOOST_AUTO_TEST_CASE(manythreads)
for (int i = 0; i < 5; i++)
microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
- MicroSleep(600);
- now = boost::chrono::system_clock::now();
+ UninterruptibleSleep(std::chrono::microseconds{600});
+ now = std::chrono::system_clock::now();
// More threads and more tasks:
for (int i = 0; i < 5; i++)
microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
for (int i = 0; i < 100; i++) {
- boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng));
- boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng));
+ std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
+ std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
@@ -167,14 +156,14 @@ BOOST_AUTO_TEST_CASE(mockforward)
scheduler.scheduleFromNow(dummy, 8*min_in_milli);
// check taskQueue
- boost::chrono::system_clock::time_point first, last;
+ std::chrono::system_clock::time_point first, last;
size_t num_tasks = scheduler.getQueueInfo(first, last);
BOOST_CHECK_EQUAL(num_tasks, 3ul);
std::thread scheduler_thread([&]() { scheduler.serviceQueue(); });
// bump the scheduler forward 5 minutes
- scheduler.MockForward(boost::chrono::seconds(5*60));
+ scheduler.MockForward(std::chrono::seconds(5*60));
// ensure scheduler has chance to process all tasks queued for before 1 ms from now.
scheduler.scheduleFromNow([&scheduler]{ scheduler.stop(false); }, 1);
@@ -188,8 +177,8 @@ BOOST_AUTO_TEST_CASE(mockforward)
BOOST_CHECK_EQUAL(counter, 2);
// check that the time of the remaining job has been updated
- boost::chrono::system_clock::time_point now = boost::chrono::system_clock::now();
- int delta = boost::chrono::duration_cast<boost::chrono::seconds>(first - now).count();
+ std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
// should be between 2 & 3 minutes from now
BOOST_CHECK(delta > 2*60 && delta < 3*60);
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 0352d2d95a..44d4e0890a 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -22,6 +22,7 @@
#include <script/standard.h>
#include <streams.h>
#include <util/strencodings.h>
+#include <test/util/transaction_utils.h>
#include <map>
#include <string>
@@ -122,10 +123,9 @@ BOOST_AUTO_TEST_CASE(tx_valid)
std::map<COutPoint, int64_t> mapprevOutValues;
UniValue inputs = test[0].get_array();
bool fValid = true;
- for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
- const UniValue& input = inputs[inpIdx];
- if (!input.isArray())
- {
+ for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
+ const UniValue& input = inputs[inpIdx];
+ if (!input.isArray()) {
fValid = false;
break;
}
@@ -209,10 +209,9 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
std::map<COutPoint, int64_t> mapprevOutValues;
UniValue inputs = test[0].get_array();
bool fValid = true;
- for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
- const UniValue& input = inputs[inpIdx];
- if (!input.isArray())
- {
+ for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
+ const UniValue& input = inputs[inpIdx];
+ if (!input.isArray()) {
fValid = false;
break;
}
@@ -282,50 +281,13 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
BOOST_CHECK_MESSAGE(!CheckTransaction(CTransaction(tx), state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
}
-//
-// Helper: create two dummy transactions, each with
-// two outputs. The first has 11 and 50 CENT outputs
-// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
-// paid to a TX_PUBKEYHASH.
-//
-static std::vector<CMutableTransaction>
-SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
-{
- std::vector<CMutableTransaction> dummyTransactions;
- dummyTransactions.resize(2);
-
- // Add some keys to the keystore:
- CKey key[4];
- for (int i = 0; i < 4; i++)
- {
- key[i].MakeNewKey(i % 2);
- keystoreRet.AddKey(key[i]);
- }
-
- // Create some dummy input transactions
- dummyTransactions[0].vout.resize(2);
- dummyTransactions[0].vout[0].nValue = 11*CENT;
- dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- dummyTransactions[0].vout[1].nValue = 50*CENT;
- dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
- AddCoins(coinsRet, CTransaction(dummyTransactions[0]), 0);
-
- dummyTransactions[1].vout.resize(2);
- dummyTransactions[1].vout[0].nValue = 21*CENT;
- dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
- dummyTransactions[1].vout[1].nValue = 22*CENT;
- dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
- AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
-
- return dummyTransactions;
-}
-
BOOST_AUTO_TEST_CASE(test_Get)
{
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
- std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
+ std::vector<CMutableTransaction> dummyTransactions =
+ SetupDummyInputs(keystore, coins, {11*CENT, 50*CENT, 21*CENT, 22*CENT});
CMutableTransaction t1;
t1.vin.resize(3);
@@ -685,7 +647,8 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
- std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
+ std::vector<CMutableTransaction> dummyTransactions =
+ SetupDummyInputs(keystore, coins, {11*CENT, 50*CENT, 21*CENT, 22*CENT});
CMutableTransaction t;
t.vin.resize(1);
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 4b0214a15a..3550a02316 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -34,7 +34,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
int64_t time_start = GetTimeMillis();
while (!txindex.BlockUntilSyncedToCurrentChain()) {
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
// Check that txindex excludes genesis block transactions.
@@ -70,6 +70,8 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// shutdown sequence (c.f. Shutdown() in init.cpp)
txindex.Stop();
+ // txindex job may be scheduled, so stop scheduler before destructing
+ m_node.scheduler->stop();
threadGroup.interrupt_all();
threadGroup.join_all();
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 360377e58a..ad3ff48860 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -13,6 +13,7 @@
#include <init.h>
#include <miner.h>
#include <net.h>
+#include <net_processing.h>
#include <noui.h>
#include <pow.h>
#include <rpc/blockchain.h>
@@ -25,7 +26,6 @@
#include <util/strencodings.h>
#include <util/time.h>
#include <util/translation.h>
-#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
@@ -63,7 +63,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
- : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / std::to_string(g_insecure_rand_ctx_temp_path.rand32())}
+ : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()}
{
fs::create_directories(m_path_root);
gArgs.ForceSetArg("-datadir", m_path_root.string());
@@ -123,7 +123,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
BlockValidationState state;
if (!ActivateBestChain(state, chainparams)) {
- throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
+ throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
@@ -137,10 +137,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
+ m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler);
}
TestingSetup::~TestingSetup()
{
+ if (m_node.scheduler) m_node.scheduler->stop();
threadGroup.interrupt_all();
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
diff --git a/src/test/util/transaction_utils.cpp b/src/test/util/transaction_utils.cpp
index 90b78effb0..999b803a8d 100644
--- a/src/test/util/transaction_utils.cpp
+++ b/src/test/util/transaction_utils.cpp
@@ -3,6 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/transaction_utils.h>
+#include <coins.h>
+#include <script/signingprovider.h>
CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int nValue)
{
@@ -37,3 +39,33 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CSc
return txSpend;
}
+
+std::vector<CMutableTransaction> SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet, const std::array<CAmount,4>& nValues)
+{
+ std::vector<CMutableTransaction> dummyTransactions;
+ dummyTransactions.resize(2);
+
+ // Add some keys to the keystore:
+ CKey key[4];
+ for (int i = 0; i < 4; i++) {
+ key[i].MakeNewKey(i % 2);
+ keystoreRet.AddKey(key[i]);
+ }
+
+ // Create some dummy input transactions
+ dummyTransactions[0].vout.resize(2);
+ dummyTransactions[0].vout[0].nValue = nValues[0];
+ dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
+ dummyTransactions[0].vout[1].nValue = nValues[1];
+ dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
+ AddCoins(coinsRet, CTransaction(dummyTransactions[0]), 0);
+
+ dummyTransactions[1].vout.resize(2);
+ dummyTransactions[1].vout[0].nValue = nValues[2];
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
+ dummyTransactions[1].vout[1].nValue = nValues[3];
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
+ AddCoins(coinsRet, CTransaction(dummyTransactions[1]), 0);
+
+ return dummyTransactions;
+}
diff --git a/src/test/util/transaction_utils.h b/src/test/util/transaction_utils.h
index 57604646e7..f843928a5f 100644
--- a/src/test/util/transaction_utils.h
+++ b/src/test/util/transaction_utils.h
@@ -7,6 +7,11 @@
#include <primitives/transaction.h>
+#include <array>
+
+class FillableSigningProvider;
+class CCoinsViewCache;
+
// create crediting transaction
// [1 coinbase input => 1 output with given scriptPubkey and value]
CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int nValue = 0);
@@ -16,4 +21,9 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int n
// 1 output with empty scriptPubKey, full value of referenced transaction]
CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CTransaction& txCredit);
+// Helper: create two dummy transactions, each with two outputs.
+// The first has nValues[0] and nValues[1] outputs paid to a TX_PUBKEY,
+// the second nValues[2] and nValues[3] outputs paid to a TX_PUBKEYHASH.
+std::vector<CMutableTransaction> SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet, const std::array<CAmount,4>& nValues);
+
#endif // BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 42c2c50fa5..eb0d31fbdc 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -5,10 +5,14 @@
#include <util/system.h>
#include <clientversion.h>
+#include <hash.h> // For Hash()
+#include <key.h> // For CKey
#include <optional.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/str.h>
+#include <uint256.h>
+#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -16,6 +20,7 @@
#include <util/spanparsing.h>
#include <util/vector.h>
+#include <array>
#include <stdint.h>
#include <thread>
#include <univalue.h>
@@ -1194,6 +1199,12 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
BOOST_CHECK(ParseMoney("0.00000001", ret));
BOOST_CHECK_EQUAL(ret, COIN/100000000);
+ // Parsing amount that can not be represented in ret should fail
+ BOOST_CHECK(!ParseMoney("0.000000001", ret));
+
+ // Parsing empty string should fail
+ BOOST_CHECK(!ParseMoney("", ret));
+
// Attempted 63 bit overflow should fail
BOOST_CHECK(!ParseMoney("92233720368.54775808", ret));
@@ -1322,7 +1333,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime)
SetMockTime(111);
// Check that mock time does not change after a sleep
for (const auto& num_sleep : {0, 1}) {
- MilliSleep(num_sleep);
+ UninterruptibleSleep(std::chrono::milliseconds{num_sleep});
BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
@@ -1333,7 +1344,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime)
// Check that system time changes after a sleep
const auto ms_0 = GetTime<std::chrono::milliseconds>();
const auto us_0 = GetTime<std::chrono::microseconds>();
- MilliSleep(1);
+ UninterruptibleSleep(std::chrono::milliseconds{1});
BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
}
@@ -2025,4 +2036,109 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector)
BOOST_CHECK_EQUAL(v8[2].copies, 0);
}
+BOOST_AUTO_TEST_CASE(message_sign)
+{
+ const std::array<unsigned char, 32> privkey_bytes = {
+ // just some random data
+ // derived address from this private key: 15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs
+ 0xD9, 0x7F, 0x51, 0x08, 0xF1, 0x1C, 0xDA, 0x6E,
+ 0xEE, 0xBA, 0xAA, 0x42, 0x0F, 0xEF, 0x07, 0x26,
+ 0xB1, 0xF8, 0x98, 0x06, 0x0B, 0x98, 0x48, 0x9F,
+ 0xA3, 0x09, 0x84, 0x63, 0xC0, 0x03, 0x28, 0x66
+ };
+
+ const std::string message = "Trust no one";
+
+ const std::string expected_signature =
+ "IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/WSVGDF4MUi4rWSswW38oimDYfNNUBUOk=";
+
+ CKey privkey;
+ std::string generated_signature;
+
+ BOOST_REQUIRE_MESSAGE(!privkey.IsValid(),
+ "Confirm the private key is invalid");
+
+ BOOST_CHECK_MESSAGE(!MessageSign(privkey, message, generated_signature),
+ "Sign with an invalid private key");
+
+ privkey.Set(privkey_bytes.begin(), privkey_bytes.end(), true);
+
+ BOOST_REQUIRE_MESSAGE(privkey.IsValid(),
+ "Confirm the private key is valid");
+
+ BOOST_CHECK_MESSAGE(MessageSign(privkey, message, generated_signature),
+ "Sign with a valid private key");
+
+ BOOST_CHECK_EQUAL(expected_signature, generated_signature);
+}
+
+BOOST_AUTO_TEST_CASE(message_verify)
+{
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "invalid address",
+ "signature should be irrelevant",
+ "message too"),
+ MessageVerificationResult::ERR_INVALID_ADDRESS);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "3B5fQsEXEaV8v6U3ejYc8XaKXAkyQj2MjV",
+ "signature should be irrelevant",
+ "message too"),
+ MessageVerificationResult::ERR_ADDRESS_NO_KEY);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "1KqbBpLy5FARmTPD4VZnDDpYjkUvkr82Pm",
+ "invalid signature, not in base64 encoding",
+ "message should be irrelevant"),
+ MessageVerificationResult::ERR_MALFORMED_SIGNATURE);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "1KqbBpLy5FARmTPD4VZnDDpYjkUvkr82Pm",
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+ "message should be irrelevant"),
+ MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs",
+ "IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/WSVGDF4MUi4rWSswW38oimDYfNNUBUOk=",
+ "I never signed this"),
+ MessageVerificationResult::ERR_NOT_SIGNED);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs",
+ "IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/WSVGDF4MUi4rWSswW38oimDYfNNUBUOk=",
+ "Trust no one"),
+ MessageVerificationResult::OK);
+
+ BOOST_CHECK_EQUAL(
+ MessageVerify(
+ "11canuhp9X2NocwCq7xNrQYTmUgZAnLK3",
+ "IIcaIENoYW5jZWxsb3Igb24gYnJpbmsgb2Ygc2Vjb25kIGJhaWxvdXQgZm9yIGJhbmtzIAaHRtbCeDZINyavx14=",
+ "Trust me"),
+ MessageVerificationResult::OK);
+}
+
+BOOST_AUTO_TEST_CASE(message_hash)
+{
+ const std::string unsigned_tx = "...";
+ const std::string prefixed_message =
+ std::string(1, (char)MESSAGE_MAGIC.length()) +
+ MESSAGE_MAGIC +
+ std::string(1, (char)unsigned_tx.length()) +
+ unsigned_tx;
+
+ const uint256 signature_hash = Hash(unsigned_tx.begin(), unsigned_tx.end());
+ const uint256 message_hash1 = Hash(prefixed_message.begin(), prefixed_message.end());
+ const uint256 message_hash2 = MessageHash(unsigned_tx);
+
+ BOOST_CHECK_EQUAL(message_hash1, message_hash2);
+ BOOST_CHECK_NE(message_hash1, signature_hash);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index dae389a167..f2c862011d 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
t.join();
}
while (GetMainSignals().CallbacksPending() > 0) {
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
UnregisterValidationInterface(&sub);
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index ab8b957f7d..c24164528f 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include <txmempool.h>
-#include <validation.h>
#include <sync.h>
#include <test/util/setup_common.h>
+#include <txmempool.h>
+#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -85,12 +85,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// This is contingent not only on the dynamic memory usage of the Coins
// that we're adding (COIN_SIZE bytes per), but also on how much memory the
// cacheCoins (unordered_map) preallocates.
- //
- // I came up with the count by examining the printed memory usage of the
- // CCoinsCacheView, so it's sort of arbitrary - but it shouldn't change
- // unless we somehow change the way the cacheCoins map allocates memory.
- //
- constexpr int COINS_UNTIL_CRITICAL = is_64_bit ? 4 : 5;
+ constexpr int COINS_UNTIL_CRITICAL{3};
for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
COutPoint res = add_coin(view);
@@ -101,17 +96,14 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
CoinsCacheSizeState::OK);
}
- // Adding an additional coin will push us over the edge to CRITICAL.
- add_coin(view);
- print_view_mem_usage(view);
-
- auto size_state = chainstate.GetCoinsCacheSizeState(
- tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0);
-
- if (!is_64_bit && size_state == CoinsCacheSizeState::LARGE) {
- // On 32 bit hosts, we may hit LARGE before CRITICAL.
+ // Adding some additional coins will push us over the edge to CRITICAL.
+ for (int i{0}; i < 4; ++i) {
add_coin(view);
print_view_mem_usage(view);
+ if (chainstate.GetCoinsCacheSizeState(tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
+ CoinsCacheSizeState::CRITICAL) {
+ break;
+ }
}
BOOST_CHECK_EQUAL(
diff --git a/src/txmempool.h b/src/txmempool.h
index de11d626b4..0de6f28777 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -753,7 +753,7 @@ public:
* determine if that transaction has not yet been visited during the current
* traversal's epoch.
* Algorithms using std::set can be replaced on a one by one basis.
- * Both techniques are not fundamentally incomaptible across the codebase.
+ * Both techniques are not fundamentally incompatible across the codebase.
* Generally speaking, however, the remaining use of std::set for mempool
* traversal should be viewed as a TODO for replacement with an epoch based
* traversal, rather than a preference for std::set over epochs in that
diff --git a/src/util/message.cpp b/src/util/message.cpp
new file mode 100644
index 0000000000..1e7128d225
--- /dev/null
+++ b/src/util/message.cpp
@@ -0,0 +1,92 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <hash.h> // For CHashWriter
+#include <key.h> // For CKey
+#include <key_io.h> // For DecodeDestination()
+#include <pubkey.h> // For CPubKey
+#include <script/standard.h> // For CTxDestination, IsValidDestination(), PKHash
+#include <serialize.h> // For SER_GETHASH
+#include <util/message.h>
+#include <util/strencodings.h> // For DecodeBase64()
+
+#include <string>
+#include <vector>
+
+/**
+ * Text used to signify that a signed message follows and to prevent
+ * inadvertently signing a transaction.
+ */
+const std::string MESSAGE_MAGIC = "Bitcoin Signed Message:\n";
+
+MessageVerificationResult MessageVerify(
+ const std::string& address,
+ const std::string& signature,
+ const std::string& message)
+{
+ CTxDestination destination = DecodeDestination(address);
+ if (!IsValidDestination(destination)) {
+ return MessageVerificationResult::ERR_INVALID_ADDRESS;
+ }
+
+ if (boost::get<PKHash>(&destination) == nullptr) {
+ return MessageVerificationResult::ERR_ADDRESS_NO_KEY;
+ }
+
+ bool invalid = false;
+ std::vector<unsigned char> signature_bytes = DecodeBase64(signature.c_str(), &invalid);
+ if (invalid) {
+ return MessageVerificationResult::ERR_MALFORMED_SIGNATURE;
+ }
+
+ CPubKey pubkey;
+ if (!pubkey.RecoverCompact(MessageHash(message), signature_bytes)) {
+ return MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED;
+ }
+
+ if (!(CTxDestination(PKHash(pubkey)) == destination)) {
+ return MessageVerificationResult::ERR_NOT_SIGNED;
+ }
+
+ return MessageVerificationResult::OK;
+}
+
+bool MessageSign(
+ const CKey& privkey,
+ const std::string& message,
+ std::string& signature)
+{
+ std::vector<unsigned char> signature_bytes;
+
+ if (!privkey.SignCompact(MessageHash(message), signature_bytes)) {
+ return false;
+ }
+
+ signature = EncodeBase64(signature_bytes.data(), signature_bytes.size());
+
+ return true;
+}
+
+uint256 MessageHash(const std::string& message)
+{
+ CHashWriter hasher(SER_GETHASH, 0);
+ hasher << MESSAGE_MAGIC << message;
+
+ return hasher.GetHash();
+}
+
+std::string SigningResultString(const SigningResult res)
+{
+ switch (res) {
+ case SigningResult::OK:
+ return "No error";
+ case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
+ return "Private key not available";
+ case SigningResult::SIGNING_FAILED:
+ return "Sign failed";
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
diff --git a/src/util/message.h b/src/util/message.h
new file mode 100644
index 0000000000..b31c5f5761
--- /dev/null
+++ b/src/util/message.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_MESSAGE_H
+#define BITCOIN_UTIL_MESSAGE_H
+
+#include <key.h> // For CKey
+#include <uint256.h>
+
+#include <string>
+
+extern const std::string MESSAGE_MAGIC;
+
+/** The result of a signed message verification.
+ * Message verification takes as an input:
+ * - address (with whose private key the message is supposed to have been signed)
+ * - signature
+ * - message
+ */
+enum class MessageVerificationResult {
+ //! The provided address is invalid.
+ ERR_INVALID_ADDRESS,
+
+ //! The provided address is valid but does not refer to a public key.
+ ERR_ADDRESS_NO_KEY,
+
+ //! The provided signature couldn't be parsed (maybe invalid base64).
+ ERR_MALFORMED_SIGNATURE,
+
+ //! A public key could not be recovered from the provided signature and message.
+ ERR_PUBKEY_NOT_RECOVERED,
+
+ //! The message was not signed with the private key of the provided address.
+ ERR_NOT_SIGNED,
+
+ //! The message verification was successful.
+ OK
+};
+
+enum class SigningResult {
+ OK, //!< No error
+ PRIVATE_KEY_NOT_AVAILABLE,
+ SIGNING_FAILED,
+};
+
+/** Verify a signed message.
+ * @param[in] address Signer's bitcoin address, it must refer to a public key.
+ * @param[in] signature The signature in base64 format.
+ * @param[in] message The message that was signed.
+ * @return result code */
+MessageVerificationResult MessageVerify(
+ const std::string& address,
+ const std::string& signature,
+ const std::string& message);
+
+/** Sign a message.
+ * @param[in] privkey Private key to sign with.
+ * @param[in] message The message to sign.
+ * @param[out] signature Signature, base64 encoded, only set if true is returned.
+ * @return true if signing was successful. */
+bool MessageSign(
+ const CKey& privkey,
+ const std::string& message,
+ std::string& signature);
+
+/**
+ * Hashes a message for signing and verification in a manner that prevents
+ * inadvertently signing a transaction.
+ */
+uint256 MessageHash(const std::string& message);
+
+std::string SigningResultString(const SigningResult res);
+
+#endif // BITCOIN_UTIL_MESSAGE_H
diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp
index 2797f450ca..40d8918dfc 100644
--- a/src/util/moneystr.cpp
+++ b/src/util/moneystr.cpp
@@ -36,14 +36,14 @@ bool ParseMoney(const std::string& str, CAmount& nRet)
if (!ValidAsCString(str)) {
return false;
}
- return ParseMoney(str.c_str(), nRet);
-}
-bool ParseMoney(const char* pszIn, CAmount& nRet)
-{
+ if (str.empty()) {
+ return false;
+ }
+
std::string strWhole;
int64_t nUnits = 0;
- const char* p = pszIn;
+ const char* p = str.c_str();
while (IsSpace(*p))
p++;
for (; *p; p++)
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index 027c7e2e53..d8b08adc24 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -18,7 +18,7 @@
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
std::string FormatMoney(const CAmount& n);
+/** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/
NODISCARD bool ParseMoney(const std::string& str, CAmount& nRet);
-NODISCARD bool ParseMoney(const char* pszIn, CAmount& nRet);
#endif // BITCOIN_UTIL_MONEYSTR_H
diff --git a/src/util/time.cpp b/src/util/time.cpp
index 2afff2626b..14937b985e 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -11,10 +11,13 @@
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/thread.hpp>
#include <ctime>
+#include <thread>
+
#include <tinyformat.h>
+void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
+
static std::atomic<int64_t> nMockTime(0); //!< For unit testing
int64_t GetTime()
@@ -72,32 +75,16 @@ int64_t GetSystemTimeInSeconds()
return GetTimeMicros()/1000000;
}
-void MilliSleep(int64_t n)
-{
-
-/**
- * Boost's sleep_for was uninterruptible when backed by nanosleep from 1.50
- * until fixed in 1.52. Use the deprecated sleep method for the broken case.
- * See: https://svn.boost.org/trac/boost/ticket/7238
- */
-#if defined(HAVE_WORKING_BOOST_SLEEP_FOR)
- boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
-#elif defined(HAVE_WORKING_BOOST_SLEEP)
- boost::this_thread::sleep(boost::posix_time::milliseconds(n));
-#else
-//should never get here
-#error missing boost sleep implementation
-#endif
-}
-
std::string FormatISO8601DateTime(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
#ifdef _MSC_VER
- gmtime_s(&ts, &time_val);
+ if (gmtime_s(&ts, &time_val) != 0) {
#else
- gmtime_r(&time_val, &ts);
+ if (gmtime_r(&time_val, &ts) == nullptr) {
#endif
+ return {};
+ }
return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
}
@@ -105,10 +92,12 @@ std::string FormatISO8601Date(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
#ifdef _MSC_VER
- gmtime_s(&ts, &time_val);
+ if (gmtime_s(&ts, &time_val) != 0) {
#else
- gmtime_r(&time_val, &ts);
+ if (gmtime_r(&time_val, &ts) == nullptr) {
#endif
+ return {};
+ }
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}
@@ -124,4 +113,4 @@ int64_t ParseISO8601DateTime(const std::string& str)
if (ptime.is_not_a_date_time() || epoch > ptime)
return 0;
return (ptime - epoch).total_seconds();
-} \ No newline at end of file
+}
diff --git a/src/util/time.h b/src/util/time.h
index af4390aa1c..77de1e047d 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -10,6 +10,8 @@
#include <string>
#include <chrono>
+void UninterruptibleSleep(const std::chrono::microseconds& n);
+
/**
* Helper to count the seconds of a duration.
*
@@ -36,8 +38,6 @@ void SetMockTime(int64_t nMockTimeIn);
/** For testing */
int64_t GetMockTime();
-void MilliSleep(int64_t n);
-
/** Return system time (or mocked time, if set) */
template <typename T>
T GetTime();
diff --git a/src/util/validation.cpp b/src/util/validation.cpp
deleted file mode 100644
index 89bc6665a4..0000000000
--- a/src/util/validation.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2020 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <util/validation.h>
-
-#include <consensus/validation.h>
-#include <tinyformat.h>
-
-std::string FormatStateMessage(const ValidationState &state)
-{
- if (state.IsValid()) {
- return "Valid";
- }
-
- const std::string debug_message = state.GetDebugMessage();
- if (!debug_message.empty()) {
- return strprintf("%s, %s", state.GetRejectReason(), debug_message);
- }
-
- return state.GetRejectReason();
-}
-
-const std::string strMessageMagic = "Bitcoin Signed Message:\n";
diff --git a/src/util/validation.h b/src/util/validation.h
deleted file mode 100644
index da2cf9f102..0000000000
--- a/src/util/validation.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2019 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_UTIL_VALIDATION_H
-#define BITCOIN_UTIL_VALIDATION_H
-
-#include <string>
-
-class ValidationState;
-
-/** Convert ValidationState to a human-readable message for logging */
-std::string FormatStateMessage(const ValidationState &state);
-
-extern const std::string strMessageMagic;
-
-#endif // BITCOIN_UTIL_VALIDATION_H
diff --git a/src/validation.cpp b/src/validation.cpp
index bab04b8e34..c0327c39bc 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -43,7 +43,6 @@
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
-#include <util/validation.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -662,7 +661,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
+ return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
}
// Check for non-standard pay-to-script-hash in inputs
@@ -951,7 +950,7 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, Precomp
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
- __func__, hash.ToString(), FormatStateMessage(state));
+ __func__, hash.ToString(), state.ToString());
}
return true;
@@ -1921,7 +1920,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// problems.
return AbortNode(state, "Corrupt block found indicating potential hardware failure; shutting down");
}
- return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
+ return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
}
// verify that the view's current state corresponds to the previous block
@@ -2099,7 +2098,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
+ return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
}
nFees += txfee;
if (!MoneyRange(nFees)) {
@@ -2142,7 +2141,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
return error("ConnectBlock(): CheckInputScripts on %s failed with %s",
- tx.GetHash().ToString(), FormatStateMessage(state));
+ tx.GetHash().ToString(), state.ToString());
}
control.Add(vChecks);
}
@@ -2359,7 +2358,7 @@ void CChainState::ForceFlushStateToDisk() {
BlockValidationState state;
const CChainParams& chainparams = Params();
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
- LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
@@ -2369,7 +2368,7 @@ void CChainState::PruneAndFlush() {
const CChainParams& chainparams = Params();
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
- LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
@@ -2596,7 +2595,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
if (!rv) {
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
- return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state));
+ return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), state.ToString());
}
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
@@ -3601,7 +3600,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
}
if (!CheckBlockHeader(block, state, chainparams.GetConsensus()))
- return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+ return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString());
// Get prev block index
CBlockIndex* pindexPrev = nullptr;
@@ -3616,7 +3615,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
- return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+ return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString());
/* Determine if this block descends from any block which has been found
* invalid (m_failed_blocks), then mark pindexPrev and any blocks between
@@ -3766,7 +3765,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
}
- return error("%s: %s", __func__, FormatStateMessage(state));
+ return error("%s: %s", __func__, state.ToString());
}
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
@@ -3816,7 +3815,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
- return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state));
+ return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString());
}
}
@@ -3824,7 +3823,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock))
- return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state));
+ return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString());
return true;
}
@@ -3842,11 +3841,11 @@ bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainpar
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
- return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
+ return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
- return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
+ return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
- return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
+ return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
assert(state.IsValid());
@@ -3944,7 +3943,7 @@ void PruneBlockFilesManual(int nManualPruneHeight)
const CChainParams& chainparams = Params();
if (!::ChainstateActive().FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
- LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
@@ -4262,7 +4261,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus()))
return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__,
- pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
+ pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
@@ -4311,7 +4310,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
- return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
+ return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
}
}
@@ -4499,7 +4498,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Disconnect block
if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state));
+ return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, state.ToString());
}
// Reduce validity flag and have-data flags.
@@ -4519,7 +4518,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Occasionally flush state to disk.
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
return false;
}
}
@@ -4550,7 +4549,7 @@ bool RewindBlockIndex(const CChainParams& params) {
// it'll get called a bunch real soon.
BlockValidationState state;
if (!::ChainstateActive().FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
return false;
}
}
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 0f513c065f..1deb93c972 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -11,7 +11,6 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <scheduler.h>
-#include <util/validation.h>
#include <future>
#include <unordered_map>
@@ -193,7 +192,7 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
void CMainSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
LOG_EVENT("%s: block hash=%s state=%s", __func__,
- block.GetHash().ToString(), FormatStateMessage(state));
+ block.GetHash().ToString(), state.ToString());
m_internals->BlockChecked(block, state);
}
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 8b042162d8..67be4d85d2 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -756,7 +756,7 @@ bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
return fSuccess;
}
}
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
}
@@ -850,7 +850,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
return BerkeleyBatch::Rewrite(*this, pszSkip);
}
-bool BerkeleyDatabase::Backup(const std::string& strDest)
+bool BerkeleyDatabase::Backup(const std::string& strDest) const
{
if (IsDummy()) {
return false;
@@ -887,7 +887,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest)
}
}
}
- MilliSleep(100);
+ UninterruptibleSleep(std::chrono::milliseconds{100});
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index abec3ae4e2..bebaa55d05 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -157,7 +157,7 @@ public:
/** Back up the entire database to a file.
*/
- bool Backup(const std::string& strDest);
+ bool Backup(const std::string& strDest) const;
/** Make sure all changes are flushed to disk.
*/
@@ -193,7 +193,7 @@ private:
* Only to be used at a low level, application should ideally not care
* about this.
*/
- bool IsDummy() { return env == nullptr; }
+ bool IsDummy() const { return env == nullptr; }
};
/** RAII class that provides access to a Berkeley database */
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index b93b9ef1bc..486a6b8bf1 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -12,7 +12,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/system.h>
-#include <util/validation.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
deleted file mode 100644
index d995fb06d4..0000000000
--- a/src/wallet/psbtwallet.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2009-2019 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 <wallet/psbtwallet.h>
-
-TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs)
-{
- LOCK(pwallet->cs_wallet);
- // Get all of the previous transactions
- complete = true;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- const CTxIn& txin = psbtx.tx->vin[i];
- PSBTInput& input = psbtx.inputs.at(i);
-
- if (PSBTInputSigned(input)) {
- continue;
- }
-
- // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
- if (!input.IsSane()) {
- return TransactionError::INVALID_PSBT;
- }
-
- // If we have no utxo, grab it from the wallet.
- if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
- const uint256& txhash = txin.prevout.hash;
- const auto it = pwallet->mapWallet.find(txhash);
- if (it != pwallet->mapWallet.end()) {
- const CWalletTx& wtx = it->second;
- // We only need the non_witness_utxo, which is a superset of the witness_utxo.
- // The signing code will switch to the smaller witness_utxo if this is ok.
- input.non_witness_utxo = wtx.tx;
- }
- }
-
- // Get the Sighash type
- if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
- return TransactionError::SIGHASH_MISMATCH;
- }
-
- // Get the scriptPubKey to know which SigningProvider to use
- CScript script;
- if (!input.witness_utxo.IsNull()) {
- script = input.witness_utxo.scriptPubKey;
- } else if (input.non_witness_utxo) {
- if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
- return TransactionError::MISSING_INPUTS;
- }
- script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
- } else {
- // There's no UTXO so we can just skip this now
- complete = false;
- continue;
- }
- SignatureData sigdata;
- input.FillSignatureData(sigdata);
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(script, sigdata);
- if (!provider) {
- complete = false;
- continue;
- }
-
- complete &= SignPSBTInput(HidingSigningProvider(provider.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
- }
-
- // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
- for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- const CTxOut& out = psbtx.tx->vout.at(i);
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(out.scriptPubKey);
- if (provider) {
- UpdatePSBTOutput(HidingSigningProvider(provider.get(), true, !bip32derivs), psbtx, i);
- }
- }
-
- return TransactionError::OK;
-}
diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h
deleted file mode 100644
index b35a0a58d1..0000000000
--- a/src/wallet/psbtwallet.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2009-2019 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_WALLET_PSBTWALLET_H
-#define BITCOIN_WALLET_PSBTWALLET_H
-
-#include <psbt.h>
-#include <wallet/wallet.h>
-
-/**
- * Fills out a PSBT with information from the wallet. Fills in UTXOs if we have
- * them. Tries to sign if sign=true. Sets `complete` if the PSBT is now complete
- * (i.e. has all required signatures or signature-parts, and is ready to
- * finalize.) Sets `error` and returns false if something goes wrong.
- *
- * @param[in] pwallet pointer to a wallet
- * @param[in] psbtx PartiallySignedTransaction to fill in
- * @param[out] complete indicates whether the PSBT is now complete
- * @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
- * @param[in] sign whether to sign or not
- * @param[in] bip32derivs whether to fill in bip32 derivation information if available
- * return error
- */
-NODISCARD TransactionError FillPSBT(const CWallet* pwallet,
- PartiallySignedTransaction& psbtx,
- bool& complete,
- int sighash_type = 1 /* SIGHASH_ALL */,
- bool sign = true,
- bool bip32derivs = false);
-
-#endif // BITCOIN_WALLET_PSBTWALLET_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7e704a95fe..8df3491060 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -54,7 +54,7 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
bool fLabelFound = false;
CKey key;
@@ -65,7 +65,7 @@ static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, CWallet* co
strAddr += ",";
}
strAddr += EncodeDestination(dest);
- strLabel = EncodeDumpString(pwallet->mapAddressBook[dest].name);
+ strLabel = EncodeDumpString(pwallet->mapAddressBook.at(dest).name);
fLabelFound = true;
}
}
@@ -676,7 +676,7 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue dumpprivkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
@@ -688,7 +688,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
},
RPCResult{
- "\"key\" (string) The private key\n"
+ RPCResult::Type::STR, "key", "The private key"
},
RPCExamples{
HelpExampleCli("dumpprivkey", "\"myaddress\"")
@@ -724,7 +724,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
UniValue dumpwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
@@ -738,9 +738,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to bitcoind)"},
},
RPCResult{
- "{ (json object)\n"
- " \"filename\" : { (string) The filename with full absolute path\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "filename", "The filename with full absolute path"},
+ }
},
RPCExamples{
HelpExampleCli("dumpwallet", "\"test\"")
@@ -1321,19 +1322,21 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"\"options\""},
},
RPCResult{
- "[ (json array) Response is an array with the same size as the input that has the execution result\n"
- " { (json object)\n"
- " \"success\" : true|false, (boolean)\n"
- " \"warnings\" : [ (json array, optional)\n"
- " \"str\", (string)\n"
- " ...\n"
- " ],\n"
- " \"error\" : { (json object, optional)\n"
- " ... JSONRPC error\n"
- " },\n"
- " },\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "success", ""},
+ {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ {RPCResult::Type::OBJ, "error", /* optional */ true, "",
+ {
+ {RPCResult::Type::ELISION, "", "JSONRPC error"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index bc4ec77e31..44e580a52a 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -19,14 +19,14 @@
#include <script/sign.h>
#include <util/bip32.h>
#include <util/fees.h>
+#include <util/message.h> // For MessageSign()
#include <util/moneystr.h>
#include <util/string.h>
#include <util/system.h>
#include <util/url.h>
-#include <util/validation.h>
+#include <util/vector.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
-#include <wallet/psbtwallet.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -39,7 +39,7 @@
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
-static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& param) {
+static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
@@ -205,7 +205,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "\"address\" (string) The new bitcoin address\n"
+ RPCResult::Type::STR, "address", "The new bitcoin address"
},
RPCExamples{
HelpExampleCli("getnewaddress", "")
@@ -256,7 +256,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "\"address\" (string) The address\n"
+ RPCResult::Type::STR, "address", "The address"
},
RPCExamples{
HelpExampleCli("getrawchangeaddress", "")
@@ -342,7 +342,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
- CAmount nFeeRequired;
+ CAmount nFeeRequired = 0;
std::string strError;
std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
@@ -390,7 +390,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" dirty if they have previously been used in a transaction."},
},
RPCResult{
- "\"txid\" (string) The transaction id.\n"
+ RPCResult::Type::STR_HEX, "txid", "The transaction id."
},
RPCExamples{
HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
@@ -457,7 +457,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
static UniValue listaddressgroupings(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -469,17 +469,18 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
"in past transactions\n",
{},
RPCResult{
- "[\n"
- " [\n"
- " [\n"
- " \"address\", (string) The bitcoin address\n"
- " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n"
- " \"label\" (string, optional) The label\n"
- " ]\n"
- " ,...\n"
- " ]\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The bitcoin address"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR, "label", /* optional */ true, "The label"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listaddressgroupings", "")
@@ -518,7 +519,7 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
static UniValue signmessage(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -532,7 +533,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
- "\"signature\" (string) The signature of the message encoded in base 64\n"
+ RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
},
RPCExamples{
"\nUnlock the wallet for 30 seconds\n"
@@ -564,33 +565,21 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
- CScript script_pub_key = GetScriptForDestination(*pkhash);
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(script_pub_key);
- if (!provider) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
+ std::string signature;
+ SigningResult err = pwallet->SignMessage(strMessage, *pkhash, signature);
+ if (err == SigningResult::SIGNING_FAILED) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, SigningResultString(err));
+ } else if (err != SigningResult::OK){
+ throw JSONRPCError(RPC_WALLET_ERROR, SigningResultString(err));
}
- CKey key;
- CKeyID keyID(*pkhash);
- if (!provider->GetKey(keyID, key)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
- }
-
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << strMessage;
-
- std::vector<unsigned char> vchSig;
- if (!key.SignCompact(ss.GetHash(), vchSig))
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
-
- return EncodeBase64(vchSig.data(), vchSig.size());
+ return signature;
}
static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -603,7 +592,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
},
RPCExamples{
"\nThe amount from transactions with at least 1 confirmation\n"
@@ -660,7 +649,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -673,7 +662,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
},
RPCExamples{
"\nAmount received by the default label with at least 1 confirmation\n"
@@ -728,7 +717,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
static UniValue getbalance(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -745,7 +734,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
},
RPCExamples{
"\nThe total amount in the wallet with 1 or more confirmations\n"
@@ -786,7 +775,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -847,8 +836,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
RPCResult{
- "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
- " the number of addresses.\n"
+ RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."
},
RPCExamples{
"\nSend two amounts to two different addresses:\n"
@@ -971,11 +960,12 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "{\n"
- " \"address\" : \"multisigaddress\", (string) The value of the new multisig address.\n"
- " \"redeemScript\" : \"script\" (string) The string value of the hex-encoded redemption script.\n"
- " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The value of the new multisig address"},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
+ {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ }
},
RPCExamples{
"\nAdd a multisig address from 2 addresses\n"
@@ -1040,7 +1030,7 @@ struct tallyitem
}
};
-static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1189,7 +1179,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -1204,20 +1194,21 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"receivingaddress\", (string) The receiving address\n"
- " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
- " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
- " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n"
- " \"txids\" : [\n"
- " \"txid\", (string) The ids of transactions received with the address \n"
- " ...\n"
- " ]\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::STR, "address", "The receiving address"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
+ {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
+ {RPCResult::Type::ARR, "txids", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listreceivedbyaddress", "")
@@ -1240,7 +1231,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -1254,15 +1245,16 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n"
- " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n"
- " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
- " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
+ {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listreceivedbylabel", "")
@@ -1299,7 +1291,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1322,7 +1314,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
if (pwallet->mapAddressBook.count(s.destination)) {
- entry.pushKV("label", pwallet->mapAddressBook[s.destination].name);
+ entry.pushKV("label", pwallet->mapAddressBook.at(s.destination).name);
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
@@ -1339,7 +1331,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
{
std::string label;
if (pwallet->mapAddressBook.count(r.destination)) {
- label = pwallet->mapAddressBook[r.destination].name;
+ label = pwallet->mapAddressBook.at(r.destination).name;
}
if (filter_label && label != *filter_label) {
continue;
@@ -1374,32 +1366,32 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
}
}
-static const std::string TransactionDescriptionString()
+static const std::vector<RPCResult> TransactionDescriptionString()
{
- return " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations means the\n"
- " transaction conflicted that many blocks ago.\n"
- " \"generated\" : xxx, (boolean) Only present if transaction only input is a coinbase one.\n"
- " \"trusted\" : xxx, (boolean) Only present if we consider transaction to be trusted and so safe to spend from.\n"
- " \"blockhash\" : \"hashvalue\", (string) The block hash containing the transaction.\n"
- " \"blockheight\" : n, (numeric) The block height containing the transaction.\n"
- " \"blockindex\" : n, (numeric) The index of the transaction in the block that includes it.\n"
- " \"blocktime\" : xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"txid\" : \"transactionid\", (string) The transaction id.\n"
- " \"walletconflicts\" : [ (json array) Conflicting transaction ids.\n"
- " \"txid\", (string) The transaction id.\n"
- " ...\n"
- " ],\n"
- " \"time\" : xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"timereceived\" : xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"comment\" : \"...\", (string) If a comment is associated with the transaction, only present if not empty.\n"
- " \"bip125-replaceable\" : \"str\", (string) (\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- " may be unknown for unconfirmed transactions not in the mempool\n";
+ return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
+ "transaction conflicted that many blocks ago."},
+ {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
+ {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
+ {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
+ {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ }},
+ {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
+ {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
+ "may be unknown for unconfirmed transactions not in the mempool"}};
}
UniValue listtransactions(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -1416,27 +1408,31 @@ UniValue listtransactions(const JSONRPCRequest& request)
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"address\", (string) The bitcoin address of the transaction.\n"
- " \"category\" : (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- " for all other categories\n"
- " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"abandoned\": xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- " 'send' category of transactions.\n"
- " }\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ "for all other categories"},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ })},
+ }
},
RPCExamples{
"\nList the most recent 10 transactions in the systems\n"
@@ -1509,7 +1505,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
static UniValue listsinceblock(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -1527,34 +1523,39 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
" (not guaranteed to work on pruned nodes)"},
},
RPCResult{
- "{ (json object)\n"
- " \"transactions\" : [ (json array)\n"
- " { (json object)\n"
- " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"str\", (string) The bitcoin address of the transaction.\n"
- " \"category\" : \"str\", (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- " for all other categories\n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"abandoned\": xxx, (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n"
- " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
- " \"to\" : \"...\", (string) If a comment to is associated with the transaction.\n"
- " },\n"
- " ...\n"
- " ],\n"
- " \"removed\" : [ (json array)\n"
- " <structure is the same as \"transactions\" above, only present if include_removed=true>\n"
- " Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.\n"
- " ],\n"
- " \"lastblock\" : \"hex\" (string) The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "transactions", "",
+ {
+ {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ "for all other categories"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
+ })},
+ }},
+ {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
+ "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
+ , {{RPCResult::Type::ELISION, "", ""},}},
+ {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
+ }
},
RPCExamples{
HelpExampleCli("listsinceblock", "")
@@ -1646,7 +1647,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
static UniValue gettransaction(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -1662,35 +1663,41 @@ static UniValue gettransaction(const JSONRPCRequest& request)
"Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
},
RPCResult{
- "{\n"
- " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n"
- " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"details\" : [\n"
- " {\n"
- " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
- " \"category\" : (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
- " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- " \"abandoned\" : xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- " 'send' category of transactions.\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"hex\" : \"data\" (string) Raw data for transaction\n"
- " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n"
- " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::ARR, "details", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
+ "'send' category of transactions."},
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ }},
+ }},
+ {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
+ {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
+ {
+ {RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
+ }},
+ })
},
RPCExamples{
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
@@ -1799,7 +1806,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
static UniValue backupwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -2149,7 +2156,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
},
},
RPCResult{
- "true|false (boolean) Whether the command was successful or not\n"
+ RPCResult::Type::BOOL, "", "Whether the command was successful or not"
},
RPCExamples{
"\nList the unspent transactions\n"
@@ -2248,7 +2255,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
static UniValue listlockunspent(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -2259,13 +2266,14 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
"See the lockunspent call to lock and unlock transactions for spending.\n",
{},
RPCResult{
- "[\n"
- " {\n"
- " \"txid\" : \"transactionid\", (string) The transaction id locked\n"
- " \"vout\" : n (numeric) The vout value\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ }},
+ }
},
RPCExamples{
"\nList the unspent transactions\n"
@@ -2315,7 +2323,7 @@ static UniValue settxfee(const JSONRPCRequest& request)
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"},
},
RPCResult{
- "true|false (boolean) Returns true if successful\n"
+ RPCResult::Type::BOOL, "", "Returns true if successful"
},
RPCExamples{
HelpExampleCli("settxfee", "0.00001")
@@ -2353,19 +2361,23 @@ static UniValue getbalances(const JSONRPCRequest& request)
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
RPCResult{
- "{\n"
- " \"mine\" : { (json object) balances from outputs that the wallet can sign\n"
- " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
- " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
- " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n"
- " \"used\" : xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n"
- " },\n"
- " \"watchonly\" : { (json object) watchonly balances (not present if wallet does not watch anything)\n"
- " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
- " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
- " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n"
- " },\n"
- "}\n"},
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
+ }},
+ {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ }},
+ }
+ },
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")},
@@ -2409,7 +2421,7 @@ static UniValue getbalances(const JSONRPCRequest& request)
static UniValue getwalletinfo(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -2419,27 +2431,29 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
- "{\n"
- " \"walletname\" : xxxxx, (string) the wallet name\n"
- " \"walletversion\" : xxxxx, (numeric) the wallet version\n"
- " \"balance\" : xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n"
- " \"unconfirmed_balance\" : xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n"
- " \"immature_balance\" : xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n"
- " \"txcount\" : xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\" : xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n"
- " \"keypoolsize\" : xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
- " \"keypoolsize_hd_internal\" : xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
- " \"unlocked_until\" : ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
- " \"paytxfee\" : x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
- " \"hdseedid\" : \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
- " \"private_keys_enabled\" : true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
- " \"avoid_reuse\" : true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n"
- " \"scanning\" : (json object) current scanning details, or false if no scan is in progress\n"
- " {\n"
- " \"duration\" : xxxx (numeric) elapsed seconds since scan start\n"
- " \"progress\" : x.xxxx, (numeric) scanning progress percentage [0.0, 1.0]\n"
- " }\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {
+ {RPCResult::Type::STR, "walletname", "the wallet name"},
+ {RPCResult::Type::NUM, "walletversion", "the wallet version"},
+ {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
+ {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
+ {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
+ {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
+ {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"},
+ {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
+ {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
+ {RPCResult::Type::NUM_TIME, "unlocked_until", "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked"},
+ {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
+ {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
+ {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
+ {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
+ {
+ {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
+ {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
+ }},
+ }},
},
RPCExamples{
HelpExampleCli("getwalletinfo", "")
@@ -2501,14 +2515,16 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
"Returns a list of wallets in the wallet directory.\n",
{},
RPCResult{
- "{\n"
- " \"wallets\" : [ (json array of objects)\n"
- " {\n"
- " \"name\" : \"name\" (string) The wallet name\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "wallets", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listwalletdir", "")
@@ -2535,10 +2551,10 @@ static UniValue listwallets(const JSONRPCRequest& request)
"For full information on the wallet, use \"getwalletinfo\"\n",
{},
RPCResult{
- "[ (json array of strings)\n"
- " \"walletname\" (string) the wallet name\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "walletname", "the wallet name"},
+ }
},
RPCExamples{
HelpExampleCli("listwallets", "")
@@ -2571,10 +2587,11 @@ static UniValue loadwallet(const JSONRPCRequest& request)
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
},
RPCResult{
- "{\n"
- " \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n"
- " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
+ {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ }
},
RPCExamples{
HelpExampleCli("loadwallet", "\"test.dat\"")
@@ -2626,11 +2643,12 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
{"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."},
},
RPCResult{
- "{\n"
- " \"flag_name\" : string (string) The name of the flag that was modified\n"
- " \"flag_state\" : bool (boolean) The new state of the flag\n"
- " \"warnings\" : string (string) Any warnings associated with the change\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
+ {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
+ {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"},
+ }
},
RPCExamples{
HelpExampleCli("setwalletflag", "avoid_reuse")
@@ -2686,10 +2704,11 @@ static UniValue createwallet(const JSONRPCRequest& request)
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
},
RPCResult{
- "{\n"
- " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
- " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
+ {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ }
},
RPCExamples{
HelpExampleCli("createwallet", "\"testwallet\"")
@@ -2784,7 +2803,7 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
static UniValue listunspent(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -2815,27 +2834,28 @@ static UniValue listunspent(const JSONRPCRequest& request)
"query_options"},
},
RPCResult{
- "[ (array of json object)\n"
- " {\n"
- " \"txid\" : \"txid\", (string) the transaction id \n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"address\" : \"address\", (string) the bitcoin address\n"
- " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
- " \"scriptPubKey\" : \"key\", (string) the script key\n"
- " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
- " \"confirmations\" : n, (numeric) The number of confirmations\n"
- " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH\n"
- " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
- " \"spendable\" : xxx, (boolean) Whether we have the private keys to spend this output\n"
- " \"solvable\" : xxx, (boolean) Whether we know how to spend this output, ignoring the lack of keys\n"
- " \"reused\" : xxx, (boolean) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n"
- " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
- " \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR, "address", "the bitcoin address"},
+ {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
+ {RPCResult::Type::STR, "scriptPubKey", "the script key"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
+ {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
+ {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
+ {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
+ {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
+ {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
- " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
- " }\n"
- " ,...\n"
- "]\n"
+ "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listunspent", "")
@@ -2925,7 +2945,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
CTxDestination address;
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
- bool reused = avoid_reuse && pwallet->IsUsedDestination(out.tx->GetHash(), out.i);
+ bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
continue;
@@ -2942,7 +2962,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("label", i->second.name);
}
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
if (provider) {
if (scriptPubKey.IsPayToScriptHash()) {
const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
@@ -2982,7 +3002,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
if (provider) {
auto descriptor = InferDescriptor(scriptPubKey, *provider);
entry.pushKV("desc", descriptor->ToString());
@@ -3173,11 +3193,12 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
},
RPCResult{
- "{\n"
- " \"hex\" : \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
- " \"fee\" : n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
- " \"changepos\" : n (numeric) The position of the added change output, or -1\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
+ {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
+ }
},
RPCExamples{
"\nCreate a transaction with no inputs\n"
@@ -3216,7 +3237,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -3252,20 +3273,22 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
" \"SINGLE|ANYONECANPAY\""},
},
RPCResult{
- "{\n"
- " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n"
- " {\n"
- " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n"
- " \"vout\" : n, (numeric) The index of the output to spent and used as input\n"
- " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n"
- " \"sequence\" : n, (numeric) Script sequence number\n"
- " \"error\" : \"text\" (string) Verification or signing error related to the input\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
+ {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
+ {RPCResult::Type::NUM, "sequence", "Script sequence number"},
+ {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
@@ -3295,23 +3318,15 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins);
- std::set<std::shared_ptr<SigningProvider>> providers;
- for (const std::pair<COutPoint, Coin> coin_pair : coins) {
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(coin_pair.second.out.scriptPubKey);
- if (provider) {
- providers.insert(std::move(provider));
- }
- }
- if (providers.size() == 0) {
- // When there are no available providers, use a dummy SigningProvider so we can check if the tx is complete
- providers.insert(std::make_shared<SigningProvider>());
- }
+ int nHashType = ParseSighashString(request.params[2]);
+ // Script verification errors
+ std::map<int, std::string> input_errors;
+
+ bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
UniValue result(UniValue::VOBJ);
- for (std::shared_ptr<SigningProvider> provider : providers) {
- SignTransaction(mtx, provider.get(), coins, request.params[2], result);
- }
- return result;
+ SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
+ return result;
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3362,13 +3377,16 @@ static UniValue bumpfee(const JSONRPCRequest& request)
"options"},
},
RPCResult{
- "{\n"
- " \"psbt\" : \"psbt\", (string) The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled.\n"
- " \"txid\" : \"value\", (string) The id of the new transaction. Only returned when wallet private keys are enabled.\n"
- " \"origfee\" : n, (numeric) The fee of the replaced transaction.\n"
- " \"fee\" : n, (numeric) The fee of the new transaction.\n"
- " \"errors\" : [ str... ] (json array of strings) Errors encountered during processing (may be empty).\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
+ {RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
+ {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
+ {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ }
},
RPCExamples{
"\nBump the fee, get the new transaction\'s txid\n" +
@@ -3487,7 +3505,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
} else {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = FillPSBT(pwallet, psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
CHECK_NONFATAL(err == TransactionError::OK);
CHECK_NONFATAL(!complete);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -3523,10 +3541,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
},
RPCResult{
- "{\n"
- " \"start_height\" (numeric) The block height where the rescan started (the requested height or 0)\n"
- " \"stop_height\" (numeric) The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"},
+ {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."},
+ }
},
RPCExamples{
HelpExampleCli("rescanblockchain", "100000 120000")
@@ -3690,14 +3709,14 @@ public:
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
-static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest)
+static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDestination& dest)
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
CScript script = GetScriptForDestination(dest);
std::unique_ptr<SigningProvider> provider = nullptr;
if (pwallet) {
- provider = pwallet->GetSigningProvider(script);
+ provider = pwallet->GetSolvingProvider(script);
}
ret.pushKVs(detail);
ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider.get()), dest));
@@ -3718,14 +3737,12 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
UniValue getaddressinfo(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
- const std::string example_address = "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"";
-
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
@@ -3733,53 +3750,56 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
},
RPCResult{
- "{\n"
- " \"address\" : \"address\", (string) The bitcoin address validated.\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address.\n"
- " \"ismine\" : true|false, (boolean) If the address is yours.\n"
- " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n"
- " \"solvable\" : true|false, (boolean) If we know how to spend coins sent to this address, ignoring the possible lack of private keys.\n"
- " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable).\n"
- " \"isscript\" : true|false, (boolean) If the key is a script.\n"
- " \"ischange\" : true|false, (boolean) If the address was used for change output.\n"
- " \"iswitness\" : true|false, (boolean) If the address is a witness address.\n"
- " \"witness_version\" : version (numeric, optional) The version number of the witness program.\n"
- " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program.\n"
- " \"script\" : \"type\" (string, optional) The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The bitcoin address validated."},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
+ {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
+ {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
+ {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
+ {RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."},
+ {RPCResult::Type::BOOL, "isscript", "If the key is a script."},
+ {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
+ {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
+ {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
+ {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
+ {RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
" types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
- " witness_v0_scripthash, witness_unknown.\n"
- " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address.\n"
- " \"pubkeys\" (array, optional) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n"
- " [\n"
- " \"pubkey\" (string)\n"
- " ,...\n"
- " ]\n"
- " \"sigsrequired\" : xxxxx (numeric, optional) The number of signatures required to spend multisig output (only if script is multisig).\n"
- " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n"
- " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. Includes all\n"
+ "witness_v0_scripthash, witness_unknown."},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
+ {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
+ {
+ {RPCResult::Type::STR, "pubkey", ""},
+ }},
+ {RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."},
+ {RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
+ {RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
+ {
+ {RPCResult::Type::ELISION, "", "Includes all\n"
" getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
- " hdseedid) and relation to the wallet (ismine, iswatchonly).\n"
- " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n"
- " \"label\" : \"label\" (string) DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below.\n"
- " \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n"
- " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n"
- " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n"
- " \"labels\" (json array) Array of labels associated with the address. Currently limited to one label but returned\n"
- " as an array to keep the API stable if multiple labels are enabled in the future.\n"
- " [\n"
- " \"label name\" (string) The label name. Defaults to \"\".\n"
- " DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`:\n"
- " {\n"
- " \"name\" : \"label name\" (string) The label name. Defaults to \"\".\n"
- " \"purpose\" : \"purpose\" (string) The purpose of the associated address (send or receive).\n"
- " }\n"
- " ]\n"
- "}\n"
+ "hdseedid) and relation to the wallet (ismine, iswatchonly)."},
+ }},
+ {RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
+ {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."},
+ {RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
+ {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
+ {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /* optional */ true, "The fingerprint of the master key."},
+ {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
+ "as an array to keep the API stable if multiple labels are enabled in the future.",
+ {
+ {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."},
+ {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`",
+ {
+ {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."},
+ {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."},
+ }},
+ }},
+ }
},
RPCExamples{
- HelpExampleCli("getaddressinfo", example_address) +
- HelpExampleRpc("getaddressinfo", example_address)
+ HelpExampleCli("getaddressinfo", EXAMPLE_ADDRESS) +
+ HelpExampleRpc("getaddressinfo", EXAMPLE_ADDRESS)
},
}.Check(request);
@@ -3798,7 +3818,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
- std::unique_ptr<SigningProvider> provider = pwallet->GetSigningProvider(scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
isminetype mine = pwallet->IsMine(dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
@@ -3819,7 +3839,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// be associated with an address, so the label should be equivalent to the
// value of the name key/value pair in the labels array below.
if ((pwallet->chain().rpcEnableDeprecated("label")) && (pwallet->mapAddressBook.count(dest))) {
- ret.pushKV("label", pwallet->mapAddressBook[dest].name);
+ ret.pushKV("label", pwallet->mapAddressBook.at(dest).name);
}
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3842,7 +3862,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// stable if we allow multiple labels to be associated with an address in
// the future.
UniValue labels(UniValue::VARR);
- std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
+ std::map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end()) {
// DEPRECATED: The previous behavior of returning an array containing a
// JSON object of `name` and `purpose` key/value pairs is deprecated.
@@ -3860,7 +3880,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -3872,11 +3892,13 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
},
RPCResult{
- "{ (json object with addresses as keys)\n"
- " \"address\" : { (json object with information about address)\n"
- " \"purpose\" : \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n"
- " },...\n"
- "}\n"
+ RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
+ {
+ {RPCResult::Type::OBJ, "address", "json object with information about address",
+ {
+ {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getaddressesbylabel", "\"tabby\"")
@@ -3917,7 +3939,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
static UniValue listlabels(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -3929,10 +3951,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
{"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
},
RPCResult{
- "[ (json array of string)\n"
- " \"label\", (string) Label name\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "label", "Label name"},
+ }
},
RPCExamples{
"\nList all labels\n"
@@ -4050,7 +4072,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
+ const CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
@@ -4070,13 +4092,14 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\""},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
- "{ (json object)\n"
- " \"psbt\" : \"str\", (string) The base64-encoded partially signed transaction\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ }
},
RPCExamples{
HelpExampleCli("walletprocesspsbt", "\"psbt\"")
@@ -4097,9 +4120,9 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
- bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
+ bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
- const TransactionError err = FillPSBT(pwallet, psbtx, complete, nHashType, sign, bip32derivs);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, nHashType, sign, bip32derivs);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4180,14 +4203,15 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
"options"},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
- "{\n"
- " \"psbt\" : \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
- " \"fee\" : n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
- " \"changepos\" : n (numeric) The position of the added change output, or -1\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
+ {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
+ }
},
RPCExamples{
"\nCreate a transaction with no inputs\n"
@@ -4219,9 +4243,9 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
PartiallySignedTransaction psbtx(rawTx);
// Fill transaction with out data but don't sign
- bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
+ bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
bool complete = true;
- const TransactionError err = FillPSBT(pwallet, psbtx, complete, 1, false, bip32derivs);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, 1, false, bip32derivs);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 4c9d88973e..6fe1d84d64 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -5,6 +5,7 @@
#include <key_io.h>
#include <outputtype.h>
#include <script/descriptor.h>
+#include <script/sign.h>
#include <util/bip32.h>
#include <util/strencodings.h>
#include <util/translation.h>
@@ -70,7 +71,15 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
return true;
}
-IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
+//! Recursively solve script and return spendable/watchonly/invalid status.
+//!
+//! @param keystore legacy key and script store
+//! @param script script to solve
+//! @param sigversion script type (top-level / redeemscript / witnessscript)
+//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
+//! scripts or simply treat any script that has been
+//! stored in the keystore as spendable
+IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
{
IsMineResult ret = IsMineResult::NO;
@@ -129,7 +138,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
+ ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::P2SH) : IsMineResult::SPENDABLE);
}
break;
}
@@ -147,7 +156,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
+ ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0) : IsMineResult::SPENDABLE);
}
break;
}
@@ -350,7 +359,7 @@ bool LegacyScriptPubKeyMan::IsHDEnabled() const
return !hdChain.seed_id.IsNull();
}
-bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal)
+bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const
{
LOCK(cs_KeyStore);
// Check if the keypool has keys
@@ -433,7 +442,7 @@ static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, Walle
return keypool.nTime;
}
-int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime()
+int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime() const
{
LOCK(cs_KeyStore);
@@ -451,7 +460,7 @@ int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime()
return oldestKey;
}
-size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys()
+size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() const
{
LOCK(cs_KeyStore);
return setExternalKeyPool.size() + set_pre_split_keypool.size();
@@ -469,18 +478,18 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
return nTimeFirstKey;
}
-std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSigningProvider(const CScript& script) const
+std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const
{
return MakeUnique<LegacySigningProvider>(*this);
}
bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata)
{
- if (IsMine(script) != ISMINE_NO) {
- // If it IsMine, we can always provide in some way
- return true;
- } else if (HaveCScript(CScriptID(script))) {
- // We can still provide some stuff if we have the script, but IsMine failed because we don't have keys
+ IsMineResult ismine = IsMineInner(*this, script, IsMineSigVersion::TOP, /* recurse_scripthash= */ false);
+ if (ismine == IsMineResult::SPENDABLE || ismine == IsMineResult::WATCH_ONLY) {
+ // If ismine, it means we recognize keys or script ids in the script, or
+ // are watching the script itself, and we can at least provide metadata
+ // or solving information, even if not able to sign fully.
return true;
} else {
// If, given the stuff in sigdata, we could make a valid sigature, then we can provide for this script
@@ -497,6 +506,67 @@ bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sig
}
}
+bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+{
+ return ::SignTransaction(tx, this, coins, sighash, input_errors);
+}
+
+SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
+{
+ CKeyID key_id(pkhash);
+ CKey key;
+ if (!GetKey(key_id, key)) {
+ return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
+ }
+
+ if (MessageSign(key, message, str_sig)) {
+ return SigningResult::OK;
+ }
+ return SigningResult::SIGNING_FAILED;
+}
+
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
+{
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ // Get the Sighash type
+ if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
+ return TransactionError::SIGHASH_MISMATCH;
+ }
+
+ // Check non_witness_utxo has specified prevout
+ if (input.non_witness_utxo) {
+ if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ } else if (input.witness_utxo.IsNull()) {
+ // There's no UTXO so we can just skip this now
+ continue;
+ }
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+ SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+ }
+
+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i);
+ }
+
+ return TransactionError::OK;
+}
+
const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
{
LOCK(cs_KeyStore);
@@ -965,7 +1035,7 @@ void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
-bool LegacyScriptPubKeyMan::CanGenerateKeys()
+bool LegacyScriptPubKeyMan::CanGenerateKeys() const
{
// A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD)
LOCK(cs_KeyStore);
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 7b1c023bc9..8512eadf31 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -5,8 +5,11 @@
#ifndef BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
#define BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
+#include <psbt.h>
#include <script/signingprovider.h>
#include <script/standard.h>
+#include <util/error.h>
+#include <util/message.h>
#include <wallet/crypter.h>
#include <wallet/ismine.h>
#include <wallet/walletdb.h>
@@ -184,7 +187,7 @@ public:
virtual bool IsHDEnabled() const { return false; }
/* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
- virtual bool CanGetAddresses(bool internal = false) { return false; }
+ virtual bool CanGetAddresses(bool internal = false) const { return false; }
/** Upgrades the wallet to the specified version */
virtual bool Upgrade(int prev_version, std::string& error) { return false; }
@@ -194,22 +197,29 @@ public:
//! The action to do when the DB needs rewrite
virtual void RewriteDB() {}
- virtual int64_t GetOldestKeyPoolTime() { return GetTime(); }
+ virtual int64_t GetOldestKeyPoolTime() const { return GetTime(); }
- virtual size_t KeypoolCountExternalKeys() { return 0; }
+ virtual size_t KeypoolCountExternalKeys() const { return 0; }
virtual unsigned int GetKeyPoolSize() const { return 0; }
virtual int64_t GetTimeFirstKey() const { return 0; }
virtual const CKeyMetadata* GetMetadata(const CTxDestination& dest) const { return nullptr; }
- virtual std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script) const { return nullptr; }
+ virtual std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const { return nullptr; }
- /** Whether this ScriptPubKeyMan can provide a SigningProvider (via GetSigningProvider) that, combined with
- * sigdata, can produce a valid signature.
+ /** Whether this ScriptPubKeyMan can provide a SigningProvider (via GetSolvingProvider) that, combined with
+ * sigdata, can produce solving data.
*/
virtual bool CanProvide(const CScript& script, SignatureData& sigdata) { return false; }
+ /** Creates new signatures and adds them to the transaction. Returns whether all inputs were signed */
+ virtual bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { return false; }
+ /** Sign a message with the given script */
+ virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
+ /** Adds script and derivation path information to a PSBT, and optionally signs it. */
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const { return TransactionError::INVALID_PSBT; }
+
virtual uint256 GetID() const { return uint256(); }
/** Prepends the wallet name in logging output to ease debugging in multi-wallet use cases */
@@ -336,20 +346,24 @@ public:
void RewriteDB() override;
- int64_t GetOldestKeyPoolTime() override;
- size_t KeypoolCountExternalKeys() override;
+ int64_t GetOldestKeyPoolTime() const override;
+ size_t KeypoolCountExternalKeys() const override;
unsigned int GetKeyPoolSize() const override;
int64_t GetTimeFirstKey() const override;
const CKeyMetadata* GetMetadata(const CTxDestination& dest) const override;
- bool CanGetAddresses(bool internal = false) override;
+ bool CanGetAddresses(bool internal = false) const override;
- std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script) const override;
+ std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
+ SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+
uint256 GetID() const override;
// Map from Key ID to key metadata.
@@ -410,7 +424,7 @@ public:
bool ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
/* Returns true if the wallet can generate new keys */
- bool CanGenerateKeys();
+ bool CanGenerateKeys() const;
/* Generates a new HD seed (will not be activated) */
CPubKey GenerateNewSeed();
@@ -447,7 +461,7 @@ public:
std::set<CKeyID> GetKeys() const override;
};
-/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr */
+/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */
class LegacySigningProvider : public SigningProvider
{
private:
@@ -458,8 +472,8 @@ public:
bool GetCScript(const CScriptID &scriptid, CScript& script) const override { return m_spk_man.GetCScript(scriptid, script); }
bool HaveCScript(const CScriptID &scriptid) const override { return m_spk_man.HaveCScript(scriptid); }
bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const override { return m_spk_man.GetPubKey(address, pubkey); }
- bool GetKey(const CKeyID &address, CKey& key) const override { return m_spk_man.GetKey(address, key); }
- bool HaveKey(const CKeyID &address) const override { return m_spk_man.HaveKey(address); }
+ bool GetKey(const CKeyID &address, CKey& key) const override { return false; }
+ bool HaveKey(const CKeyID &address) const override { return false; }
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override { return m_spk_man.GetKeyOrigin(keyid, info); }
};
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index d65a0e9075..21d57cb898 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -189,6 +189,19 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
actual_selection.clear();
selection.clear();
+ // Cost of change is greater than the difference between target value and utxo sum
+ add_coin(1 * CENT, 1, actual_selection);
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK_EQUAL(value_ret, 1 * CENT);
+ BOOST_CHECK(equal_sets(selection, actual_selection));
+ actual_selection.clear();
+ selection.clear();
+
+ // Cost of change is less than the difference between target value and utxo sum
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret, not_input_fees));
+ actual_selection.clear();
+ selection.clear();
+
// Select 10 Cent
add_coin(5 * CENT, 5, utxo_pool);
add_coin(4 * CENT, 4, actual_selection);
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index f923de6178..8b7b7af21d 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -5,7 +5,6 @@
#include <key_io.h>
#include <util/bip32.h>
#include <util/strencodings.h>
-#include <wallet/psbtwallet.h>
#include <wallet/wallet.h>
#include <boost/test/unit_test.hpp>
@@ -61,7 +60,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Fill transaction with our data
bool complete = true;
- BOOST_REQUIRE_EQUAL(TransactionError::OK, FillPSBT(&m_wallet, psbtx, complete, SIGHASH_ALL, false, true));
+ BOOST_REQUIRE_EQUAL(TransactionError::OK, m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false, true));
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -74,9 +73,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- psbtx.inputs[0].FillSignatureData(sigdata);
- const std::unique_ptr<SigningProvider> provider = m_wallet.GetSigningProvider(ws1, sigdata);
- BOOST_CHECK(!SignPSBTInput(*provider, psbtx, 0, SIGHASH_ALL));
+ BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
new file mode 100644
index 0000000000..757865ea37
--- /dev/null
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <script/standard.h>
+#include <test/util/setup_common.h>
+#include <wallet/scriptpubkeyman.h>
+#include <wallet/wallet.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
+
+// Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true
+// for recognized scripts even when keys may not be available for signing.
+BOOST_AUTO_TEST_CASE(CanProvide)
+{
+ // Set up wallet and keyman variables.
+ NodeContext node;
+ std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
+
+ // Make a 1 of 2 multisig script
+ std::vector<CKey> keys(2);
+ std::vector<CPubKey> pubkeys;
+ for (CKey& key : keys) {
+ key.MakeNewKey(true);
+ pubkeys.emplace_back(key.GetPubKey());
+ }
+ CScript multisig_script = GetScriptForMultisig(1, pubkeys);
+ CScript p2sh_script = GetScriptForDestination(ScriptHash(multisig_script));
+ SignatureData data;
+
+ // Verify the p2sh(multisig) script is not recognized until the multisig
+ // script is added to the keystore to make it solvable
+ BOOST_CHECK(!keyman.CanProvide(p2sh_script, data));
+ keyman.AddCScript(multisig_script);
+ BOOST_CHECK(keyman.CanProvide(p2sh_script, data));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6b9f53f7c5..79e29d050f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -27,7 +27,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/translation.h>
-#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
@@ -714,7 +713,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
return success;
}
-void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
+void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
{
AssertLockHeld(cs_wallet);
const CWalletTx* srctx = GetWalletTx(hash);
@@ -734,7 +733,7 @@ void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, u
}
}
-bool CWallet::IsUsedDestination(const uint256& hash, unsigned int n) const
+bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
{
AssertLockHeld(cs_wallet);
CTxDestination dst;
@@ -777,7 +776,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
for (const CTxIn& txin : wtxIn.tx->vin) {
const COutPoint& op = txin.prevout;
- SetUsedDestinationState(batch, op.hash, op.n, true, tx_destinations);
+ SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
}
MarkDestinationsDirty(tx_destinations);
@@ -1151,7 +1150,7 @@ void CWallet::UpdatedBlockTip()
}
-void CWallet::BlockUntilSyncedToCurrentChain() {
+void CWallet::BlockUntilSyncedToCurrentChain() const {
AssertLockNotHeld(cs_wallet);
// Skip the queue-draining stuff if we know we're caught up with
// ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
@@ -1334,7 +1333,7 @@ bool CWallet::IsHDEnabled() const
return result;
}
-bool CWallet::CanGetAddresses(bool internal)
+bool CWallet::CanGetAddresses(bool internal) const
{
LOCK(cs_wallet);
if (m_spk_managers.empty()) return false;
@@ -1408,7 +1407,7 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
if (!provider) {
// We don't know about this scriptpbuKey;
return false;
@@ -1878,7 +1877,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
+ if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
@@ -2168,11 +2167,11 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- if (!allow_used_addresses && IsUsedDestination(wtxid, i)) {
+ if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
continue;
}
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(wtx.tx->vout[i].scriptPubKey);
+ std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
@@ -2411,34 +2410,172 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
-bool CWallet::SignTransaction(CMutableTransaction& tx)
+bool CWallet::SignTransaction(CMutableTransaction& tx) const
{
AssertLockHeld(cs_wallet);
- // sign the new tx
- int nIn = 0;
+ // Build coins map
+ std::map<COutPoint, Coin> coins;
for (auto& input : tx.vin) {
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
return false;
}
- const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
- const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
+ const CWalletTx& wtx = mi->second;
+ coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
+ }
+ std::map<int, std::string> input_errors;
+ return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
+}
+
+bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+{
+ // Sign the tx with ScriptPubKeyMans
+ // Because each ScriptPubKeyMan can sign more than one input, we need to keep track of each ScriptPubKeyMan that has signed this transaction.
+ // Each iteration, we may sign more txins than the txin that is specified in that iteration.
+ // We assume that each input is signed by only one ScriptPubKeyMan.
+ std::set<uint256> visited_spk_mans;
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ // Get the prevout
+ CTxIn& txin = tx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ input_errors[i] = "Input not found or already spent";
+ continue;
+ }
+
+ // Check if this input is complete
+ SignatureData sigdata = DataFromTransaction(tx, i, coin->second.out);
+ if (sigdata.complete) {
+ continue;
+ }
+
+ // Input needs to be signed, find the right ScriptPubKeyMan
+ std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(coin->second.out.scriptPubKey, sigdata);
+ if (spk_mans.size() == 0) {
+ input_errors[i] = "Unable to sign input, missing keys";
+ continue;
+ }
+
+ for (auto& spk_man : spk_mans) {
+ // If we've already been signed by this spk_man, skip it
+ if (visited_spk_mans.count(spk_man->GetID()) > 0) {
+ continue;
+ }
+
+ // Sign the tx.
+ // spk_man->SignTransaction will return true if the transaction is complete,
+ // so we can exit early and return true if that happens.
+ if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) {
+ return true;
+ }
+
+ // Add this spk_man to visited_spk_mans so we can skip it later
+ visited_spk_mans.insert(spk_man->GetID());
+ }
+ }
+ return false;
+}
+
+TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs) const
+{
+ LOCK(cs_wallet);
+ // Get all of the previous transactions
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ // If we have no utxo, grab it from the wallet.
+ if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
+ const uint256& txhash = txin.prevout.hash;
+ const auto it = mapWallet.find(txhash);
+ if (it != mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ // We only need the non_witness_utxo, which is a superset of the witness_utxo.
+ // The signing code will switch to the smaller witness_utxo if this is ok.
+ input.non_witness_utxo = wtx.tx;
+ }
+ }
+ }
+
+ // Fill in information from ScriptPubKeyMans
+ // Because each ScriptPubKeyMan may be able to fill more than one input, we need to keep track of each ScriptPubKeyMan that has filled this psbt.
+ // Each iteration, we may fill more inputs than the input that is specified in that iteration.
+ // We assume that each input is filled by only one ScriptPubKeyMan
+ std::set<uint256> visited_spk_mans;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Get the scriptPubKey to know which ScriptPubKeyMan to use
+ CScript script;
+ if (!input.witness_utxo.IsNull()) {
+ script = input.witness_utxo.scriptPubKey;
+ } else if (input.non_witness_utxo) {
+ if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
+ } else {
+ // There's no UTXO so we can just skip this now
+ continue;
+ }
SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+ std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(script, sigdata);
+ if (spk_mans.size() == 0) {
+ continue;
+ }
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
- if (!provider) {
- // We don't know about this scriptpbuKey;
- return false;
+ for (auto& spk_man : spk_mans) {
+ // If we've already been signed by this spk_man, skip it
+ if (visited_spk_mans.count(spk_man->GetID()) > 0) {
+ continue;
+ }
+
+ // Fill in the information from the spk_man
+ TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
+ if (res != TransactionError::OK) {
+ return res;
+ }
+
+ // Add this spk_man to visited_spk_mans so we can skip it later
+ visited_spk_mans.insert(spk_man->GetID());
}
+ }
- if (!ProduceSignature(*provider, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
- return false;
+ // Complete if every input is now signed
+ complete = true;
+ for (const auto& input : psbtx.inputs) {
+ complete &= PSBTInputSigned(input);
+ }
+
+ return TransactionError::OK;
+}
+
+SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
+{
+ SignatureData sigdata;
+ CScript script_pub_key = GetScriptForDestination(pkhash);
+ for (const auto& spk_man_pair : m_spk_managers) {
+ if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
+ return spk_man_pair.second->SignMessage(message, pkhash, str_sig);
}
- UpdateInput(input, sigdata);
- nIn++;
}
- return true;
+ return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -2887,25 +3024,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
- if (sign)
- {
- int nIn = 0;
- for (const auto& coin : selected_coins)
- {
- const CScript& scriptPubKey = coin.txout.scriptPubKey;
- SignatureData sigdata;
-
- std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
- if (!provider || !ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
- {
- strFailReason = _("Signing transaction failed").translated;
- return false;
- } else {
- UpdateInput(txNew.vin.at(nIn), sigdata);
- }
-
- nIn++;
- }
+ if (sign && !SignTransaction(txNew)) {
+ strFailReason = _("Signing transaction failed").translated;
+ return false;
}
// Return the constructed transaction data.
@@ -3113,7 +3234,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return WalletBatch(*database).EraseName(EncodeDestination(address));
}
-size_t CWallet::KeypoolCountExternalKeys()
+size_t CWallet::KeypoolCountExternalKeys() const
{
AssertLockHeld(cs_wallet);
@@ -3178,7 +3299,7 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des
return true;
}
-int64_t CWallet::GetOldestKeyPoolTime()
+int64_t CWallet::GetOldestKeyPoolTime() const
{
LOCK(cs_wallet);
int64_t oldestKey = std::numeric_limits<int64_t>::max();
@@ -3202,7 +3323,7 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations
}
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain)
+std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain) const
{
std::map<CTxDestination, CAmount> balances;
@@ -3243,7 +3364,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
return balances;
}
-std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
+std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
{
AssertLockHeld(cs_wallet);
std::set< std::set<CTxDestination> > groupings;
@@ -4005,7 +4126,7 @@ void CWallet::postInitProcess()
chain().requestMempoolTransactions(*this);
}
-bool CWallet::BackupWallet(const std::string& strDest)
+bool CWallet::BackupWallet(const std::string& strDest) const
{
return database->Backup(strDest);
}
@@ -4156,6 +4277,17 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern
return it->second;
}
+std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const
+{
+ std::set<ScriptPubKeyMan*> spk_mans;
+ for (const auto& spk_man_pair : m_spk_managers) {
+ if (spk_man_pair.second->CanProvide(script, sigdata)) {
+ spk_mans.insert(spk_man_pair.second.get());
+ }
+ }
+ return spk_mans;
+}
+
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
{
SignatureData sigdata;
@@ -4175,17 +4307,17 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const
return nullptr;
}
-std::unique_ptr<SigningProvider> CWallet::GetSigningProvider(const CScript& script) const
+std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script) const
{
SignatureData sigdata;
- return GetSigningProvider(script, sigdata);
+ return GetSolvingProvider(script, sigdata);
}
-std::unique_ptr<SigningProvider> CWallet::GetSigningProvider(const CScript& script, SignatureData& sigdata) const
+std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const
{
for (const auto& spk_man_pair : m_spk_managers) {
if (spk_man_pair.second->CanProvide(script, sigdata)) {
- return spk_man_pair.second->GetSigningProvider(script);
+ return spk_man_pair.second->GetSolvingProvider(script);
}
}
return nullptr;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a918bb8833..0c86a0c1e8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -11,8 +11,10 @@
#include <interfaces/handler.h>
#include <outputtype.h>
#include <policy/feerate.h>
+#include <psbt.h>
#include <tinyformat.h>
#include <ui_interface.h>
+#include <util/message.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validationinterface.h>
@@ -141,7 +143,7 @@ class ReserveDestination
{
protected:
//! The wallet to reserve from
- CWallet* const pwallet;
+ const CWallet* const pwallet;
//! The ScriptPubKeyMan to reserve from. Based on type when GetReservedDestination is called
ScriptPubKeyMan* m_spk_man{nullptr};
OutputType const type;
@@ -817,8 +819,8 @@ public:
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Whether this or any known UTXO with the same single key has been spent.
- bool IsUsedDestination(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
@@ -832,8 +834,8 @@ public:
* Rescan abort properties
*/
void AbortRescan() { fAbortRescan = true; }
- bool IsAbortingRescan() { return fAbortRescan; }
- bool IsScanning() { return fScanningWallet; }
+ bool IsAbortingRescan() const { return fAbortRescan; }
+ bool IsScanning() const { return fScanningWallet; }
int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
@@ -916,7 +918,30 @@ public:
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
- bool SignTransaction(CMutableTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ // Fetch the inputs and sign with SIGHASH_ALL.
+ bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ // Sign the tx given the input coins and sighash.
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const;
+ SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const;
+
+ /**
+ * Fills out a PSBT with information from the wallet. Fills in UTXOs if we have
+ * them. Tries to sign if sign=true. Sets `complete` if the PSBT is now complete
+ * (i.e. has all required signatures or signature-parts, and is ready to
+ * finalize.) Sets `error` and returns false if something goes wrong.
+ *
+ * @param[in] psbtx PartiallySignedTransaction to fill in
+ * @param[out] complete indicates whether the PSBT is now complete
+ * @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
+ * @param[in] sign whether to sign or not
+ * @param[in] bip32derivs whether to fill in bip32 derivation information if available
+ * return error
+ */
+ TransactionError FillPSBT(PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = true) const;
/**
* Create a new transaction paying the recipients with a set of coins
@@ -968,13 +993,13 @@ public:
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
- size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
- int64_t GetOldestKeyPoolTime();
+ int64_t GetOldestKeyPoolTime() const;
- std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain);
+ std::set<std::set<CTxDestination>> GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain) const;
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
@@ -1027,7 +1052,7 @@ public:
bool SetMaxVersion(int nVersion);
//! get the current wallet format (the oldest client version guaranteed to understand this wallet)
- int GetVersion() { LOCK(cs_wallet); return nWalletVersion; }
+ int GetVersion() const { LOCK(cs_wallet); return nWalletVersion; }
//! Get wallet transactions that conflict with given transaction (spend same outputs)
std::set<uint256> GetConflicts(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -1098,13 +1123,13 @@ public:
*/
void postInitProcess();
- bool BackupWallet(const std::string& strDest);
+ bool BackupWallet(const std::string& strDest) const;
/* Returns true if HD is enabled */
bool IsHDEnabled() const;
/* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
- bool CanGetAddresses(bool internal = false);
+ bool CanGetAddresses(bool internal = false) const;
/**
* Blocks until the wallet state is up-to-date to /at least/ the current
@@ -1112,7 +1137,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1153,9 +1178,12 @@ public:
//! Get the ScriptPubKeyMan by id
ScriptPubKeyMan* GetScriptPubKeyMan(const uint256& id) const;
+ //! Get all of the ScriptPubKeyMans for a script given additional information in sigdata (populated by e.g. a psbt)
+ std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const;
+
//! Get the SigningProvider for a script
- std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script) const;
- std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script, SignatureData& sigdata) const;
+ std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const;
+ std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script, SignatureData& sigdata) const;
//! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index 9c9b27a413..0fbefb6023 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -19,14 +19,15 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
"\nReturns information about the active ZeroMQ notifications.\n",
{},
RPCResult{
- "[\n"
- " { (json object)\n"
- " \"type\" : \"pubhashtx\", (string) Type of notification\n"
- " \"address\" : \"...\", (string) Address of the publisher\n"
- " \"hwm\" : n (numeric) Outbound message high water mark\n"
- " },\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "type", "Type of notification"},
+ {RPCResult::Type::STR, "address", "Address of the publisher"},
+ {RPCResult::Type::NUM, "hwm", "Outbound message high water mark"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getzmqnotifications", "")
diff --git a/test/README.md b/test/README.md
index c3e4ae9ad2..e1dab92a06 100644
--- a/test/README.md
+++ b/test/README.md
@@ -145,7 +145,7 @@ levels using the logger included in the test_framework, e.g.
`test_framework.log` and no logs are output to the console.
- when run directly, *all* logs are written to `test_framework.log` and INFO
level and above are output to the console.
-- when run on Travis, no logs are output to the console. However, if a test
+- when run by [our CI (Continuous Integration)](/ci/README.md), no logs are output to the console. However, if a test
fails, the `test_framework.log` and bitcoind `debug.log`s will all be dumped
to the console to help troubleshooting.
diff --git a/test/functional/README.md b/test/functional/README.md
index 77a9ce9acb..6582c1cbcd 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -51,10 +51,13 @@ don't have test cases for.
#### General test-writing advice
+- Instead of inline comments or no test documentation at all, log the comments to the test log, e.g.
+ `self.log.info('Create enough transactions to fill a block')`. Logs make the test code easier to read and the test
+ logic easier [to debug](/test/README.md#test-logging).
- Set `self.num_nodes` to the minimum number of nodes necessary for the test.
Having additional unrequired nodes adds to the execution time of the test as
well as memory/CPU/disk requirements (which is important when running tests in
- parallel or on Travis).
+ parallel).
- Avoid stop-starting the nodes multiple times during the test if possible. A
stop-start takes several seconds, so doing it several times blows up the
runtime of the test.
diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py
new file mode 100755
index 0000000000..2c6553fbe2
--- /dev/null
+++ b/test/functional/feature_asmap.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test asmap config argument for ASN-based IP bucketing.
+
+Verify node behaviour and debug log when launching bitcoind in these cases:
+
+1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing
+
+2. `bitcoind -asmap=<absolute path>`, using the unit test skeleton asmap
+
+3. `bitcoind -asmap=<relative path>`, using the unit test skeleton asmap
+
+4. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap
+
+5. `bitcoind -asmap` with no file specified and a missing default asmap file
+
+6. `bitcoind -asmap` with an empty (unparsable) default asmap file
+
+The tests are order-independent.
+
+"""
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework
+
+DEFAULT_ASMAP_FILENAME = 'ip_asn.map' # defined in src/init.cpp
+ASMAP = '../../src/test/data/asmap.raw' # path to unit test skeleton asmap
+VERSION = 'fec61fa21a9f46f3b17bdcd660d7f4cd90b966aad3aec593c99b35f0aca15853'
+
+def expected_messages(filename):
+ return ['Opened asmap file "{}" (59 bytes) from disk'.format(filename),
+ 'Using asmap version {} for IP bucketing'.format(VERSION)]
+
+class AsmapTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+
+ def test_without_asmap_arg(self):
+ self.log.info('Test bitcoind with no -asmap arg passed')
+ self.stop_node(0)
+ with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']):
+ self.start_node(0)
+
+ def test_asmap_with_absolute_path(self):
+ self.log.info('Test bitcoind -asmap=<absolute path>')
+ self.stop_node(0)
+ filename = os.path.join(self.datadir, 'my-map-file.map')
+ shutil.copyfile(self.asmap_raw, filename)
+ with self.node.assert_debug_log(expected_messages(filename)):
+ self.start_node(0, ['-asmap={}'.format(filename)])
+ os.remove(filename)
+
+ def test_asmap_with_relative_path(self):
+ self.log.info('Test bitcoind -asmap=<relative path>')
+ self.stop_node(0)
+ name = 'ASN_map'
+ filename = os.path.join(self.datadir, name)
+ shutil.copyfile(self.asmap_raw, filename)
+ with self.node.assert_debug_log(expected_messages(filename)):
+ self.start_node(0, ['-asmap={}'.format(name)])
+ os.remove(filename)
+
+ def test_default_asmap(self):
+ shutil.copyfile(self.asmap_raw, self.default_asmap)
+ for arg in ['-asmap', '-asmap=']:
+ self.log.info('Test bitcoind {} (using default map file)'.format(arg))
+ self.stop_node(0)
+ with self.node.assert_debug_log(expected_messages(self.default_asmap)):
+ self.start_node(0, [arg])
+ os.remove(self.default_asmap)
+
+ def test_default_asmap_with_missing_file(self):
+ self.log.info('Test bitcoind -asmap with missing default map file')
+ self.stop_node(0)
+ msg = "Error: Could not find asmap file \"{}\"".format(self.default_asmap)
+ self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg)
+
+ def test_empty_asmap(self):
+ self.log.info('Test bitcoind -asmap with empty map file')
+ self.stop_node(0)
+ with open(self.default_asmap, "w", encoding="utf-8") as f:
+ f.write("")
+ msg = "Error: Could not parse asmap file \"{}\"".format(self.default_asmap)
+ self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg)
+ os.remove(self.default_asmap)
+
+ def run_test(self):
+ self.node = self.nodes[0]
+ self.datadir = os.path.join(self.node.datadir, self.chain)
+ self.default_asmap = os.path.join(self.datadir, DEFAULT_ASMAP_FILENAME)
+ self.asmap_raw = os.path.join(os.path.dirname(os.path.realpath(__file__)), ASMAP)
+
+ self.test_without_asmap_arg()
+ self.test_asmap_with_absolute_path()
+ self.test_asmap_with_relative_path()
+ self.test_default_asmap()
+ self.test_default_asmap_with_missing_file()
+ self.test_empty_asmap()
+
+
+if __name__ == '__main__':
+ AsmapTest().main()
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 1ac10f971a..0db74432e2 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -113,7 +113,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Create another conflicting transaction using RBF
tx3_id = self.nodes[1].sendtoaddress(return_address, 1)
tx4_id = self.nodes[1].bumpfee(tx3_id)["txid"]
- # Abondon transaction, but don't confirm
+ # Abandon transaction, but don't confirm
self.nodes[1].abandontransaction(tx3_id)
# w1_v19: regular wallet, created with v0.19
@@ -122,6 +122,9 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
info = wallet.getwalletinfo()
assert info['private_keys_enabled']
assert info['keypoolsize'] > 0
+ # Use addmultisigaddress (see #18075)
+ address_18075 = wallet.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
+ assert wallet.getaddressinfo(address_18075)["solvable"]
# w1_v18: regular wallet, created with v0.18
node_v18.createwallet(wallet_name="w1_v18")
@@ -319,7 +322,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
hdkeypath = info["hdkeypath"]
pubkey = info["pubkey"]
- # Copy the wallet to the last Bitcoin Core version and open it:
+ # Copy the 0.17 wallet to the last Bitcoin Core version and open it:
node_v17.unloadwallet("u1_v17")
shutil.copytree(
os.path.join(node_v17_wallets_dir, "u1_v17"),
@@ -331,5 +334,14 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + pubkey + ")"
assert_equal(info["desc"], descsum_create(descriptor))
+ # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
+ shutil.copytree(
+ os.path.join(node_v19_wallets_dir, "w1_v19"),
+ os.path.join(node_master_wallets_dir, "w1_v19")
+ )
+ node_master.loadwallet("w1_v19")
+ wallet = node_master.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+
if __name__ == '__main__':
BackwardsCompatibilityTest().main()
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 95905f477b..38bf2faf89 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1261,7 +1261,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.send_blocks(blocks, True, timeout=960)
+ self.send_blocks(blocks, True, timeout=1920)
chain1_tip = i
# now create alt chain of same length
@@ -1273,14 +1273,14 @@ class FullBlockTest(BitcoinTestFramework):
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4)
- self.send_blocks([block], True, timeout=960)
+ self.send_blocks([block], True, timeout=1920)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4)
self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4)
- self.send_blocks([block], True, timeout=960)
+ self.send_blocks([block], True, timeout=1920)
self.log.info("Reject a block with an invalid block header version")
b_v1 = self.next_block('b_v1', version=1)
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 2c6f2e733b..073ed8d7c7 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -55,12 +55,12 @@ class BIP65Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
- '-whitelist=127.0.0.1',
+ '-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact reject reason for testing
'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
]]
self.setup_clean_chain = True
- self.rpc_timeout = 120
+ self.rpc_timeout = 480
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index c2b4de54f2..a98480a6dd 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -35,6 +35,7 @@ bip112txs_vary_nSequence_9 - 16 txs with nSequence relative_locktimes of 9 evalu
bip112txs_vary_OP_CSV - 16 txs with nSequence = 10 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP
bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP
bip112tx_special - test negative argument to OP_CSV
+bip112tx_emptystack - test empty stack (= no argument) OP_CSV
"""
from decimal import Decimal
from itertools import product
@@ -56,6 +57,8 @@ from test_framework.util import (
softfork_active,
)
+TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
+COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
BASE_RELATIVE_LOCKTIME = 10
CSV_ACTIVATION_HEIGHT = 432
SEQ_DISABLE_FLAG = 1 << 31
@@ -95,6 +98,13 @@ def create_bip112special(node, input, txversion, address):
signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
return signtx
+def create_bip112emptystack(node, input, txversion, address):
+ tx = create_transaction(node, input, address, amount=Decimal("49.98"))
+ tx.nVersion = txversion
+ signtx = sign_transaction(node, tx)
+ signtx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(signtx.vin[0].scriptSig)))
+ return signtx
+
def send_generic_input_tx(node, coinbases, address):
return node.sendrawtransaction(ToHex(sign_transaction(node, create_transaction(node, node.getblock(coinbases.pop())['tx'][0], address, amount=Decimal("49.99")))))
@@ -138,7 +148,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']]
+ self.extra_args = [[
+ '-whitelist=noban@127.0.0.1',
+ '-blockversion=4',
+ '-addresstype=legacy',
+ '-par=1', # Use only one script thread to get the exact reject reason for testing
+ ]]
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -163,11 +178,11 @@ class BIP68_112_113Test(BitcoinTestFramework):
block.solve()
return block
- def send_blocks(self, blocks, success=True):
+ def send_blocks(self, blocks, success=True, reject_reason=None):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success)
+ self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
def run_test(self):
self.nodes[0].add_p2p_connection(P2PDataStore())
@@ -175,15 +190,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("Generate blocks in the past for coinbase outputs.")
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
self.nodes[0].setmocktime(long_past_time - 100) # enough so that the generated blocks will still all be before long_past_time
- self.coinbase_blocks = self.nodes[0].generate(1 + 16 + 2 * 32 + 1) # 82 blocks generated for inputs
+ self.coinbase_blocks = self.nodes[0].generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
self.nodes[0].setmocktime(0) # set time back to present so yielded blocks aren't in the future as we advance last_block_time
- self.tipheight = 82 # height of the next block to build
+ self.tipheight = COINBASE_BLOCK_COUNT # height of the next block to build
self.last_block_time = long_past_time
self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.nodeaddress = self.nodes[0].getnewaddress()
# Activation height is hardcoded
- test_blocks = self.generate_blocks(345)
+ # We advance to block height five below BIP112 activation for the following tests
+ test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT)
self.send_blocks(test_blocks)
assert not softfork_active(self.nodes[0], 'csv')
@@ -214,6 +230,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
# 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
+ # 1 special input with (empty stack) OP_CSV (actually will be prepended to spending scriptSig)
+ bip112emptystackinput = send_generic_input_tx(self.nodes[0],self.coinbase_blocks, self.nodeaddress)
# 1 normal input
bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
@@ -224,7 +242,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.tip = int(inputblockhash, 16)
self.tipheight += 1
self.last_block_time += 600
- assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)
+ assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), TESTING_TX_COUNT + 1)
# 2 more version 4 blocks
test_blocks = self.generate_blocks(2)
@@ -263,6 +281,9 @@ class BIP68_112_113Test(BitcoinTestFramework):
# -1 OP_CSV OP_DROP input
bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)
+ # (empty stack) OP_CSV input
+ bip112tx_emptystack_v1 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 1, self.nodeaddress)
+ bip112tx_emptystack_v2 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 2, self.nodeaddress)
self.log.info("TESTING")
@@ -270,11 +291,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("Test version 1 txs")
success_txs = []
- # add BIP113 tx and -1 CSV tx
+ # BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
success_txs.append(bip113signed1)
success_txs.append(bip112tx_special_v1)
+ success_txs.append(bip112tx_emptystack_v1)
# add BIP 68 txs
success_txs.extend(all_rlt_txs(bip68txs_v1))
# add BIP 112 with seq=10 txs
@@ -289,11 +311,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("Test version 2 txs")
success_txs = []
- # add BIP113 tx and -1 CSV tx
+ # BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
success_txs.append(bip113signed2)
success_txs.append(bip112tx_special_v2)
+ success_txs.append(bip112tx_emptystack_v2)
# add BIP 68 txs
success_txs.extend(all_rlt_txs(bip68txs_v2))
# add BIP 112 with seq=10 txs
@@ -320,7 +343,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
for bip113tx in [bip113signed1, bip113signed2]:
- self.send_blocks([self.create_test_block([bip113tx])], success=False)
+ self.send_blocks([self.create_test_block([bip113tx])], success=False, reject_reason='bad-txns-nonfinal')
# BIP 113 tests should now pass if the locktime is < MTP
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
@@ -352,11 +375,11 @@ class BIP68_112_113Test(BitcoinTestFramework):
# All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
bip68timetxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and tx['stf']]
for tx in bip68timetxs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False, reject_reason='bad-txns-nonfinal')
bip68heighttxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and not tx['stf']]
for tx in bip68heighttxs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False, reject_reason='bad-txns-nonfinal')
# Advance one block to 438
test_blocks = self.generate_blocks(1)
@@ -367,7 +390,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
for tx in bip68heighttxs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False, reject_reason='bad-txns-nonfinal')
# Advance one block to 439
test_blocks = self.generate_blocks(1)
@@ -381,8 +404,11 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("BIP 112 tests")
self.log.info("Test version 1 txs")
- # -1 OP_CSV tx should fail
- self.send_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
+ # -1 OP_CSV tx and (empty stack) OP_CSV tx should fail
+ self.send_blocks([self.create_test_block([bip112tx_special_v1])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Negative locktime)')
+ self.send_blocks([self.create_test_block([bip112tx_emptystack_v1])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']]
@@ -393,15 +419,19 @@ class BIP68_112_113Test(BitcoinTestFramework):
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
fail_txs = all_rlt_txs(bip112txs_vary_nSequence_v1)
fail_txs += all_rlt_txs(bip112txs_vary_nSequence_9_v1)
- fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
+ fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if not tx['sdf']]
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
for tx in fail_txs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Locktime requirement not satisfied)')
self.log.info("Test version 2 txs")
- # -1 OP_CSV tx should fail
- self.send_blocks([self.create_test_block([bip112tx_special_v2])], success=False)
+ # -1 OP_CSV tx and (empty stack) OP_CSV tx should fail
+ self.send_blocks([self.create_test_block([bip112tx_special_v2])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Negative locktime)')
+ self.send_blocks([self.create_test_block([bip112tx_emptystack_v2])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']]
@@ -416,18 +446,21 @@ class BIP68_112_113Test(BitcoinTestFramework):
fail_txs = all_rlt_txs(bip112txs_vary_nSequence_9_v2)
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']]
for tx in fail_txs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Locktime requirement not satisfied)')
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']]
for tx in fail_txs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Locktime requirement not satisfied)')
# If sequencelock types mismatch, tx should fail
fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and tx['stf']]
fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]
for tx in fail_txs:
- self.send_blocks([self.create_test_block([tx])], success=False)
+ self.send_blocks([self.create_test_block([tx])], success=False,
+ reject_reason='non-mandatory-script-verify-flag (Locktime requirement not satisfied)')
# Remaining txs should pass, just test masking works properly
success_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and not tx['stf']]
@@ -445,7 +478,5 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block(time_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- # TODO: Test empty stack fails
-
if __name__ == '__main__':
BIP68_112_113Test().main()
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 27da49cf24..9628945a80 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -40,7 +40,10 @@ def unDERify(tx):
class BIP66Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-whitelist=127.0.0.1', '-par=1']] # Use only one script thread to get the exact log msg for testing
+ self.extra_args = [[
+ '-whitelist=noban@127.0.0.1',
+ '-par=1', # Use only one script thread to get the exact log msg for testing
+ ]]
self.setup_clean_chain = True
self.rpc_timeout = 120
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index d2d41b1206..1b33724594 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -128,11 +128,11 @@ class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
# mine non-standard txs (e.g. txs with "dust" outputs)
- # Force fSendTrickle to true (via whitelist)
+ # Force fSendTrickle to true (via whitelist.noban)
self.extra_args = [
- ["-acceptnonstdtxn", "-whitelist=127.0.0.1"],
- ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=68000"],
- ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=32000"],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=68000"],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=32000"],
]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 8d200915bd..974388d798 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -140,10 +140,9 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
- #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
- self.log.info("Restarting nodes with -whitelist=127.0.0.1")
+ self.log.info("Restarting node 0 with noban permission and 1MB maxuploadtarget")
self.stop_node(0)
- self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1"])
+ self.start_node(0, ["-whitelist=noban@127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
self.nodes[0].add_p2p_connection(TestP2PConn())
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index aaf56a42d0..9c92ee7f90 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -41,7 +41,10 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
- self.extra_args = [['-whitelist=127.0.0.1', '-segwitheight=432', '-addresstype=legacy']]
+ self.extra_args = [[
+ '-segwitheight=432',
+ '-addresstype=legacy',
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 82c7e55245..909a43c8d9 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -72,6 +72,7 @@ class SegWitTest(BitcoinTestFramework):
"-addresstype=legacy",
],
]
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py
new file mode 100755
index 0000000000..8b9b7b155a
--- /dev/null
+++ b/test/functional/mempool_expiry.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Tests that a mempool transaction expires after a given timeout and that its
+children are removed as well.
+
+Both the default expiry timeout defined by DEFAULT_MEMPOOL_EXPIRY and a user
+definable expiry timeout via the '-mempoolexpiry=<n>' command line argument
+(<n> is the timeout in hours) are tested.
+"""
+
+from datetime import timedelta
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ find_vout_for_address,
+)
+
+DEFAULT_MEMPOOL_EXPIRY = 336 # hours
+CUSTOM_MEMPOOL_EXPIRY = 10 # hours
+
+
+class MempoolExpiryTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def test_transaction_expiry(self, timeout):
+ """Tests that a transaction expires after the expiry timeout and its
+ children are removed as well."""
+ node = self.nodes[0]
+
+ # Send a parent transaction that will expire.
+ parent_address = node.getnewaddress()
+ parent_txid = node.sendtoaddress(parent_address, 1.0)
+
+ # Set the mocktime to the arrival time of the parent transaction.
+ entry_time = node.getmempoolentry(parent_txid)['time']
+ node.setmocktime(entry_time)
+
+ # Create child transaction spending the parent transaction
+ vout = find_vout_for_address(node, parent_txid, parent_address)
+ inputs = [{'txid': parent_txid, 'vout': vout}]
+ outputs = {node.getnewaddress(): 0.99}
+ child_raw = node.createrawtransaction(inputs, outputs)
+ child_signed = node.signrawtransactionwithwallet(child_raw)['hex']
+
+ # Let half of the timeout elapse and broadcast the child transaction.
+ half_expiry_time = entry_time + int(60 * 60 * timeout/2)
+ node.setmocktime(half_expiry_time)
+ child_txid = node.sendrawtransaction(child_signed)
+ self.log.info('Broadcast child transaction after {} hours.'.format(
+ timedelta(seconds=(half_expiry_time-entry_time))))
+
+ # Let most of the timeout elapse and check that the parent tx is still
+ # in the mempool.
+ nearly_expiry_time = entry_time + 60 * 60 * timeout - 5
+ node.setmocktime(nearly_expiry_time)
+ # Expiry of mempool transactions is only checked when a new transaction
+ # is added to the to the mempool.
+ node.sendtoaddress(node.getnewaddress(), 1.0)
+ self.log.info('Test parent tx not expired after {} hours.'.format(
+ timedelta(seconds=(nearly_expiry_time-entry_time))))
+ assert_equal(entry_time, node.getmempoolentry(parent_txid)['time'])
+
+ # Transaction should be evicted from the mempool after the expiry time
+ # has passed.
+ expiry_time = entry_time + 60 * 60 * timeout + 5
+ node.setmocktime(expiry_time)
+ # Expiry of mempool transactions is only checked when a new transaction
+ # is added to the to the mempool.
+ node.sendtoaddress(node.getnewaddress(), 1.0)
+ self.log.info('Test parent tx expiry after {} hours.'.format(
+ timedelta(seconds=(expiry_time-entry_time))))
+ assert_raises_rpc_error(-5, 'Transaction not in mempool',
+ node.getmempoolentry, parent_txid)
+
+ # The child transaction should be removed from the mempool as well.
+ self.log.info('Test child tx is evicted as well.')
+ assert_raises_rpc_error(-5, 'Transaction not in mempool',
+ node.getmempoolentry, child_txid)
+
+ def run_test(self):
+ self.log.info('Test default mempool expiry timeout of %d hours.' %
+ DEFAULT_MEMPOOL_EXPIRY)
+ self.test_transaction_expiry(DEFAULT_MEMPOOL_EXPIRY)
+
+ self.log.info('Test custom mempool expiry timeout of %d hours.' %
+ CUSTOM_MEMPOOL_EXPIRY)
+ self.restart_node(0, ['-mempoolexpiry=%d' % CUSTOM_MEMPOOL_EXPIRY])
+ self.test_transaction_expiry(CUSTOM_MEMPOOL_EXPIRY)
+
+
+if __name__ == '__main__':
+ MempoolExpiryTest().main()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 7014105d88..a07dad18d6 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -12,6 +12,7 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
satoshi_round,
+ wait_until,
)
# default limits
@@ -19,13 +20,22 @@ MAX_ANCESTORS = 25
MAX_DESCENDANTS = 25
# custom limits for node1
MAX_ANCESTORS_CUSTOM = 5
+MAX_DESCENDANTS_CUSTOM = 10
+assert MAX_DESCENDANTS_CUSTOM >= MAX_ANCESTORS_CUSTOM
class MempoolPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [
- ["-maxorphantx=1000"],
- ["-maxorphantx=1000", "-limitancestorcount={}".format(MAX_ANCESTORS_CUSTOM)],
+ [
+ "-maxorphantx=1000",
+ "-whitelist=noban@127.0.0.1", # immediate tx relay
+ ],
+ [
+ "-maxorphantx=1000",
+ "-limitancestorcount={}".format(MAX_ANCESTORS_CUSTOM),
+ "-limitdescendantcount={}".format(MAX_DESCENDANTS_CUSTOM),
+ ],
]
def skip_test_if_missing_module(self):
@@ -219,9 +229,11 @@ class MempoolPackagesTest(BitcoinTestFramework):
transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
# Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
+ chain = [] # save sent txs for the purpose of checking node1's mempool later (see below)
for i in range(MAX_DESCENDANTS - 1):
utxo = transaction_package.pop(0)
(txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ chain.append(txid)
if utxo['txid'] is parent_transaction:
tx_children.append(txid)
for j in range(10):
@@ -238,7 +250,21 @@ class MempoolPackagesTest(BitcoinTestFramework):
utxo = transaction_package.pop(0)
assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
- # TODO: check that node1's mempool is as expected
+ # Check that node1's mempool is as expected, containing:
+ # - txs from previous ancestor test (-> custom ancestor limit)
+ # - parent tx for descendant test
+ # - txs chained off parent tx (-> custom descendant limit)
+ wait_until(lambda: len(self.nodes[1].getrawmempool(False)) ==
+ MAX_ANCESTORS_CUSTOM + 1 + MAX_DESCENDANTS_CUSTOM, timeout=10)
+ mempool0 = self.nodes[0].getrawmempool(False)
+ mempool1 = self.nodes[1].getrawmempool(False)
+ assert set(mempool1).issubset(set(mempool0))
+ assert parent_transaction in mempool1
+ for tx in chain[:MAX_DESCENDANTS_CUSTOM]:
+ assert tx in mempool1
+ for tx in chain[MAX_DESCENDANTS_CUSTOM:]:
+ assert tx not in mempool1
+ # TODO: more detailed check of node1's mempool (fees etc.)
# TODO: test descendant size limits
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 905534b862..801407757f 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -22,7 +22,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [["-whitelist=127.0.0.1"]]
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]]
def run_test(self):
# Add p2p connection to node0
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 93e2957fd0..3a7bf4bfc3 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -132,6 +132,16 @@ class P2PPermissionsTests(BitcoinTestFramework):
p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
wait_until(lambda: txid in self.nodes[0].getrawmempool())
+ self.log.debug("Check that node[1] will not send an invalid tx to node[0]")
+ tx.vout[0].nValue += 1
+ txid = tx.rehash()
+ p2p_rebroadcast_wallet.send_txs_and_test(
+ [tx],
+ self.nodes[1],
+ success=False,
+ reject_reason='Not relaying non-mempool transaction {} from whitelisted peer=0'.format(txid),
+ )
+
def checkpermission(self, args, expectedPermissions, whitelisted):
self.restart_node(1, args)
connect_nodes(self.nodes[0], 1)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 3223c27e0b..ad5a124680 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -188,9 +188,9 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 3
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT)],
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight=-1"]
+ ["-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT), "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-acceptnonstdtxn=1", "-segwitheight=-1"],
]
self.supports_cli = False
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 6f1ae0d3ba..c435ef24ce 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -30,7 +30,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This test isn't testing tx relay. Set whitelist on the peers for
# instant tx relay.
- self.extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes
+ self.extra_args = [['-whitelist=noban@127.0.0.1']] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py
new file mode 100755
index 0000000000..977dc805ef
--- /dev/null
+++ b/test/functional/rpc_getdescriptorinfo.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 getdescriptorinfo RPC.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.descriptors import descsum_create
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+
+class DescriptorTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.extra_args = [["-disablewallet"]]
+
+ def test_desc(self, desc, isrange, issolvable, hasprivatekeys):
+ info = self.nodes[0].getdescriptorinfo(desc)
+ assert_equal(info, self.nodes[0].getdescriptorinfo(descsum_create(desc)))
+ assert_equal(info['descriptor'], descsum_create(desc))
+ assert_equal(info['isrange'], isrange)
+ assert_equal(info['issolvable'], issolvable)
+ assert_equal(info['hasprivatekeys'], hasprivatekeys)
+
+ def run_test(self):
+ assert_raises_rpc_error(-1, 'getdescriptorinfo', self.nodes[0].getdescriptorinfo)
+ assert_raises_rpc_error(-3, 'Expected type string', self.nodes[0].getdescriptorinfo, 1)
+ assert_raises_rpc_error(-5, 'is not a valid descriptor function', self.nodes[0].getdescriptorinfo, '')
+
+ # P2PK output with the specified public key.
+ self.test_desc('pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # P2PKH output with the specified public key.
+ self.test_desc('pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # P2WPKH output with the specified public key.
+ self.test_desc('wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # P2SH-P2WPKH output with the specified public key.
+ self.test_desc('sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))', isrange=False, issolvable=True, hasprivatekeys=False)
+ # Any P2PK, P2PKH, P2WPKH, or P2SH-P2WPKH output with the specified public key.
+ self.test_desc('combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # An (overly complicated) P2SH-P2WSH-P2PKH output with the specified public key.
+ self.test_desc('sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A bare *1-of-2* multisig output with keys in the specified order.
+ self.test_desc('multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A P2SH *2-of-2* multisig output with keys in the specified order.
+ self.test_desc('sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A P2WSH *2-of-3* multisig output with keys in the specified order.
+ self.test_desc('wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A P2SH-P2WSH *1-of-3* multisig output with keys in the specified order.
+ self.test_desc('sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A P2PK output with the public key of the specified xpub.
+ self.test_desc('pk(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B)', isrange=False, issolvable=True, hasprivatekeys=False)
+ # A P2PKH output with child key *1'/2* of the specified xpub.
+ self.test_desc("pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/2)", isrange=False, issolvable=True, hasprivatekeys=False)
+ # A set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`.
+ self.test_desc("pkh([d34db33f/44'/0'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)", isrange=True, issolvable=True, hasprivatekeys=False)
+ # A set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default).
+ self.test_desc("wsh(multi(1,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/0/*,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/0/*))", isrange=True, issolvable=True, hasprivatekeys=False)
+
+
+if __name__ == '__main__':
+ DescriptorTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 33af819d34..3a63377545 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -193,12 +193,20 @@ class PSBTTest(BitcoinTestFramework):
psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
# Update psbts, should only have data for one input and not the other
- psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt']
+ psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
psbt1_decoded = self.nodes[0].decodepsbt(psbt1)
assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1]
- psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt']
+ # Check that BIP32 path was added
+ assert "bip32_derivs" in psbt1_decoded['inputs'][0]
+ psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt']
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1]
+ # Check that BIP32 paths were not added
+ assert "bip32_derivs" not in psbt2_decoded['inputs'][1]
+
+ # Sign PSBTs (workaround issue #18039)
+ psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt']
+ psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt']
# Combine, finalize, and send the psbts
combined = self.nodes[0].combinepsbt([psbt1, psbt2])
@@ -231,16 +239,18 @@ class PSBTTest(BitcoinTestFramework):
# Same construction without optional arguments
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
- for tx_in in decoded_psbt["tx"]["vin"]:
+ for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert "bip32_derivs" in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], 0)
# Same construction without optional arguments, for a node with -walletrbf=0
unspent1 = self.nodes[1].listunspent()[0]
psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height)
decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
- for tx_in in decoded_psbt["tx"]["vin"]:
+ for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert "bip32_derivs" in psbt_in
# Make sure change address wallet does not have P2SH innerscript access to results in success
# when attempting BnB coin selection
@@ -427,6 +437,10 @@ class PSBTTest(BitcoinTestFramework):
assert_equal(analysis['next'], 'creator')
assert_equal(analysis['error'], 'PSBT is not valid. Input 0 has invalid value')
+ self.log.info("PSBT with signed, but not finalized, inputs should have Finalizer as next")
+ analysis = self.nodes[0].analyzepsbt('cHNidP8BAHECAAAAAZYezcxdnbXoQCmrD79t/LzDgtUo9ERqixk8wgioAobrAAAAAAD9////AlDDAAAAAAAAFgAUy/UxxZuzZswcmFnN/E9DGSiHLUsuGPUFAAAAABYAFLsH5o0R38wXx+X2cCosTMCZnQ4baAAAAAABAR8A4fUFAAAAABYAFOBI2h5thf3+Lflb2LGCsVSZwsltIgIC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnJHMEQCIGx7zKcMIGr7cEES9BR4Kdt/pzPTK3fKWcGyCJXb7MVnAiALOBgqlMH4GbC1HDh/HmylmO54fyEy4lKde7/BT/PWxwEBAwQBAAAAIgYC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnIYDwVpQ1QAAIABAACAAAAAgAAAAAAAAAAAAAAiAgL+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+xgPBWlDVAAAgAEAAIAAAACAAQAAAAAAAAAA')
+ assert_equal(analysis['next'], 'finalizer')
+
analysis = self.nodes[0].analyzepsbt('cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAR8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA')
assert_equal(analysis['next'], 'creator')
assert_equal(analysis['error'], 'PSBT is not valid. Output amount invalid')
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index ea8a340081..7edb36c151 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -175,6 +175,7 @@ BASE_SCRIPTS = [
'wallet_balance.py',
'feature_nulldummy.py',
'mempool_accept.py',
+ 'mempool_expiry.py',
'wallet_import_rescan.py',
'wallet_import_with_label.py',
'rpc_bind.py --ipv4',
@@ -205,6 +206,7 @@ BASE_SCRIPTS = [
'p2p_dos_header_tree.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
+ 'feature_asmap.py',
'rpc_deriveaddresses.py',
'rpc_deriveaddresses.py --usecli',
'rpc_scantxoutset.py',
@@ -215,6 +217,7 @@ BASE_SCRIPTS = [
'feature_config_args.py',
'rpc_getaddressinfo_labels_purpose_deprecation.py',
'rpc_getaddressinfo_label_deprecation.py',
+ 'rpc_getdescriptorinfo.py',
'rpc_help.py',
'feature_help.py',
'feature_shutdown.py',
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index f08606e622..79b6db986b 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -82,7 +82,7 @@ class AddressTypeTest(BitcoinTestFramework):
]
# whitelist all peers to speed up tx relay / mempool sync
for args in self.extra_args:
- args.append("-whitelist=127.0.0.1")
+ args.append("-whitelist=noban@127.0.0.1")
self.supports_cli = False
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 1ca02a695c..8e2dc03ac2 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -70,7 +70,7 @@ class AvoidReuseTest(BitcoinTestFramework):
self.num_nodes = 2
# This test isn't testing txn relay/timing, so set whitelist on the
# peers for instant txn relay. This speeds up the test run time 2-3x.
- self.extra_args = [["-whitelist=127.0.0.1"]] * self.num_nodes
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index 16c69f304a..fb80a06433 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -50,10 +50,10 @@ class WalletBackupTest(BitcoinTestFramework):
# nodes 1, 2,3 are spenders, let's give them a keypool=100
# whitelist all peers to speed up tx relay / mempool sync
self.extra_args = [
- ["-keypool=100", "-whitelist=127.0.0.1"],
- ["-keypool=100", "-whitelist=127.0.0.1"],
- ["-keypool=100", "-whitelist=127.0.0.1"],
- ["-whitelist=127.0.0.1"]
+ ["-whitelist=noban@127.0.0.1", "-keypool=100"],
+ ["-whitelist=noban@127.0.0.1", "-keypool=100"],
+ ["-whitelist=noban@127.0.0.1", "-keypool=100"],
+ ["-whitelist=noban@127.0.0.1"],
]
self.rpc_timeout = 120
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 4eb0d19a4f..336e246e33 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -71,28 +71,29 @@ class BumpFeeTest(BitcoinTestFramework):
test_simple_bumpfee_succeeds(self, "default", rbf_node, peer_node, dest_address)
test_simple_bumpfee_succeeds(self, "fee_rate", rbf_node, peer_node, dest_address)
test_feerate_args(self, rbf_node, peer_node, dest_address)
- test_segwit_bumpfee_succeeds(rbf_node, dest_address)
- test_nonrbf_bumpfee_fails(peer_node, dest_address)
- test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address)
- test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
- test_small_output_fails(rbf_node, dest_address)
- test_dust_to_fee(rbf_node, dest_address)
- test_settxfee(rbf_node, dest_address)
+ test_segwit_bumpfee_succeeds(self, rbf_node, dest_address)
+ test_nonrbf_bumpfee_fails(self, peer_node, dest_address)
+ test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address)
+ test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address)
+ test_small_output_fails(self, rbf_node, dest_address)
+ test_dust_to_fee(self, rbf_node, dest_address)
+ test_settxfee(self, rbf_node, dest_address)
test_watchonly_psbt(self, peer_node, rbf_node, dest_address)
- test_rebumping(rbf_node, dest_address)
- test_rebumping_not_replaceable(rbf_node, dest_address)
- test_unconfirmed_not_spendable(rbf_node, rbf_node_address)
- test_bumpfee_metadata(rbf_node, dest_address)
- test_locked_wallet_fails(rbf_node, dest_address)
- test_change_script_match(rbf_node, dest_address)
+ test_rebumping(self, rbf_node, dest_address)
+ test_rebumping_not_replaceable(self, rbf_node, dest_address)
+ test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address)
+ test_bumpfee_metadata(self, rbf_node, dest_address)
+ test_locked_wallet_fails(self, rbf_node, dest_address)
+ test_change_script_match(self, rbf_node, dest_address)
test_maxtxfee_fails(self, rbf_node, dest_address)
# These tests wipe out a number of utxos that are expected in other tests
- test_small_output_with_feerate_succeeds(rbf_node, dest_address)
- test_no_more_inputs_fails(rbf_node, dest_address)
+ test_small_output_with_feerate_succeeds(self, rbf_node, dest_address)
+ test_no_more_inputs_fails(self, rbf_node, dest_address)
self.log.info("Success")
def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
+ self.log.info('Test simple bumpfee')
rbfid = spend_one_input(rbf_node, dest_address)
rbftx = rbf_node.gettransaction(rbfid)
self.sync_mempools((rbf_node, peer_node))
@@ -119,6 +120,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
assert_equal(bumpedwtx["replaces_txid"], rbfid)
def test_feerate_args(self, rbf_node, peer_node, dest_address):
+ self.log.info('Test feerate args')
rbfid = spend_one_input(rbf_node, dest_address)
self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
@@ -135,7 +137,8 @@ def test_feerate_args(self, rbf_node, peer_node, dest_address):
assert_raises_rpc_error(-4, "is too high (cannot be higher than", rbf_node.bumpfee, rbfid, {"fee_rate":1})
-def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
+def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address):
+ self.log.info('Test that segwit-sourcing bumpfee works')
# Create a transaction with segwit output, then create an RBF transaction
# which spends it, and make sure bumpfee can be called on it.
@@ -165,14 +168,14 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
assert rbfid not in rbf_node.getrawmempool()
-def test_nonrbf_bumpfee_fails(peer_node, dest_address):
- # cannot replace a non RBF transaction (from node which did not enable RBF)
+def test_nonrbf_bumpfee_fails(self, peer_node, dest_address):
+ self.log.info('Test that we cannot replace a non RBF transaction')
not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
-def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
- # cannot bump fee unless the tx has only inputs that we own.
+def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address):
+ self.log.info('Test that it cannot bump fee if non-owned inputs are included')
# here, the rbftx has a peer_node coin and then adds a rbf_node input
# Note that this test depends upon the RPC code checking input ownership prior to change outputs
# (since it can't use fundrawtransaction, it lacks a proper change output)
@@ -192,8 +195,8 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
rbf_node.bumpfee, rbfid)
-def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
- # cannot bump fee if the transaction has a descendant
+def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address):
+ self.log.info('Test that fee cannot be bumped when it has descendant')
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
parent_id = spend_one_input(rbf_node, rbf_node_address)
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
@@ -201,7 +204,8 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address)
rbf_node.sendrawtransaction(tx["hex"])
assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
-def test_small_output_fails(rbf_node, dest_address):
+def test_small_output_fails(self, rbf_node, dest_address):
+ self.log.info('Test totalFee bump with small output fails')
# cannot bump fee with a too-small output
rbfid = spend_one_input(rbf_node, dest_address)
rbf_node.bumpfee(rbfid, {"totalFee": 50000})
@@ -209,7 +213,8 @@ def test_small_output_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001})
-def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
+def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
+ self.log.info('Testing small output with feerate bump succeeds')
# Make sure additional inputs exist
rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
@@ -217,9 +222,9 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
assert_equal(len(input_list), 1)
original_txin = input_list[0]
- # Keep bumping until we out-spend change output
+ self.log.info('Keep bumping until transaction fee out-spends non-destination value')
tx_fee = 0
- while tx_fee < Decimal("0.0005"):
+ while True:
input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
new_item = list(input_list)[0]
assert_equal(len(input_list), 1)
@@ -231,7 +236,11 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
assert rbfid not in raw_pool
assert rbfid_new in raw_pool
rbfid = rbfid_new
- tx_fee = rbfid_new_details["origfee"]
+ tx_fee = rbfid_new_details["fee"]
+
+ # Total value from input not going to destination
+ if tx_fee > Decimal('0.00050000'):
+ break
# input(s) have been added
final_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
@@ -244,8 +253,8 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address):
rbf_node.generatetoaddress(1, rbf_node.getnewaddress())
assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1)
-def test_dust_to_fee(rbf_node, dest_address):
- # check that if output is reduced to dust, it will be converted to fee
+def test_dust_to_fee(self, rbf_node, dest_address):
+ self.log.info('Test that bumped output that is dust is dropped to fee')
# the bumped tx sets fee=49,900, but it converts to 50,000
rbfid = spend_one_input(rbf_node, dest_address)
fulltx = rbf_node.getrawtransaction(rbfid, 1)
@@ -257,7 +266,8 @@ def test_dust_to_fee(rbf_node, dest_address):
assert_equal(len(full_bumped_tx["vout"]), 1) # change output is eliminated
-def test_settxfee(rbf_node, dest_address):
+def test_settxfee(self, rbf_node, dest_address):
+ self.log.info('Test settxfee')
assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005'))
assert_raises_rpc_error(-8, "txfee cannot be less than wallet min fee", rbf_node.settxfee, Decimal('0.000015'))
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
@@ -272,17 +282,19 @@ def test_settxfee(rbf_node, dest_address):
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
-def test_maxtxfee_fails(test, rbf_node, dest_address):
+def test_maxtxfee_fails(self, rbf_node, dest_address):
+ self.log.info('Test that bumpfee fails when it hits -matxfee')
# size of bumped transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes
# expected bumping feerate of 20 sats/vbyte => 141*20 sats = 0.00002820 btc
- test.restart_node(1, ['-maxtxfee=0.000025'] + test.extra_args[1])
+ self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
rbfid = spend_one_input(rbf_node, dest_address)
assert_raises_rpc_error(-4, "Unable to create transaction: Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid)
- test.restart_node(1, test.extra_args[1])
+ self.restart_node(1, self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
-def test_watchonly_psbt(test, peer_node, rbf_node, dest_address):
+def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
+ self.log.info('Test that PSBT is returned for bumpfee in watchonly wallets')
priv_rec_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0"
pub_rec_desc = rbf_node.getdescriptorinfo(priv_rec_desc)["descriptor"]
priv_change_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh"
@@ -334,7 +346,7 @@ def test_watchonly_psbt(test, peer_node, rbf_node, dest_address):
funding_address2 = watcher.getnewaddress(address_type='bech32')
peer_node.sendmany("", {funding_address1: 0.001, funding_address2: 0.001})
peer_node.generate(1)
- test.sync_all()
+ self.sync_all()
# Create single-input PSBT for transaction to be bumped
psbt = watcher.walletcreatefundedpsbt([], {dest_address:0.0005}, 0, {"feeRate": 0.00001}, True)['psbt']
@@ -363,24 +375,24 @@ def test_watchonly_psbt(test, peer_node, rbf_node, dest_address):
rbf_node.unloadwallet("watcher")
rbf_node.unloadwallet("signer")
-def test_rebumping(rbf_node, dest_address):
- # check that re-bumping the original tx fails, but bumping the bumper succeeds
+def test_rebumping(self, rbf_node, dest_address):
+ self.log.info('Test that re-bumping the original tx fails, but bumping successor works')
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000})
assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000})
rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000})
-def test_rebumping_not_replaceable(rbf_node, dest_address):
- # check that re-bumping a non-replaceable bump tx fails
+def test_rebumping_not_replaceable(self, rbf_node, dest_address):
+ self.log.info('Test that re-bumping non-replaceable fails')
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
{"totalFee": 20000})
-def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
- # check that unconfirmed outputs from bumped transactions are not spendable
+def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address):
+ self.log.info('Test that unconfirmed outputs from bumped txns are not spendable')
rbfid = spend_one_input(rbf_node, rbf_node_address)
rbftx = rbf_node.gettransaction(rbfid)["hex"]
assert rbfid in rbf_node.getrawmempool()
@@ -418,7 +430,8 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1)
-def test_bumpfee_metadata(rbf_node, dest_address):
+def test_bumpfee_metadata(self, rbf_node, dest_address):
+ self.log.info('Test that bumped txn metadata persists to new txn record')
assert(rbf_node.getbalance() < 49)
rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value")
@@ -428,15 +441,17 @@ def test_bumpfee_metadata(rbf_node, dest_address):
assert_equal(bumped_wtx["to"], "to value")
-def test_locked_wallet_fails(rbf_node, dest_address):
+def test_locked_wallet_fails(self, rbf_node, dest_address):
+ self.log.info('Test that locked wallet cannot bump txn')
rbfid = spend_one_input(rbf_node, dest_address)
rbf_node.walletlock()
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
-def test_change_script_match(rbf_node, dest_address):
- """Test that the same change addresses is used for the replacement transaction when possible."""
+def test_change_script_match(self, rbf_node, dest_address):
+ self.log.info('Test that the same change addresses is used for the replacement transaction when possible.')
+
def get_change_address(tx):
tx_details = rbf_node.getrawtransaction(tx, 1)
txout_addresses = [txout['scriptPubKey']['addresses'][0] for txout in tx_details["vout"]]
@@ -480,7 +495,8 @@ def submit_block_with_tx(node, tx):
node.submitblock(block.serialize().hex())
return block
-def test_no_more_inputs_fails(rbf_node, dest_address):
+def test_no_more_inputs_fails(self, rbf_node, dest_address):
+ self.log.info('Test that bumpfee fails when there are no available confirmed outputs')
# feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient
rbf_node.generatetoaddress(1, dest_address)
# spend all funds, no change output
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index 048b3127ff..dabd78f66c 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -79,7 +79,7 @@ class CreateWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress)
# Now set a seed and it should work. Wallet should also be encrypted
- w4.walletpassphrase('pass', 2)
+ w4.walletpassphrase('pass', 60)
w4.sethdseed()
w4.getnewaddress()
w4.getrawchangeaddress()
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 6f248c9bd3..229eda9806 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -2,7 +2,7 @@
# Copyright (c) 2017-2019 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 the listsincelast RPC."""
+"""Test the listsinceblock RPC."""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import BIP125_SEQUENCE_NUMBER
@@ -38,6 +38,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
self.double_spends_filtered()
def test_no_blockhash(self):
+ self.log.info("Test no blockhash")
txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
blockhash, = self.nodes[2].generate(1)
blockheight = self.nodes[2].getblockheader(blockhash)['height']
@@ -63,6 +64,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
"transactions": txs})
def test_invalid_blockhash(self):
+ self.log.info("Test invalid blockhash")
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
"42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4")
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
@@ -100,6 +102,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
This test only checks that [tx0] is present.
'''
+ self.log.info("Test reorg")
# Split network into two
self.split_network()
@@ -110,7 +113,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
# generate on both sides
lastblockhash = self.nodes[1].generate(6)[5]
self.nodes[2].generate(7)
- self.log.info('lastblockhash=%s' % (lastblockhash))
+ self.log.debug('lastblockhash={}'.format(lastblockhash))
self.sync_all(self.nodes[:2])
self.sync_all(self.nodes[2:])
@@ -155,6 +158,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
until the fork point, and to include all transactions that relate to the
node wallet.
'''
+ self.log.info("Test double spend")
self.sync_all()
@@ -234,6 +238,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
3. It is listed with a confirmation count of 2 (bb3, bb4), not
3 (aa1, aa2, aa3).
'''
+ self.log.info("Test double send")
self.sync_all()
@@ -302,6 +307,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
`listsinceblock` was returning conflicted transactions even if they
occurred before the specified cutoff blockhash
'''
+ self.log.info("Test spends filtered")
spending_node = self.nodes[2]
dest_address = spending_node.getnewaddress()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index 61c3e700c5..520a2b5a95 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -12,40 +12,6 @@ import sys
import subprocess
import logging
-# Fuzzers known to lack a seed corpus in https://github.com/bitcoin-core/qa-assets/tree/master/fuzz_seed_corpus
-FUZZERS_MISSING_CORPORA = [
- "addr_info_deserialize",
- "asmap",
- "base_encode_decode",
- "block",
- "block_file_info_deserialize",
- "block_filter_deserialize",
- "block_header_and_short_txids_deserialize",
- "decode_tx",
- "fee_rate_deserialize",
- "flat_file_pos_deserialize",
- "hex",
- "integer",
- "key_origin_info_deserialize",
- "merkle_block_deserialize",
- "out_point_deserialize",
- "parse_hd_keypath",
- "parse_numbers",
- "parse_script",
- "parse_univalue",
- "partial_merkle_tree_deserialize",
- "partially_signed_transaction_deserialize",
- "prefilled_transaction_deserialize",
- "psbt_input_deserialize",
- "psbt_output_deserialize",
- "pub_key_deserialize",
- "script_deserialize",
- "strprintf",
- "sub_net_deserialize",
- "tx_in",
- "tx_in_deserialize",
- "tx_out",
-]
def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
@@ -64,7 +30,12 @@ def main():
parser.add_argument(
'--valgrind',
action='store_true',
- help='If true, run fuzzing binaries under the valgrind memory error detector. Valgrind 3.14 or later required.',
+ help='If true, run fuzzing binaries under the valgrind memory error detector',
+ )
+ parser.add_argument(
+ '-x',
+ '--exclude',
+ help="A comma-separated list of targets to exclude",
)
parser.add_argument(
'seed_dir',
@@ -100,7 +71,7 @@ def main():
logging.error("No fuzz targets found")
sys.exit(1)
- logging.info("Fuzz targets found: {}".format(test_list_all))
+ logging.debug("{} fuzz target(s) found: {}".format(len(test_list_all), " ".join(sorted(test_list_all))))
args.target = args.target or test_list_all # By default run all
test_list_error = list(set(args.target).difference(set(test_list_all)))
@@ -109,7 +80,29 @@ def main():
test_list_selection = list(set(test_list_all).intersection(set(args.target)))
if not test_list_selection:
logging.error("No fuzz targets selected")
- logging.info("Fuzz targets selected: {}".format(test_list_selection))
+ if args.exclude:
+ for excluded_target in args.exclude.split(","):
+ if excluded_target not in test_list_selection:
+ logging.error("Target \"{}\" not found in current target list.".format(excluded_target))
+ continue
+ test_list_selection.remove(excluded_target)
+ test_list_selection.sort()
+
+ logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))
+
+ test_list_seedless = []
+ for t in test_list_selection:
+ corpus_path = os.path.join(args.seed_dir, t)
+ if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0:
+ test_list_seedless.append(t)
+ test_list_seedless.sort()
+ if test_list_seedless:
+ logging.info(
+ "Fuzzing harnesses lacking a seed corpus: {}".format(
+ " ".join(test_list_seedless)
+ )
+ )
+ logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets")
try:
help_output = subprocess.run(
@@ -117,7 +110,7 @@ def main():
os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', test_list_selection[0]),
'-help=1',
],
- timeout=10,
+ timeout=20,
check=True,
stderr=subprocess.PIPE,
universal_newlines=True,
@@ -141,21 +134,27 @@ def main():
def run_once(*, corpus, test_list, build_dir, export_coverage, use_valgrind):
for t in test_list:
corpus_path = os.path.join(corpus, t)
- if t in FUZZERS_MISSING_CORPORA:
- os.makedirs(corpus_path, exist_ok=True)
+ os.makedirs(corpus_path, exist_ok=True)
args = [
os.path.join(build_dir, 'src', 'test', 'fuzz', t),
'-runs=1',
- '-detect_leaks=0',
corpus_path,
]
if use_valgrind:
- args = ['valgrind', '--quiet', '--error-exitcode=1', '--exit-on-first-error=yes'] + args
+ args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
logging.debug('Run {} with args {}'.format(t, args))
result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
output = result.stderr
logging.debug('Output: {}'.format(output))
- result.check_returncode()
+ try:
+ result.check_returncode()
+ except subprocess.CalledProcessError as e:
+ if e.stdout:
+ logging.info(e.stdout)
+ if e.stderr:
+ logging.info(e.stderr)
+ logging.info("Target \"{}\" failed with exit code {}: {}".format(t, e.returncode, " ".join(args)))
+ sys.exit(1)
if not export_coverage:
continue
for l in output.splitlines():
diff --git a/test/lint/extended-lint-cppcheck.sh b/test/lint/extended-lint-cppcheck.sh
index 4d6c70660f..ae18d74ebf 100755
--- a/test/lint/extended-lint-cppcheck.sh
+++ b/test/lint/extended-lint-cppcheck.sh
@@ -66,7 +66,7 @@ function join_array {
ENABLED_CHECKS_REGEXP=$(join_array "|" "${ENABLED_CHECKS[@]}")
IGNORED_WARNINGS_REGEXP=$(join_array "|" "${IGNORED_WARNINGS[@]}")
WARNINGS=$(git ls-files -- "*.cpp" "*.h" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" | \
- xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++11 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -DHAVE_WORKING_BOOST_SLEEP_FOR -I src/ -q 2>&1 | sort -u | \
+ xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++11 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -I src/ -q 2>&1 | sort -u | \
grep -E "${ENABLED_CHECKS_REGEXP}" | \
grep -vE "${IGNORED_WARNINGS_REGEXP}")
if [[ ${WARNINGS} != "" ]]; then
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index ced2fd2bb6..1cece6a525 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -53,7 +53,6 @@ EXPECTED_BOOST_INCLUDES=(
boost/algorithm/string/classification.hpp
boost/algorithm/string/replace.hpp
boost/algorithm/string/split.hpp
- boost/chrono/chrono.hpp
boost/date_time/posix_time/posix_time.hpp
boost/filesystem.hpp
boost/filesystem/fstream.hpp
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 35e58c2df6..0cb38b6fdb 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -22,6 +22,7 @@ KNOWN_VIOLATIONS=(
"src/test/blockchain_tests.cpp.*std::to_string"
"src/test/dbwrapper_tests.cpp:.*snprintf"
"src/test/denialofservice_tests.cpp.*std::to_string"
+ "src/test/fuzz/locale.cpp"
"src/test/fuzz/parse_numbers.cpp:.*atoi"
"src/test/key_tests.cpp.*std::to_string"
"src/test/net_tests.cpp.*std::to_string"
diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh
index 63624e3ae0..f59b2c9945 100755
--- a/test/lint/lint-shell.sh
+++ b/test/lint/lint-shell.sh
@@ -41,7 +41,7 @@ if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|sec
fi
if ! command -v yq > /dev/null; then
- echo "Skipping Gitian desriptor scripts checking since yq is not installed."
+ echo "Skipping Gitian descriptor scripts checking since yq is not installed."
exit $EXIT_CODE
fi
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index 576ae94098..a7a97eb41f 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -12,3 +12,5 @@ keyserver
homogenous
setban
hist
+ser
+unselect
diff --git a/test/lint/lint-submodule.sh b/test/lint/lint-submodule.sh
new file mode 100755
index 0000000000..d9aa021df7
--- /dev/null
+++ b/test/lint/lint-submodule.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# This script checks for git modules
+export LC_ALL=C
+EXIT_CODE=0
+
+CMD=$(git submodule status --recursive)
+if test -n "$CMD";
+then
+ echo These submodules were found, delete them:
+ echo "$CMD"
+ EXIT_CODE=1
+fi
+
+exit $EXIT_CODE
+