aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml22
-rwxr-xr-x.travis/extended_lint_04_install.sh12
-rwxr-xr-x.travis/extended_lint_06_script.sh9
-rwxr-xr-x.travis/test_04_install.sh3
-rw-r--r--CONTRIBUTING.md24
-rw-r--r--build-aux/m4/bitcoin_qt.m44
-rw-r--r--build_msvc/README.md2
-rw-r--r--build_msvc/bench_bitcoin/bench_bitcoin.vcxproj1
-rw-r--r--configure.ac56
-rwxr-xr-xcontrib/devtools/symbol-check.py2
-rw-r--r--contrib/guix/README.md229
-rwxr-xr-xcontrib/guix/guix-build.sh39
-rw-r--r--contrib/guix/libexec/build.sh206
-rw-r--r--contrib/guix/manifest.scm158
-rw-r--r--depends/packages/expat.mk8
-rw-r--r--depends/packages/libX11.mk32
-rw-r--r--depends/packages/libXext.mk53
-rw-r--r--depends/packages/libxcb.mk2
-rw-r--r--depends/packages/packages.mk2
-rw-r--r--depends/packages/qt.mk35
-rw-r--r--depends/packages/xextproto.mk25
-rw-r--r--depends/packages/xtrans.mk26
-rw-r--r--depends/patches/qt/no-xlib.patch69
-rw-r--r--doc/benchmarking.md51
-rw-r--r--doc/bips.md4
-rw-r--r--doc/bitcoin-conf.md15
-rw-r--r--doc/dependencies.md13
-rw-r--r--doc/developer-notes.md2
-rw-r--r--doc/fuzzing.md4
-rw-r--r--doc/release-notes-15993.md3
-rw-r--r--doc/release-notes-16394.md4
-rw-r--r--doc/release-notes.md43
-rw-r--r--doc/release-notes/release-notes-16152.md7
-rw-r--r--src/Makefile.am17
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/banman.cpp3
-rw-r--r--src/bench/bench_bitcoin.cpp16
-rw-r--r--src/bench/ccoins_caching.cpp6
-rw-r--r--src/bench/chacha_poly_aead.cpp123
-rw-r--r--src/bench/rpc_blockchain.cpp33
-rw-r--r--src/bitcoin-cli.cpp35
-rw-r--r--src/bitcoin-tx.cpp46
-rw-r--r--src/bitcoin-wallet.cpp16
-rw-r--r--src/bitcoind.cpp31
-rw-r--r--src/chainparams.cpp17
-rw-r--r--src/chainparams.h11
-rw-r--r--src/chainparamsbase.cpp6
-rw-r--r--src/chainparamsbase.h1
-rw-r--r--src/coins.cpp17
-rw-r--r--src/coins.h25
-rw-r--r--src/crypto/chacha_poly_aead.cpp126
-rw-r--r--src/crypto/chacha_poly_aead.h146
-rw-r--r--src/httprpc.cpp11
-rw-r--r--src/index/blockfilterindex.cpp4
-rw-r--r--src/index/txindex.cpp5
-rw-r--r--src/init.cpp433
-rw-r--r--src/interfaces/chain.cpp19
-rw-r--r--src/interfaces/chain.h15
-rw-r--r--src/interfaces/node.cpp1
-rw-r--r--src/interfaces/node.h3
-rw-r--r--src/interfaces/wallet.cpp56
-rw-r--r--src/interfaces/wallet.h28
-rw-r--r--src/keystore.h83
-rw-r--r--src/net.cpp65
-rw-r--r--src/net_processing.cpp77
-rw-r--r--src/net_processing.h4
-rw-r--r--src/netbase.cpp224
-rw-r--r--src/node/transaction.cpp64
-rw-r--r--src/node/transaction.h15
-rw-r--r--src/noui.cpp44
-rw-r--r--src/noui.h6
-rw-r--r--src/outputtype.cpp6
-rw-r--r--src/outputtype.h4
-rw-r--r--src/protocol.h5
-rw-r--r--src/psbt.h1
-rw-r--r--src/qt/addresstablemodel.cpp16
-rw-r--r--src/qt/bitcoin.cpp74
-rw-r--r--src/qt/bitcoin.h1
-rw-r--r--src/qt/bitcoingui.cpp6
-rw-r--r--src/qt/clientmodel.cpp18
-rw-r--r--src/qt/coincontroldialog.cpp6
-rw-r--r--src/qt/guiutil.cpp11
-rw-r--r--src/qt/main.cpp2
-rw-r--r--src/qt/paymentserver.cpp8
-rw-r--r--src/qt/rpcconsole.cpp5
-rw-r--r--src/qt/rpcconsole.h1
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/splashscreen.cpp8
-rw-r--r--src/qt/test/wallettests.cpp3
-rw-r--r--src/qt/transactiontablemodel.cpp15
-rw-r--r--src/qt/walletcontroller.cpp3
-rw-r--r--src/qt/walletmodel.cpp32
-rw-r--r--src/qt/walletmodeltransaction.cpp6
-rw-r--r--src/qt/walletmodeltransaction.h5
-rw-r--r--src/random.cpp5
-rw-r--r--src/random.h4
-rw-r--r--src/rest.cpp1
-rw-r--r--src/rpc/blockchain.cpp136
-rw-r--r--src/rpc/mining.cpp41
-rw-r--r--src/rpc/misc.cpp47
-rw-r--r--src/rpc/net.cpp47
-rw-r--r--src/rpc/protocol.h23
-rw-r--r--src/rpc/rawtransaction.cpp129
-rw-r--r--src/rpc/rawtransaction_util.cpp15
-rw-r--r--src/rpc/rawtransaction_util.h6
-rw-r--r--src/rpc/request.cpp (renamed from src/rpc/protocol.cpp)42
-rw-r--r--src/rpc/request.h42
-rw-r--r--src/rpc/server.cpp43
-rw-r--r--src/rpc/server.h18
-rw-r--r--src/rpc/util.cpp8
-rw-r--r--src/rpc/util.h18
-rw-r--r--src/script/descriptor.h1
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/script/keyorigin.h37
-rw-r--r--src/script/script.h4
-rw-r--r--src/script/sign.cpp60
-rw-r--r--src/script/sign.h70
-rw-r--r--src/script/signingprovider.cpp (renamed from src/keystore.cpp)158
-rw-r--r--src/script/signingprovider.h92
-rw-r--r--src/script/standard.cpp1
-rw-r--r--src/test/addrman_tests.cpp9
-rw-r--r--src/test/blockencodings_tests.cpp2
-rw-r--r--src/test/crypto_tests.cpp126
-rw-r--r--src/test/denialofservice_tests.cpp5
-rw-r--r--src/test/getarg_tests.cpp26
-rw-r--r--src/test/mempool_tests.cpp22
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/multisig_tests.cpp4
-rw-r--r--src/test/script_p2sh_tests.cpp8
-rw-r--r--src/test/script_standard_tests.cpp2
-rw-r--r--src/test/script_tests.cpp4
-rw-r--r--src/test/setup_common.cpp3
-rw-r--r--src/test/transaction_tests.cpp14
-rw-r--r--src/test/txvalidationcache_tests.cpp4
-rw-r--r--src/test/util.cpp11
-rw-r--r--src/test/util_tests.cpp54
-rw-r--r--src/timedata.cpp3
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txdb.cpp11
-rw-r--r--src/txmempool.h7
-rw-r--r--src/util/error.cpp6
-rw-r--r--src/util/system.cpp121
-rw-r--r--src/util/system.h41
-rw-r--r--src/util/translation.h42
-rw-r--r--src/validation.cpp257
-rw-r--r--src/validation.h158
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coincontrol.h7
-rw-r--r--src/wallet/crypter.cpp192
-rw-r--r--src/wallet/crypter.h54
-rw-r--r--src/wallet/db.cpp9
-rw-r--r--src/wallet/db.h11
-rw-r--r--src/wallet/feebumper.cpp9
-rw-r--r--src/wallet/init.cpp57
-rw-r--r--src/wallet/ismine.cpp1
-rw-r--r--src/wallet/load.cpp11
-rw-r--r--src/wallet/rpcdump.cpp154
-rw-r--r--src/wallet/rpcwallet.cpp385
-rw-r--r--src/wallet/test/init_tests.cpp7
-rw-r--r--src/wallet/test/wallet_tests.cpp14
-rw-r--r--src/wallet/wallet.cpp663
-rw-r--r--src/wallet/wallet.h328
-rw-r--r--src/wallet/walletdb.cpp220
-rw-r--r--src/wallet/walletdb.h30
-rw-r--r--src/warnings.cpp10
-rw-r--r--src/zmq/zmqrpc.cpp5
-rw-r--r--test/README.md23
-rwxr-xr-xtest/functional/combine_logs.py40
-rwxr-xr-xtest/functional/example_test.py5
-rwxr-xr-xtest/functional/feature_abortnode.py48
-rwxr-xr-xtest/functional/feature_assumevalid.py2
-rwxr-xr-xtest/functional/feature_bip68_sequence.py5
-rwxr-xr-xtest/functional/feature_block.py10
-rwxr-xr-xtest/functional/feature_blocksdir.py8
-rwxr-xr-xtest/functional/feature_cltv.py6
-rwxr-xr-xtest/functional/feature_config_args.py24
-rwxr-xr-xtest/functional/feature_dbcrash.py23
-rwxr-xr-xtest/functional/feature_fee_estimation.py11
-rwxr-xr-xtest/functional/feature_includeconf.py6
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py2
-rwxr-xr-xtest/functional/feature_pruning.py1
-rwxr-xr-xtest/functional/feature_rbf.py2
-rwxr-xr-xtest/functional/feature_segwit.py3
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py2
-rwxr-xr-xtest/functional/mempool_accept.py1
-rwxr-xr-xtest/functional/mempool_limit.py6
-rwxr-xr-xtest/functional/mempool_package_onemore.py86
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py5
-rwxr-xr-xtest/functional/p2p_compactblocks.py3
-rwxr-xr-xtest/functional/p2p_invalid_messages.py28
-rwxr-xr-xtest/functional/p2p_invalid_tx.py3
-rwxr-xr-xtest/functional/p2p_node_network_limited.py6
-rwxr-xr-xtest/functional/p2p_segwit.py6
-rwxr-xr-xtest/functional/p2p_tx_download.py175
-rwxr-xr-xtest/functional/rpc_bind.py2
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py157
-rwxr-xr-xtest/functional/rpc_psbt.py26
-rwxr-xr-xtest/functional/rpc_users.py182
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py35
-rwxr-xr-xtest/functional/test_framework/test_framework.py21
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rw-r--r--test/functional/test_framework/util.py43
-rwxr-xr-xtest/functional/test_runner.py27
-rwxr-xr-xtest/functional/wallet_basic.py3
-rwxr-xr-xtest/functional/wallet_bumpfee.py11
-rwxr-xr-xtest/functional/wallet_bumpfee_totalfee_deprecation.py54
-rwxr-xr-xtest/functional/wallet_create_tx.py39
-rwxr-xr-xtest/functional/wallet_createwallet.py16
-rwxr-xr-xtest/functional/wallet_encryption.py2
-rwxr-xr-xtest/lint/extended-lint-all.sh26
-rwxr-xr-xtest/lint/extended-lint-cppcheck.sh80
-rwxr-xr-xtest/lint/lint-format-strings.py2
215 files changed, 5334 insertions, 3355 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 0a644992dd..ac17e2eeb6 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -33,7 +33,7 @@ before_build:
- ps: Start-Process clcache-server
- ps: fsutil behavior set disablelastaccess 0 # Enable Access time feature on Windows (for clcache)
build_script:
-- cmd: msbuild /p:TrackFileAccess=false /p:CLToolExe=clcache.exe build_msvc\bitcoin.sln /m /v:q /nologo
+- cmd: msbuild /p:TrackFileAccess=false /p:CLToolExe=clcache.exe build_msvc\bitcoin.sln /m /v:n /nologo
after_build:
- ps: fsutil behavior set disablelastaccess 1 # Disable Access time feature on Windows (better performance)
- ps: clcache -z
diff --git a/.gitignore b/.gitignore
index 394c76b51a..edc683e48b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,7 +77,7 @@ src/qt/bitcoin-qt.includes
# Only ignore unexpected patches
*.patch
-!depends/patches/*.patch
+!depends/patches/**/*.patch
#libtool object files
*.lo
@@ -86,10 +86,14 @@ src/qt/bitcoin-qt.includes
# Compilation and Qt preprocessor part
*.qm
Makefile
+!depends/Makefile
bitcoin-qt
Bitcoin-Qt.app
background.tiff*
+# Qt Creator
+Makefile.am.user
+
# Unit-tests
Makefile.test
bitcoin-qt_test
diff --git a/.travis.yml b/.travis.yml
index adf2140642..422e9149f4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,6 +37,7 @@ cache:
stages:
- lint
- test
+ - extended-lint
env:
global:
- MAKEJOBS=-j3
@@ -85,6 +86,19 @@ jobs:
script:
- set -o errexit; source .travis/lint_06_script.sh
+ - stage: extended-lint
+ name: 'extended lint [runtime >= 60 seconds]'
+ env:
+ cache: false
+ language: python
+ python: '3.5'
+ install:
+ - set -o errexit; source .travis/extended_lint_04_install.sh
+ before_script:
+ - set -o errexit; source .travis/lint_05_before_script.sh
+ script:
+ - set -o errexit; source .travis/extended_lint_06_script.sh
+
- stage: test
name: 'ARM [GOAL: install] [no unit or functional tests]'
env: >-
@@ -116,21 +130,21 @@ jobs:
CONFIG_SHELL="/bin/dash"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package instead of depends Qt to speed up build and avoid timeout]'
+ name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package instead of depends Qt to speed up build and avoid timeout] [unsigned char]'
env: >-
HOST=x86_64-unknown-linux-gnu
PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev"
DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
TEST_RUNNER_EXTRA="--coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CXXFLAGS=\"-g0 -O2\""
+ BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
- stage: test
name: 'x86_64 Linux [GOAL: install] [trusty] [no functional tests, no depends, only system libs]'
env: >-
HOST=x86_64-unknown-linux-gnu
DOCKER_NAME_TAG=ubuntu:14.04
- PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libicu-dev libpng-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev"
+ PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libicu-dev libpng-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev"
NO_DEPENDS=1
RUN_FUNCTIONAL_TESTS=false
GOAL="install"
@@ -165,7 +179,7 @@ jobs:
RUN_FUNCTIONAL_TESTS=false
RUN_FUZZ_TESTS=true
GOAL="install"
- BITCOIN_CONFIG="--disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++"
+ BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++"
- stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
diff --git a/.travis/extended_lint_04_install.sh b/.travis/extended_lint_04_install.sh
new file mode 100755
index 0000000000..123d874a84
--- /dev/null
+++ b/.travis/extended_lint_04_install.sh
@@ -0,0 +1,12 @@
+#!/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
+
+CPPCHECK_VERSION=1.86
+curl -s https://codeload.github.com/danmar/cppcheck/tar.gz/${CPPCHECK_VERSION} | tar -zxf - --directory /tmp/
+(cd /tmp/cppcheck-${CPPCHECK_VERSION}/ && make CFGDIR=/tmp/cppcheck-${CPPCHECK_VERSION}/cfg/ > /dev/null)
+export PATH="$PATH:/tmp/cppcheck-${CPPCHECK_VERSION}/"
diff --git a/.travis/extended_lint_06_script.sh b/.travis/extended_lint_06_script.sh
new file mode 100755
index 0000000000..e8228c9c4d
--- /dev/null
+++ b/.travis/extended_lint_06_script.sh
@@ -0,0 +1,9 @@
+#!/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
+
+test/lint/extended-lint-all.sh
diff --git a/.travis/test_04_install.sh b/.travis/test_04_install.sh
index b589ee7a16..319f2c5b21 100755
--- a/.travis/test_04_install.sh
+++ b/.travis/test_04_install.sh
@@ -6,6 +6,9 @@
export LC_ALL=C.UTF-8
+free -m -h
+echo "Number of CPUs (nproc): $(nproc)"
+
travis_retry docker pull "$DOCKER_NAME_TAG"
export DIR_FUZZ_IN=${TRAVIS_BUILD_DIR}/qa-assets
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a2456f5b8c..268259c82d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -80,21 +80,15 @@ The title of the pull request should be prefixed by the component or area that
the pull request affects. Valid areas as:
- *Consensus* for changes to consensus critical code
- - *Docs* for changes to the documentation
+ - *Doc* for changes to the documentation
- *Qt* for changes to bitcoin-qt
+ - *Log* Changes to log messages
- *Mining* for changes to the mining code
- *Net* or *P2P* for changes to the peer-to-peer network code
+ - *Refactor* for structural changes that do not change behavior
- *RPC/REST/ZMQ* for changes to the RPC, REST or ZMQ APIs
- *Scripts and tools* for changes to the scripts and tools
- - *Tests* for changes to the bitcoin unit tests or QA tests
- - *Trivial* should **only** be used for PRs that do not change generated
- executable code. Notably, refactors (change of function arguments and code
- reorganization) and changes in behavior should **not** be marked as trivial.
- Examples of trivial PRs are changes to:
- - comments
- - whitespace
- - variable names
- - logging and messages
+ - *Test* for changes to the bitcoin unit tests or QA tests
- *Utils and libraries* for changes to the utils and libraries
- *Wallet* for changes to the wallet code
@@ -103,10 +97,10 @@ Examples:
Consensus: Add new opcode for BIP-XXXX OP_CHECKAWESOMESIG
Net: Automatically create hidden service, listen on Tor
Qt: Add feed bump button
- Trivial: Fix typo in init.cpp
+ Log: Fix typo in log message
Note that translations should not be submitted as pull requests, please see
-[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md)
+[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md)
for more information on helping with translations.
If a pull request is not to be considered for merging (yet), please
@@ -322,7 +316,7 @@ The project leader is the release manager for each Bitcoin Core release.
Copyright
---------
-By contributing to this repository, you agree to license your work under the
-MIT license unless specified otherwise in `contrib/debian/copyright` or at
-the top of the file itself. Any work contributed where you are not the original
+By contributing to this repository, you agree to license your work under the
+MIT license unless specified otherwise in `contrib/debian/copyright` or at
+the top of the file itself. Any work contributed where you are not the original
author must contain its license header with the original author(s) and source.
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 1a7c5d5f7d..675fb6d3fb 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -268,7 +268,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[
dnl Internal. Check if the included version of Qt is greater than Qt58.
dnl Requires: INCLUDES must be populated as necessary.
-dnl Output: bitcoin_cv_qt5=yes|no
+dnl Output: bitcoin_cv_qt58=yes|no
AC_DEFUN([_BITCOIN_QT_CHECK_QT58],[
AC_CACHE_CHECK(for > Qt 5.7, bitcoin_cv_qt58,[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -355,7 +355,6 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
fi
if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"])
PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
elif test "x$TARGET_OS" = xdarwin; then
PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
@@ -469,7 +468,6 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
])
BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,AC_MSG_WARN([zlib not found. Assuming qt has it built-in])))
- BITCOIN_QT_CHECK(AC_SEARCH_LIBS([jpeg_create_decompress] ,[qtjpeg jpeg],,AC_MSG_WARN([libjpeg not found. Assuming qt has it built-in])))
if test x$bitcoin_cv_qt58 = xno; then
BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in])))
BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre16_exec], [qtpcre pcre16],,AC_MSG_WARN([libpcre16 not found. Assuming qt has it built-in])))
diff --git a/build_msvc/README.md b/build_msvc/README.md
index d6543e6d67..2e93979aca 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -44,7 +44,7 @@ The instructions below use `vcpkg` to install the dependencies.
- Use Python to generate *.vcxproj from Makefile
```
- PS >python msvc-autogen.py
+ PS >py -3 msvc-autogen.py
```
- Build in Visual Studio.
diff --git a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
index 9b6a298859..e64614c09d 100644
--- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
+++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
@@ -24,6 +24,7 @@
<ClCompile Include="..\..\src\bench\examples.cpp" />
<ClCompile Include="..\..\src\bench\lockedpool.cpp" />
<ClCompile Include="..\..\src\bench\mempool_eviction.cpp" />
+ <ClCompile Include="..\..\src\bench\rpc_blockchain.cpp" />
<ClCompile Include="..\..\src\bench\rpc_mempool.cpp" />
<ClCompile Include="..\..\src\bench\merkle_root.cpp" />
<ClCompile Include="..\..\src\bench\rollingbloom.cpp" />
diff --git a/configure.ac b/configure.ac
index 1d5a41ab9a..118d52d423 100644
--- a/configure.ac
+++ b/configure.ac
@@ -148,7 +148,8 @@ AC_ARG_ENABLE([extended-functional-tests],
[use_extended_functional_tests=no])
AC_ARG_ENABLE([fuzz],
- AS_HELP_STRING([--enable-fuzz],[enable building of fuzz targets (default no)]),
+ AS_HELP_STRING([--enable-fuzz],
+ [enable building of fuzz targets (default no). enabling this will disable all other targets]),
[enable_fuzz=$enableval],
[enable_fuzz=no])
@@ -322,6 +323,7 @@ if test "x$enable_werror" = "xyes"; then
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
fi
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Werror=switch],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=switch"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=thread-safety-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety-analysis"],,[[$CXXFLAG_WERROR]])
fi
@@ -330,6 +332,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wextra],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wextra"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wformat],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wvla],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wswitch],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wswitch"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wformat-security],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]])
@@ -956,6 +959,29 @@ AC_SUBST(LEVELDB_CPPFLAGS)
AC_SUBST(LIBLEVELDB)
AC_SUBST(LIBMEMENV)
+dnl enable-fuzz should disable all other targets
+if test "x$enable_fuzz" = "xyes"; then
+ AC_MSG_WARN(enable-fuzz will disable all other targets)
+ build_bitcoin_utils=no
+ build_bitcoin_cli=no
+ build_bitcoin_tx=no
+ build_bitcoin_wallet=no
+ build_bitcoind=no
+ build_bitcoin_libs=no
+ bitcoin_enable_qt=no
+ bitcoin_enable_qt_test=no
+ bitcoin_enable_qt_dbus=no
+ enable_wallet=no
+ use_bench=no
+ use_upnp=no
+ use_zmq=no
+else
+ BITCOIN_QT_INIT
+
+ dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
+ BITCOIN_QT_CONFIGURE([$use_pkgconfig])
+fi
+
if test x$enable_wallet != xno; then
dnl Check for libdb_cxx only if wallet enabled
BITCOIN_FIND_BDB48
@@ -968,12 +994,27 @@ if test x$use_upnp != xno; then
[AC_CHECK_LIB([miniupnpc], [upnpDiscover], [MINIUPNPC_LIBS=-lminiupnpc], [have_miniupnpc=no])],
[have_miniupnpc=no]
)
+dnl The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
+dnl with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
+if test x$have_miniupnpc != xno; then
+ AC_MSG_CHECKING([whether miniUPnPc API version is supported])
+ AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[
+ @%:@include <miniupnpc/miniupnpc.h>
+ ]], [[
+ #if MINIUPNPC_API_VERSION >= 10
+ // Everything is okay
+ #else
+ # error miniUPnPc API version is too old
+ #endif
+ ]])],[
+ AC_MSG_RESULT(yes)
+ ],[
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN([miniUPnPc API version < 10 is unsupported, disabling UPnP support.])
+ have_miniupnpc=no
+ ])
+fi
fi
-
-BITCOIN_QT_INIT
-
-dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
-BITCOIN_QT_CONFIGURE([$use_pkgconfig])
if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then
use_boost=no
@@ -1366,9 +1407,10 @@ dnl enable upnp support
AC_MSG_CHECKING([whether to build with support for UPnP])
if test x$have_miniupnpc = xno; then
if test x$use_upnp = xyes; then
- AC_MSG_ERROR("UPnP requested but cannot be built. use --without-miniupnpc")
+ AC_MSG_ERROR("UPnP requested but cannot be built. Use --without-miniupnpc.")
fi
AC_MSG_RESULT(no)
+ use_upnp=no
else
if test x$use_upnp != xno; then
AC_MSG_RESULT(yes)
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 7729dd7257..dd35d862c9 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -67,8 +67,6 @@ ALLOWED_LIBRARIES = {
'ld-linux-armhf.so.3', # 32-bit ARM dynamic linker
'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker
# bitcoin-qt only
-'libX11-xcb.so.1', # part of X11
-'libX11.so.6', # part of X11
'libxcb.so.1', # part of X11
'libfontconfig.so.1', # font support
'libfreetype.so.6', # font parsing
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
new file mode 100644
index 0000000000..4dfa1729a5
--- /dev/null
+++ b/contrib/guix/README.md
@@ -0,0 +1,229 @@
+# Bootstrappable Bitcoin Core Builds
+
+This directory contains the files necessary to perform bootstrappable Bitcoin
+Core builds.
+
+[Bootstrappability][b17e] furthers our binary security guarantees by allowing us
+to _audit and reproduce_ our toolchain instead of blindly _trusting_ binary
+downloads.
+
+We achieve bootstrappability by using Guix as a functional package manager.
+
+## Requirements
+
+Conservatively, a x86_64 machine with:
+
+- 2 or more logical cores
+- 4GB of free disk space on the partition that /gnu/store will reside in
+- 24GB of free disk space on the partition that the Bitcoin Core git repository
+ resides in
+
+> Note: these requirements are slightly less onerous than those of Gitian builds
+
+## Setup
+
+### Installing Guix
+
+If you're just testing this out, you can use the
+[Dockerfile][fanquake/guix-docker] for convenience. It automatically speeds up
+your builds by [using substitutes](#speeding-up-builds-with-substitute-servers).
+If you don't want this behaviour, refer to the [next
+section](#choosing-your-security-model).
+
+Otherwise, follow the [Guix installation guide][guix/bin-install].
+
+> Note: For those who like to keep their filesystems clean, Guix is designed to
+> be very standalone and _will not_ conflict with your system's package
+> manager/existing setup. It _only_ touches `/var/guix`, `/gnu`, and
+> `~/.config/guix`.
+
+### Choosing your security model
+
+Guix allows us to achieve better binary security by using our CPU time to build
+everything from scratch. However, it doesn't sacrifice user choice in pursuit of
+this: users can decide whether or not to bootstrap and to use substitutes.
+
+After installation, you may want to consider [adding substitute
+servers](#speeding-up-builds-with-substitute-servers) to speed up your build if
+that fits your security model (say, if you're just testing that this works).
+This is skippable if you're using the [Dockerfile][fanquake/guix-docker].
+
+If you prefer not to use any substitutes, make sure to set
+`ADDITIONAL_GUIX_ENVIRONMENT_FLAGS` like the following snippet. The first build
+will take a while, but the resulting packages will be cached for future builds.
+
+```sh
+export ADDITIONAL_GUIX_ENVIRONMENT_FLAGS='--no-substitutes'
+```
+
+Likewise, to perform a bootstrapped build (takes even longer):
+
+```sh
+export ADDITIONAL_GUIX_ENVIRONMENT_FLAGS='--bootstrap --no-substitutes'
+```
+
+### Using the right Guix
+
+Once Guix is installed, deploy our patched version into your current Guix
+profile. The changes there are slowly being upstreamed.
+
+```sh
+guix pull --url=https://github.com/dongcarl/guix.git \
+ --commit=82c77e52b8b46e0a3aad2cb12307c2e30547deec \
+ --max-jobs=4 # change accordingly
+```
+
+Make sure that you are using your current profile. (You are prompted to do this
+at the end of the `guix pull`)
+
+```bash
+export PATH="${HOME}/.config/guix/current/bin${PATH:+:}$PATH"
+```
+
+> Note: There is ongoing work to eliminate this entire section using Guix
+> [inferiors][guix/inferiors] and [channels][guix/channels].
+
+## Usage
+
+### As a Development Environment
+
+For a Bitcoin Core depends development environment, simply invoke
+
+```sh
+guix environment --manifest=contrib/guix/manifest.scm
+```
+
+And you'll land back in your shell with all the build dependencies required for
+a `depends` build injected into your environment.
+
+### As a Tool for Deterministic Builds
+
+From the top of a clean Bitcoin Core repository:
+
+```sh
+./contrib/guix/guix-build.sh
+```
+
+After the build finishes successfully (check the status code please), compare
+hashes:
+
+```sh
+find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
+```
+
+#### Recognized environment variables
+
+* _**HOSTS**_
+
+ Override the space-separated list of platform triples for which to perform a
+ bootstrappable build. _(defaults to "i686-linux-gnu x86\_64-linux-gnu
+ arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu")_
+
+ > Windows and OS X platform triplet support are WIP.
+
+* _**SOURCES_PATH**_
+
+ Set the depends tree download cache for sources. This is passed through to the
+ depends tree. Setting this to the same directory across multiple builds of the
+ depends tree can eliminate unnecessary redownloading of package sources.
+
+* _**MAX_JOBS**_
+
+ Override the maximum number of jobs to run simultaneously, you might want to
+ do so on a memory-limited machine. This may be passed to `make` as in `make
+ --jobs="$MAX_JOBS"` or `xargs` as in `xargs -P"$MAX_JOBS"`. _(defaults to the
+ value of `nproc` outside the container)_
+
+* _**SOURCE_DATE_EPOCH**_
+
+ Override the reference UNIX timestamp used for bit-for-bit reproducibility,
+ the variable name conforms to [standard][r12e/source-date-epoch]. _(defaults
+ to the output of `$(git log --format=%at -1)`)_
+
+* _**V**_
+
+ If non-empty, will pass `V=1` to all `make` invocations, making `make` output
+ verbose.
+
+* _**ADDITIONAL_GUIX_ENVIRONMENT_FLAGS**_
+
+ Additional flags to be passed to `guix environment`. For a fully-bootstrapped
+ build, set this to `--bootstrap --no-substitutes` (refer to the [security
+ model section](#choosing-your-security-model) for more details). Note that a
+ fully-bootstrapped build will take quite a long time on the first run.
+
+## Tips and Tricks
+
+### Speeding up builds with substitute servers
+
+_This whole section is automatically done in the convenience
+[Dockerfiles][fanquake/guix-docker]_
+
+For those who are used to life in the fast _(and trustful)_ lane, you can use
+[substitute servers][guix/substitutes] to enable binary downloads of packages.
+
+> For those who only want to use substitutes from the official Guix build farm
+> and have authorized the build farm's signing key during Guix's installation,
+> you don't need to do anything.
+
+#### Authorize the signing keys
+
+For the official Guix build farm at https://ci.guix.gnu.org, run as root:
+
+```
+guix archive --authorize < ~root/.config/guix/current/share/guix/ci.guix.gnu.org.pub
+```
+
+For dongcarl's substitute server at https://guix.carldong.io, run as root:
+
+```sh
+wget -qO- 'https://guix.carldong.io/signing-key.pub' | guix archive --authorize
+```
+
+#### Use the substitute servers
+
+The official Guix build farm at https://ci.guix.gnu.org is automatically used
+unless the `--no-substitutes` flag is supplied.
+
+This can be overridden for all `guix` invocations by passing the
+`--substitute-urls` option to your invocation of `guix-daemon`. This can also be
+overridden on a call-by-call basis by passing the same `--substitute-urls`
+option to client tools such at `guix environment`.
+
+To use dongcarl's substitute server for Bitcoin Core builds after having
+[authorized his signing key](#authorize-the-signing-keys):
+
+```
+export ADDITIONAL_GUIX_ENVIRONMENT_FLAGS='--substitute-urls="https://guix.carldong.io https://ci.guix.gnu.org"'
+```
+
+## FAQ
+
+### How can I trust the binary installation?
+
+As mentioned at the bottom of [this manual page][guix/bin-install]:
+
+> The binary installation tarballs can be (re)produced and verified simply by
+> running the following command in the Guix source tree:
+>
+> make guix-binary.x86_64-linux.tar.xz
+
+### When will Guix be packaged in debian?
+
+Vagrant Cascadian has been making good progress on this
+[here][debian/guix-package]. We have all the pieces needed to put up an APT
+repository and will likely put one up soon.
+
+[b17e]: http://bootstrappable.org/
+[r12e/source-date-epoch]: https://reproducible-builds.org/docs/source-date-epoch/
+
+[guix/install.sh]: https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh
+[guix/bin-install]: https://www.gnu.org/software/guix/manual/en/html_node/Binary-Installation.html
+[guix/env-setup]: https://www.gnu.org/software/guix/manual/en/html_node/Build-Environment-Setup.html
+[guix/substitutes]: https://www.gnu.org/software/guix/manual/en/html_node/Substitutes.html
+[guix/substitute-server-auth]: https://www.gnu.org/software/guix/manual/en/html_node/Substitute-Server-Authorization.html
+[guix/inferiors]: https://www.gnu.org/software/guix/manual/en/html_node/Inferiors.html
+[guix/channels]: https://www.gnu.org/software/guix/manual/en/html_node/Channels.html
+
+[debian/guix-package]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=850644
+[fanquake/guix-docker]: https://github.com/fanquake/core-review/tree/master/guix
diff --git a/contrib/guix/guix-build.sh b/contrib/guix/guix-build.sh
new file mode 100755
index 0000000000..f8ba8c7ed2
--- /dev/null
+++ b/contrib/guix/guix-build.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Determine the maximum number of jobs to run simultaneously (overridable by
+# environment)
+MAX_JOBS="${MAX_JOBS:-$(nproc)}"
+
+# Download the depends sources now as we won't have internet access in the build
+# container
+make -C "${PWD}/depends" -j"$MAX_JOBS" download ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
+
+# Determine the reference time used for determinism (overridable by environment)
+SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log --format=%at -1)}"
+
+# Deterministically build Bitcoin Core for HOSTs (overriable by environment)
+for host in ${HOSTS=i686-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu}; do
+
+ # Display proper warning when the user interrupts the build
+ trap 'echo "** INT received while building ${host}, you may want to clean up the relevant output and distsrc-* directories before rebuilding"' INT
+
+ # Run the build script 'contrib/guix/libexec/build.sh' in the build
+ # container specified by 'contrib/guix/manifest.scm'
+ # shellcheck disable=SC2086
+ guix environment --manifest="${PWD}/contrib/guix/manifest.scm" \
+ --container \
+ --pure \
+ --no-cwd \
+ --share="$PWD"=/bitcoin \
+ ${SOURCES_PATH:+--share="$SOURCES_PATH"} \
+ ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
+ -- env HOST="$host" \
+ MAX_JOBS="$MAX_JOBS" \
+ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \
+ ${V:+V=1} \
+ ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
+ bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
+
+done
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
new file mode 100644
index 0000000000..56b972a5cb
--- /dev/null
+++ b/contrib/guix/libexec/build.sh
@@ -0,0 +1,206 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Check that environment variables assumed to be set by the environment are set
+echo "Building for platform triple ${HOST:?not set} with reference timestamp ${SOURCE_DATE_EPOCH:?not set}..."
+echo "At most ${MAX_JOBS:?not set} jobs will run at once..."
+
+#####################
+# Environment Setup #
+#####################
+
+# The depends folder also serves as a base-prefix for depends packages for
+# $HOSTs after successfully building.
+BASEPREFIX="${PWD}/depends"
+
+# Setup an output directory for our build
+OUTDIR="${OUTDIR:-${PWD}/output}"
+[ -e "$OUTDIR" ] || mkdir -p "$OUTDIR"
+
+# Setup the directory where our Bitcoin Core build for HOST will occur
+DISTSRC="${DISTSRC:-${PWD}/distsrc-${HOST}}"
+if [ -e "$DISTSRC" ]; then
+ echo "DISTSRC directory '${DISTSRC}' exists, probably because of previous builds... Aborting..."
+ exit 1
+else
+ mkdir -p "$DISTSRC"
+fi
+
+# Given a package name and an output name, return the path of that output in our
+# current guix environment
+store_path() {
+ grep --extended-regexp "/[^-]{32}-${1}-cross-${HOST}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \
+ | head --lines=1 \
+ | sed --expression='s|^[[:space:]]*"||' \
+ --expression='s|"[[:space:]]*$||'
+}
+
+# Determine output paths to use in CROSS_* environment variables
+CROSS_GLIBC="$(store_path glibc)"
+CROSS_GLIBC_STATIC="$(store_path glibc static)"
+CROSS_KERNEL="$(store_path linux-libre-headers)"
+CROSS_GCC="$(store_path gcc)"
+
+# Set environment variables to point Guix's cross-toolchain to the right
+# includes/libs for $HOST
+export CROSS_C_INCLUDE_PATH="${CROSS_GCC}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
+export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
+export CROSS_LIBRARY_PATH="${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib:${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_KERNEL}/lib"
+
+# Disable Guix ld auto-rpath behavior
+export GUIX_LD_WRAPPER_DISABLE_RPATH=yes
+
+# Make /usr/bin if it doesn't exist
+[ -e /usr/bin ] || mkdir -p /usr/bin
+
+# Symlink file and env to a conventional path
+[ -e /usr/bin/file ] || ln -s --no-dereference "$(command -v file)" /usr/bin/file
+[ -e /usr/bin/env ] || ln -s --no-dereference "$(command -v env)" /usr/bin/env
+
+# Determine the correct value for -Wl,--dynamic-linker for the current $HOST
+glibc_dynamic_linker=$(
+ case "$HOST" in
+ i686-linux-gnu) echo /lib/ld-linux.so.2 ;;
+ x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;;
+ arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;;
+ aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;;
+ riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;;
+ *) exit 1 ;;
+ esac
+)
+
+# Environment variables for determinism
+export QT_RCC_TEST=1
+export QT_RCC_SOURCE_DATE_OVERRIDE=1
+export TAR_OPTIONS="--owner=0 --group=0 --numeric-owner --mtime='@${SOURCE_DATE_EPOCH}' --sort=name"
+export TZ="UTC"
+
+####################
+# Depends Building #
+####################
+
+# Build the depends tree, overriding variables that assume multilib gcc
+make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
+ ${V:+V=1} \
+ ${SOURCES_PATH+SOURCES_PATH="$SOURCES_PATH"} \
+ i686_linux_CC=i686-linux-gnu-gcc \
+ i686_linux_CXX=i686-linux-gnu-g++ \
+ i686_linux_AR=i686-linux-gnu-ar \
+ i686_linux_RANLIB=i686-linux-gnu-ranlib \
+ i686_linux_NM=i686-linux-gnu-nm \
+ i686_linux_STRIP=i686-linux-gnu-strip \
+ x86_64_linux_CC=x86_64-linux-gnu-gcc \
+ x86_64_linux_CXX=x86_64-linux-gnu-g++ \
+ x86_64_linux_AR=x86_64-linux-gnu-ar \
+ x86_64_linux_RANLIB=x86_64-linux-gnu-ranlib \
+ x86_64_linux_NM=x86_64-linux-gnu-nm \
+ x86_64_linux_STRIP=x86_64-linux-gnu-strip \
+ qt_config_opts_i686_linux='-platform linux-g++ -xplatform bitcoin-linux-g++'
+
+
+###########################
+# Source Tarball Building #
+###########################
+
+# Create the source tarball and move it to "${OUTDIR}/src" if not already there
+if [ -z "$(find "${OUTDIR}/src" -name 'bitcoin-*.tar.gz')" ]; then
+ ./autogen.sh
+ env CONFIG_SITE="${BASEPREFIX}/${HOST}/share/config.site" ./configure --prefix=/
+ make dist GZIP_ENV='-9n' ${V:+V=1}
+ mkdir -p "${OUTDIR}/src"
+ mv "$(find "${PWD}" -name 'bitcoin-*.tar.gz')" "${OUTDIR}/src/"
+fi
+
+# Determine the full path to our source tarball
+SOURCEDIST="$(find "${OUTDIR}/src" -name 'bitcoin-*.tar.gz')"
+# Determine our distribution name (e.g. bitcoin-0.18.0)
+DISTNAME="$(basename "$SOURCEDIST" '.tar.gz')"
+
+###########################
+# Binary Tarball Building #
+###########################
+
+# Create a spec file to normalize ssp linking behaviour
+spec_file="$(mktemp)"
+cat << EOF > "$spec_file"
+*link_ssp:
+%{fstack-protector|fstack-protector-all|fstack-protector-strong|fstack-protector-explicit:}
+EOF
+
+# Similar flags to Gitian
+CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests"
+HOST_CFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
+HOST_CXXFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
+HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++"
+
+# Make $HOST-specific native binaries from depends available in $PATH
+export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
+(
+ cd "$DISTSRC"
+
+ # Extract the source tarball
+ tar --strip-components=1 -xf "${SOURCEDIST}"
+
+ # Configure this DISTSRC for $HOST
+ # shellcheck disable=SC2086
+ env CONFIG_SITE="${BASEPREFIX}/${HOST}/share/config.site" \
+ ./configure --prefix=/ \
+ --disable-ccache \
+ --disable-maintainer-mode \
+ --disable-dependency-tracking \
+ ${CONFIGFLAGS} \
+ CFLAGS="${HOST_CFLAGS}" \
+ CXXFLAGS="${HOST_CXXFLAGS}" \
+ LDFLAGS="${HOST_LDFLAGS}"
+
+ sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool
+
+ # Build Bitcoin Core
+ make --jobs="$MAX_JOBS" ${V:+V=1}
+
+ # Perform basic ELF security checks on a series of executables.
+ make -C src --jobs=1 check-security ${V:+V=1}
+ # Check that executables only contain allowed gcc, glibc and libstdc++
+ # version symbols for Linux distro back-compatibility.
+ make -C src --jobs=1 check-symbols ${V:+V=1}
+
+ # Setup the directory where our Bitcoin Core build for HOST will be
+ # installed. This directory will also later serve as the input for our
+ # binary tarballs.
+ INSTALLPATH="${PWD}/installed/${DISTNAME}"
+ mkdir -p "${INSTALLPATH}"
+ # Install built Bitcoin Core to $INSTALLPATH
+ make install DESTDIR="${INSTALLPATH}" ${V:+V=1}
+ (
+ cd installed
+
+ # Prune libtool and object archives
+ find . -name "lib*.la" -delete
+ find . -name "lib*.a" -delete
+
+ # Prune pkg-config files
+ rm -r "${DISTNAME}/lib/pkgconfig"
+
+ # Split binaries and libraries from their debug symbols
+ {
+ find "${DISTNAME}/bin" -type f -executable -print0
+ find "${DISTNAME}/lib" -type f -print0
+ } | xargs -0 -n1 -P"$MAX_JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg
+
+ cp "${DISTSRC}/doc/README.md" "${DISTNAME}/"
+
+ # Finally, deterministically produce {non-,}debug binary tarballs ready
+ # for release
+ find "${DISTNAME}" -not -name "*.dbg" -print0 \
+ | sort --zero-terminated \
+ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
+ | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \
+ || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 )
+ find "${DISTNAME}" -name "*.dbg" -print0 \
+ | sort --zero-terminated \
+ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
+ | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \
+ || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" && exit 1 )
+ )
+)
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
new file mode 100644
index 0000000000..ca11d7a0f0
--- /dev/null
+++ b/contrib/guix/manifest.scm
@@ -0,0 +1,158 @@
+(use-modules (gnu)
+ (gnu packages)
+ (gnu packages autotools)
+ (gnu packages base)
+ (gnu packages bash)
+ (gnu packages check)
+ (gnu packages commencement)
+ (gnu packages compression)
+ (gnu packages cross-base)
+ (gnu packages file)
+ (gnu packages gawk)
+ (gnu packages gcc)
+ (gnu packages linux)
+ (gnu packages perl)
+ (gnu packages pkg-config)
+ (gnu packages python)
+ (gnu packages shells)
+ (guix build-system trivial)
+ (guix gexp)
+ (guix packages)
+ (guix profiles)
+ (guix utils))
+
+(define (make-ssp-fixed-gcc xgcc)
+ "Given a XGCC package, return a modified package that uses the SSP function
+from glibc instead of from libssp.so. Taken from:
+http://www.linuxfromscratch.org/hlfs/view/development/chapter05/gcc-pass1.html"
+ (package
+ (inherit xgcc)
+ (arguments
+ (substitute-keyword-arguments (package-arguments xgcc)
+ ((#:make-flags flags)
+ `(cons "gcc_cv_libc_provides_ssp=yes" ,flags))))))
+
+(define (make-gcc-rpath-link xgcc)
+ "Given a XGCC package, return a modified package that replace each instance of
+-rpath in the default system spec that's inserted by Guix with -rpath-link"
+ (package
+ (inherit xgcc)
+ (arguments
+ (substitute-keyword-arguments (package-arguments xgcc)
+ ((#:phases phases)
+ `(modify-phases ,phases
+ (add-after 'pre-configure 'replace-rpath-with-rpath-link
+ (lambda _
+ (substitute* (cons "gcc/config/rs6000/sysv4.h"
+ (find-files "gcc/config"
+ "^gnu-user.*\\.h$"))
+ (("-rpath=") "-rpath-link="))
+ #t))))))))
+
+(define (make-cross-toolchain target
+ base-gcc-for-libc
+ base-kernel-headers
+ base-libc
+ base-gcc)
+ "Create a cross-compilation toolchain package for TARGET"
+ (let* ((xbinutils (cross-binutils target))
+ ;; 1. Build a cross-compiling gcc without targeting any libc, derived
+ ;; from BASE-GCC-FOR-LIBC
+ (xgcc-sans-libc (cross-gcc target
+ #:xgcc base-gcc-for-libc
+ #:xbinutils xbinutils))
+ ;; 2. Build cross-compiled kernel headers with XGCC-SANS-LIBC, derived
+ ;; from BASE-KERNEL-HEADERS
+ (xkernel (cross-kernel-headers target
+ base-kernel-headers
+ xgcc-sans-libc
+ xbinutils))
+ ;; 3. Build a cross-compiled libc with XGCC-SANS-LIBC and XKERNEL,
+ ;; derived from BASE-LIBC
+ (xlibc (cross-libc target
+ base-libc
+ xgcc-sans-libc
+ xbinutils
+ xkernel))
+ ;; 4. Build a cross-compiling gcc targeting XLIBC, derived from
+ ;; BASE-GCC
+ (xgcc (cross-gcc target
+ #:xgcc base-gcc
+ #:xbinutils xbinutils
+ #:libc xlibc)))
+ ;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and
+ ;; XGCC
+ (package
+ (name (string-append target "-toolchain"))
+ (version (package-version xgcc))
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments '(#:builder (begin (mkdir %output) #t)))
+ (propagated-inputs
+ `(("binutils" ,xbinutils)
+ ("libc" ,xlibc)
+ ("libc:static" ,xlibc "static")
+ ("gcc" ,xgcc)))
+ (synopsis (string-append "Complete GCC tool chain for " target))
+ (description (string-append "This package provides a complete GCC tool
+chain for " target " development."))
+ (home-page (package-home-page xgcc))
+ (license (package-license xgcc)))))
+
+(define* (make-bitcoin-cross-toolchain target
+ #:key
+ (base-gcc-for-libc gcc-5)
+ (base-kernel-headers linux-libre-headers-4.19)
+ (base-libc glibc-2.27)
+ (base-gcc (make-gcc-rpath-link
+ (make-ssp-fixed-gcc gcc-9))))
+ "Convienience wrapper around MAKE-CROSS-TOOLCHAIN with default values
+desirable for building Bitcoin Core release binaries."
+ (make-cross-toolchain target
+ base-gcc-for-libc
+ base-kernel-headers
+ base-libc
+ base-gcc))
+
+(packages->manifest
+ (list ;; The Basics
+ bash-minimal
+ which
+ coreutils
+ util-linux
+ ;; File(system) inspection
+ file
+ grep
+ diffutils
+ findutils
+ ;; File transformation
+ patch
+ gawk
+ sed
+ ;; Compression and archiving
+ tar
+ bzip2
+ gzip
+ xz
+ zlib
+ ;; Build tools
+ gnu-make
+ libtool
+ autoconf
+ automake
+ pkg-config
+ ;; Scripting
+ perl
+ python-3.7
+ ;; Native gcc 9 toolchain targeting glibc 2.27
+ (make-gcc-toolchain gcc-9 glibc-2.27)
+ ;; Cross gcc 9 toolchains targeting glibc 2.27
+ (make-bitcoin-cross-toolchain "i686-linux-gnu")
+ (make-bitcoin-cross-toolchain "x86_64-linux-gnu")
+ (make-bitcoin-cross-toolchain "aarch64-linux-gnu")
+ (make-bitcoin-cross-toolchain "arm-linux-gnueabihf")
+ ;; The glibc 2.27 for riscv64 needs gcc 7 to successfully build (see:
+ ;; https://www.gnu.org/software/gcc/gcc-7/changes.html#riscv). The final
+ ;; toolchain is still a gcc 9 toolchain targeting glibc 2.27.
+ (make-bitcoin-cross-toolchain "riscv64-linux-gnu"
+ #:base-gcc-for-libc gcc-7)))
diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk
index b811f84a38..4784381915 100644
--- a/depends/packages/expat.mk
+++ b/depends/packages/expat.mk
@@ -1,11 +1,11 @@
package=expat
-$(package)_version=2.2.6
-$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_2_6/
+$(package)_version=2.2.7
+$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_2_7/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2
+$(package)_sha256_hash=cbc9102f4a31a8dafd42d642e9a3aa31e79a0aedaa1f6efd2795ebc83174ec18
define $(package)_set_vars
- $(package)_config_opts=--disable-shared --without-docbook
+ $(package)_config_opts=--disable-shared --without-docbook --without-tests --without-examples
$(package)_config_opts_linux=--with-pic
endef
diff --git a/depends/packages/libX11.mk b/depends/packages/libX11.mk
deleted file mode 100644
index f46bd9219e..0000000000
--- a/depends/packages/libX11.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-package=libX11
-$(package)_version=1.6.2
-$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=2aa027e837231d2eeea90f3a4afe19948a6eb4c8b2bec0241eba7dbc8106bd16
-$(package)_dependencies=libxcb xtrans xextproto xproto
-
-define $(package)_set_vars
- # See libXext for --disable-malloc0returnsnull rationale.
- $(package)_config_opts=--disable-xkb --disable-static --disable-malloc0returnsnull
- $(package)_config_opts_linux=--with-pic
-endef
-
-define $(package)_preprocess_cmds
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
- $(MAKE)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
-
-define $(package)_postprocess_cmds
- rm lib/*.la
-endef
diff --git a/depends/packages/libXext.mk b/depends/packages/libXext.mk
deleted file mode 100644
index 77f32a340e..0000000000
--- a/depends/packages/libXext.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-package=libXext
-$(package)_version=1.3.3
-$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=b518d4d332231f313371fdefac59e3776f4f0823bcb23cf7c7305bfb57b16e35
-$(package)_dependencies=xproto xextproto libX11 libXau
-
-define $(package)_set_vars
- # A number of steps in the autoconfig process implicitly assume that the build
- # system and the host system are the same. For example, library components
- # want to build and run test programs to determine the behavior of certain
- # host system elements. This is clearly impossible when crosscompiling. To
- # work around these issues, the --enable-malloc0returnsnull (or
- # --disable-malloc0returnsnull, depending on the host system) must be passed
- # to configure.
- # -- https://www.x.org/wiki/CrossCompilingXorg/
- #
- # Concretely, between the releases of libXext 1.3.2 and 1.3.3,
- # XORG_CHECK_MALLOC_ZERO from xorg-macros was changed to use the autoconf
- # cache, expecting cross-compilation environments to seed this cache as there
- # is no single correct value when cross compiling (think uclibc, musl, etc.).
- # You can see the actual change in commit 72fdc868b56fe2b7bdc9a69872651baeca72
- # in the freedesktop/xorg-macros repo.
- #
- # As a result of this change, if we don't seed the cache and we don't use
- # either --{en,dis}able-malloc0returnsnull, the AC_RUN_IFELSE block has no
- # optional action-if-cross-compiling argument and configure prints an error
- # message and exits as documented in the autoconf manual. Prior to this
- # commit, the AC_RUN_IFELSE block had an action-if-cross-compiling argument
- # which set the more pessimistic default value MALLOC_ZERO_RETURNS_NULL=yes.
- # This is why the flag was not required prior to libXext 1.3.3.
- $(package)_config_opts=--disable-static --disable-malloc0returnsnull
-endef
-
-define $(package)_preprocess_cmds
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
- $(MAKE)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
-
-define $(package)_postprocess_cmds
- rm lib/*.la
-endef
diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk
index 9402951826..c497c5ac20 100644
--- a/depends/packages/libxcb.mk
+++ b/depends/packages/libxcb.mk
@@ -3,7 +3,7 @@ $(package)_version=1.10
$(package)_download_path=https://xcb.freedesktop.org/dist
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5
-$(package)_dependencies=xcb_proto libXau xproto
+$(package)_dependencies=xcb_proto libXau
define $(package)_set_vars
$(package)_config_opts=--disable-static
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 93f0918fe9..9edcd1eb38 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -3,7 +3,7 @@ packages:=boost openssl libevent
qt_native_packages = native_protobuf
qt_packages = qrencode protobuf zlib
-qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans
+qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig
rapidcheck_packages = rapidcheck
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 23cde9ee6d..2610c1e748 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -5,10 +5,10 @@ $(package)_suffix=opensource-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
$(package)_sha256_hash=36dd9574f006eaa1e5af780e4b33d11fe39d09fd7c12f3b9d83294174bd28f00
$(package)_dependencies=openssl zlib
-$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext
+$(package)_linux_dependencies=freetype fontconfig libxcb
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib network widgets gui plugins testlib
-$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch
+$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
$(package)_qttranslations_sha256_hash=b36da7d93c3ab6fca56b32053bb73bc619c8b192bb89b74e3bcde2705f1c2a14
@@ -35,9 +35,11 @@ $(package)_config_opts += -no-freetype
$(package)_config_opts += -no-gif
$(package)_config_opts += -no-glib
$(package)_config_opts += -no-icu
+$(package)_config_opts += -no-ico
$(package)_config_opts += -no-iconv
$(package)_config_opts += -no-kms
$(package)_config_opts += -no-linuxfb
+$(package)_config_opts += -no-libjpeg
$(package)_config_opts += -no-libudev
$(package)_config_opts += -no-mtdev
$(package)_config_opts += -no-openvg
@@ -63,26 +65,42 @@ $(package)_config_opts += -pch
$(package)_config_opts += -pkg-config
$(package)_config_opts += -prefix $(host_prefix)
$(package)_config_opts += -qt-libpng
-$(package)_config_opts += -qt-libjpeg
$(package)_config_opts += -qt-pcre
$(package)_config_opts += -qt-harfbuzz
$(package)_config_opts += -system-zlib
$(package)_config_opts += -static
$(package)_config_opts += -silent
$(package)_config_opts += -v
+$(package)_config_opts += -no-feature-bearermanagement
+$(package)_config_opts += -no-feature-colordialog
+$(package)_config_opts += -no-feature-commandlineparser
+$(package)_config_opts += -no-feature-concurrent
$(package)_config_opts += -no-feature-dial
+$(package)_config_opts += -no-feature-filesystemwatcher
+$(package)_config_opts += -no-feature-fontcombobox
$(package)_config_opts += -no-feature-ftp
+$(package)_config_opts += -no-feature-image_heuristic_mask
+$(package)_config_opts += -no-feature-keysequenceedit
$(package)_config_opts += -no-feature-lcdnumber
$(package)_config_opts += -no-feature-pdf
-$(package)_config_opts += -no-feature-printer
$(package)_config_opts += -no-feature-printdialog
-$(package)_config_opts += -no-feature-concurrent
+$(package)_config_opts += -no-feature-printer
+$(package)_config_opts += -no-feature-printpreviewdialog
+$(package)_config_opts += -no-feature-printpreviewwidget
+$(package)_config_opts += -no-feature-regularexpression
+$(package)_config_opts += -no-feature-sessionmanager
$(package)_config_opts += -no-feature-sql
$(package)_config_opts += -no-feature-statemachine
$(package)_config_opts += -no-feature-syntaxhighlighter
$(package)_config_opts += -no-feature-textbrowser
$(package)_config_opts += -no-feature-textodfwriter
+$(package)_config_opts += -no-feature-topleveldomain
$(package)_config_opts += -no-feature-udpsocket
+$(package)_config_opts += -no-feature-undocommand
+$(package)_config_opts += -no-feature-undogroup
+$(package)_config_opts += -no-feature-undostack
+$(package)_config_opts += -no-feature-undoview
+$(package)_config_opts += -no-feature-vnc
$(package)_config_opts += -no-feature-wizard
$(package)_config_opts += -no-feature-xml
@@ -98,8 +116,9 @@ endif
$(package)_config_opts_linux = -qt-xkbcommon-x11
$(package)_config_opts_linux += -qt-xcb
+$(package)_config_opts_linux += -no-xcb-xlib
+$(package)_config_opts_linux += -no-feature-xlib
$(package)_config_opts_linux += -system-freetype
-$(package)_config_opts_linux += -no-feature-sessionmanager
$(package)_config_opts_linux += -fontconfig
$(package)_config_opts_linux += -no-opengl
$(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++
@@ -156,11 +175,13 @@ define $(package)_preprocess_cmds
echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
patch -p1 -i $($(package)_patch_dir)/fix_riscv64_arch.patch &&\
+ patch -p1 -i $($(package)_patch_dir)/no-xlib.patch &&\
echo "QMAKE_LINK_OBJECT_MAX = 10" >> qtbase/mkspecs/win32-g++/qmake.conf &&\
echo "QMAKE_LINK_OBJECT_SCRIPT = object_script" >> qtbase/mkspecs/win32-g++/qmake.conf &&\
sed -i.old "s|QMAKE_CFLAGS = |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
sed -i.old "s|QMAKE_LFLAGS = |!host_build: QMAKE_LFLAGS = $($(package)_ldflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
- sed -i.old "s|QMAKE_CXXFLAGS = |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf
+ sed -i.old "s|QMAKE_CXXFLAGS = |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
+ sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf
endef
define $(package)_config_cmds
diff --git a/depends/packages/xextproto.mk b/depends/packages/xextproto.mk
deleted file mode 100644
index 157b76edf6..0000000000
--- a/depends/packages/xextproto.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-package=xextproto
-$(package)_version=7.3.0
-$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=f3f4b23ac8db9c3a9e0d8edb591713f3d70ef9c3b175970dd8823dfc92aa5bb0
-
-define $(package)_preprocess_cmds
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
-endef
-
-define $(package)_set_vars
-$(package)_config_opts=--disable-shared
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
- $(MAKE)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
diff --git a/depends/packages/xtrans.mk b/depends/packages/xtrans.mk
deleted file mode 100644
index 6201d1d270..0000000000
--- a/depends/packages/xtrans.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-package=xtrans
-$(package)_version=1.3.4
-$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/
-$(package)_file_name=$(package)-$($(package)_version).tar.bz2
-$(package)_sha256_hash=054d4ee3efd52508c753e9f7bc655ef185a29bd2850dd9e2fc2ccc33544f583a
-$(package)_dependencies=
-
-define $(package)_set_vars
- $(package)_config_opts_linux=--disable-docs --without-xmlto --without-fop --without-xsltproc
-endef
-
-define $(package)_preprocess_cmds
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
- $(MAKE)
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch
new file mode 100644
index 0000000000..fe82c2c73c
--- /dev/null
+++ b/depends/patches/qt/no-xlib.patch
@@ -0,0 +1,69 @@
+From 9563cef873ae82e06f60708d706d054717e801ce Mon Sep 17 00:00:00 2001
+From: Carl Dong <contact@carldong.me>
+Date: Thu, 18 Jul 2019 17:22:05 -0400
+Subject: [PATCH] Wrap xlib related code blocks in #if's
+
+They are not necessary to compile QT.
+---
+ qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
+index 7c62c2e2b3..c05c6c0a07 100644
+--- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
++++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
+@@ -49,7 +49,9 @@
+ #include <QtGui/QWindow>
+ #include <QtGui/QBitmap>
+ #include <QtGui/private/qguiapplication_p.h>
++#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
+ #include <X11/cursorfont.h>
++#endif
+ #include <xcb/xfixes.h>
+ #include <xcb/xcb_image.h>
+
+@@ -384,6 +386,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget)
+ w->setCursor(c, isBitmapCursor);
+ }
+
++#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
+ static int cursorIdForShape(int cshape)
+ {
+ int cursorId = 0;
+@@ -437,6 +440,7 @@ static int cursorIdForShape(int cshape)
+ }
+ return cursorId;
+ }
++#endif
+
+ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
+ {
+@@ -558,7 +562,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
+ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+ {
+ xcb_connection_t *conn = xcb_connection();
++#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
+ int cursorId = cursorIdForShape(cshape);
++#endif
+ xcb_cursor_t cursor = XCB_NONE;
+
+ // Try Xcursor first
+@@ -589,6 +595,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+ // Non-standard X11 cursors are created from bitmaps
+ cursor = createNonStandardCursor(cshape);
+
++#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
+ // Create a glpyh cursor if everything else failed
+ if (!cursor && cursorId) {
+ cursor = xcb_generate_id(conn);
+@@ -596,6 +603,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+ cursorId, cursorId + 1,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0);
+ }
++#endif
+
+ if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
+ const char *name = cursorNames[cshape];
+--
+2.22.0
+
diff --git a/doc/benchmarking.md b/doc/benchmarking.md
index 48f81cf6c1..8e3d88ab7a 100644
--- a/doc/benchmarking.md
+++ b/doc/benchmarking.md
@@ -2,10 +2,17 @@ Benchmarking
============
Bitcoin Core has an internal benchmarking framework, with benchmarks
-for cryptographic algorithms (e.g. SHA1, SHA256, SHA512, RIPEMD160), as well as the rolling bloom filter.
+for cryptographic algorithms (e.g. SHA1, SHA256, SHA512, RIPEMD160, Poly1305, ChaCha20), rolling bloom filter, coins selection,
+thread queue, wallet balance.
Running
---------------------
+
+For benchmarks purposes you only need to compile `bitcoin_bench`. Beware of configuring without `--enable-debug` as this would impact
+benchmarking by unlatching log printers and lock analysis.
+
+ make -C src bench_bitcoin
+
After compiling bitcoin-core, the benchmarks can be run with:
src/bench/bench_bitcoin
@@ -13,43 +20,29 @@ After compiling bitcoin-core, the benchmarks can be run with:
The output will look similar to:
```
# Benchmark, evals, iterations, total, min, max, median
-Base58CheckEncode, 5, 320000, 120.772, 7.49351e-05, 7.59374e-05, 7.54759e-05
-Base58Decode, 5, 800000, 122.833, 3.0467e-05, 3.11732e-05, 3.06304e-05
-Base58Encode, 5, 470000, 137.094, 5.81061e-05, 5.85109e-05, 5.84462e-05
-BenchLockedPool, 5, 530, 34.2023, 0.0128247, 0.0129613, 0.0129026
-CCheckQueueSpeedPrevectorJob, 5, 1400, 26.1762, 0.00365048, 0.00388629, 0.00367108
-CCoinsCaching, 5, 170000, 48.1074, 5.60229e-05, 5.72316e-05, 5.66214e-05
-CoinSelection, 5, 650, 34.6426, 0.0105801, 0.0107699, 0.010664
-DeserializeAndCheckBlockTest, 5, 160, 39.2084, 0.0483662, 0.0494199, 0.0490138
-DeserializeBlockTest, 5, 130, 23.8129, 0.0357731, 0.0373763, 0.0365858
-FastRandom_1bit, 5, 440000000, 38.1609, 1.72974e-08, 1.73882e-08, 1.73478e-08
-FastRandom_32bit, 5, 110000000, 72.8237, 1.29992e-07, 1.37014e-07, 1.30115e-07
-MempoolEviction, 5, 41000, 89.8883, 0.000432748, 0.000446857, 0.000438483
-PrevectorClear, 5, 5600, 47.9229, 0.00169952, 0.0017455, 0.00170315
-PrevectorDestructor, 5, 5700, 44.5498, 0.0015561, 0.00156977, 0.00156469
-RIPEMD160, 5, 440, 135.988, 0.0615496, 0.062268, 0.0617779
-RollingBloom, 5, 1500000, 36.5109, 4.80961e-06, 4.97463e-06, 4.85811e-06
-SHA1, 5, 570, 51.808, 0.018065, 0.0182623, 0.0181865
-SHA256, 5, 340, 8.31841, 0.00483231, 0.00499803, 0.00485486
-SHA256_32b, 5, 4700000, 10.469, 4.43441e-07, 4.47611e-07, 4.45223e-07
-SHA512, 5, 330, 33.3408, 0.02017, 0.0202554, 0.0201921
-SipHash_32b, 5, 40000000, 38.7088, 1.91103e-07, 1.96998e-07, 1.93792e-07
-Sleep100ms, 5, 10, 5.01062, 0.100131, 0.100368, 0.100147
-Trig, 5, 12000000, 5.95494, 9.78115e-08, 1.04354e-07, 9.80682e-08
-VerifyScriptBench, 5, 6300, 9.02493, 0.000285566, 0.000288433, 0.000286175
+AssembleBlock, 5, 700, 1.79954, 0.000510913, 0.000517018, 0.000514497
+...
```
Help
---------------------
-`-?` will print a list of options and exit:
- src/bench/bench_bitcoin -?
+ src/bench/bench_bitcoin --help
+
+To print options like scaling factor or per-benchmark filter.
Notes
---------------------
More benchmarks are needed for, in no particular order:
- Script Validation
-- CCoinDBView caching
- Coins database
- Memory pool
-- Wallet coin selection
+- Cuckoo Cache
+- P2P throughput
+
+Going Further
+--------------------
+
+To monitor Bitcoin Core performance more in depth (like reindex or IBD): https://github.com/chaincodelabs/bitcoinperf
+
+To generate Flame Graphs for Bitcoin Core: https://github.com/eklitzke/bitcoin/blob/flamegraphs/doc/flamegraphs.md
diff --git a/doc/bips.md b/doc/bips.md
index eb24ce6f66..3085fa424b 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -12,8 +12,8 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.18.0**):
* [`BIP 31`](https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki): The 'pong' protocol message (and the protocol version bump to 60001) has been implemented since **v0.6.1** ([PR #1081](https://github.com/bitcoin/bitcoin/pull/1081)).
* [`BIP 32`](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki): Hierarchical Deterministic Wallets has been implemented since **v0.13.0** ([PR #8035](https://github.com/bitcoin/bitcoin/pull/8035)).
* [`BIP 34`](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki): The rule that requires blocks to contain their height (number) in the coinbase input, and the introduction of version 2 blocks has been implemented since **v0.7.0**. The rule took effect for version 2 blocks as of *block 224413* (March 5th 2013), and version 1 blocks are no longer allowed since *block 227931* (March 25th 2013) ([PR #1526](https://github.com/bitcoin/bitcoin/pull/1526)).
-* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)).
-* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)).
+* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)). As of **v0.13.0**, this is only available for `NODE_BLOOM` (BIP 111) peers.
+* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)). Disabled by default since **v0.19.0**, can be enabled by the `-peerbloomfilters` option.
* [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)).
* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting **v0.17.0**, whether to send reject messages can be configured with the `-enablebip61` option, and support is deprecated as of **v0.18.0**.
* [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124).
diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md
index 88ecb8fe65..f8146b5d75 100644
--- a/doc/bitcoin-conf.md
+++ b/doc/bitcoin-conf.md
@@ -30,6 +30,21 @@ Network specific options can be:
- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`;
- prefixed with a chain name; e.g., `regtest.maxmempool=100`.
+Network specific options take precedence over non-network specific options.
+If multiple values for the same option are found with the same precedence, the
+first one is generally chosen.
+
+This means that given the following configuration, `regtest.rpcport` is set to `3000`:
+
+```
+regtest=1
+rpcport=2000
+regtest.rpcport=3000
+
+[regtest]
+rpcport=4000
+```
+
## Configuration File Path
The configuration file is not automatically created; you can create it using your favorite text editor. By default, the configuration file name is `bitcoin.conf` and it is located in the Bitcoin data directory, but both the Bitcoin data directory and the configuration file path may be changed using the `-datadir` and `-conf` command-line options.
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 54eca143c5..0b23ca0a2d 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -7,25 +7,24 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| --- | --- | --- | --- | --- | --- |
| Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | |
| Boost | [1.70.0](https://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | |
-| Clang | | [3.3+](https://llvm.org/releases/download.html) (C++11 support) | | | |
-| Expat | [2.2.6](https://libexpat.github.io/) | | No | Yes | |
+| Clang | | [3.3+](https://releases.llvm.org/download.html) (C++11 support) | | | |
+| Expat | [2.2.7](https://libexpat.github.io/) | | No | Yes | |
| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | |
| FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | |
| GCC | | [4.8+](https://gcc.gnu.org/) (C++11 support) | | | |
| HarfBuzz-NG | | | | | |
| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | |
-| libjpeg | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L65) |
-| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L64) |
+| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| librsvg | | | | | |
| MiniUPnPc | [2.0.20180203](http://miniupnp.free.fr/files) | | No | | |
| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | |
-| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L66) |
+| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| protobuf | [2.6.1](https://github.com/google/protobuf/releases) | | No | | |
| Python (tests) | | [3.5](https://www.python.org/downloads) | | | |
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | |
-| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L87) (Linux only) |
-| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L86) (Linux only) |
+| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
+| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | |
| zlib | [1.2.11](https://zlib.net/) | | | | No |
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index ecd720539e..39463dc6f8 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -375,7 +375,7 @@ reported in the debug.log file.
Re-architecting the core code so there are better-defined interfaces
between the various components is a goal, with any necessary locking
-done by the components (e.g. see the self-contained `CBasicKeyStore` class
+done by the components (e.g. see the self-contained `FillableSigningProvider` class
and its `cs_KeyStore` lock for example).
Threads
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
index f9221dde5b..3dc6be8b86 100644
--- a/doc/fuzzing.md
+++ b/doc/fuzzing.md
@@ -46,7 +46,7 @@ export AFLPATH=$PWD
To build Bitcoin Core using AFL instrumentation (this assumes that the
`AFLPATH` was set as above):
```
-./configure --disable-ccache --disable-shared --enable-tests --enable-fuzz --disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
+./configure --disable-ccache --disable-shared --enable-tests --enable-fuzz CC=${AFLPATH}/afl-gcc CXX=${AFLPATH}/afl-g++
export AFL_HARDEN=1
cd src/
make
@@ -83,7 +83,7 @@ found in the `compiler-rt` runtime libraries package).
To build all fuzz targets with libFuzzer, run
```
-./configure --disable-ccache --disable-wallet --disable-bench --with-utils=no --with-daemon=no --with-libs=no --with-gui=no --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
+./configure --disable-ccache --enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++
make
```
diff --git a/doc/release-notes-15993.md b/doc/release-notes-15993.md
new file mode 100644
index 0000000000..493c7126ee
--- /dev/null
+++ b/doc/release-notes-15993.md
@@ -0,0 +1,3 @@
+Build system changes
+--------------------
+The minimum supported miniUPnPc API version is set to 10. This keeps compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` packages. Please note, on Debian this package is still vulnerable to [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) (in jessie only) and [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) (both in jessie and in stretch).
diff --git a/doc/release-notes-16394.md b/doc/release-notes-16394.md
new file mode 100644
index 0000000000..f09cba4b6d
--- /dev/null
+++ b/doc/release-notes-16394.md
@@ -0,0 +1,4 @@
+RPC changes
+-----------
+`createwallet` now returns a warning if an empty string is used as an encryption password, and does not encrypt the wallet, instead of raising an error.
+This makes it easier to disable encryption but also specify other options when using the `bitcoin-cli` tool.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 9efb6cbabb..e0673ae7ee 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -19,8 +19,8 @@ Bitcoin Core version *version* is now available from:
<https://bitcoincore.org/bin/bitcoin-core-*version*/>
-This is a new major version release, including new features, various bugfixes
-and performance improvements, as well as updated translations.
+This release includes new features, various bug fixes and performance
+improvements, as well as updated translations.
Please report bugs using the issue tracker at GitHub:
@@ -42,30 +42,19 @@ Upgrading directly from a version of Bitcoin Core that has reached its EOL is
possible, but might take some time if the datadir needs to be migrated. Old
wallet versions of Bitcoin Core are generally supported.
-Downgrading warning
--------------------
-
-The chainstate database for this release is not compatible with previous
-releases, so if you run 0.15 and then decide to switch back to any
-older version, you will need to run the old release with the `-reindex-chainstate`
-option to rebuild the chainstate data structures in the old format.
-
-If your node has pruning enabled, this will entail re-downloading and
-processing the entire blockchain.
-
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
+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
-frequently tested on them.
+as frequently tested on them.
-From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is
+From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is
built using Qt 5.9.x, which doesn't support versions of macOS older than
-10.10. Additionally, Bitcoin Core does not yet change appearance when
+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
@@ -99,10 +88,30 @@ Low-level Changes section below.
`-limitancestorcount`, `-limitdescendantcount` and `-walletrejectlongchains`
command line arguments.
+Deprecated or removed RPCs
+--------------------------
+
+- The `totalFee` option of the `bumpfee` RPC has been deprecated and will be
+ removed in 0.20. To continue using this option start with
+ `-deprecatedrpc=totalFee`. See the `bumpfee` RPC help text for more details.
Low-level changes
=================
+RPC
+---
+
+
+Tests
+-----
+
+- The regression test chain, that can be enabled by the `-regtest` command line
+ flag, now requires transactions to not violate standard policy by default.
+ Making the default the same as for mainnet, makes it easier to test mainnet
+ behavior on regtest. Be reminded that the testnet still allows non-standard
+ txs by default and that the policy can be locally adjusted with the
+ `-acceptnonstdtxn` command line flag for both test chains.
+
Configuration
------------
diff --git a/doc/release-notes/release-notes-16152.md b/doc/release-notes/release-notes-16152.md
new file mode 100644
index 0000000000..9c77cb9ae5
--- /dev/null
+++ b/doc/release-notes/release-notes-16152.md
@@ -0,0 +1,7 @@
+P2P Changes
+-----------
+- The default value for the -peerbloomfilters configuration option (and, thus, NODE_BLOOM support) has been changed to false.
+ This resolves well-known DoS vectors in Bitcoin Core, especially for nodes with spinning disks. It is not anticipated that
+ this will result in a significant lack of availability of NODE_BLOOM-enabled nodes in the coming years, however, clients
+ which rely on the availability of NODE_BLOOM-supporting nodes on the P2P network should consider the process of migrating
+ to a more modern (and less trustful and privacy-violating) alternative over the coming years.
diff --git a/src/Makefile.am b/src/Makefile.am
index 39e8d3d689..ef5b1900d9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -143,7 +143,6 @@ BITCOIN_CORE_H = \
interfaces/wallet.h \
key.h \
key_io.h \
- keystore.h \
dbwrapper.h \
limitedmap.h \
logging.h \
@@ -175,14 +174,17 @@ BITCOIN_CORE_H = \
rpc/blockchain.h \
rpc/client.h \
rpc/protocol.h \
- rpc/server.h \
rpc/rawtransaction_util.h \
rpc/register.h \
+ rpc/request.h \
+ rpc/server.h \
rpc/util.h \
scheduler.h \
script/descriptor.h \
+ script/keyorigin.h \
script/sigcache.h \
script/sign.h \
+ script/signingprovider.h \
script/standard.h \
shutdown.h \
streams.h \
@@ -210,6 +212,7 @@ BITCOIN_CORE_H = \
util/rbf.h \
util/threadnames.h \
util/time.h \
+ util/translation.h \
util/url.h \
util/validation.h \
validation.h \
@@ -307,7 +310,7 @@ libbitcoin_server_a_SOURCES += dummywallet.cpp
endif
if ENABLE_ZMQ
-libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
+libbitcoin_zmq_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
@@ -351,6 +354,8 @@ crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
+ crypto/chacha_poly_aead.h \
+ crypto/chacha_poly_aead.cpp \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
@@ -446,7 +451,6 @@ libbitcoin_common_a_SOURCES = \
core_write.cpp \
key.cpp \
key_io.cpp \
- keystore.cpp \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
@@ -460,6 +464,7 @@ libbitcoin_common_a_SOURCES = \
scheduler.cpp \
script/descriptor.cpp \
script/sign.cpp \
+ script/signingprovider.cpp \
script/standard.cpp \
versionbitsinfo.cpp \
warnings.cpp \
@@ -481,7 +486,7 @@ libbitcoin_util_a_SOURCES = \
interfaces/handler.cpp \
logging.cpp \
random.cpp \
- rpc/protocol.cpp \
+ rpc/request.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
@@ -613,7 +618,7 @@ bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREAD
# bitcoinconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
-libbitcoinconsensus_la_SOURCES = $(crypto_libbitcoin_crypto_base_a_SOURCES) $(libbitcoin_consensus_a_SOURCES)
+libbitcoinconsensus_la_SOURCES = support/cleanse.cpp $(crypto_libbitcoin_crypto_base_a_SOURCES) $(libbitcoin_consensus_a_SOURCES)
if GLIBC_BACK_COMPAT
libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 1b01b50b07..e421b377a0 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -24,11 +24,13 @@ bench_bench_bitcoin_SOURCES = \
bench/examples.cpp \
bench/rollingbloom.cpp \
bench/chacha20.cpp \
+ bench/chacha_poly_aead.cpp \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
+ bench/rpc_blockchain.cpp \
bench/rpc_mempool.cpp \
bench/util_time.cpp \
bench/verify_script.cpp \
diff --git a/src/banman.cpp b/src/banman.cpp
index 47d64a8f31..37fca7dd82 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -9,12 +9,13 @@
#include <ui_interface.h>
#include <util/system.h>
#include <util/time.h>
+#include <util/translation.h>
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
- if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist..."));
+ if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist...").translated);
int64_t n_start = GetTimeMillis();
m_is_dirty = false;
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 8eea96d930..d0d7c03ee1 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -21,14 +21,14 @@ static void SetupBenchArgs()
{
SetupHelpOptions(gArgs);
- gArgs.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-scaling=<n>", strprintf("Scaling factor for benchmark's runtime (default: %u)", DEFAULT_BENCH_SCALING), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-printer=(console|plot)", strprintf("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)", DEFAULT_BENCH_PRINTER), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-scaling=<n>", strprintf("Scaling factor for benchmark's runtime (default: %u)", DEFAULT_BENCH_SCALING), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-printer=(console|plot)", strprintf("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)", DEFAULT_BENCH_PRINTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
int main(int argc, char** argv)
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 1041a22303..39cab092cf 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -5,7 +5,7 @@
#include <bench/bench.h>
#include <coins.h>
#include <policy/policy.h>
-#include <wallet/crypter.h>
+#include <script/signingprovider.h>
#include <vector>
@@ -17,7 +17,7 @@
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
-SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
+SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
@@ -55,7 +55,7 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CCoinsCaching(benchmark::State& state)
{
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp
new file mode 100644
index 0000000000..f5f7297490
--- /dev/null
+++ b/src/bench/chacha_poly_aead.cpp
@@ -0,0 +1,123 @@
+// 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 <iostream>
+
+#include <bench/bench.h>
+#include <crypto/chacha_poly_aead.h>
+#include <crypto/poly1305.h> // for the POLY1305_TAGLEN constant
+#include <hash.h>
+
+#include <limits>
+#include <assert.h>
+
+/* Number of bytes to process per iteration */
+static constexpr uint64_t BUFFER_SIZE_TINY = 64;
+static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
+static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024;
+
+static const unsigned char k1[32] = {0};
+static const unsigned char k2[32] = {0};
+
+static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32);
+
+static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, bool include_decryption)
+{
+ std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ uint64_t seqnr_payload = 0;
+ uint64_t seqnr_aad = 0;
+ int aad_pos = 0;
+ uint32_t len = 0;
+ while (state.KeepRunning()) {
+ // encrypt or decrypt the buffer with a static key
+ assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true));
+
+ if (include_decryption) {
+ // if we decrypt, include the GetLength
+ assert(aead.GetLength(&len, seqnr_aad, aad_pos, in.data()));
+ assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true));
+ }
+
+ // increase main sequence number
+ seqnr_payload++;
+ // increase aad position (position in AAD keystream)
+ aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
+ if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
+ aad_pos = 0;
+ seqnr_aad++;
+ }
+ if (seqnr_payload + 1 == std::numeric_limits<uint64_t>::max()) {
+ // reuse of nonce+key is okay while benchmarking.
+ seqnr_payload = 0;
+ seqnr_aad = 0;
+ aad_pos = 0;
+ }
+ }
+}
+
+static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, false);
+}
+
+static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, false);
+}
+
+static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, false);
+}
+
+static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, true);
+}
+
+static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, true);
+}
+
+static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::State& state)
+{
+ CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, true);
+}
+
+// Add Hash() (dbl-sha256) bench for comparison
+
+static void HASH(benchmark::State& state, size_t buffersize)
+{
+ uint8_t hash[CHash256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(buffersize,0);
+ while (state.KeepRunning())
+ CHash256().Write(in.data(), in.size()).Finalize(hash);
+}
+
+static void HASH_64BYTES(benchmark::State& state)
+{
+ HASH(state, BUFFER_SIZE_TINY);
+}
+
+static void HASH_256BYTES(benchmark::State& state)
+{
+ HASH(state, BUFFER_SIZE_SMALL);
+}
+
+static void HASH_1MB(benchmark::State& state)
+{
+ HASH(state, BUFFER_SIZE_LARGE);
+}
+
+BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT, 500000);
+BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT, 250000);
+BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT, 340);
+BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT, 500000);
+BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT, 250000);
+BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT, 340);
+BENCHMARK(HASH_64BYTES, 500000);
+BENCHMARK(HASH_256BYTES, 250000);
+BENCHMARK(HASH_1MB, 340);
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
new file mode 100644
index 0000000000..29e448fc43
--- /dev/null
+++ b/src/bench/rpc_blockchain.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2016-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 <bench/bench.h>
+#include <bench/data.h>
+
+#include <validation.h>
+#include <streams.h>
+#include <consensus/validation.h>
+#include <rpc/blockchain.h>
+
+#include <univalue.h>
+
+static void BlockToJsonVerbose(benchmark::State& state) {
+ CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
+ char a = '\0';
+ stream.write(&a, 1); // Prevent compaction
+
+ CBlock block;
+ stream >> block;
+
+ CBlockIndex blockindex;
+ const uint256 blockHash = block.GetHash();
+ blockindex.phashBlock = &blockHash;
+ blockindex.nBits = 403014710;
+
+ while (state.KeepRunning()) {
+ (void)blockToJSON(block, &blockindex, &blockindex, /*verbose*/ true);
+ }
+}
+
+BENCHMARK(BlockToJsonVerbose, 10);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 38010c461e..5f6d69a4f3 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -12,9 +12,12 @@
#include <fs.h>
#include <rpc/client.h>
#include <rpc/protocol.h>
-#include <util/system.h>
+#include <rpc/request.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <functional>
#include <memory>
#include <stdio.h>
#include <tuple>
@@ -40,22 +43,22 @@ static void SetupCliArgs()
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
- gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
- gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
/** libevent event log callback */
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 933b34744d..f4972c3cd4 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -11,18 +11,20 @@
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
-#include <keystore.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <univalue.h>
-#include <util/rbf.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <functional>
#include <memory>
#include <stdio.h>
@@ -38,36 +40,36 @@ static void SetupBitcoinTxArgs()
{
SetupHelpOptions(gArgs);
- gArgs.AddArg("-create", "Create new, empty TX.", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-json", "Select JSON output", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-create", "Create new, empty TX.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-json", "Select JSON output", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
- gArgs.AddArg("delin=N", "Delete input N from TX", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("delout=N", "Delete output N from TX", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("locktime=N", "Set TX lock time to N", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("nversion=N", "Set TX version to N", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("delin=N", "Delete input N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("delout=N", "Delete output N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("locktime=N", "Set TX lock time to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
"Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
- "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", "Add pay-to-pubkey output to TX. "
"Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output. "
- "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. "
"Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
- "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", false, OptionsCategory::COMMANDS);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
"This command requires JSON registers:"
"prevtxs=JSON object, "
"privatekeys=JSON object. "
- "See signrawtransactionwithkey docs for format of sighash flags, JSON objects.", false, OptionsCategory::COMMANDS);
+ "See signrawtransactionwithkey docs for format of sighash flags, JSON objects.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS);
- gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS);
+ gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
+ gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
}
//
@@ -557,7 +559,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!registers.count("privatekeys"))
throw std::runtime_error("privatekeys register variable must be set.");
- CBasicKeyStore tempKeystore;
+ FillableSigningProvider tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
@@ -631,7 +633,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
}
}
- const CKeyStore& keystore = tempKeystore;
+ const FillableSigningProvider& keystore = tempKeystore;
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index cbb4ea750c..203f909cc4 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -9,10 +9,12 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <logging.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
#include <wallet/wallettool.h>
+#include <functional>
#include <stdio.h>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
@@ -22,13 +24,13 @@ static void SetupWalletToolArgs()
SetupHelpOptions(gArgs);
SetupChainParamsBaseOptions();
- gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("info", "Get wallet info", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("create", "Create new wallet file", false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
}
static bool WalletAppInit(int argc, char* argv[])
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index ba6de702e0..8e31f6e32b 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -11,15 +11,17 @@
#include <clientversion.h>
#include <compat.h>
#include <fs.h>
-#include <interfaces/chain.h>
#include <init.h>
+#include <interfaces/chain.h>
#include <noui.h>
#include <shutdown.h>
+#include <ui_interface.h>
+#include <util/strencodings.h>
#include <util/system.h>
#include <util/threadnames.h>
-#include <util/strencodings.h>
+#include <util/translation.h>
-#include <stdio.h>
+#include <functional>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
@@ -70,8 +72,7 @@ static bool AppInit(int argc, char* argv[])
SetupServerArgs();
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
- tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
- return false;
+ return InitError(strprintf("Error parsing command line arguments: %s\n", error));
}
// Process help and version before taking care about datadir
@@ -96,26 +97,22 @@ static bool AppInit(int argc, char* argv[])
{
if (!fs::is_directory(GetDataDir(false)))
{
- tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
- return false;
+ return InitError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
}
if (!gArgs.ReadConfigFiles(error, true)) {
- tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
- return false;
+ return InitError(strprintf("Error reading configuration file: %s\n", error));
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
- tfm::format(std::cerr, "Error: %s\n", e.what());
- return false;
+ return InitError(strprintf("%s\n", e.what()));
}
// Error out when loose non-argument tokens are encountered on command line
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
- tfm::format(std::cerr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
- return false;
+ return InitError(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]));
}
}
@@ -146,19 +143,17 @@ static bool AppInit(int argc, char* argv[])
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
- tfm::format(std::cout, "Bitcoin server starting\n");
+ tfm::format(std::cout, PACKAGE_NAME " daemon starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
- tfm::format(std::cerr, "Error: daemon() failed: %s\n", strerror(errno));
- return false;
+ return InitError(strprintf("daemon() failed: %s\n", strerror(errno)));
}
#if defined(MAC_OSX)
#pragma GCC diagnostic pop
#endif
#else
- tfm::format(std::cerr, "Error: -daemon is not supported on this operating system\n");
- return false;
+ return InitError("-daemon is not supported on this operating system\n");
#endif // HAVE_DECL_DAEMON
}
// Lock data directory after daemonization
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index b8e0ea23dd..c24234aeb7 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -141,7 +141,7 @@ public:
fDefaultConsistencyChecks = false;
fRequireStandard = true;
- fMineBlocksOnDemand = false;
+ m_is_test_chain = false;
checkpointData = {
{
@@ -167,9 +167,6 @@ public:
/* nTxCount */ 383732546,
/* dTxRate */ 3.685496590998308
};
-
- /* disable fallback fee on mainnet */
- m_fallback_fee_enabled = false;
}
};
@@ -247,7 +244,7 @@ public:
fDefaultConsistencyChecks = false;
fRequireStandard = false;
- fMineBlocksOnDemand = false;
+ m_is_test_chain = true;
checkpointData = {
@@ -262,9 +259,6 @@ public:
/* nTxCount */ 19438708,
/* dTxRate */ 0.626
};
-
- /* enable fallback fee on testnet */
- m_fallback_fee_enabled = true;
}
};
@@ -324,8 +318,8 @@ public:
vSeeds.clear(); //!< Regtest mode doesn't have any DNS seeds.
fDefaultConsistencyChecks = true;
- fRequireStandard = false;
- fMineBlocksOnDemand = true;
+ fRequireStandard = true;
+ m_is_test_chain = true;
checkpointData = {
{
@@ -346,9 +340,6 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "bcrt";
-
- /* enable fallback fee on regtest */
- m_fallback_fee_enabled = true;
}
/**
diff --git a/src/chainparams.h b/src/chainparams.h
index 6ff3dbb7e5..8f1d27e03c 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -66,17 +66,17 @@ public:
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
/** Policy: Filter transactions that do not match well-defined patterns */
bool RequireStandard() const { return fRequireStandard; }
+ /** If this chain is exclusively used for testing */
+ bool IsTestChain() const { return m_is_test_chain; }
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Minimum free space (in GB) needed for data directory */
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
/** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
- /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
- bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
+ /** Whether it is possible to mine blocks on demand (no retargeting) */
+ bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
- /** Return true if the fallback fee is by default enabled for this network */
- bool IsFallbackFeeEnabled() const { return m_fallback_fee_enabled; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
@@ -101,10 +101,9 @@ protected:
std::vector<SeedSpec6> vFixedSeeds;
bool fDefaultConsistencyChecks;
bool fRequireStandard;
- bool fMineBlocksOnDemand;
+ bool m_is_test_chain;
CCheckpointData checkpointData;
ChainTxData chainTxData;
- bool m_fallback_fee_enabled;
};
/**
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index f0559a319a..deb8e0fb57 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -18,9 +18,9 @@ const std::string CBaseChainParams::REGTEST = "regtest";
void SetupChainParamsBaseOptions()
{
gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
- "This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS);
- gArgs.AddArg("-testnet", "Use the test chain", false, OptionsCategory::CHAINPARAMS);
- gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::CHAINPARAMS);
+ "This is intended for regression testing tools and app development.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-testnet", "Use the test chain", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 355df043d3..f34646f7ac 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -7,7 +7,6 @@
#include <memory>
#include <string>
-#include <vector>
/**
* CBaseChainParams defines the base parameters (shared between bitcoin-cli and bitcoind)
diff --git a/src/coins.cpp b/src/coins.cpp
index 3ef9e0463c..6b85edd01a 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -5,6 +5,7 @@
#include <coins.h>
#include <consensus/consensus.h>
+#include <logging.h>
#include <random.h>
#include <version.h>
@@ -258,3 +259,19 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
}
return coinEmpty;
}
+
+bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
+ try {
+ return CCoinsViewBacked::GetCoin(outpoint, coin);
+ } catch(const std::runtime_error& e) {
+ for (auto f : m_err_callbacks) {
+ f();
+ }
+ LogPrintf("Error reading from database: %s\n", e.what());
+ // Starting the shutdown sequence and returning false to the caller would be
+ // interpreted as 'entry not found' (as opposed to unable to read data), and
+ // could lead to invalid interpretation. Just exit immediately, as we can't
+ // continue anyway, and all writes should be atomic.
+ std::abort();
+ }
+}
diff --git a/src/coins.h b/src/coins.h
index 482e233e8c..dca1beabb6 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -17,6 +17,7 @@
#include <assert.h>
#include <stdint.h>
+#include <functional>
#include <unordered_map>
/**
@@ -315,4 +316,28 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool
//! lookups to database, so it should be used with care.
const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid);
+/**
+ * This is a minimally invasive approach to shutdown on LevelDB read errors from the
+ * chainstate, while keeping user interface out of the common library, which is shared
+ * between bitcoind, and bitcoin-qt and non-server tools.
+ *
+ * Writes do not need similar protection, as failure to write is handled by the caller.
+*/
+class CCoinsViewErrorCatcher final : public CCoinsViewBacked
+{
+public:
+ explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
+
+ void AddReadErrCallback(std::function<void()> f) {
+ m_err_callbacks.emplace_back(std::move(f));
+ }
+
+ bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
+
+private:
+ /** A list of callbacks to execute upon leveldb read error. */
+ std::vector<std::function<void()>> m_err_callbacks;
+
+};
+
#endif // BITCOIN_COINS_H
diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp
new file mode 100644
index 0000000000..6a3d43deb1
--- /dev/null
+++ b/src/crypto/chacha_poly_aead.cpp
@@ -0,0 +1,126 @@
+// 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 <crypto/chacha_poly_aead.h>
+
+#include <crypto/common.h>
+#include <crypto/poly1305.h>
+#include <support/cleanse.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <cstdio>
+#include <limits>
+
+#ifndef HAVE_TIMINGSAFE_BCMP
+
+int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n)
+{
+ const unsigned char *p1 = b1, *p2 = b2;
+ int ret = 0;
+
+ for (; n > 0; n--)
+ ret |= *p1++ ^ *p2++;
+ return (ret != 0);
+}
+
+#endif // TIMINGSAFE_BCMP
+
+ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len)
+{
+ assert(K_1_len == CHACHA20_POLY1305_AEAD_KEY_LEN);
+ assert(K_2_len == CHACHA20_POLY1305_AEAD_KEY_LEN);
+ m_chacha_main.SetKey(K_1, CHACHA20_POLY1305_AEAD_KEY_LEN);
+ m_chacha_header.SetKey(K_2, CHACHA20_POLY1305_AEAD_KEY_LEN);
+
+ // set the cached sequence number to uint64 max which hints for an unset cache.
+ // we can't hit uint64 max since the rekey rule (which resets the sequence number) is 1GB
+ m_cached_aad_seqnr = std::numeric_limits<uint64_t>::max();
+}
+
+bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len /* length of the output buffer for sanity checks */, const unsigned char* src, size_t src_len, bool is_encrypt)
+{
+ // check buffer boundaries
+ if (
+ // if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC
+ (is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) ||
+ // if we decrypt, make sure the source contains at least the expected AAD+MAC and the destination has at least space for the source - MAC
+ (!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) {
+ return false;
+ }
+
+ unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
+ memset(poly_key, 0, sizeof(poly_key));
+ m_chacha_main.SetIV(seqnr_payload);
+
+ // block counter 0 for the poly1305 key
+ // use lower 32bytes for the poly1305 key
+ // (throws away 32 unused bytes (upper 32) from this ChaCha20 round)
+ m_chacha_main.Seek(0);
+ m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key));
+
+ // if decrypting, verify the tag prior to decryption
+ if (!is_encrypt) {
+ const unsigned char* tag = src + src_len - POLY1305_TAGLEN;
+ poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key);
+
+ // constant time compare the calculated MAC with the provided MAC
+ if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
+ memory_cleanse(expected_tag, sizeof(expected_tag));
+ memory_cleanse(poly_key, sizeof(poly_key));
+ return false;
+ }
+ memory_cleanse(expected_tag, sizeof(expected_tag));
+ // MAC has been successfully verified, make sure we don't covert it in decryption
+ src_len -= POLY1305_TAGLEN;
+ }
+
+ // calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache
+ if (m_cached_aad_seqnr != seqnr_aad) {
+ m_cached_aad_seqnr = seqnr_aad;
+ m_chacha_header.SetIV(seqnr_aad);
+ m_chacha_header.Seek(0);
+ m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT);
+ }
+ // crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream
+ dest[0] = src[0] ^ m_aad_keystream_buffer[aad_pos];
+ dest[1] = src[1] ^ m_aad_keystream_buffer[aad_pos + 1];
+ dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2];
+
+ // Set the playload ChaCha instance block counter to 1 and crypt the payload
+ m_chacha_main.Seek(1);
+ m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN);
+
+ // If encrypting, calculate and append tag
+ if (is_encrypt) {
+ // the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload
+ poly1305_auth(dest + src_len, dest, src_len, poly_key);
+ }
+
+ // cleanse no longer required MAC and polykey
+ memory_cleanse(poly_key, sizeof(poly_key));
+ return true;
+}
+
+bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext)
+{
+ // enforce valid aad position to avoid accessing outside of the 64byte keystream cache
+ // (there is space for 21 times 3 bytes)
+ assert(aad_pos >= 0 && aad_pos < CHACHA20_ROUND_OUTPUT - CHACHA20_POLY1305_AEAD_AAD_LEN);
+ if (m_cached_aad_seqnr != seqnr_aad) {
+ // we need to calculate the 64 keystream bytes since we reached a new aad sequence number
+ m_cached_aad_seqnr = seqnr_aad;
+ m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce
+ m_chacha_header.Seek(0); // block counter 0
+ m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache
+ }
+
+ // decrypt the ciphertext length by XORing the right position of the 64byte keystream cache with the ciphertext
+ *len24_out = (ciphertext[0] ^ m_aad_keystream_buffer[aad_pos + 0]) |
+ (ciphertext[1] ^ m_aad_keystream_buffer[aad_pos + 1]) << 8 |
+ (ciphertext[2] ^ m_aad_keystream_buffer[aad_pos + 2]) << 16;
+
+ return true;
+}
diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h
new file mode 100644
index 0000000000..b3ba781cdd
--- /dev/null
+++ b/src/crypto/chacha_poly_aead.h
@@ -0,0 +1,146 @@
+// 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.
+
+#ifndef BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H
+#define BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H
+
+#include <crypto/chacha20.h>
+
+#include <cmath>
+
+static constexpr int CHACHA20_POLY1305_AEAD_KEY_LEN = 32;
+static constexpr int CHACHA20_POLY1305_AEAD_AAD_LEN = 3; /* 3 bytes length */
+static constexpr int CHACHA20_ROUND_OUTPUT = 64; /* 64 bytes per round */
+static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/
+
+/* A AEAD class for ChaCha20-Poly1305@bitcoin.
+ *
+ * ChaCha20 is a stream cipher designed by Daniel Bernstein and described in
+ * <ref>[http://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]</ref>. It operates
+ * by permuting 128 fixed bits, 128 or 256 bits of key, a 64 bit nonce and a 64
+ * bit counter into 64 bytes of output. This output is used as a keystream, with
+ * any unused bytes simply discarded.
+ *
+ * Poly1305 <ref>[http://cr.yp.to/mac/poly1305-20050329.pdf Poly1305]</ref>, also
+ * by Daniel Bernstein, is a one-time Carter-Wegman MAC that computes a 128 bit
+ * integrity tag given a message and a single-use 256 bit secret key.
+ *
+ * The chacha20-poly1305@bitcoin combines these two primitives into an
+ * authenticated encryption mode. The construction used is based on that proposed
+ * for TLS by Adam Langley in
+ * <ref>[http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 "ChaCha20
+ * and Poly1305 based Cipher Suites for TLS", Adam Langley]</ref>, but differs in
+ * the layout of data passed to the MAC and in the addition of encryption of the
+ * packet lengths.
+ *
+ * ==== Detailed Construction ====
+ *
+ * The chacha20-poly1305@bitcoin cipher requires two 256 bits of key material as
+ * output from the key exchange. Each key (K_1 and K_2) are used by two separate
+ * instances of chacha20.
+ *
+ * The instance keyed by K_1 is a stream cipher that is used only to encrypt the 3
+ * byte packet length field and has its own sequence number. The second instance,
+ * keyed by K_2, is used in conjunction with poly1305 to build an AEAD
+ * (Authenticated Encryption with Associated Data) that is used to encrypt and
+ * authenticate the entire packet.
+ *
+ * Two separate cipher instances are used here so as to keep the packet lengths
+ * confidential but not create an oracle for the packet payload cipher by
+ * decrypting and using the packet length prior to checking the MAC. By using an
+ * independently-keyed cipher instance to encrypt the length, an active attacker
+ * seeking to exploit the packet input handling as a decryption oracle can learn
+ * nothing about the payload contents or its MAC (assuming key derivation,
+ * ChaCha20 and Poly1305 are secure).
+ *
+ * The AEAD is constructed as follows: for each packet, generate a Poly1305 key by
+ * taking the first 256 bits of ChaCha20 stream output generated using K_2, an IV
+ * consisting of the packet sequence number encoded as an LE uint64 and a ChaCha20
+ * block counter of zero. The K_2 ChaCha20 block counter is then set to the
+ * little-endian encoding of 1 (i.e. {1, 0, 0, 0, 0, 0, 0, 0}) and this instance
+ * is used for encryption of the packet payload.
+ *
+ * ==== Packet Handling ====
+ *
+ * When receiving a packet, the length must be decrypted first. When 3 bytes of
+ * ciphertext length have been received, they may be decrypted.
+ *
+ * A ChaCha20 round always calculates 64bytes which is sufficient to crypt 21
+ * times a 3 bytes length field (21*3 = 63). The length field sequence number can
+ * thus be used 21 times (keystream caching).
+ *
+ * The length field must be enc-/decrypted with the ChaCha20 keystream keyed with
+ * K_1 defined by block counter 0, the length field sequence number in little
+ * endian and a keystream position from 0 to 60.
+ *
+ * Once the entire packet has been received, the MAC MUST be checked before
+ * decryption. A per-packet Poly1305 key is generated as described above and the
+ * MAC tag calculated using Poly1305 with this key over the ciphertext of the
+ * packet length and the payload together. The calculated MAC is then compared in
+ * constant time with the one appended to the packet and the packet decrypted
+ * using ChaCha20 as described above (with K_2, the packet sequence number as
+ * nonce and a starting block counter of 1).
+ *
+ * Detection of an invalid MAC MUST lead to immediate connection termination.
+ *
+ * To send a packet, first encode the 3 byte length and encrypt it using K_1 as
+ * described above. Encrypt the packet payload (using K_2) and append it to the
+ * encrypted length. Finally, calculate a MAC tag and append it.
+ *
+ * The initiating peer MUST use <code>K_1_A, K_2_A</code> to encrypt messages on
+ * the send channel, <code>K_1_B, K_2_B</code> MUST be used to decrypt messages on
+ * the receive channel.
+ *
+ * The responding peer MUST use <code>K_1_A, K_2_A</code> to decrypt messages on
+ * the receive channel, <code>K_1_B, K_2_B</code> MUST be used to encrypt messages
+ * on the send channel.
+ *
+ * Optimized implementations of ChaCha20-Poly1305@bitcoin are relatively fast in
+ * general, therefore it is very likely that encrypted messages require not more
+ * CPU cycles per bytes then the current unencrypted p2p message format
+ * (ChaCha20/Poly1305 versus double SHA256).
+ *
+ * The initial packet sequence numbers are 0.
+ *
+ * K_2 ChaCha20 cipher instance (payload) must never reuse a {key, nonce} for
+ * encryption nor may it be used to encrypt more than 2^70 bytes under the same
+ * {key, nonce}.
+ *
+ * K_1 ChaCha20 cipher instance (length field/AAD) must never reuse a {key, nonce,
+ * position-in-keystream} for encryption nor may it be used to encrypt more than
+ * 2^70 bytes under the same {key, nonce}.
+ *
+ * We use message sequence numbers for both communication directions.
+ */
+
+class ChaCha20Poly1305AEAD
+{
+private:
+ ChaCha20 m_chacha_main; // payload and poly1305 key-derivation cipher instance
+ ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length)
+ unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache
+ uint64_t m_cached_aad_seqnr; // aad keystream cache hint
+
+public:
+ ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len);
+
+ explicit ChaCha20Poly1305AEAD(const ChaCha20Poly1305AEAD&) = delete;
+
+ /** Encrypts/decrypts a packet
+ seqnr_payload, the message sequence number
+ seqnr_aad, the messages AAD sequence number which allows reuse of the AAD keystream
+ aad_pos, position to use in the AAD keystream to encrypt the AAD
+ dest, output buffer, must be of a size equal or larger then CHACHA20_POLY1305_AEAD_AAD_LEN + payload (+ POLY1305_TAG_LEN in encryption) bytes
+ destlen, length of the destination buffer
+ src, the AAD+payload to encrypt or the AAD+payload+MAC to decrypt
+ src_len, the length of the source buffer
+ is_encrypt, set to true if we encrypt (creates and appends the MAC instead of verifying it)
+ */
+ bool Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len, const unsigned char* src, size_t src_len, bool is_encrypt);
+
+ /** decrypts the 3 bytes AAD data and decodes it into a uint32_t field */
+ bool GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext);
+};
+
+#endif // BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index c7a119440b..306d718574 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -5,19 +5,20 @@
#include <httprpc.h>
#include <chainparams.h>
+#include <crypto/hmac_sha256.h>
#include <httpserver.h>
#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <sync.h>
-#include <util/system.h>
-#include <util/strencodings.h>
#include <ui_interface.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
#include <walletinitinterface.h>
-#include <crypto/hmac_sha256.h>
-#include <stdio.h>
#include <memory>
+#include <stdio.h>
#include <boost/algorithm/string.hpp> // boost::trim
@@ -218,7 +219,7 @@ static bool InitRPCAuthentication()
LogPrintf("No rpcpassword set - using random cookie authentication.\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
- _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
+ _("Error: A fatal internal error occurred, see debug.log for details").translated, // Same message as AbortNode
"", CClientUIInterface::MSG_ERROR);
return false;
}
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index 20f33baf2c..c3ce8d7af0 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -53,7 +53,7 @@ struct DBHeightKey {
int height;
DBHeightKey() : height(0) {}
- DBHeightKey(int height_in) : height(height_in) {}
+ explicit DBHeightKey(int height_in) : height(height_in) {}
template<typename Stream>
void Serialize(Stream& s) const
@@ -76,7 +76,7 @@ struct DBHeightKey {
struct DBHashKey {
uint256 hash;
- DBHashKey(const uint256& hash_in) : hash(hash_in) {}
+ explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
ADD_SERIALIZE_METHODS;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 929b85bfb5..62db38f894 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -6,6 +6,7 @@
#include <shutdown.h>
#include <ui_interface.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <boost/thread.hpp>
@@ -137,7 +138,7 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
int64_t count = 0;
LogPrintf("Upgrading txindex database... [0%%]\n");
- uiInterface.ShowProgress(_("Upgrading txindex database"), 0, true);
+ uiInterface.ShowProgress(_("Upgrading txindex database").translated, 0, true);
int report_done = 0;
const size_t batch_size = 1 << 24; // 16 MiB
@@ -174,7 +175,7 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
(static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
- uiInterface.ShowProgress(_("Upgrading txindex database"), percentage_done, true);
+ uiInterface.ShowProgress(_("Upgrading txindex database").translated, percentage_done, true);
if (report_done < percentage_done/10) {
LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
report_done = percentage_done/10;
diff --git a/src/init.cpp b/src/init.cpp
index 5d7c3b9af7..bb3ff8d88f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -15,43 +15,46 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
+#include <coins.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
-#include <httpserver.h>
#include <httprpc.h>
+#include <httpserver.h>
#include <index/blockfilterindex.h>
-#include <interfaces/chain.h>
#include <index/txindex.h>
+#include <interfaces/chain.h>
#include <key.h>
-#include <validation.h>
#include <miner.h>
-#include <netbase.h>
#include <net.h>
#include <net_processing.h>
+#include <netbase.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
-#include <rpc/server.h>
-#include <rpc/register.h>
#include <rpc/blockchain.h>
+#include <rpc/register.h>
+#include <rpc/server.h>
#include <rpc/util.h>
-#include <script/standard.h>
-#include <script/sigcache.h>
#include <scheduler.h>
+#include <script/sigcache.h>
+#include <script/standard.h>
#include <shutdown.h>
-#include <util/threadnames.h>
#include <timedata.h>
+#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
-#include <torcontrol.h>
#include <ui_interface.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/system.h>
+#include <util/threadnames.h>
+#include <util/translation.h>
#include <util/validation.h>
+#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
+
#include <stdint.h>
#include <stdio.h>
@@ -117,7 +120,7 @@ NODISCARD static bool CreatePidFile()
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, GetPidFile().string(), std::strerror(errno)));
}
}
@@ -146,31 +149,6 @@ NODISCARD static bool CreatePidFile()
// shutdown thing.
//
-/**
- * This is a minimally invasive approach to shutdown on LevelDB read errors from the
- * chainstate, while keeping user interface out of the common library, which is shared
- * between bitcoind, and bitcoin-qt and non-server tools.
-*/
-class CCoinsViewErrorCatcher final : public CCoinsViewBacked
-{
-public:
- explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
- bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
- try {
- return CCoinsViewBacked::GetCoin(outpoint, coin);
- } catch(const std::runtime_error& e) {
- uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR);
- LogPrintf("Error reading from database: %s\n", e.what());
- // Starting the shutdown sequence and returning false to the caller would be
- // interpreted as 'entry not found' (as opposed to unable to read data), and
- // could lead to invalid interpretation. Just exit immediately, as we can't
- // continue anyway, and all writes should be atomic.
- abort();
- }
- }
- // Writes do not need similar protection, as failure to write is handled by the caller.
-};
-
static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
@@ -360,7 +338,7 @@ static void OnRPCStopped()
void SetupServerArgs()
{
SetupHelpOptions(gArgs);
- gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST); // server-only for now
+ gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
@@ -375,103 +353,103 @@ void SetupServerArgs()
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"};
- gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
- gArgs.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- gArgs.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
- gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
- gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS);
- gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)",
- -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS);
+ -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
- "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", false, OptionsCategory::OPTIONS);
+ "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#ifndef WIN32
- gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-sysperms");
#endif
- gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
- false, 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.", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-enablebip61", strprintf("Send reject messages per BIP61 (default: %u)", DEFAULT_ENABLE_BIP61), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-externalip=<ip>", "Specify your own public address", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-listenonion", strprintf("Automatically create Tor hidden service (default: %d)", DEFAULT_LISTEN_ONION), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u)", DEFAULT_MAX_PEER_CONNECTIONS), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION);
- 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), true, OptionsCategory::CONNECTION);
- gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", false, OptionsCategory::CONNECTION);
+ 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("-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);
+ gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-enablebip61", strprintf("Send reject messages per BIP61 (default: %u)", DEFAULT_ENABLE_BIP61), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listenonion", strprintf("Automatically create Tor hidden service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u)", DEFAULT_MAX_PEER_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ 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, 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)", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
- gArgs.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#endif
#else
hidden_args.emplace_back("-upnp");
#endif
- gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-whitelist=<IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times."
- " Whitelisted peers cannot be DoS banned", false, OptionsCategory::CONNECTION);
+ " Whitelisted peers cannot be DoS banned", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions();
#if ENABLE_ZMQ
- gArgs.AddArg("-zmqpubhashblock=<address>", "Enable publish hash block in <address>", false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashblock=<address>", "Enable publish hash block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
@@ -483,7 +461,7 @@ void SetupServerArgs()
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
#endif
- gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
"level 0 reads the blocks from disk, "
"level 1 verifies block validity, "
@@ -491,68 +469,68 @@ void SetupServerArgs()
"level 3 checks disconnection of tip blocks, "
"and level 4 tries to reconnect the blocks, "
"each level includes the checks of the previous levels "
- "(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST);
+ "(0-4, default: %u)", DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for the block tree, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
- "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", false, OptionsCategory::DEBUG_TEST);
+ "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions();
- gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ 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)), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY);
-
-
- gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), false, OptionsCategory::BLOCK_CREATION);
- gArgs.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), false, OptionsCategory::BLOCK_CREATION);
- gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", true, OptionsCategory::BLOCK_CREATION);
-
- gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcauth=<userpw>", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC);
- gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC);
- gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", false, OptionsCategory::RPC);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers 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("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+
+
+ gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
+ gArgs.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
+ gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
+
+ gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
#if HAVE_DECL_DAEMON
- gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
#endif
@@ -566,20 +544,20 @@ std::string LicenseInfo()
const std::string URL_SOURCE_CODE = "<https://github.com/bitcoin/bitcoin>";
const std::string URL_WEBSITE = "<https://bitcoincore.org>";
- return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" +
+ return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009, COPYRIGHT_YEAR) + " ") + "\n" +
"\n" +
strprintf(_("Please contribute if you find %s useful. "
- "Visit %s for further information about the software."),
+ "Visit %s for further information about the software.").translated,
PACKAGE_NAME, URL_WEBSITE) +
"\n" +
- strprintf(_("The source code is available from %s."),
+ strprintf(_("The source code is available from %s.").translated,
URL_SOURCE_CODE) +
"\n" +
"\n" +
- _("This is experimental software.") + "\n" +
- strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s"), "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" +
+ _("This is experimental software.").translated + "\n" +
+ strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" +
"\n" +
- strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard."), "<https://www.openssl.org>") +
+ strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.").translated, "<https://www.openssl.org>") +
"\n";
}
@@ -845,7 +823,7 @@ void InitParameterInteraction()
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
{
- return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
+ return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
}
/**
@@ -951,16 +929,16 @@ bool AppInitParameterInteraction()
// on the command line or in this network's section of the config file.
std::string network = gArgs.GetChainName();
for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
- return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
+ return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section.").translated, arg, network, network));
}
// Warn if unrecognized section name are present in the config file.
for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
+ InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized.").translated, section.m_file, section.m_line, section.m_name));
}
if (!fs::is_directory(GetBlocksDir())) {
- return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "").c_str()));
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.").translated, gArgs.GetArg("-blocksdir", "").c_str()));
}
// parse and validate enabled filter types
@@ -973,7 +951,7 @@ bool AppInitParameterInteraction()
for (const auto& name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
- return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name));
+ return InitError(strprintf(_("Unknown -blockfilterindex value %s.").translated, name));
}
g_enabled_filter_types.push_back(filter_type);
}
@@ -982,9 +960,9 @@ bool AppInitParameterInteraction()
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
- return InitError(_("Prune mode is incompatible with -txindex."));
+ return InitError(_("Prune mode is incompatible with -txindex.").translated);
if (!g_enabled_filter_types.empty()) {
- return InitError(_("Prune mode is incompatible with -blockfilterindex."));
+ return InitError(_("Prune mode is incompatible with -blockfilterindex.").translated);
}
}
@@ -1009,11 +987,11 @@ bool AppInitParameterInteraction()
#endif
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
- return InitError(_("Not enough file descriptors available."));
+ return InitError(_("Not enough file descriptors available.").translated);
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
- InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
+ InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations.").translated, nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
if (gArgs.IsArgSet("-debug")) {
@@ -1024,7 +1002,7 @@ bool AppInitParameterInteraction()
[](std::string cat){return cat == "0" || cat == "none";})) {
for (const auto& cat : categories) {
if (!LogInstance().EnableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
+ InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debug", cat));
}
}
}
@@ -1033,7 +1011,7 @@ bool AppInitParameterInteraction()
// Now remove the logging categories which were explicitly excluded
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
+ InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debugexclude", cat));
}
}
@@ -1069,7 +1047,7 @@ bool AppInitParameterInteraction()
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
- return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
+ return InitError(strprintf(_("-maxmempool must be at least %d MB").translated, std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
if (gArgs.IsArgSet("-incrementalrelayfee"))
@@ -1092,7 +1070,7 @@ bool AppInitParameterInteraction()
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
- return InitError(_("Prune cannot be configured with a negative value."));
+ return InitError(_("Prune cannot be configured with a negative value.").translated);
}
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
if (nPruneArg == 1) { // manual pruning: -prune=1
@@ -1101,7 +1079,7 @@ bool AppInitParameterInteraction()
fPruneMode = true;
} else if (nPruneTarget) {
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
- return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
+ return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number.").translated, MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
}
LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
fPruneMode = true;
@@ -1150,8 +1128,9 @@ bool AppInitParameterInteraction()
}
fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
- if (chainparams.RequireStandard() && !fRequireStandard)
+ if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
+ }
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
@@ -1182,10 +1161,10 @@ static bool LockDataDirectory(bool probeOnly)
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = GetDataDir();
if (!DirIsWritable(datadir)) {
- return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string()));
+ return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, datadir.string()));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
- return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME));
+ return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, datadir.string(), PACKAGE_NAME));
}
return true;
}
@@ -1203,7 +1182,7 @@ bool AppInitSanityChecks()
// Sanity check
if (!InitSanityCheck())
- return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
+ return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down.").translated, PACKAGE_NAME));
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
@@ -1254,7 +1233,7 @@ bool AppInitMain(InitInterfaces& interfaces)
LogPrintf("Config file: %s\n", config_file_path.string());
} else if (gArgs.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
- InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string()));
+ InitWarning(strprintf(_("The specified config file %s does not exist\n").translated, config_file_path.string()));
} else {
// Not categorizing as "Warning" because it's the default behavior
LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
@@ -1314,7 +1293,7 @@ bool AppInitMain(InitInterfaces& interfaces)
{
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers())
- return InitError(_("Unable to start HTTP server. See debug log for details."));
+ return InitError(_("Unable to start HTTP server. See debug log for details.").translated);
}
// ********************************************************* Step 5: verify wallet database integrity
@@ -1342,12 +1321,12 @@ bool AppInitMain(InitInterfaces& interfaces)
std::vector<std::string> uacomments;
for (const std::string& cmt : gArgs.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
- return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
+ return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters.").translated, cmt));
uacomments.push_back(cmt);
}
strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
- return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."),
+ return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.").translated,
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
@@ -1356,7 +1335,7 @@ bool AppInitMain(InitInterfaces& interfaces)
for (const std::string& snet : gArgs.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
- return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
+ return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'").translated, snet));
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
@@ -1377,12 +1356,12 @@ bool AppInitMain(InitInterfaces& interfaces)
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) {
- return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
+ return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid())
- return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
+ return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg));
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
@@ -1401,11 +1380,11 @@ bool AppInitMain(InitInterfaces& interfaces)
} else {
CService onionProxy;
if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) {
- return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
+ return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid())
- return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
+ return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg));
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
}
@@ -1480,7 +1459,7 @@ bool AppInitMain(InitInterfaces& interfaces)
bool fReset = fReindex;
std::string strLoadError;
- uiInterface.InitMessage(_("Loading block index..."));
+ uiInterface.InitMessage(_("Loading block index...").translated);
do {
const int64_t load_block_index_start_time = GetTimeMillis();
@@ -1511,20 +1490,21 @@ bool AppInitMain(InitInterfaces& interfaces)
// From here on out fReindex and fReset mean something different!
if (!LoadBlockIndex(chainparams)) {
if (ShutdownRequested()) break;
- strLoadError = _("Error loading block database");
+ strLoadError = _("Error loading block database").translated;
break;
}
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
- if (!mapBlockIndex.empty() && !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
- return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
+ if (!::BlockIndex().empty() &&
+ !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+ return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?").translated);
}
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
// in the past, but is now trying to run unpruned.
if (fHavePruned && !fPruneMode) {
- strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
+ strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain").translated;
break;
}
@@ -1533,26 +1513,31 @@ bool AppInitMain(InitInterfaces& interfaces)
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
if (!fReindex && !LoadGenesisBlock(chainparams)) {
- strLoadError = _("Error initializing block database");
+ strLoadError = _("Error initializing block database").translated;
break;
}
// At this point we're either in reindex or we've loaded a useful
- // block tree into mapBlockIndex!
+ // block tree into BlockIndex()!
pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
+ pcoinscatcher->AddReadErrCallback([]() {
+ uiInterface.ThreadSafeMessageBox(
+ _("Error reading from database, shutting down.").translated,
+ "", CClientUIInterface::MSG_ERROR);
+ });
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
if (!pcoinsdbview->Upgrade()) {
- strLoadError = _("Error upgrading chainstate database");
+ strLoadError = _("Error upgrading chainstate database").translated;
break;
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
if (!ReplayBlocks(chainparams, pcoinsdbview.get())) {
- strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
+ strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
break;
}
@@ -1563,24 +1548,24 @@ bool AppInitMain(InitInterfaces& interfaces)
if (!is_coinsview_empty) {
// LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
if (!LoadChainTip(chainparams)) {
- strLoadError = _("Error initializing block database");
+ strLoadError = _("Error initializing block database").translated;
break;
}
assert(::ChainActive().Tip() != nullptr);
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- strLoadError = _("Error opening block database");
+ strLoadError = _("Error opening block database").translated;
break;
}
if (!fReset) {
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
// It both disconnects blocks based on ::ChainActive(), and drops block data in
- // mapBlockIndex based on lack of available witness data.
- uiInterface.InitMessage(_("Rewinding blocks..."));
+ // BlockIndex() based on lack of available witness data.
+ uiInterface.InitMessage(_("Rewinding blocks...").translated);
if (!RewindBlockIndex(chainparams)) {
- strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
+ strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain").translated;
break;
}
}
@@ -1588,7 +1573,7 @@ bool AppInitMain(InitInterfaces& interfaces)
try {
LOCK(cs_main);
if (!is_coinsview_empty) {
- uiInterface.InitMessage(_("Verifying blocks..."));
+ uiInterface.InitMessage(_("Verifying blocks...").translated);
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
@@ -1599,19 +1584,19 @@ bool AppInitMain(InitInterfaces& interfaces)
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. "
- "Only rebuild the block database if you are sure that your computer's date and time are correct");
+ "Only rebuild the block database if you are sure that your computer's date and time are correct").translated;
break;
}
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
- strLoadError = _("Corrupted block database detected");
+ strLoadError = _("Corrupted block database detected").translated;
break;
}
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- strLoadError = _("Error opening block database");
+ strLoadError = _("Error opening block database").translated;
break;
}
@@ -1623,7 +1608,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
- strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"),
+ strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?").translated,
strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.",
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
if (fRet) {
@@ -1680,7 +1665,7 @@ bool AppInitMain(InitInterfaces& interfaces)
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
- uiInterface.InitMessage(_("Pruning blockstore..."));
+ uiInterface.InitMessage(_("Pruning blockstore...").translated);
::ChainstateActive().PruneAndFlush();
}
}
@@ -1697,11 +1682,11 @@ bool AppInitMain(InitInterfaces& interfaces)
// ********************************************************* Step 11: import blocks
if (!CheckDiskSpace(GetDataDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir()));
+ InitError(strprintf(_("Error: Disk space is low for %s").translated, GetDataDir()));
return false;
}
if (!CheckDiskSpace(GetBlocksDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir()));
+ InitError(strprintf(_("Error: Disk space is low for %s").translated, GetBlocksDir()));
return false;
}
@@ -1749,7 +1734,7 @@ bool AppInitMain(InitInterfaces& interfaces)
//// debug print
{
LOCK(cs_main);
- LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
+ LogPrintf("block tree size = %u\n", ::BlockIndex().size());
chain_active_height = ::ChainActive().Height();
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
@@ -1795,7 +1780,7 @@ bool AppInitMain(InitInterfaces& interfaces)
return InitError(ResolveErrMsg("whitebind", strBind));
}
if (addrBind.GetPort() == 0) {
- return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
+ return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
}
connOptions.vWhiteBinds.push_back(addrBind);
}
@@ -1804,7 +1789,7 @@ bool AppInitMain(InitInterfaces& interfaces)
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
- return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
+ return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
connOptions.vWhitelistedRange.push_back(subnet);
}
@@ -1825,7 +1810,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// ********************************************************* Step 13: finished
SetRPCWarmupFinished();
- uiInterface.InitMessage(_("Done loading"));
+ uiInterface.InitMessage(_("Done loading").translated);
for (const auto& client : interfaces.chain_clients) {
client->start(scheduler);
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 161dd01ffe..1ad4308f29 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -9,7 +9,9 @@
#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <net.h>
+#include <net_processing.h>
#include <node/coin.h>
+#include <node/transaction.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -149,12 +151,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
LockAssertion lock(::cs_main);
return CheckFinalTx(tx);
}
- bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
- {
- LockAssertion lock(::cs_main);
- return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
- false /* bypass limits */, absurd_fee);
- }
using UniqueLock::UniqueLock;
};
@@ -205,7 +201,7 @@ public:
class RpcHandlerImpl : public Handler
{
public:
- RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command)
+ explicit RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command)
{
m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
if (!m_wrapped_command) return false;
@@ -290,10 +286,13 @@ public:
auto it = ::mempool.GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
}
- void relayTransaction(const uint256& txid) override
+ bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) override
{
- CInv inv(MSG_TX, txid);
- g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
+ const TransactionError err = BroadcastTransaction(tx, err_string, max_tx_fee, relay, /*wait_callback*/ false);
+ // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
+ // Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
+ // that Chain clients do not need to know about.
+ return TransactionError::OK == err;
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index e675defd47..1d6ed05522 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -43,10 +43,6 @@ class Wallet;
//! asynchronously
//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
//!
-//! * The relayTransactions() and submitToMemoryPool() methods could be replaced
-//! with a higher-level broadcastTransaction method
-//! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984).
-//!
//! * The initMessages() and loadWallet() methods which the wallet uses to send
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
@@ -127,11 +123,6 @@ public:
//! Check if transaction will be final given chain height current time.
virtual bool checkFinalTx(const CTransaction& tx) = 0;
-
- //! Add transaction to memory pool if the transaction fee is below the
- //! amount specified by absurd_fee. Returns false if the transaction
- //! could not be added due to the fee or for another reason.
- virtual bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) = 0;
};
//! Return Lock interface. Chain is locked when this is called, and
@@ -164,8 +155,10 @@ public:
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
- //! Relay transaction.
- virtual void relayTransaction(const uint256& txid) = 0;
+ //! Transaction is added to memory pool, if the transaction fee is below the
+ //! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true.
+ //! Return false if the transaction could not be added due to the fee or for another reason.
+ virtual bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 584d218dba..fd2fb6531b 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -54,6 +54,7 @@ class NodeImpl : public Node
{
public:
NodeImpl() { m_interfaces.chain = MakeChain(); }
+ void initError(const std::string& message) override { InitError(message); }
bool parseParameters(int argc, const char* const argv[], std::string& error) override
{
return gArgs.ParseParameters(argc, argv, error);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 1ccd2a31b7..bb4b3e1fae 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -38,6 +38,9 @@ class Node
public:
virtual ~Node() {}
+ //! Send init error.
+ virtual void initError(const std::string& message) = 0;
+
//! Set command line arguments.
virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 34c982e1e6..077dc1ab4d 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -33,32 +33,6 @@
namespace interfaces {
namespace {
-class PendingWalletTxImpl : public PendingWalletTx
-{
-public:
- explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {}
-
- const CTransaction& get() override { return *m_tx; }
-
- bool commit(WalletValueMap value_map,
- WalletOrderForm order_form,
- std::string& reject_reason) override
- {
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- CValidationState state;
- if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, state)) {
- reject_reason = state.GetRejectReason();
- return false;
- }
- return true;
- }
-
- CTransactionRef m_tx;
- CWallet& m_wallet;
- CReserveKey m_key;
-};
-
//! Construct wallet tx struct.
WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, const CWalletTx& wtx)
{
@@ -140,9 +114,11 @@ public:
void abortRescan() override { m_wallet->AbortRescan(); }
bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
std::string getWalletName() override { return m_wallet->GetName(); }
- bool getKeyFromPool(bool internal, CPubKey& pub_key) override
+ bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) override
{
- return m_wallet->GetKeyFromPool(pub_key, internal);
+ LOCK(m_wallet->cs_wallet);
+ std::string error;
+ return m_wallet->GetNewDestination(type, label, dest, error);
}
bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); }
bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); }
@@ -226,7 +202,7 @@ public:
LOCK(m_wallet->cs_wallet);
return m_wallet->ListLockedCoins(outputs);
}
- std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
+ CTransactionRef createTransaction(const std::vector<CRecipient>& recipients,
const CCoinControl& coin_control,
bool sign,
int& change_pos,
@@ -235,12 +211,26 @@ public:
{
auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
- auto pending = MakeUnique<PendingWalletTxImpl>(*m_wallet);
- if (!m_wallet->CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
+ CTransactionRef tx;
+ if (!m_wallet->CreateTransaction(*locked_chain, recipients, tx, fee, change_pos,
fail_reason, coin_control, sign)) {
return {};
}
- return std::move(pending);
+ return tx;
+ }
+ bool commitTransaction(CTransactionRef tx,
+ WalletValueMap value_map,
+ WalletOrderForm order_form,
+ std::string& reject_reason) override
+ {
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ CValidationState state;
+ if (!m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form), state)) {
+ reject_reason = state.GetRejectReason();
+ return false;
+ }
+ return true;
}
bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
@@ -476,7 +466,7 @@ public:
}
std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
{
- return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
+ return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CWallet*) { fn(); }));
}
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 9c9b29a813..89e056b18b 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -34,7 +34,6 @@ struct CRecipient;
namespace interfaces {
class Handler;
-class PendingWalletTx;
struct WalletAddress;
struct WalletBalances;
struct WalletTx;
@@ -78,8 +77,8 @@ public:
//! Get wallet name.
virtual std::string getWalletName() = 0;
- // Get key from pool.
- virtual bool getKeyFromPool(bool internal, CPubKey& pub_key) = 0;
+ // Get a new address.
+ virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0;
//! Get public key.
virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
@@ -134,13 +133,19 @@ public:
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
//! Create transaction.
- virtual std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
+ virtual CTransactionRef createTransaction(const std::vector<CRecipient>& recipients,
const CCoinControl& coin_control,
bool sign,
int& change_pos,
CAmount& fee,
std::string& fail_reason) = 0;
+ //! Commit transaction.
+ virtual bool commitTransaction(CTransactionRef tx,
+ WalletValueMap value_map,
+ WalletOrderForm order_form,
+ std::string& reject_reason) = 0;
+
//! Return whether transaction can be abandoned.
virtual bool transactionCanBeAbandoned(const uint256& txid) = 0;
@@ -288,21 +293,6 @@ public:
virtual std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) = 0;
};
-//! Tracking object returned by CreateTransaction and passed to CommitTransaction.
-class PendingWalletTx
-{
-public:
- virtual ~PendingWalletTx() {}
-
- //! Get transaction data.
- virtual const CTransaction& get() = 0;
-
- //! Send pending transaction and commit to wallet.
- virtual bool commit(WalletValueMap value_map,
- WalletOrderForm order_form,
- std::string& reject_reason) = 0;
-};
-
//! Information about one wallet address.
struct WalletAddress
{
diff --git a/src/keystore.h b/src/keystore.h
deleted file mode 100644
index 4bd99e255d..0000000000
--- a/src/keystore.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_KEYSTORE_H
-#define BITCOIN_KEYSTORE_H
-
-#include <key.h>
-#include <pubkey.h>
-#include <script/script.h>
-#include <script/sign.h>
-#include <script/standard.h>
-#include <sync.h>
-
-#include <boost/signals2/signal.hpp>
-
-/** A virtual base class for key stores */
-class CKeyStore : public SigningProvider
-{
-public:
- //! Add a key to the store.
- virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0;
-
- //! Check whether a key corresponding to a given address is present in the store.
- virtual bool HaveKey(const CKeyID &address) const =0;
- virtual std::set<CKeyID> GetKeys() const =0;
-
- //! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki
- virtual bool AddCScript(const CScript& redeemScript) =0;
- virtual bool HaveCScript(const CScriptID &hash) const =0;
- virtual std::set<CScriptID> GetCScripts() const =0;
-
- //! Support for Watch-only addresses
- virtual bool AddWatchOnly(const CScript &dest) =0;
- virtual bool RemoveWatchOnly(const CScript &dest) =0;
- virtual bool HaveWatchOnly(const CScript &dest) const =0;
- virtual bool HaveWatchOnly() const =0;
-};
-
-/** Basic key store, that keeps keys in an address->secret map */
-class CBasicKeyStore : public CKeyStore
-{
-protected:
- mutable CCriticalSection cs_KeyStore;
-
- using KeyMap = std::map<CKeyID, CKey>;
- using WatchKeyMap = std::map<CKeyID, CPubKey>;
- using ScriptMap = std::map<CScriptID, CScript>;
- using WatchOnlySet = std::set<CScript>;
-
- KeyMap mapKeys GUARDED_BY(cs_KeyStore);
- WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
- ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
- WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
-
- void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
-
-public:
- bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
- bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
- bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
- bool HaveKey(const CKeyID &address) const override;
- std::set<CKeyID> GetKeys() const override;
- bool GetKey(const CKeyID &address, CKey &keyOut) const override;
- bool AddCScript(const CScript& redeemScript) override;
- bool HaveCScript(const CScriptID &hash) const override;
- std::set<CScriptID> GetCScripts() const override;
- bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
-
- bool AddWatchOnly(const CScript &dest) override;
- bool RemoveWatchOnly(const CScript &dest) override;
- bool HaveWatchOnly(const CScript &dest) const override;
- bool HaveWatchOnly() const override;
-};
-
-/** Return the CKeyID of the key involved in a script (if there is a unique one). */
-CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
-
-/** Checks if a CKey is in the given CKeyStore compressed or otherwise*/
-bool HaveKey(const CKeyStore& store, const CKey& key);
-
-#endif // BITCOIN_KEYSTORE_H
diff --git a/src/net.cpp b/src/net.cpp
index 7d11111b25..7d6eb31a7c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -15,11 +15,12 @@
#include <consensus/consensus.h>
#include <crypto/common.h>
#include <crypto/sha256.h>
-#include <primitives/transaction.h>
#include <netbase.h>
+#include <primitives/transaction.h>
#include <scheduler.h>
#include <ui_interface.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#ifdef WIN32
#include <string.h>
@@ -36,6 +37,9 @@
#include <miniupnpc/miniwget.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
+// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
+// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
+static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
#endif
#include <unordered_map>
@@ -1403,16 +1407,10 @@ static void ThreadMapPort()
struct UPNPDev * devlist = nullptr;
char lanaddr[64];
-#ifndef UPNPDISCOVER_SUCCESS
- /* miniupnpc 1.5 */
- devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);
-#elif MINIUPNPC_API_VERSION < 14
- /* miniupnpc 1.6 */
int error = 0;
+#if MINIUPNPC_API_VERSION < 14
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
#else
- /* miniupnpc 1.9.20150730 */
- int error = 0;
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
#endif
@@ -1426,43 +1424,32 @@ static void ThreadMapPort()
if (fDiscover) {
char externalIPAddress[40];
r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
- if(r != UPNPCOMMAND_SUCCESS)
+ if (r != UPNPCOMMAND_SUCCESS) {
LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
- else
- {
- if(externalIPAddress[0])
- {
+ } else {
+ if (externalIPAddress[0]) {
CNetAddr resolved;
- if(LookupHost(externalIPAddress, resolved, false)) {
+ if (LookupHost(externalIPAddress, resolved, false)) {
LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str());
AddLocal(resolved, LOCAL_UPNP);
}
- }
- else
+ } else {
LogPrintf("UPnP: GetExternalIPAddress failed.\n");
+ }
}
}
- std::string strDesc = "Bitcoin " + FormatFullVersion();
+ std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
do {
-#ifndef UPNPDISCOVER_SUCCESS
- /* miniupnpc 1.5 */
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
- port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
-#else
- /* miniupnpc 1.6 */
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
- port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
-#endif
+ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
- if(r!=UPNPCOMMAND_SUCCESS)
- LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
- port, port, lanaddr, r, strupnperror(r));
- else
+ if (r != UPNPCOMMAND_SUCCESS) {
+ LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
+ } else {
LogPrintf("UPnP Port Mapping successful.\n");
- }
- while(g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
+ }
+ } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
@@ -2039,9 +2026,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
{
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
- strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
+ strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running.").translated, addrBind.ToString(), PACKAGE_NAME);
else
- strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
+ strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)").translated, addrBind.ToString(), NetworkErrorString(nErr));
LogPrintf("%s\n", strError);
CloseSocket(hListenSocket);
return false;
@@ -2051,7 +2038,7 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
// Listen for incoming connections
if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
{
- strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)").translated, NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
CloseSocket(hListenSocket);
return false;
@@ -2192,7 +2179,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) {
if (clientInterface) {
clientInterface->ThreadSafeMessageBox(
- _("Failed to listen on any port. Use -listen=0 if you want this."),
+ _("Failed to listen on any port. Use -listen=0 if you want this.").translated,
"", CClientUIInterface::MSG_ERROR);
}
return false;
@@ -2203,7 +2190,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
if (clientInterface) {
- clientInterface->InitMessage(_("Loading P2P addresses..."));
+ clientInterface->InitMessage(_("Loading P2P addresses...").translated);
}
// Load addresses from peers.dat
int64_t nStart = GetTimeMillis();
@@ -2218,7 +2205,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
}
- uiInterface.InitMessage(_("Starting network threads..."));
+ uiInterface.InitMessage(_("Starting network threads...").translated);
fAddressesInitialized = true;
@@ -2258,7 +2245,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
if (clientInterface) {
clientInterface->ThreadSafeMessageBox(
- _("Cannot provide specific connections and have addrman find outgoing connections at the same."),
+ _("Cannot provide specific connections and have addrman find outgoing connections at the same.").translated,
"", CClientUIInterface::MSG_ERROR);
}
return false;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 4b43b2cdf2..5efb4adee6 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -68,13 +68,13 @@ static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
/** Maximum number of announced transactions from a peer */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
/** How many microseconds to delay requesting transactions from inbound peers */
-static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; // 2 seconds
+static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
-static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; // 1 minute
+static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}};
/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
-static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; // 2 seconds
+static constexpr std::chrono::microseconds MAX_GETDATA_RANDOM_DELAY{std::chrono::seconds{2}};
/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
-static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL;
+static constexpr std::chrono::microseconds TX_EXPIRY_INTERVAL{GETDATA_TX_INTERVAL * 10};
static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
"To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY");
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
@@ -340,16 +340,16 @@ struct CNodeState {
/* Track when to attempt download of announced transactions (process
* time in micros -> txid)
*/
- std::multimap<int64_t, uint256> m_tx_process_time;
+ std::multimap<std::chrono::microseconds, uint256> m_tx_process_time;
//! Store all the transactions a peer has recently announced
std::set<uint256> m_tx_announced;
//! Store transactions which were requested by us, with timestamp
- std::map<uint256, int64_t> m_tx_in_flight;
+ std::map<uint256, std::chrono::microseconds> m_tx_in_flight;
//! Periodically check for stuck getdata requests
- int64_t m_check_expiry_timer{0};
+ std::chrono::microseconds m_check_expiry_timer{0};
};
TxDownloadState m_tx_download;
@@ -391,7 +391,7 @@ struct CNodeState {
};
// Keeps track of the time (in microseconds) when transactions were requested last time
-limitedmap<uint256, int64_t> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
+limitedmap<uint256, std::chrono::microseconds> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
/** Map maintaining per-node state. */
static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
@@ -688,16 +688,16 @@ void EraseTxRequest(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
g_already_asked_for.erase(txid);
}
-int64_t GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
auto it = g_already_asked_for.find(txid);
if (it != g_already_asked_for.end()) {
return it->second;
}
- return 0;
+ return {};
}
-void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void UpdateTxRequestTime(const uint256& txid, std::chrono::microseconds request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
auto it = g_already_asked_for.find(txid);
if (it == g_already_asked_for.end()) {
@@ -707,17 +707,17 @@ void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LO
}
}
-int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chrono::microseconds current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- int64_t process_time;
- int64_t last_request_time = GetTxRequestTime(txid);
+ std::chrono::microseconds process_time;
+ const auto last_request_time = GetTxRequestTime(txid);
// First time requesting this tx
- if (last_request_time == 0) {
+ if (last_request_time.count() == 0) {
process_time = current_time;
} else {
// Randomize the delay to avoid biasing some peers over others (such as due to
// fixed ordering of peer processing in ThreadMessageHandler)
- process_time = last_request_time + GETDATA_TX_INTERVAL + GetRand(MAX_GETDATA_RANDOM_DELAY);
+ process_time = last_request_time + GETDATA_TX_INTERVAL + GetRandMicros(MAX_GETDATA_RANDOM_DELAY);
}
// We delay processing announcements from inbound peers
@@ -726,7 +726,7 @@ int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool u
return process_time;
}
-void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void RequestTx(CNodeState* state, const uint256& txid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
@@ -740,7 +740,7 @@ void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_L
// Calculate the time to try requesting this transaction. Use
// fPreferredDownload as a proxy for outbound peers.
- int64_t process_time = CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload);
+ const auto process_time = CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload);
peer_download_state.m_tx_process_time.emplace(process_time, txid);
}
@@ -1305,10 +1305,10 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return true;
}
-static void RelayTransaction(const CTransaction& tx, CConnman* connman)
+void RelayTransaction(const uint256& txid, const CConnman& connman)
{
- CInv inv(MSG_TX, tx.GetHash());
- connman->ForEachNode([&inv](CNode* pnode)
+ CInv inv(MSG_TX, txid);
+ connman.ForEachNode([&inv](CNode* pnode)
{
pnode->PushInventory(inv);
});
@@ -1811,7 +1811,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
if (setMisbehaving.count(fromPeer)) continue;
if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &fMissingInputs2, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanTx, connman);
+ RelayTransaction(orphanHash, *connman);
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
@@ -2223,7 +2223,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
uint32_t nFetchFlags = GetFetchFlags(pfrom);
- int64_t nNow = GetTimeMicros();
+ const auto current_time = GetTime<std::chrono::microseconds>();
for (CInv &inv : vInv)
{
@@ -2255,7 +2255,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
} else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
- RequestTx(State(pfrom->GetId()), inv.hash, nNow);
+ RequestTx(State(pfrom->GetId()), inv.hash, current_time);
}
}
}
@@ -2498,7 +2498,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!AlreadyHave(inv) &&
AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
mempool.check(pcoinsTip.get());
- RelayTransaction(tx, connman);
+ RelayTransaction(tx.GetHash(), *connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
@@ -2529,12 +2529,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (!fRejectedParents) {
uint32_t nFetchFlags = GetFetchFlags(pfrom);
- int64_t nNow = GetTimeMicros();
+ const auto current_time = GetTime<std::chrono::microseconds>();
for (const CTxIn& txin : tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv)) RequestTx(State(pfrom->GetId()), _inv.hash, nNow);
+ if (!AlreadyHave(_inv)) RequestTx(State(pfrom->GetId()), _inv.hash, current_time);
}
AddOrphanTx(ptx, pfrom->GetId());
@@ -2577,7 +2577,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
} else {
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
- RelayTransaction(tx, connman);
+ RelayTransaction(tx.GetHash(), *connman);
}
}
}
@@ -3906,6 +3906,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
+ const auto current_time = GetTime<std::chrono::microseconds>();
+ // nNow is the current system time (GetTimeMicros is not mockable) and
+ // should be replaced by the mockable current_time eventually
nNow = GetTimeMicros();
if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
@@ -3998,9 +4001,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// were unresponsive in the past.
// Eventually we should consider disconnecting peers, but this is
// conservative.
- if (state.m_tx_download.m_check_expiry_timer <= nNow) {
+ if (state.m_tx_download.m_check_expiry_timer <= current_time) {
for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) {
- if (it->second <= nNow - TX_EXPIRY_INTERVAL) {
+ if (it->second <= current_time - TX_EXPIRY_INTERVAL) {
LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId());
state.m_tx_download.m_tx_announced.erase(it->first);
state.m_tx_download.m_tx_in_flight.erase(it++);
@@ -4010,11 +4013,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
// On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
// so that we're not doing this for all peers at the same time.
- state.m_tx_download.m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand(TX_EXPIRY_INTERVAL);
+ state.m_tx_download.m_check_expiry_timer = current_time + TX_EXPIRY_INTERVAL / 2 + GetRandMicros(TX_EXPIRY_INTERVAL);
}
auto& tx_process_time = state.m_tx_download.m_tx_process_time;
- while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
+ while (!tx_process_time.empty() && tx_process_time.begin()->first <= current_time && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
const uint256 txid = tx_process_time.begin()->second;
// Erase this entry from tx_process_time (it may be added back for
// processing at a later time, see below)
@@ -4023,22 +4026,22 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!AlreadyHave(inv)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
- int64_t last_request_time = GetTxRequestTime(inv.hash);
- if (last_request_time <= nNow - GETDATA_TX_INTERVAL) {
+ const auto last_request_time = GetTxRequestTime(inv.hash);
+ if (last_request_time <= current_time - GETDATA_TX_INTERVAL) {
LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
vGetData.push_back(inv);
if (vGetData.size() >= MAX_GETDATA_SZ) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
vGetData.clear();
}
- UpdateTxRequestTime(inv.hash, nNow);
- state.m_tx_download.m_tx_in_flight.emplace(inv.hash, nNow);
+ UpdateTxRequestTime(inv.hash, current_time);
+ state.m_tx_download.m_tx_in_flight.emplace(inv.hash, current_time);
} else {
// This transaction is in flight from someone else; queue
// up processing to happen after the download times out
// (with a slight delay for inbound peers, to prefer
// requests to outbound peers).
- int64_t next_process_time = CalculateTxGetDataTime(txid, nNow, !state.fPreferredDownload);
+ const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload);
tx_process_time.emplace(next_process_time, txid);
}
} else {
diff --git a/src/net_processing.h b/src/net_processing.h
index 39c22d7118..1d26164b18 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -19,6 +19,7 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
/** Default for BIP61 (sending reject messages) */
static constexpr bool DEFAULT_ENABLE_BIP61{false};
+static const bool DEFAULT_PEERBLOOMFILTERS = false;
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
@@ -89,4 +90,7 @@ struct CNodeStateStats {
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
+/** Relay transaction to every node */
+void RelayTransaction(const uint256&, const CConnman& connman);
+
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 78b3b6ae3a..6d4738c835 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -65,6 +65,12 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
{
CNetAddr addr;
+ // From our perspective, onion addresses are not hostnames but rather
+ // direct encodings of CNetAddr much like IPv4 dotted-decimal notation
+ // or IPv6 colon-separated hextet notation. Since we can't use
+ // getaddrinfo to decode them and it wouldn't make sense to resolve
+ // them, we return a network address representing it instead. See
+ // CNetAddr::SetSpecial(const std::string&) for more details.
if (addr.SetSpecial(std::string(pszName))) {
vIP.push_back(addr);
return true;
@@ -74,15 +80,25 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
+ // We want a TCP port, which is a streaming socket type
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
+ // We don't care which address family (IPv4 or IPv6) is returned
aiHint.ai_family = AF_UNSPEC;
+ // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
+ // return addresses whose family we have an address configured for.
+ //
+ // If we don't allow lookups, then use the AI_NUMERICHOST flag for
+ // getaddrinfo to only decode numerical network addresses and suppress
+ // hostname lookups.
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
struct addrinfo *aiRes = nullptr;
int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
if (nErr)
return false;
+ // Traverse the linked list starting with aiTrav, add all non-internal
+ // IPv4,v6 addresses to vIP while respecting nMaxSolutions.
struct addrinfo *aiTrav = aiRes;
while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
{
@@ -112,6 +128,21 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
return (vIP.size() > 0);
}
+/**
+ * Resolve a host string to its corresponding network addresses.
+ *
+ * @param pszName The string representing a host. Could be a name or a numerical
+ * IP address (IPv6 addresses in their bracketed form are
+ * allowed).
+ * @param[out] vIP The resulting network addresses to which the specified host
+ * string resolved.
+ *
+ * @returns Whether or not the specified host string successfully resolved to
+ * any resulting network addresses.
+ *
+ * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int)
+ * for additional parameter descriptions.
+ */
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
{
std::string strHost(pszName);
@@ -124,6 +155,12 @@ bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nM
return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup);
}
+ /**
+ * Resolve a host string to its first corresponding network address.
+ *
+ * @see LookupHost(const char *, std::vector<CNetAddr>&, unsigned int, bool) for
+ * additional parameter descriptions.
+ */
bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup)
{
std::vector<CNetAddr> vIP;
@@ -134,6 +171,26 @@ bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup)
return true;
}
+/**
+ * Resolve a service string to its corresponding service.
+ *
+ * @param pszName The string representing a service. Could be a name or a
+ * numerical IP address (IPv6 addresses should be in their
+ * disambiguated bracketed form), optionally followed by a port
+ * number. (e.g. example.com:8333 or
+ * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
+ * @param[out] vAddr The resulting services to which the specified service string
+ * resolved.
+ * @param portDefault The default port for resulting services if not specified
+ * by the service string.
+ * @param fAllowLookup Whether or not hostname lookups are permitted. If yes,
+ * external queries may be performed.
+ * @param nMaxSolutions The maximum number of results we want, specifying 0
+ * means "as many solutions as we get."
+ *
+ * @returns Whether or not the service string successfully resolved to any
+ * resulting services.
+ */
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
{
if (pszName[0] == 0)
@@ -152,6 +209,12 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault,
return true;
}
+/**
+ * Resolve a service string to its first corresponding service.
+ *
+ * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int)
+ * for additional parameter descriptions.
+ */
bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup)
{
std::vector<CService> vService;
@@ -162,6 +225,16 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo
return true;
}
+/**
+ * Resolve a service string with a numeric IP to its first corresponding
+ * service.
+ *
+ * @returns The resulting CService if the resolution was successful, [::]:0
+ * otherwise.
+ *
+ * @see Lookup(const char *, CService&, int, bool) for additional parameter
+ * descriptions.
+ */
CService LookupNumeric(const char *pszName, int portDefault)
{
CService addr;
@@ -231,22 +304,29 @@ enum class IntrRecvError {
};
/**
- * Read bytes from socket. This will either read the full number of bytes requested
- * or return False on error or timeout.
- * This function can be interrupted by calling InterruptSocks5()
+ * Try to read a specified number of bytes from a socket. Please read the "see
+ * also" section for more detail.
*
- * @param data Buffer to receive into
- * @param len Length of data to receive
- * @param timeout Timeout in milliseconds for receive operation
+ * @param data The buffer where the read bytes should be stored.
+ * @param len The number of bytes to read into the specified buffer.
+ * @param timeout The total timeout in milliseconds for this read.
+ * @param hSocket The socket (has to be in non-blocking mode) from which to read
+ * bytes.
*
- * @note This function requires that hSocket is in non-blocking mode.
+ * @returns An IntrRecvError indicating the resulting status of this read.
+ * IntrRecvError::OK only if all of the specified number of bytes were
+ * read.
+ *
+ * @see This function can be interrupted by calling InterruptSocks5(bool).
+ * Sockets can be made non-blocking with SetSocketNonBlocking(const
+ * SOCKET&, bool).
*/
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
- // Maximum time to wait in one select call. It will take up until this time (in millis)
- // to break off in case of an interruption.
+ // Maximum time to wait for I/O readiness. It will take up until this time
+ // (in millis) to break off in case of an interruption.
const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
@@ -261,11 +341,13 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
if (!IsSelectableSocket(hSocket)) {
return IntrRecvError::NetworkError;
}
+ // Only wait at most maxWait milliseconds at a time, unless
+ // we're approaching the end of the specified total timeout
int timeout_ms = std::min(endTime - curTime, maxWait);
#ifdef USE_POLL
struct pollfd pollfd = {};
pollfd.fd = hSocket;
- pollfd.events = POLLIN | POLLOUT;
+ pollfd.events = POLLIN;
int nRet = poll(&pollfd, 1, timeout_ms);
#else
struct timeval tval = MillisToTimeval(timeout_ms);
@@ -320,7 +402,24 @@ static std::string Socks5ErrorString(uint8_t err)
}
}
-/** Connect using SOCKS5 (as described in RFC1928) */
+/**
+ * Connect to a specified destination service through an already connected
+ * SOCKS5 proxy.
+ *
+ * @param strDest The destination fully-qualified domain name.
+ * @param port The destination port.
+ * @param auth The credentials with which to authenticate with the specified
+ * SOCKS5 proxy.
+ * @param hSocket The SOCKS5 proxy socket.
+ *
+ * @returns Whether or not the operation succeeded.
+ *
+ * @note The specified SOCKS5 proxy socket must already be connected to the
+ * SOCKS5 proxy.
+ *
+ * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
+ * Version 5</a>
+ */
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
{
IntrRecvError recvr;
@@ -328,15 +427,15 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
if (strDest.size() > 255) {
return error("Hostname too long");
}
- // Accepted authentication methods
+ // Construct the version identifier/method selection message
std::vector<uint8_t> vSocks5Init;
- vSocks5Init.push_back(SOCKSVersion::SOCKS5);
+ vSocks5Init.push_back(SOCKSVersion::SOCKS5); // We want the SOCK5 protocol
if (auth) {
- vSocks5Init.push_back(0x02); // Number of methods
+ vSocks5Init.push_back(0x02); // 2 method identifiers follow...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
vSocks5Init.push_back(SOCKS5Method::USER_PASS);
} else {
- vSocks5Init.push_back(0x01); // Number of methods
+ vSocks5Init.push_back(0x01); // 1 method identifier follows...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
@@ -440,8 +539,16 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return true;
}
+/**
+ * Try to create a socket file descriptor with specific properties in the
+ * communications domain (address family) of the specified service.
+ *
+ * For details on the desired properties, see the inline comments in the source
+ * code.
+ */
SOCKET CreateSocket(const CService &addrConnect)
{
+ // Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
@@ -449,10 +556,13 @@ SOCKET CreateSocket(const CService &addrConnect)
return INVALID_SOCKET;
}
+ // Create a TCP socket in the address family of the specified service.
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
return INVALID_SOCKET;
+ // Ensure that waiting for I/O on this socket won't result in undefined
+ // behavior.
if (!IsSelectableSocket(hSocket)) {
CloseSocket(hSocket);
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
@@ -461,17 +571,18 @@ SOCKET CreateSocket(const CService &addrConnect)
#ifdef SO_NOSIGPIPE
int set = 1;
- // Different way of disabling SIGPIPE on BSD
+ // Set the no-sigpipe option on the socket for BSD systems, other UNIXes
+ // should use the MSG_NOSIGNAL flag for every send.
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
#endif
- //Disable Nagle's algorithm
+ // Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
SetSocketNoDelay(hSocket);
- // Set to non-blocking
+ // Set the non-blocking option on the socket.
if (!SetSocketNonBlocking(hSocket, true)) {
CloseSocket(hSocket);
- LogPrintf("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ LogPrintf("CreateSocket: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
}
return hSocket;
}
@@ -486,8 +597,21 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg
}
}
+/**
+ * Try to connect to the specified service on the specified socket.
+ *
+ * @param addrConnect The service to which to connect.
+ * @param hSocket The socket on which to connect.
+ * @param nTimeout Wait this many milliseconds for the connection to be
+ * established.
+ * @param manual_connection Whether or not the connection was manually requested
+ * (e.g. thru the addnode RPC)
+ *
+ * @returns Whether or not a connection was successfully made.
+ */
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection)
{
+ // Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (hSocket == INVALID_SOCKET) {
@@ -498,12 +622,17 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
return false;
}
+
+ // Connect to the addrConnect service on the hSocket socket.
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
// WSAEINVAL is here because some legacy version of winsock uses it
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
{
+ // Connection didn't actually fail, but is being established
+ // asynchronously. Thus, use async I/O api (select/poll)
+ // synchronously to check for successful connection with a timeout.
#ifdef USE_POLL
struct pollfd pollfd = {};
pollfd.fd = hSocket;
@@ -516,6 +645,10 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
FD_SET(hSocket, &fdset);
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
#endif
+ // Upon successful completion, both select and poll return the total
+ // number of file descriptors that have been selected. A value of 0
+ // indicates that the call timed out and no file descriptors have
+ // been selected.
if (nRet == 0)
{
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
@@ -526,6 +659,11 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
return false;
}
+
+ // Even if the select/poll was successful, the connect might not
+ // have been successful. The reason for this failure is hidden away
+ // in the SO_ERROR for the socket in modern systems. We read it into
+ // nRet here.
socklen_t nRetSize = sizeof(nRet);
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR)
{
@@ -569,6 +707,22 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
return true;
}
+/**
+ * Set the name proxy to use for all connections to nodes specified by a
+ * hostname. After setting this proxy, connecting to a node sepcified by a
+ * hostname won't result in a local lookup of said hostname, rather, connect to
+ * the node by asking the name proxy for a proxy connection to the hostname,
+ * effectively delegating the hostname lookup to the specified proxy.
+ *
+ * This delegation increases privacy for those who set the name proxy as they no
+ * longer leak their external hostname queries to their DNS servers.
+ *
+ * @returns Whether or not the operation succeeded.
+ *
+ * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5
+ * server in common use (most notably Tor) actually implements UDP
+ * support, and a DNS resolver is beyond the scope of this project.
+ */
bool SetNameProxy(const proxyType &addrProxy) {
if (!addrProxy.IsValid())
return false;
@@ -599,6 +753,21 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}
+/**
+ * Connect to a specified destination service through a SOCKS5 proxy by first
+ * connecting to the SOCKS5 proxy.
+ *
+ * @param proxy The SOCKS5 proxy.
+ * @param strDest The destination service to which to connect.
+ * @param port The destination port.
+ * @param hSocket The socket on which to connect to the SOCKS5 proxy.
+ * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
+ * proxy to be established.
+ * @param outProxyConnectionFailed[out] Whether or not the connection to the
+ * SOCKS5 proxy failed.
+ *
+ * @returns Whether or not the operation succeeded.
+ */
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed)
{
// first connect to proxy server
@@ -623,6 +792,17 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int
return true;
}
+/**
+ * Parse and resolve a specified subnet string into the appropriate internal
+ * representation.
+ *
+ * @param pszName A string representation of a subnet of the form `network
+ * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g.
+ * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`).
+ * @param ret The resulting internal representation of a subnet.
+ *
+ * @returns Whether the operation succeeded or not.
+ */
bool LookupSubNet(const char* pszName, CSubNet& ret)
{
std::string strSubnet(pszName);
@@ -630,6 +810,8 @@ bool LookupSubNet(const char* pszName, CSubNet& ret)
std::vector<CNetAddr> vIP;
std::string strAddress = strSubnet.substr(0, slash);
+ // TODO: Use LookupHost(const char *, CNetAddr&, bool) instead to just get
+ // one CNetAddr.
if (LookupHost(strAddress.c_str(), vIP, 1, false))
{
CNetAddr network = vIP[0];
@@ -637,8 +819,8 @@ bool LookupSubNet(const char* pszName, CSubNet& ret)
{
std::string strNetmask = strSubnet.substr(slash + 1);
int32_t n;
- // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- if (ParseInt32(strNetmask, &n)) { // If valid number, assume /24 syntax
+ if (ParseInt32(strNetmask, &n)) {
+ // If valid number, assume CIDR variable-length subnet masking
ret = CSubNet(network, n);
return ret.IsValid();
}
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 5ffb15ed3c..8e56496358 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -5,6 +5,7 @@
#include <consensus/validation.h>
#include <net.h>
+#include <net_processing.h>
#include <txmempool.h>
#include <util/validation.h>
#include <validation.h>
@@ -13,26 +14,30 @@
#include <future>
-TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
+TransactionError BroadcastTransaction(const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
{
+ assert(g_connman);
std::promise<void> promise;
- hashTx = tx->GetHash();
+ uint256 hashTx = tx->GetHash();
+ bool callback_set = false;
{ // cs_main scope
LOCK(cs_main);
+ // If the transaction is already confirmed in the chain, don't do anything
+ // and return early.
CCoinsViewCache &view = *pcoinsTip;
- bool fHaveChain = false;
- for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
+ for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
- fHaveChain = !existingCoin.IsSpent();
+ // IsSpent doesnt mean the coin is spent, it means the output doesnt' exist.
+ // So if the output does exist, then this transaction exists in the chain.
+ if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
}
- bool fHaveMempool = mempool.exists(hashTx);
- if (!fHaveMempool && !fHaveChain) {
- // push to local node and sync with wallets
+ if (!mempool.exists(hashTx)) {
+ // Transaction is not already in the mempool. Submit it.
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, highfee)) {
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
if (state.IsInvalid()) {
err_string = FormatStateMessage(state);
return TransactionError::MEMPOOL_REJECTED;
@@ -43,36 +48,37 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx,
err_string = FormatStateMessage(state);
return TransactionError::MEMPOOL_ERROR;
}
- } else {
- // If wallet is enabled, ensure that the wallet has been made aware
- // of the new transaction prior to returning. This prevents a race
- // where a user might call sendrawtransaction with a transaction
- // to/from their wallet, immediately call some wallet RPC, and get
- // a stale result because callbacks have not yet been processed.
+ }
+
+ // Transaction was accepted to the mempool.
+
+ if (wait_callback) {
+ // For transactions broadcast from outside the wallet, make sure
+ // that the wallet has been notified of the transaction before
+ // continuing.
+ //
+ // This prevents a race where a user might call sendrawtransaction
+ // with a transaction to/from their wallet, immediately call some
+ // wallet RPC, and get a stale result because callbacks have not
+ // yet been processed.
CallFunctionInValidationInterfaceQueue([&promise] {
promise.set_value();
});
+ callback_set = true;
}
- } else if (fHaveChain) {
- return TransactionError::ALREADY_IN_CHAIN;
- } else {
- // Make sure we don't block forever if re-sending
- // a transaction already in mempool.
- promise.set_value();
}
} // cs_main
- promise.get_future().wait();
-
- if (!g_connman) {
- return TransactionError::P2P_DISABLED;
+ if (callback_set) {
+ // Wait until Validation Interface clients have been notified of the
+ // transaction entering the mempool.
+ promise.get_future().wait();
}
- CInv inv(MSG_TX, hashTx);
- g_connman->ForEachNode([&inv](CNode* pnode) {
- pnode->PushInventory(inv);
- });
+ if (relay) {
+ RelayTransaction(hashTx, *g_connman);
+ }
return TransactionError::OK;
}
diff --git a/src/node/transaction.h b/src/node/transaction.h
index 51033f94e5..cf64fc28d9 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -11,14 +11,21 @@
#include <util/error.h>
/**
- * Broadcast a transaction
+ * Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
+ *
+ * Mempool submission can be synchronous (will await mempool entry notification
+ * over the CValidationInterface) or asynchronous (will submit and not wait for
+ * notification), depending on the value of wait_callback. wait_callback MUST
+ * NOT be set while cs_main, cs_mempool or cs_wallet are held to avoid
+ * deadlock.
*
* @param[in] tx the transaction to broadcast
- * @param[out] &txid the txid of the transaction, if successfully broadcast
* @param[out] &err_string reference to std::string to fill with error string if available
- * @param[in] highfee Reject txs with fees higher than this (if 0, accept any fee)
+ * @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
+ * @param[in] relay flag if both mempool insertion and p2p relay are requested
+ * @param[in] wait_callback, wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
* return error
*/
-NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, uint256& txid, std::string& err_string, const CAmount& highfee);
+NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback);
#endif // BITCOIN_NODE_TRANSACTION_H
diff --git a/src/noui.cpp b/src/noui.cpp
index caab9f326e..c07939cc79 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -13,6 +13,12 @@
#include <string>
#include <boost/signals2/connection.hpp>
+#include <boost/signals2/signal.hpp>
+
+/** Store connections so we can disconnect them when suppressing output */
+boost::signals2::connection noui_ThreadSafeMessageBoxConn;
+boost::signals2::connection noui_ThreadSafeQuestionConn;
+boost::signals2::connection noui_InitMessageConn;
bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style)
{
@@ -57,7 +63,39 @@ void noui_InitMessage(const std::string& message)
void noui_connect()
{
- uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
- uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
- uiInterface.InitMessage_connect(noui_InitMessage);
+ noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
+ noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
+ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage);
+}
+
+bool noui_ThreadSafeMessageBoxSuppressed(const std::string& message, const std::string& caption, unsigned int style)
+{
+ return false;
+}
+
+bool noui_ThreadSafeQuestionSuppressed(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
+{
+ return false;
}
+
+void noui_InitMessageSuppressed(const std::string& message)
+{
+}
+
+void noui_suppress()
+{
+ noui_ThreadSafeMessageBoxConn.disconnect();
+ noui_ThreadSafeQuestionConn.disconnect();
+ noui_InitMessageConn.disconnect();
+ noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBoxSuppressed);
+ noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionSuppressed);
+ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessageSuppressed);
+}
+
+void noui_reconnect()
+{
+ noui_ThreadSafeMessageBoxConn.disconnect();
+ noui_ThreadSafeQuestionConn.disconnect();
+ noui_InitMessageConn.disconnect();
+ noui_connect();
+} \ No newline at end of file
diff --git a/src/noui.h b/src/noui.h
index 79a79a9af2..854aeeacca 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -17,4 +17,10 @@ void noui_InitMessage(const std::string& message);
/** Connect all bitcoind signal handlers */
void noui_connect();
+/** Suppress all bitcoind signal handlers. Used to suppress output during test runs that produce expected errors */
+void noui_suppress();
+
+/** Reconnects the regular Non-GUI handlers after having used noui_suppress */
+void noui_reconnect();
+
#endif // BITCOIN_NOUI_H
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 73ffb801f2..bcaa05f4b6 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -5,9 +5,10 @@
#include <outputtype.h>
-#include <keystore.h>
#include <pubkey.h>
#include <script/script.h>
+#include <script/sign.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <assert.h>
@@ -73,7 +74,7 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
}
}
-CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType type)
+CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType type)
{
// Add script to keystore
keystore.AddCScript(script);
@@ -98,4 +99,3 @@ CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript&
default: assert(false);
}
}
-
diff --git a/src/outputtype.h b/src/outputtype.h
index 6c30fd1950..6acbaa2f3e 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -7,7 +7,7 @@
#define BITCOIN_OUTPUTTYPE_H
#include <attributes.h>
-#include <keystore.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <string>
@@ -44,7 +44,7 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
* This function will automatically add the script (and any other
* necessary scripts) to the keystore.
*/
-CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType);
+CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType);
#endif // BITCOIN_OUTPUTTYPE_H
diff --git a/src/protocol.h b/src/protocol.h
index a790a06906..91d043947b 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// 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.
@@ -261,9 +261,6 @@ enum ServiceFlags : uint64_t {
// NODE_WITNESS indicates that a node can be asked for blocks and transactions including
// witness data.
NODE_WITNESS = (1 << 3),
- // NODE_XTHIN means the node supports Xtreme Thinblocks
- // If this is turned off then the node will not service nor make xthin requests
- NODE_XTHIN = (1 << 4),
// NODE_NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only
// serving the last 288 (2 day) blocks
// See BIP159 for details on how this is implemented.
diff --git a/src/psbt.h b/src/psbt.h
index f3840b9ed3..6d77db0c6f 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -12,6 +12,7 @@
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index fa6c9c9f7a..29423db3d0 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -358,12 +358,15 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
+
+ // Add entry
+ walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send");
}
else if(type == Receive)
{
// Generate a new address to associate with given label
- CPubKey newKey;
- if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
+ CTxDestination dest;
+ if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
{
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
@@ -372,23 +375,18 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
- if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
+ if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
}
- walletModel->wallet().learnRelatedScripts(newKey, address_type);
- strAddress = EncodeDestination(GetDestinationForKey(newKey, address_type));
+ strAddress = EncodeDestination(dest);
}
else
{
return QString();
}
-
- // Add entry
- walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel,
- (type == Send ? "send" : "receive"));
return QString::fromStdString(strAddress);
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index cf0b48b545..482bf0543d 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -10,8 +10,8 @@
#include <qt/bitcoingui.h>
#include <chainparams.h>
-#include <qt/clientmodel.h>
#include <fs.h>
+#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/intro.h>
@@ -25,20 +25,18 @@
#ifdef ENABLE_WALLET
#include <qt/paymentserver.h>
#include <qt/walletcontroller.h>
-#endif
+#include <qt/walletmodel.h>
+#endif // ENABLE_WALLET
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <noui.h>
-#include <util/threadnames.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
+#include <util/threadnames.h>
#include <memory>
-#include <stdint.h>
-
-#include <boost/thread.hpp>
#include <QApplication>
#include <QDebug>
@@ -210,12 +208,6 @@ BitcoinApplication::~BitcoinApplication()
delete window;
window = nullptr;
-#ifdef ENABLE_WALLET
- delete paymentServer;
- paymentServer = nullptr;
- delete m_wallet_controller;
- m_wallet_controller = nullptr;
-#endif
delete optionsModel;
optionsModel = nullptr;
delete platformStyle;
@@ -331,24 +323,21 @@ void BitcoinApplication::initializeResult(bool success)
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qInfo() << "Platform customization:" << platformStyle->getName();
-#ifdef ENABLE_WALLET
- m_wallet_controller = new WalletController(m_node, platformStyle, optionsModel, this);
-#ifdef ENABLE_BIP70
- PaymentServer::LoadRootCAs();
-#endif
- if (paymentServer) {
- paymentServer->setOptionsModel(optionsModel);
-#ifdef ENABLE_BIP70
- connect(m_wallet_controller, &WalletController::coinsSent, paymentServer, &PaymentServer::fetchPaymentACK);
-#endif
- }
-#endif
-
clientModel = new ClientModel(m_node, optionsModel);
window->setClientModel(clientModel);
#ifdef ENABLE_WALLET
- window->setWalletController(m_wallet_controller);
+ if (WalletModel::isWalletEnabled()) {
+ m_wallet_controller = new WalletController(m_node, platformStyle, optionsModel, this);
+ window->setWalletController(m_wallet_controller);
+ if (paymentServer) {
+ paymentServer->setOptionsModel(optionsModel);
+#ifdef ENABLE_BIP70
+ PaymentServer::LoadRootCAs();
+ connect(m_wallet_controller, &WalletController::coinsSent, paymentServer, &PaymentServer::fetchPaymentACK);
#endif
+ }
+ }
+#endif // ENABLE_WALLET
// If -min option passed, start window minimized (iconified) or minimized to tray
if (!gArgs.GetBoolArg("-min", false)) {
@@ -402,15 +391,15 @@ WId BitcoinApplication::getMainWinId() const
static void SetupUIArgs()
{
#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
- gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
+ gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
#endif
- gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
- gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", false, OptionsCategory::GUI);
- gArgs.AddArg("-min", "Start minimized", false, OptionsCategory::GUI);
- gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", false, OptionsCategory::GUI);
- gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", false, OptionsCategory::GUI);
- gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), false, OptionsCategory::GUI);
- gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI);
+ gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
}
int GuiMain(int argc, char* argv[])
@@ -448,6 +437,9 @@ int GuiMain(int argc, char* argv[])
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
+#ifdef ENABLE_WALLET
+ qRegisterMetaType<WalletModel*>();
+#endif
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType< CAmount >("CAmount");
@@ -459,8 +451,11 @@ int GuiMain(int argc, char* argv[])
SetupUIArgs();
std::string error;
if (!node->parseParameters(argc, argv, error)) {
+ node->initError(strprintf("Error parsing command line arguments: %s\n", error));
+ // Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
- QObject::tr("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
+ // message can not be translated because translations have not been initialized
+ QString::fromStdString("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
@@ -496,11 +491,13 @@ int GuiMain(int argc, char* argv[])
/// - Do not call GetDataDir(true) before this step finishes
if (!fs::is_directory(GetDataDir(false)))
{
+ node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
if (!node->readConfigFiles(error)) {
+ node->initError(strprintf("Error reading configuration file: %s\n", error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
@@ -516,6 +513,7 @@ int GuiMain(int argc, char* argv[])
try {
node->selectParams(gArgs.GetChainName());
} catch(std::exception &e) {
+ node->initError(strprintf("%s\n", e.what()));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
@@ -543,8 +541,10 @@ int GuiMain(int argc, char* argv[])
// Start up the payment server early, too, so impatient users that click on
// bitcoin: links repeatedly have their payment requests routed to this process:
- app.createPaymentServer();
-#endif
+ if (WalletModel::isWalletEnabled()) {
+ app.createPaymentServer();
+ }
+#endif // ENABLE_WALLET
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 370712d953..40537c1813 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -11,7 +11,6 @@
#include <QApplication>
#include <memory>
-#include <vector>
class BitcoinGUI;
class ClientModel;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 266e16ebeb..3533227483 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -36,9 +36,6 @@
#include <ui_interface.h>
#include <util/system.h>
-#include <iostream>
-#include <memory>
-
#include <QAction>
#include <QApplication>
#include <QComboBox>
@@ -1375,12 +1372,13 @@ static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, co
style &= ~CClientUIInterface::SECURE;
bool ret = false;
// In case of modal message, use blocking connection to wait for user to click a button
- QMetaObject::invokeMethod(gui, "message",
+ bool invoked = QMetaObject::invokeMethod(gui, "message",
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(unsigned int, style),
Q_ARG(bool*, &ret));
+ assert(invoked);
return ret;
}
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index ce950150df..238be08480 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -184,34 +184,39 @@ void ClientModel::updateBanlist()
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
{
// emits signal "showProgress"
- QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(title)),
Q_ARG(int, nProgress));
+ assert(invoked);
}
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
{
// Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
- QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
Q_ARG(int, newNumConnections));
+ assert(invoked);
}
static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
{
- QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
Q_ARG(bool, networkActive));
+ assert(invoked);
}
static void NotifyAlertChanged(ClientModel *clientmodel)
{
qDebug() << "NotifyAlertChanged";
- QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
+ assert(invoked);
}
static void BannedListChanged(ClientModel *clientmodel)
{
qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
- QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
+ assert(invoked);
}
static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, double verificationProgress, bool fHeader)
@@ -233,11 +238,12 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig
// if we are in-sync or if we notify a header update, update the UI regardless of last update time
if (fHeader || !initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass an async signal to the UI thread
- QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
Q_ARG(int, height),
Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)),
Q_ARG(double, verificationProgress),
Q_ARG(bool, fHeader));
+ assert(invoked);
nLastUpdateNotification = now;
}
}
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 6b9f79aaf8..03d18d2845 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -418,7 +418,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (amount > 0)
{
- CTxOut txout(amount, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
+ // Assumes a p2pkh script size
+ CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
fDust |= IsDust(txout, model->node().getDustRelayFee());
}
@@ -509,7 +510,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < MIN_CHANGE)
{
- CTxOut txout(nChange, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
+ // Assumes a p2pkh script size
+ CTxOut txout(nChange, CScript() << std::vector<unsigned char>(24, 0));
if (IsDust(txout, model->node().getDustRelayFee()))
{
nPayFee += nChange;
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 70e52c9f1d..dc1da7f8a9 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-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.
@@ -11,10 +11,10 @@
#include <base58.h>
#include <chainparams.h>
-#include <primitives/transaction.h>
-#include <key_io.h>
#include <interfaces/node.h>
+#include <key_io.h>
#include <policy/policy.h>
+#include <primitives/transaction.h>
#include <protocol.h>
#include <script/script.h>
#include <script/standard.h>
@@ -639,7 +639,7 @@ fs::path static GetAutostartFilePath()
std::string chain = gArgs.GetChainName();
if (chain == CBaseChainParams::MAIN)
return GetAutostartDir() / "bitcoin.desktop";
- return GetAutostartDir() / strprintf("bitcoin-%s.lnk", chain);
+ return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain);
}
bool GetStartOnSystemStartup()
@@ -841,9 +841,6 @@ QString formatServicesStr(quint64 mask)
case NODE_WITNESS:
strList.append("WITNESS");
break;
- case NODE_XTHIN:
- strList.append("XTHIN");
- break;
default:
strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check));
}
diff --git a/src/qt/main.cpp b/src/qt/main.cpp
index 6a3c2249d1..999c434d23 100644
--- a/src/qt/main.cpp
+++ b/src/qt/main.cpp
@@ -4,6 +4,8 @@
#include <qt/bitcoin.h>
+#include <util/translation.h>
+
#include <QCoreApplication>
#include <functional>
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index c99515fe1c..f3f5d28af9 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -666,16 +666,14 @@ void PaymentServer::fetchPaymentACK(WalletModel* walletModel, const SendCoinsRec
payment.add_transactions(transaction.data(), transaction.size());
// Create a new refund address, or re-use:
- CPubKey newKey;
- if (walletModel->wallet().getKeyFromPool(false /* internal */, newKey)) {
+ CTxDestination dest;
+ const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
+ if (walletModel->wallet().getNewDestination(change_type, "", dest)) {
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
// types supported by the receiver. As a result, we choose the address format we also
// use for change. Despite an actual payment and not change, this is a close match:
// it's the output type we use subject to privacy issues, but not restricted by what
// other software supports.
- const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
- walletModel->wallet().learnRelatedScripts(newKey, change_type);
- CTxDestination dest = GetDestinationForKey(newKey, change_type);
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();
walletModel->wallet().setAddressBook(dest, label, "refund");
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 84b4a2d0d8..cdf84eae9a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1265,11 +1265,6 @@ void RPCConsole::showOrHideBanTableIfRequired()
ui->banHeading->setVisible(visible);
}
-RPCConsole::TabTypes RPCConsole::tabFocus() const
-{
- return (TabTypes) ui->tabWidget->currentIndex();
-}
-
void RPCConsole::setTabFocus(enum TabTypes tabType)
{
ui->tabWidget->setCurrentIndex(tabType);
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 79b0f3b19c..38015e38fd 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -67,7 +67,6 @@ public:
std::vector<TabTypes> tabs() const { return {TAB_INFO, TAB_CONSOLE, TAB_GRAPH, TAB_PEERS}; }
- TabTypes tabFocus() const;
QString tabTitle(TabTypes tab_type) const;
protected:
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index cb9efe9319..193fba78b1 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -392,7 +392,7 @@ void SendCoinsDialog::on_sendButton_clicked()
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
- Q_EMIT coinsSent(currentTransaction.getWtx()->get().GetHash());
+ Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
}
fNewRecipientAllowed = true;
}
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index dcb9d1923f..5bceb1f945 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -16,6 +16,7 @@
#include <interfaces/wallet.h>
#include <ui_interface.h>
#include <util/system.h>
+#include <util/translation.h>
#include <version.h>
#include <QApplication>
@@ -156,18 +157,19 @@ void SplashScreen::finish()
static void InitMessage(SplashScreen *splash, const std::string &message)
{
- QMetaObject::invokeMethod(splash, "showMessage",
+ bool invoked = QMetaObject::invokeMethod(splash, "showMessage",
Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter),
Q_ARG(QColor, QColor(55,55,55)));
+ assert(invoked);
}
static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
InitMessage(splash, title + std::string("\n") +
- (resume_possible ? _("(press q to shutdown and continue later)")
- : _("press q to shutdown")) +
+ (resume_possible ? _("(press q to shutdown and continue later)").translated
+ : _("press q to shutdown").translated) +
strprintf("\n%d", nProgress) + "%");
}
#ifdef ENABLE_WALLET
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index e54915ec75..120dff95c0 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -68,7 +68,8 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
if (status == CT_NEW) txid = hash;
}));
ConfirmSend();
- QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
+ bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
+ assert(invoked);
return txid;
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 6fe35b13cf..1064c60dfd 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -687,10 +687,11 @@ public:
{
QString strHash = QString::fromStdString(hash.GetHex());
qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status);
- QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
Q_ARG(QString, strHash),
Q_ARG(int, status),
Q_ARG(bool, showTransaction));
+ assert(invoked);
}
private:
uint256 hash;
@@ -725,12 +726,16 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i
if (nProgress == 100)
{
fQueueNotifications = false;
- if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
- QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
+ if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons
+ bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
+ assert(invoked);
+ }
for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
{
- if (vQueueNotifications.size() - i <= 10)
- QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
+ if (vQueueNotifications.size() - i <= 10) {
+ bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
+ assert(invoked);
+ }
vQueueNotifications[i].invoke(ttm);
}
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 4d489d7f44..2aedb77798 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -124,7 +124,8 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
} else {
// Handler callback runs in a different thread so fix wallet model thread affinity.
wallet_model->moveToThread(thread());
- QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
+ bool invoked = QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
+ assert(invoked);
}
return wallet_model;
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index c1eba61749..49a13330ec 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -221,9 +221,12 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return TransactionCreationFailed;
}
- // Reject absurdly high fee
- if (nFeeRequired > m_wallet->getDefaultMaxTxFee())
+ // Reject absurdly high fee. (This can never happen because the
+ // wallet never creates transactions with fee greater than
+ // m_default_max_tx_fee. This merely a belt-and-suspenders check).
+ if (nFeeRequired > m_wallet->getDefaultMaxTxFee()) {
return AbsurdFee;
+ }
}
return SendCoinsReturn(OK);
@@ -258,11 +261,11 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
auto& newTx = transaction.getWtx();
std::string rejectReason;
- if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), rejectReason))
+ if (!wallet().commitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), rejectReason))
return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << newTx->get();
+ ssTx << *newTx;
transaction_array.append(&(ssTx[0]), ssTx.size());
}
@@ -374,13 +377,15 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
static void NotifyUnload(WalletModel* walletModel)
{
qDebug() << "NotifyUnload";
- QMetaObject::invokeMethod(walletModel, "unload");
+ bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
+ assert(invoked);
}
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
{
qDebug() << "NotifyKeyStoreStatusChanged";
- QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
+ assert(invoked);
}
static void NotifyAddressBookChanged(WalletModel *walletmodel,
@@ -392,38 +397,43 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel,
QString strPurpose = QString::fromStdString(purpose);
qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
- QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
Q_ARG(QString, strAddress),
Q_ARG(QString, strLabel),
Q_ARG(bool, isMine),
Q_ARG(QString, strPurpose),
Q_ARG(int, status));
+ assert(invoked);
}
static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status)
{
Q_UNUSED(hash);
Q_UNUSED(status);
- QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
+ assert(invoked);
}
static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
{
// emits signal "showProgress"
- QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(title)),
Q_ARG(int, nProgress));
+ assert(invoked);
}
static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly)
{
- QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection,
Q_ARG(bool, fHaveWatchonly));
+ assert(invoked);
}
static void NotifyCanGetAddressesChanged(WalletModel* walletmodel)
{
- QMetaObject::invokeMethod(walletmodel, "canGetAddressesChanged");
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "canGetAddressesChanged");
+ assert(invoked);
}
void WalletModel::subscribeToCoreSignals()
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 8c0dc276b0..d00ccf70d9 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -21,14 +21,14 @@ QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const
return recipients;
}
-std::unique_ptr<interfaces::PendingWalletTx>& WalletModelTransaction::getWtx()
+CTransactionRef& WalletModelTransaction::getWtx()
{
return wtx;
}
unsigned int WalletModelTransaction::getTransactionSize()
{
- return wtx ? GetVirtualTransactionSize(wtx->get()) : 0;
+ return wtx ? GetVirtualTransactionSize(*wtx) : 0;
}
CAmount WalletModelTransaction::getTransactionFee() const
@@ -43,7 +43,7 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee)
void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
- const CTransaction* walletTransaction = &wtx->get();
+ const CTransaction* walletTransaction = wtx.get();
int i = 0;
for (QList<SendCoinsRecipient>::iterator it = recipients.begin(); it != recipients.end(); ++it)
{
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 289aee847b..a41d8f2457 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -16,7 +16,6 @@ class SendCoinsRecipient;
namespace interfaces {
class Node;
-class PendingWalletTx;
}
/** Data model for a walletmodel transaction. */
@@ -27,7 +26,7 @@ public:
QList<SendCoinsRecipient> getRecipients() const;
- std::unique_ptr<interfaces::PendingWalletTx>& getWtx();
+ CTransactionRef& getWtx();
unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee);
@@ -39,7 +38,7 @@ public:
private:
QList<SendCoinsRecipient> recipients;
- std::unique_ptr<interfaces::PendingWalletTx> wtx;
+ CTransactionRef wtx;
CAmount fee;
};
diff --git a/src/random.cpp b/src/random.cpp
index de26e6de1a..675b177af3 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -667,6 +667,11 @@ uint64_t GetRand(uint64_t nMax) noexcept
return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
}
+std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept
+{
+ return std::chrono::microseconds{GetRand(duration_max.count())};
+}
+
int GetRandInt(int nMax) noexcept
{
return GetRand(nMax);
diff --git a/src/random.h b/src/random.h
index 75d037738d..22801ec155 100644
--- a/src/random.h
+++ b/src/random.h
@@ -10,7 +10,8 @@
#include <crypto/common.h>
#include <uint256.h>
-#include <stdint.h>
+#include <chrono> // For std::chrono::microseconds
+#include <cstdint>
#include <limits>
/**
@@ -69,6 +70,7 @@
*/
void GetRandBytes(unsigned char* buf, int num) noexcept;
uint64_t GetRand(uint64_t nMax) noexcept;
+std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept;
int GetRandInt(int nMax) noexcept;
uint256 GetRandHash() noexcept;
diff --git a/src/rest.cpp b/src/rest.cpp
index ab409947d3..eba7aae50f 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -12,6 +12,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
+#include <rpc/protocol.h>
#include <rpc/server.h>
#include <streams.h>
#include <sync.h>
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 743efc1e65..b7dcd59c6d 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -165,8 +165,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
static UniValue getblockcount(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getblockcount",
"\nReturns the height of the most-work fully-validated chain.\n"
"The genesis block has height 0.\n",
@@ -178,7 +176,7 @@ static UniValue getblockcount(const JSONRPCRequest& request)
HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
return ::ChainActive().Height();
@@ -186,8 +184,6 @@ static UniValue getblockcount(const JSONRPCRequest& request)
static UniValue getbestblockhash(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getbestblockhash",
"\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
@@ -198,7 +194,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
return ::ChainActive().Tip()->GetBlockHash().GetHex();
@@ -216,8 +212,6 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
static UniValue waitfornewblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"waitfornewblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
@@ -234,7 +228,7 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
HelpExampleCli("waitfornewblock", "1000")
+ HelpExampleRpc("waitfornewblock", "1000")
},
- }.ToString());
+ }.Check(request);
int timeout = 0;
if (!request.params[0].isNull())
timeout = request.params[0].get_int();
@@ -257,8 +251,6 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
static UniValue waitforblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"waitforblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
@@ -276,7 +268,7 @@ static UniValue waitforblock(const JSONRPCRequest& request)
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
},
- }.ToString());
+ }.Check(request);
int timeout = 0;
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -302,8 +294,6 @@ static UniValue waitforblock(const JSONRPCRequest& request)
static UniValue waitforblockheight(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"waitforblockheight",
"\nWaits for (at least) block height and returns the height and hash\n"
"of the current tip.\n"
@@ -322,7 +312,7 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
HelpExampleCli("waitforblockheight", "\"100\", 1000")
+ HelpExampleRpc("waitforblockheight", "\"100\", 1000")
},
- }.ToString());
+ }.Check(request);
int timeout = 0;
int height = request.params[0].get_int();
@@ -347,8 +337,6 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 0) {
- throw std::runtime_error(
RPCHelpMan{"syncwithvalidationinterfacequeue",
"\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
{},
@@ -357,16 +345,14 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
HelpExampleCli("syncwithvalidationinterfacequeue","")
+ HelpExampleRpc("syncwithvalidationinterfacequeue","")
},
- }.ToString());
- }
+ }.Check(request);
+
SyncWithValidationInterfaceQueue();
return NullUniValue;
}
static UniValue getdifficulty(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getdifficulty",
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
{},
@@ -377,7 +363,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
return GetDifficulty(::ChainActive().Tip());
@@ -504,8 +490,6 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
static UniValue getrawmempool(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"getrawmempool",
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
@@ -528,7 +512,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
HelpExampleCli("getrawmempool", "true")
+ HelpExampleRpc("getrawmempool", "true")
},
- }.ToString());
+ }.Check(request);
bool fVerbose = false;
if (!request.params[0].isNull())
@@ -539,8 +523,6 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"getmempoolancestors",
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
{
@@ -566,8 +548,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
+ HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
},
- }.ToString());
- }
+ }.Check(request);
bool fVerbose = false;
if (!request.params[1].isNull())
@@ -609,8 +590,6 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"getmempooldescendants",
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
{
@@ -636,8 +615,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
+ HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
},
- }.ToString());
- }
+ }.Check(request);
bool fVerbose = false;
if (!request.params[1].isNull())
@@ -679,8 +657,6 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
static UniValue getmempoolentry(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"getmempoolentry",
"\nReturns mempool data for given transaction\n",
{
@@ -695,8 +671,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
},
- }.ToString());
- }
+ }.Check(request);
uint256 hash = ParseHashV(request.params[0], "parameter 1");
@@ -715,8 +690,6 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
static UniValue getblockhash(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"getblockhash",
"\nReturns hash of block in best-block-chain at height provided.\n",
{
@@ -729,7 +702,7 @@ static UniValue getblockhash(const JSONRPCRequest& request)
HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -743,8 +716,6 @@ static UniValue getblockhash(const JSONRPCRequest& request)
static UniValue getblockheader(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getblockheader",
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
"If verbose is true, returns an Object with information about blockheader <hash>.\n",
@@ -780,7 +751,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.ToString());
+ }.Check(request);
uint256 hash(ParseHashV(request.params[0], "hash"));
@@ -846,7 +817,7 @@ static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
static UniValue getblock(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"getblock",
+ RPCHelpMan{"getblock",
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbosity is 1, returns an Object with information about block <hash>.\n"
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
@@ -898,11 +869,7 @@ static UniValue getblock(const JSONRPCRequest& request)
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -1013,8 +980,6 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
static UniValue pruneblockchain(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"pruneblockchain", "",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
@@ -1027,7 +992,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
HelpExampleCli("pruneblockchain", "1000")
+ HelpExampleRpc("pruneblockchain", "1000")
},
- }.ToString());
+ }.Check(request);
if (!fPruneMode)
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
@@ -1071,8 +1036,6 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
@@ -1093,7 +1056,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
},
- }.ToString());
+ }.Check(request);
UniValue ret(UniValue::VOBJ);
@@ -1116,8 +1079,6 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
UniValue gettxout(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"gettxout",
"\nReturns details about an unspent transaction output.\n",
{
@@ -1151,7 +1112,7 @@ UniValue gettxout(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -1197,8 +1158,6 @@ static UniValue verifychain(const JSONRPCRequest& request)
{
int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL);
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
- if (request.fHelp || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
@@ -1212,7 +1171,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -1297,8 +1256,6 @@ static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::
UniValue getblockchaininfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
@@ -1350,7 +1307,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -1417,8 +1374,6 @@ struct CompareBlocksByHeight
static UniValue getchaintips(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getchaintips",
"Return information about all known tips in the block tree,"
" including the main chain as well as orphaned branches.\n",
@@ -1449,14 +1404,14 @@ static UniValue getchaintips(const JSONRPCRequest& request)
HelpExampleCli("getchaintips", "")
+ HelpExampleRpc("getchaintips", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
/*
* Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm:
- * - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
+ * - Make one pass through g_blockman.m_block_index, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
* - add ::ChainActive().Tip()
*/
@@ -1464,7 +1419,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
std::set<const CBlockIndex*> setOrphans;
std::set<const CBlockIndex*> setPrevs;
- for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : ::BlockIndex())
{
if (!::ChainActive().Contains(item.second)) {
setOrphans.insert(item.second);
@@ -1540,8 +1495,6 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
static UniValue getmempoolinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getmempoolinfo",
"\nReturns details on the active state of the TX memory pool.\n",
{},
@@ -1560,15 +1513,13 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
HelpExampleCli("getmempoolinfo", "")
+ HelpExampleRpc("getmempoolinfo", "")
},
- }.ToString());
+ }.Check(request);
return MempoolInfoToJSON(::mempool);
}
static UniValue preciousblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"preciousblock",
"\nTreats a block as if it were received before others with the same work.\n"
"\nA later preciousblock call can override the effect of an earlier one.\n"
@@ -1581,7 +1532,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
HelpExampleCli("preciousblock", "\"blockhash\"")
+ HelpExampleRpc("preciousblock", "\"blockhash\"")
},
- }.ToString());
+ }.Check(request);
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CBlockIndex* pblockindex;
@@ -1606,8 +1557,6 @@ static UniValue preciousblock(const JSONRPCRequest& request)
static UniValue invalidateblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"invalidateblock",
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
{
@@ -1618,7 +1567,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
HelpExampleCli("invalidateblock", "\"blockhash\"")
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
},
- }.ToString());
+ }.Check(request);
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CValidationState state;
@@ -1646,8 +1595,6 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
static UniValue reconsiderblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"reconsiderblock",
"\nRemoves invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n",
@@ -1659,7 +1606,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
HelpExampleCli("reconsiderblock", "\"blockhash\"")
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
},
- }.ToString());
+ }.Check(request);
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -1685,8 +1632,6 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
static UniValue getchaintxstats(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getchaintxstats",
"\nCompute statistics about the total number and rate of transactions in the chain.\n",
{
@@ -1708,7 +1653,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
HelpExampleCli("getchaintxstats", "")
+ HelpExampleRpc("getchaintxstats", "2016")
},
- }.ToString());
+ }.Check(request);
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
@@ -1818,7 +1763,7 @@ static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t)
static UniValue getblockstats(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"getblockstats",
+ RPCHelpMan{"getblockstats",
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
"It won't work for some heights with pruning.\n",
{
@@ -1873,10 +1818,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
},
- };
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
LOCK(cs_main);
@@ -2077,8 +2019,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
static UniValue savemempool(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0) {
- throw std::runtime_error(
RPCHelpMan{"savemempool",
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
{},
@@ -2087,8 +2027,7 @@ static UniValue savemempool(const JSONRPCRequest& request)
HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
},
- }.ToString());
- }
+ }.Check(request);
if (!::mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
@@ -2163,8 +2102,6 @@ public:
UniValue scantxoutset(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"scantxoutset",
"\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
"\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
@@ -2213,8 +2150,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
"]\n"
},
RPCExamples{""},
- }.ToString()
- );
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
@@ -2301,8 +2237,6 @@ UniValue scantxoutset(const JSONRPCRequest& request)
static UniValue getblockfilter(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"getblockfilter",
"\nRetrieve a BIP 157 content filter for a particular block.\n",
{
@@ -2318,9 +2252,7 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
}
- }.ToString()
- );
- }
+ }.Check(request);
uint256 block_hash = ParseHashV(request.params[0], "blockhash");
std::string filtertype_name = "basic";
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 6636a8a29a..48bc88823a 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -78,8 +78,6 @@ static UniValue GetNetworkHashPS(int lookup, int height) {
static UniValue getnetworkhashps(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getnetworkhashps",
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
@@ -95,7 +93,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
@@ -144,8 +142,6 @@ static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, ui
static UniValue generatetoaddress(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
@@ -162,7 +158,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
+ "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
- }.ToString());
+ }.Check(request);
int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
@@ -182,8 +178,6 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
static UniValue getmininginfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0) {
- throw std::runtime_error(
RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
@@ -203,8 +197,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
- }.ToString());
- }
+ }.Check(request);
LOCK(cs_main);
@@ -224,8 +217,6 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 3)
- throw std::runtime_error(
RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
@@ -244,7 +235,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -290,8 +281,6 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
static UniValue getblocktemplate(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"getblocktemplate",
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
@@ -301,7 +290,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
" https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
{
- {"template_request", RPCArg::Type::OBJ, RPCArg::Optional::NO, "A json object in the following spec",
+ {"template_request", RPCArg::Type::OBJ, "{}", "A json object in the following spec",
{
{"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
{"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
@@ -366,7 +355,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -696,8 +685,6 @@ protected:
static UniValue submitblock(const JSONRPCRequest& request)
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"submitblock",
"\nAttempts to submit new block to network.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
@@ -710,8 +697,7 @@ static UniValue submitblock(const JSONRPCRequest& request)
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
- }.ToString());
- }
+ }.Check(request);
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
@@ -761,8 +747,6 @@ static UniValue submitblock(const JSONRPCRequest& request)
static UniValue submitheader(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"submitheader",
"\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n",
@@ -776,8 +760,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
},
- }.ToString());
- }
+ }.Check(request);
CBlockHeader h;
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
@@ -801,8 +784,6 @@ static UniValue submitheader(const JSONRPCRequest& request)
static UniValue estimatesmartfee(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"estimatesmartfee",
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
"confirmation within conf_target blocks if possible and return the number of blocks\n"
@@ -835,7 +816,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
@@ -866,8 +847,6 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
static UniValue estimaterawfee(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"estimaterawfee",
"\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
@@ -908,7 +887,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimaterawfee", "6 0.9")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 7a1bdec7b9..6be4057366 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -25,8 +25,6 @@
static UniValue validateaddress(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"validateaddress",
"\nReturn information about the given bitcoin address.\n",
{
@@ -47,7 +45,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
},
- }.ToString());
+ }.Check(request);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
bool isValid = IsValidDestination(dest);
@@ -70,9 +68,6 @@ static UniValue validateaddress(const JSONRPCRequest& request)
static UniValue createmultisig(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
- {
- std::string msg =
RPCHelpMan{"createmultisig",
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n",
@@ -96,9 +91,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"")
},
- }.ToString();
- throw std::runtime_error(msg);
- }
+ }.Check(request);
int required = request.params[0].get_int();
@@ -122,7 +115,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
}
// Construct using pay-to-script-hash:
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CScript inner;
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
@@ -135,8 +128,6 @@ static UniValue createmultisig(const JSONRPCRequest& request)
UniValue getdescriptorinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"getdescriptorinfo",
{"\nAnalyses a descriptor.\n"},
{
@@ -153,9 +144,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCExamples{
"Analyse a descriptor\n" +
HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
- }}.ToString()
- );
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -175,8 +164,6 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
UniValue deriveaddresses(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.empty() || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"deriveaddresses",
{"\nDerives one or more addresses corresponding to an output descriptor.\n"
"Examples of output descriptors are:\n"
@@ -197,9 +184,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
RPCExamples{
"First three native segwit receive addresses\n" +
HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
- }}.ToString()
- );
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
const std::string desc_str = request.params[0].get_str();
@@ -254,8 +239,6 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
static UniValue verifymessage(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 3)
- throw std::runtime_error(
RPCHelpMan{"verifymessage",
"\nVerify a signed message\n",
{
@@ -276,7 +259,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -313,8 +296,6 @@ static UniValue verifymessage(const JSONRPCRequest& request)
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 2)
- throw std::runtime_error(
RPCHelpMan{"signmessagewithprivkey",
"\nSign a message with the private key of an address\n",
{
@@ -332,7 +313,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
},
- }.ToString());
+ }.Check(request);
std::string strPrivkey = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
@@ -355,8 +336,6 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
static UniValue setmocktime(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
@@ -365,8 +344,7 @@ static UniValue setmocktime(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
- }.ToString()
- );
+ }.Check(request);
if (!Params().MineBlocksOnDemand())
throw std::runtime_error("setmocktime for regression testing (-regtest mode) only");
@@ -421,8 +399,6 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
*/
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"getmemoryinfo",
"Returns an object containing information about memory usage.\n",
{
@@ -451,7 +427,7 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
HelpExampleCli("getmemoryinfo", "")
+ HelpExampleRpc("getmemoryinfo", "")
},
- }.ToString());
+ }.Check(request);
std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
if (mode == "stats") {
@@ -489,8 +465,6 @@ static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
UniValue logging(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"logging",
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
@@ -522,8 +496,7 @@ UniValue logging(const JSONRPCRequest& request)
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")
},
- }.ToString());
- }
+ }.Check(request);
uint32_t original_log_categories = LogInstance().GetCategoryMask();
if (request.params[0].isArray()) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index d993a88458..16b59e3d58 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -26,8 +26,6 @@
static UniValue getconnectioncount(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getconnectioncount",
"\nReturns the number of connections to other nodes.\n",
{},
@@ -38,7 +36,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
HelpExampleCli("getconnectioncount", "")
+ HelpExampleRpc("getconnectioncount", "")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -48,8 +46,6 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
static UniValue ping(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"ping",
"\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
"Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
@@ -60,7 +56,7 @@ static UniValue ping(const JSONRPCRequest& request)
HelpExampleCli("ping", "")
+ HelpExampleRpc("ping", "")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -74,8 +70,6 @@ static UniValue ping(const JSONRPCRequest& request)
static UniValue getpeerinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getpeerinfo",
"\nReturns data about each connected network node as a json array of objects.\n",
{},
@@ -131,7 +125,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
HelpExampleCli("getpeerinfo", "")
+ HelpExampleRpc("getpeerinfo", "")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -258,8 +252,6 @@ static UniValue addnode(const JSONRPCRequest& request)
static UniValue disconnectnode(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
- throw std::runtime_error(
RPCHelpMan{"disconnectnode",
"\nImmediately disconnects from the specified peer node.\n"
"\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
@@ -275,7 +267,7 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleRpc("disconnectnode", "\"\", 1")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -304,8 +296,6 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"getaddednodeinfo",
"\nReturns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
@@ -331,7 +321,7 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
+ HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -374,8 +364,6 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
static UniValue getnettotals(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 0)
- throw std::runtime_error(
RPCHelpMan{"getnettotals",
"\nReturns information about network traffic, including bytes in, bytes out,\n"
"and current time.\n",
@@ -400,7 +388,7 @@ static UniValue getnettotals(const JSONRPCRequest& request)
HelpExampleCli("getnettotals", "")
+ HelpExampleRpc("getnettotals", "")
},
- }.ToString());
+ }.Check(request);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -443,8 +431,6 @@ static UniValue GetNetworksInfo()
static UniValue getnetworkinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"getnetworkinfo",
"Returns an object containing various state info regarding P2P networking.\n",
{},
@@ -485,7 +471,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
HelpExampleCli("getnetworkinfo", "")
+ HelpExampleRpc("getnetworkinfo", "")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
@@ -602,8 +588,6 @@ static UniValue setban(const JSONRPCRequest& request)
static UniValue listbanned(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"listbanned",
"\nList all banned IPs/Subnets.\n",
{},
@@ -612,7 +596,7 @@ static UniValue listbanned(const JSONRPCRequest& request)
HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
},
- }.ToString());
+ }.Check(request);
if(!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
@@ -639,8 +623,6 @@ static UniValue listbanned(const JSONRPCRequest& request)
static UniValue clearbanned(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"clearbanned",
"\nClear all banned IPs.\n",
{},
@@ -649,7 +631,7 @@ static UniValue clearbanned(const JSONRPCRequest& request)
HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
},
- }.ToString());
+ }.Check(request);
if (!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
@@ -661,8 +643,6 @@ static UniValue clearbanned(const JSONRPCRequest& request)
static UniValue setnetworkactive(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"setnetworkactive",
"\nDisable/enable all p2p network activity.\n",
{
@@ -670,9 +650,7 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
- }.ToString()
- );
- }
+ }.Check(request);
if (!g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -685,8 +663,6 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
static UniValue getnodeaddresses(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1) {
- throw std::runtime_error(
RPCHelpMan{"getnodeaddresses",
"\nReturn known addresses which can potentially be used to find new nodes in the network\n",
{
@@ -707,8 +683,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
HelpExampleCli("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "8")
},
- }.ToString());
- }
+ }.Check(request);
if (!g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 6bcbccbd4f..ef6537e4ec 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -6,15 +6,6 @@
#ifndef BITCOIN_RPC_PROTOCOL_H
#define BITCOIN_RPC_PROTOCOL_H
-#include <fs.h>
-
-#include <list>
-#include <map>
-#include <stdint.h>
-#include <string>
-
-#include <univalue.h>
-
//! HTTP status codes
enum HTTPStatusCode
{
@@ -92,18 +83,4 @@ enum RPCErrorCode
RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
};
-UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
-UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
-std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
-UniValue JSONRPCError(int code, const std::string& message);
-
-/** Generate a new RPC authentication cookie and write it to disk */
-bool GenerateAuthCookie(std::string *cookie_out);
-/** Read the RPC authentication cookie from disk */
-bool GetAuthCookie(std::string *cookie_out);
-/** Delete RPC authentication cookie from disk */
-void DeleteAuthCookie();
-/** Parse JSON-RPC batch reply into a vector */
-std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
-
#endif // BITCOIN_RPC_PROTOCOL_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 6ac16622c2..966c159f0f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,7 +10,6 @@
#include <core_io.h>
#include <index/txindex.h>
#include <key_io.h>
-#include <keystore.h>
#include <merkleblock.h>
#include <node/coin.h>
#include <node/psbt.h>
@@ -24,6 +23,7 @@
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
#include <util/moneystr.h>
@@ -71,7 +71,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
static UniValue getrawtransaction(const JSONRPCRequest& request)
{
- const RPCHelpMan help{
+ RPCHelpMan{
"getrawtransaction",
"\nReturn the raw transaction data.\n"
@@ -149,11 +149,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
@@ -217,8 +213,6 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
static UniValue gettxoutproof(const JSONRPCRequest& request)
{
- if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
- throw std::runtime_error(
RPCHelpMan{"gettxoutproof",
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
"\nNOTE: By default this function only works sometimes. This is when there is an\n"
@@ -237,8 +231,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
"\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n"
},
RPCExamples{""},
- }.ToString()
- );
+ }.Check(request);
std::set<uint256> setTxids;
uint256 oneTxid;
@@ -313,8 +306,6 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
static UniValue verifytxoutproof(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"verifytxoutproof",
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n",
@@ -325,8 +316,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
},
RPCExamples{""},
- }.ToString()
- );
+ }.Check(request);
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock merkleBlock;
@@ -358,8 +348,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
static UniValue createrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- throw std::runtime_error(
RPCHelpMan{"createrawtransaction",
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
@@ -408,8 +396,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -419,14 +406,18 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
}, true
);
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ bool rbf = false;
+ if (!request.params[3].isNull()) {
+ rbf = request.params[3].isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
return EncodeHexTx(CTransaction(rawTx));
}
static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"decoderawtransaction",
+ RPCHelpMan{"decoderawtransaction",
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
@@ -483,11 +474,7 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -518,7 +505,7 @@ static std::string GetAllOutputTypes()
static UniValue decodescript(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"decodescript",
+ RPCHelpMan{"decodescript",
"\nDecode a hex-encoded script.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
@@ -549,11 +536,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -610,8 +593,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"combinerawtransaction",
"\nCombine multiple partially signed transactions into one transaction.\n"
"The combined transaction may be another partially signed transaction or a \n"
@@ -629,7 +610,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]")
},
- }.ToString());
+ }.Check(request);
UniValue txs = request.params[0].get_array();
@@ -694,8 +675,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"signrawtransactionwithkey",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second argument is an array of base58-encoded private\n"
@@ -752,7 +731,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
@@ -761,7 +740,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
const UniValue& keys = request.params[1].get_array();
for (unsigned int idx = 0; idx < keys.size(); ++idx) {
UniValue k = keys[idx];
@@ -784,8 +763,11 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"sendrawtransaction",
- "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
+ RPCHelpMan{"sendrawtransaction",
+ "\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
+ "\nNote that the transaction will be sent unconditionally to all peers, so using this\n"
+ "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
+ "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
@@ -806,11 +788,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {
UniValue::VSTR,
@@ -836,19 +814,19 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
max_raw_tx_fee = fr.GetFee((weight+3)/4);
}
- uint256 txid;
std::string err_string;
- const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
+ AssertLockNotHeld(cs_main);
+ const TransactionError err = BroadcastTransaction(tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
- return txid.GetHex();
+ return tx->GetHash().GetHex();
}
static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"testmempoolaccept",
+ RPCHelpMan{"testmempoolaccept",
"\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
@@ -881,11 +859,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -964,8 +938,6 @@ static std::string WriteHDKeypath(std::vector<uint32_t>& keypath)
UniValue decodepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"decodepsbt",
"\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
{
@@ -1062,7 +1034,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("decodepsbt", "\"psbt\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -1239,8 +1211,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
UniValue combinepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"combinepsbt",
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n",
@@ -1257,7 +1227,7 @@ UniValue combinepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VARR}, true);
@@ -1289,8 +1259,6 @@ UniValue combinepsbt(const JSONRPCRequest& request)
UniValue finalizepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"finalizepsbt",
"Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
"network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
@@ -1312,7 +1280,7 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("finalizepsbt", "\"psbt\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
@@ -1348,8 +1316,6 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
UniValue createpsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"createpsbt",
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n",
@@ -1392,7 +1358,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {
@@ -1403,7 +1369,11 @@ UniValue createpsbt(const JSONRPCRequest& request)
}, true
);
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ bool rbf = false;
+ if (!request.params[3].isNull()) {
+ rbf = request.params[3].isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
// Make a blank psbt
PartiallySignedTransaction psbtx;
@@ -1424,7 +1394,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
UniValue converttopsbt(const JSONRPCRequest& request)
{
- const RPCHelpMan help{"converttopsbt",
+ RPCHelpMan{"converttopsbt",
"\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
{
@@ -1448,11 +1418,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\nConvert the transaction to a PSBT\n"
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
@@ -1495,8 +1461,6 @@ UniValue converttopsbt(const JSONRPCRequest& request)
UniValue utxoupdatepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"utxoupdatepsbt",
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
@@ -1514,8 +1478,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
@@ -1585,8 +1548,6 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
UniValue joinpsbts(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"joinpsbts",
"\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
"No input in any of the PSBTs can be in more than one of the PSBTs.\n",
@@ -1601,8 +1562,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VARR}, true);
@@ -1659,8 +1619,6 @@ UniValue joinpsbts(const JSONRPCRequest& request)
UniValue analyzepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"analyzepsbt",
"\nAnalyzes and provides information about the current status of a PSBT and its inputs\n",
{
@@ -1694,8 +1652,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("analyzepsbt", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 14fcb628eb..55425cca35 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -8,17 +8,18 @@
#include <coins.h>
#include <core_io.h>
#include <key_io.h>
-#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
-#include <rpc/protocol.h>
+#include <rpc/request.h>
#include <rpc/util.h>
+#include <script/sign.h>
+#include <script/signingprovider.h>
#include <tinyformat.h>
#include <univalue.h>
#include <util/rbf.h>
#include <util/strencodings.h>
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
@@ -36,8 +37,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.nLockTime = nLockTime;
}
- bool rbfOptIn = rbf.isTrue();
-
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
@@ -52,7 +51,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
uint32_t nSequence;
- if (rbfOptIn) {
+ if (rbf) {
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
} else if (rawTx.nLockTime) {
nSequence = CTxIn::SEQUENCE_FINAL - 1;
@@ -124,7 +123,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
}
}
- if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
+ if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
@@ -148,7 +147,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
{
// Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index c115d33a77..c85593e71e 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -7,7 +7,7 @@
#include <map>
-class CBasicKeyStore;
+class FillableSigningProvider;
class UniValue;
struct CMutableTransaction;
class Coin;
@@ -24,9 +24,9 @@ class COutPoint;
* @param hashType The signature hash type
* @returns JSON object with details of signed transaction
*/
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf);
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
diff --git a/src/rpc/protocol.cpp b/src/rpc/request.cpp
index 33b0130a94..56cac6661e 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/request.cpp
@@ -1,15 +1,16 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// 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 <rpc/protocol.h>
+#include <rpc/request.h>
+
+#include <fs.h>
#include <random.h>
-#include <tinyformat.h>
+#include <rpc/protocol.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <util/time.h>
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
@@ -148,3 +149,36 @@ std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num)
}
return batch;
}
+
+void JSONRPCRequest::parse(const UniValue& valRequest)
+{
+ // Parse request
+ if (!valRequest.isObject())
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
+ const UniValue& request = valRequest.get_obj();
+
+ // Parse id now so errors from here on will have the id
+ id = find_value(request, "id");
+
+ // Parse method
+ UniValue valMethod = find_value(request, "method");
+ if (valMethod.isNull())
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
+ if (!valMethod.isStr())
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
+ strMethod = valMethod.get_str();
+ if (fLogIPs)
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
+ this->authUser, this->peerAddr);
+ else
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
+
+ // Parse params
+ UniValue valParams = find_value(request, "params");
+ if (valParams.isArray() || valParams.isObject())
+ params = valParams;
+ else if (valParams.isNull())
+ params = UniValue(UniValue::VARR);
+ else
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
+}
diff --git a/src/rpc/request.h b/src/rpc/request.h
new file mode 100644
index 0000000000..99eb4f9354
--- /dev/null
+++ b/src/rpc/request.h
@@ -0,0 +1,42 @@
+// Copyright (c) 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_RPC_REQUEST_H
+#define BITCOIN_RPC_REQUEST_H
+
+#include <string>
+
+#include <univalue.h>
+
+UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
+UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
+std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
+UniValue JSONRPCError(int code, const std::string& message);
+
+/** Generate a new RPC authentication cookie and write it to disk */
+bool GenerateAuthCookie(std::string *cookie_out);
+/** Read the RPC authentication cookie from disk */
+bool GetAuthCookie(std::string *cookie_out);
+/** Delete RPC authentication cookie from disk */
+void DeleteAuthCookie();
+/** Parse JSON-RPC batch reply into a vector */
+std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
+
+class JSONRPCRequest
+{
+public:
+ UniValue id;
+ std::string strMethod;
+ UniValue params;
+ bool fHelp;
+ std::string URI;
+ std::string authUser;
+ std::string peerAddr;
+
+ JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
+ void parse(const UniValue& valRequest);
+};
+
+#endif // BITCOIN_RPC_REQUEST_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 145b99b567..18f7426bcf 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -178,8 +178,6 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 0)
- throw std::runtime_error(
RPCHelpMan{"uptime",
"\nReturns the total uptime of the server.\n",
{},
@@ -190,15 +188,13 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
HelpExampleCli("uptime", "")
+ HelpExampleRpc("uptime", "")
},
- }.ToString());
+ }.Check(jsonRequest);
return GetTime() - GetStartupTime();
}
static UniValue getrpcinfo(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 0) {
- throw std::runtime_error(
RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
@@ -217,9 +213,7 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getrpcinfo", "")
+ HelpExampleRpc("getrpcinfo", "")},
- }.ToString()
- );
- }
+ }.Check(request);
LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
@@ -334,39 +328,6 @@ bool RPCIsInWarmup(std::string *outStatus)
return fRPCInWarmup;
}
-void JSONRPCRequest::parse(const UniValue& valRequest)
-{
- // Parse request
- if (!valRequest.isObject())
- throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
- const UniValue& request = valRequest.get_obj();
-
- // Parse id now so errors from here on will have the id
- id = find_value(request, "id");
-
- // Parse method
- UniValue valMethod = find_value(request, "method");
- if (valMethod.isNull())
- throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
- if (!valMethod.isStr())
- throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
- strMethod = valMethod.get_str();
- if (fLogIPs)
- LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
- this->authUser, this->peerAddr);
- else
- LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
-
- // Parse params
- UniValue valParams = find_value(request, "params");
- if (valParams.isArray() || valParams.isObject())
- params = valParams;
- else if (valParams.isNull())
- params = UniValue(UniValue::VARR);
- else
- throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
-}
-
bool IsDeprecatedRPCEnabled(const std::string& method)
{
const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 431ff0bb7c..b060db5bf9 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -7,13 +7,14 @@
#define BITCOIN_RPC_SERVER_H
#include <amount.h>
-#include <rpc/protocol.h>
+#include <rpc/request.h>
#include <uint256.h>
#include <list>
#include <map>
#include <stdint.h>
#include <string>
+#include <functional>
#include <univalue.h>
@@ -27,21 +28,6 @@ namespace RPCServer
void OnStopped(std::function<void ()> slot);
}
-class JSONRPCRequest
-{
-public:
- UniValue id;
- std::string strMethod;
- UniValue params;
- bool fHelp;
- std::string URI;
- std::string authUser;
- std::string peerAddr;
-
- JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
- void parse(const UniValue& valRequest);
-};
-
/** Query whether RPC is running */
bool IsRPCRunning();
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 67ccb225b5..de90276677 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -3,8 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key_io.h>
-#include <keystore.h>
#include <outputtype.h>
+#include <script/signingprovider.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <tinyformat.h>
@@ -131,8 +131,8 @@ CPubKey HexToPubKey(const std::string& hex_in)
return vchPubKey;
}
-// Retrieves a public key for an address from the given CKeyStore
-CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in)
+// Retrieves a public key for an address from the given FillableSigningProvider
+CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in)
{
CTxDestination dest = DecodeDestination(addr_in);
if (!IsValidDestination(dest)) {
@@ -153,7 +153,7 @@ CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in)
}
// Creates a multisig address from a given list of public keys, number of signatures required, and the address type
-CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out)
+CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out)
{
// Gather public keys
if (required < 1) {
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 8762281d73..4c3322b879 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -9,6 +9,7 @@
#include <outputtype.h>
#include <pubkey.h>
#include <rpc/protocol.h>
+#include <rpc/request.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/standard.h>
@@ -19,7 +20,7 @@
#include <boost/variant.hpp>
-class CKeyStore;
+class FillableSigningProvider;
class CPubKey;
class CScript;
struct InitInterfaces;
@@ -72,8 +73,8 @@ extern std::string HelpExampleCli(const std::string& methodname, const std::stri
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
CPubKey HexToPubKey(const std::string& hex_in);
-CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
-CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out);
+CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in);
+CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out);
UniValue DescribeAddress(const CTxDestination& dest);
@@ -226,7 +227,7 @@ struct RPCResults {
struct RPCExamples {
const std::string m_examples;
- RPCExamples(
+ explicit RPCExamples(
std::string examples)
: m_examples(std::move(examples))
{
@@ -242,6 +243,15 @@ public:
std::string ToString() const;
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
+ /**
+ * Check if the given request is valid according to this command or if
+ * the user is asking for help information, and throw help when appropriate.
+ */
+ inline void Check(const JSONRPCRequest& request) const {
+ if (request.fHelp || !IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(ToString());
+ }
+ }
private:
const std::string m_name;
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index af7ae229ca..29915c6c92 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -7,6 +7,7 @@
#include <script/script.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <vector>
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 95b25b4911..f8701b6d01 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -926,7 +926,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SigVersion::BASE) {
- int found = FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript() << vchSig);
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
@@ -992,7 +992,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
valtype& vchSig = stacktop(-isig-k);
if (sigversion == SigVersion::BASE) {
- int found = FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript() << vchSig);
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
diff --git a/src/script/keyorigin.h b/src/script/keyorigin.h
new file mode 100644
index 0000000000..610f233500
--- /dev/null
+++ b/src/script/keyorigin.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef BITCOIN_SCRIPT_KEYORIGIN_H
+#define BITCOIN_SCRIPT_KEYORIGIN_H
+
+#include <serialize.h>
+#include <streams.h>
+#include <vector>
+
+struct KeyOriginInfo
+{
+ unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
+ std::vector<uint32_t> path;
+
+ friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
+ }
+
+ ADD_SERIALIZE_METHODS;
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action)
+ {
+ READWRITE(fingerprint);
+ READWRITE(path);
+ }
+
+ void clear()
+ {
+ memset(fingerprint, 0, 4);
+ path.clear();
+ }
+};
+
+#endif // BITCOIN_SCRIPT_KEYORIGIN_H
diff --git a/src/script/script.h b/src/script/script.h
index 11e8661a5b..6355b8a704 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -437,7 +437,9 @@ public:
explicit CScript(opcodetype b) { operator<<(b); }
explicit CScript(const CScriptNum& b) { operator<<(b); }
- explicit CScript(const std::vector<unsigned char>& b) { operator<<(b); }
+ // delete non-existent constructor to defend against future introduction
+ // e.g. via prevector
+ explicit CScript(const std::vector<unsigned char>& b) = delete;
CScript& operator<<(int64_t b) { return push_int64(b); }
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 5320dc0876..13481af9c5 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -8,6 +8,7 @@
#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
@@ -423,22 +424,10 @@ public:
}
};
-template<typename M, typename K, typename V>
-bool LookupHelper(const M& map, const K& key, V& value)
-{
- auto it = map.find(key);
- if (it != map.end()) {
- value = it->second;
- return true;
- }
- return false;
-}
-
}
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32);
const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32);
-const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
@@ -459,53 +448,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
return false;
}
-bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
-{
- return m_provider->GetCScript(scriptid, script);
-}
-
-bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
-{
- return m_provider->GetPubKey(keyid, pubkey);
-}
-
-bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
-{
- if (m_hide_secret) return false;
- return m_provider->GetKey(keyid, key);
-}
-
-bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
-{
- if (m_hide_origin) return false;
- return m_provider->GetKeyOrigin(keyid, info);
-}
-
-bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
-bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
-bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
-{
- std::pair<CPubKey, KeyOriginInfo> out;
- bool ret = LookupHelper(origins, keyid, out);
- if (ret) info = std::move(out.second);
- return ret;
-}
-bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
-
-FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
-{
- FlatSigningProvider ret;
- ret.scripts = a.scripts;
- ret.scripts.insert(b.scripts.begin(), b.scripts.end());
- ret.pubkeys = a.pubkeys;
- ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
- ret.keys = a.keys;
- ret.keys.insert(b.keys.begin(), b.keys.end());
- ret.origins = a.origins;
- ret.origins.insert(b.origins.begin(), b.origins.end());
- return ret;
-}
-
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
std::vector<valtype> solutions;
diff --git a/src/script/sign.h b/src/script/sign.h
index e5c0329a61..0e751afd3b 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -10,6 +10,7 @@
#include <hash.h>
#include <pubkey.h>
#include <script/interpreter.h>
+#include <script/keyorigin.h>
#include <streams.h>
class CKey;
@@ -17,77 +18,10 @@ class CKeyID;
class CScript;
class CScriptID;
class CTransaction;
+class SigningProvider;
struct CMutableTransaction;
-struct KeyOriginInfo
-{
- unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
- std::vector<uint32_t> path;
-
- friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
- {
- return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
- }
-
- ADD_SERIALIZE_METHODS;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
- {
- READWRITE(fingerprint);
- READWRITE(path);
- }
-
- void clear()
- {
- memset(fingerprint, 0, 4);
- path.clear();
- }
-};
-
-/** An interface to be implemented by keystores that support signing. */
-class SigningProvider
-{
-public:
- virtual ~SigningProvider() {}
- virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
- virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
- virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
- virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
-};
-
-extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
-
-class HidingSigningProvider : public SigningProvider
-{
-private:
- const bool m_hide_secret;
- const bool m_hide_origin;
- const SigningProvider* m_provider;
-
-public:
- HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
- bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
- bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
- bool GetKey(const CKeyID& keyid, CKey& key) const override;
- bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
-};
-
-struct FlatSigningProvider final : public SigningProvider
-{
- std::map<CScriptID, CScript> scripts;
- std::map<CKeyID, CPubKey> pubkeys;
- std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
- std::map<CKeyID, CKey> keys;
-
- bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
- bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
- bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
- bool GetKey(const CKeyID& keyid, CKey& key) const override;
-};
-
-FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
-
/** Interface for signature creators. */
class BaseSignatureCreator {
public:
diff --git a/src/keystore.cpp b/src/script/signingprovider.cpp
index f6d19416ce..01757e2f65 100644
--- a/src/keystore.cpp
+++ b/src/script/signingprovider.cpp
@@ -1,18 +1,78 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// 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 <keystore.h>
+#include <script/keyorigin.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
#include <util/system.h>
-void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
+const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
+
+template<typename M, typename K, typename V>
+bool LookupHelper(const M& map, const K& key, V& value)
+{
+ auto it = map.find(key);
+ if (it != map.end()) {
+ value = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(keyid, pubkey);
+}
+
+bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
+{
+ if (m_hide_secret) return false;
+ return m_provider->GetKey(keyid, key);
+}
+
+bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ if (m_hide_origin) return false;
+ return m_provider->GetKeyOrigin(keyid, info);
+}
+
+bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
+bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ std::pair<CPubKey, KeyOriginInfo> out;
+ bool ret = LookupHelper(origins, keyid, out);
+ if (ret) info = std::move(out.second);
+ return ret;
+}
+bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+
+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
+{
+ FlatSigningProvider ret;
+ ret.scripts = a.scripts;
+ ret.scripts.insert(b.scripts.begin(), b.scripts.end());
+ ret.pubkeys = a.pubkeys;
+ ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
+ ret.keys = a.keys;
+ ret.keys.insert(b.keys.begin(), b.keys.end());
+ ret.origins = a.origins;
+ ret.origins.insert(b.origins.begin(), b.origins.end());
+ return ret;
+}
+
+void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
{
AssertLockHeld(cs_KeyStore);
CKeyID key_id = pubkey.GetID();
- // We must actually know about this key already.
- assert(HaveKey(key_id) || mapWatchKeys.count(key_id));
// This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
// outputs. Technically P2WPKH outputs don't have a redeemscript to be
// spent. However, our current IsMine logic requires the corresponding
@@ -32,23 +92,17 @@ void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
}
}
-bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
+bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
{
CKey key;
if (!GetKey(address, key)) {
- LOCK(cs_KeyStore);
- WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
- if (it != mapWatchKeys.end()) {
- vchPubKeyOut = it->second;
- return true;
- }
return false;
}
vchPubKeyOut = key.GetPubKey();
return true;
}
-bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
+bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
mapKeys[pubkey.GetID()] = key;
@@ -56,13 +110,13 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
return true;
}
-bool CBasicKeyStore::HaveKey(const CKeyID &address) const
+bool FillableSigningProvider::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
return mapKeys.count(address) > 0;
}
-std::set<CKeyID> CBasicKeyStore::GetKeys() const
+std::set<CKeyID> FillableSigningProvider::GetKeys() const
{
LOCK(cs_KeyStore);
std::set<CKeyID> set_address;
@@ -72,7 +126,7 @@ std::set<CKeyID> CBasicKeyStore::GetKeys() const
return set_address;
}
-bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const
+bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
{
LOCK(cs_KeyStore);
KeyMap::const_iterator mi = mapKeys.find(address);
@@ -83,23 +137,23 @@ bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const
return false;
}
-bool CBasicKeyStore::AddCScript(const CScript& redeemScript)
+bool FillableSigningProvider::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
- return error("CBasicKeyStore::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
+ return error("FillableSigningProvider::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
LOCK(cs_KeyStore);
mapScripts[CScriptID(redeemScript)] = redeemScript;
return true;
}
-bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const
+bool FillableSigningProvider::HaveCScript(const CScriptID& hash) const
{
LOCK(cs_KeyStore);
return mapScripts.count(hash) > 0;
}
-std::set<CScriptID> CBasicKeyStore::GetCScripts() const
+std::set<CScriptID> FillableSigningProvider::GetCScripts() const
{
LOCK(cs_KeyStore);
std::set<CScriptID> set_script;
@@ -109,7 +163,7 @@ std::set<CScriptID> CBasicKeyStore::GetCScripts() const
return set_script;
}
-bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
+bool FillableSigningProvider::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
{
LOCK(cs_KeyStore);
ScriptMap::const_iterator mi = mapScripts.find(hash);
@@ -121,60 +175,7 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
return false;
}
-static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
-{
- //TODO: Use Solver to extract this?
- CScript::const_iterator pc = dest.begin();
- opcodetype opcode;
- std::vector<unsigned char> vch;
- if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch))
- return false;
- pubKeyOut = CPubKey(vch);
- if (!pubKeyOut.IsFullyValid())
- return false;
- if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch))
- return false;
- return true;
-}
-
-bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
-{
- LOCK(cs_KeyStore);
- setWatchOnly.insert(dest);
- CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey)) {
- mapWatchKeys[pubKey.GetID()] = pubKey;
- ImplicitlyLearnRelatedKeyScripts(pubKey);
- }
- return true;
-}
-
-bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest)
-{
- LOCK(cs_KeyStore);
- setWatchOnly.erase(dest);
- CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey)) {
- mapWatchKeys.erase(pubKey.GetID());
- }
- // Related CScripts are not removed; having superfluous scripts around is
- // harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
- return true;
-}
-
-bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const
-{
- LOCK(cs_KeyStore);
- return setWatchOnly.count(dest) > 0;
-}
-
-bool CBasicKeyStore::HaveWatchOnly() const
-{
- LOCK(cs_KeyStore);
- return (!setWatchOnly.empty());
-}
-
-CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
+CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest)
{
// Only supports destinations which map to single public keys, i.e. P2PKH,
// P2WPKH, and P2SH-P2WPKH.
@@ -196,10 +197,3 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
}
return CKeyID();
}
-
-bool HaveKey(const CKeyStore& store, const CKey& key)
-{
- CKey key2;
- key2.Set(key.begin(), key.end(), !key.IsCompressed());
- return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID());
-}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
new file mode 100644
index 0000000000..4eec2311d4
--- /dev/null
+++ b/src/script/signingprovider.h
@@ -0,0 +1,92 @@
+// 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_SCRIPT_SIGNINGPROVIDER_H
+#define BITCOIN_SCRIPT_SIGNINGPROVIDER_H
+
+#include <key.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <sync.h>
+
+struct KeyOriginInfo;
+
+/** An interface to be implemented by keystores that support signing. */
+class SigningProvider
+{
+public:
+ virtual ~SigningProvider() {}
+ virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
+ virtual bool HaveCScript(const CScriptID &scriptid) const { return false; }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
+ virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
+ virtual bool HaveKey(const CKeyID &address) const { return false; }
+ virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
+};
+
+extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+
+class HidingSigningProvider : public SigningProvider
+{
+private:
+ const bool m_hide_secret;
+ const bool m_hide_origin;
+ const SigningProvider* m_provider;
+
+public:
+ HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
+ bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
+ bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKey(const CKeyID& keyid, CKey& key) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+};
+
+struct FlatSigningProvider final : public SigningProvider
+{
+ std::map<CScriptID, CScript> scripts;
+ std::map<CKeyID, CPubKey> pubkeys;
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
+ std::map<CKeyID, CKey> keys;
+
+ bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
+ bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+ bool GetKey(const CKeyID& keyid, CKey& key) const override;
+};
+
+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
+
+/** Fillable signing provider that keeps keys in an address->secret map */
+class FillableSigningProvider : public SigningProvider
+{
+protected:
+ mutable CCriticalSection cs_KeyStore;
+
+ using KeyMap = std::map<CKeyID, CKey>;
+ using ScriptMap = std::map<CScriptID, CScript>;
+
+ KeyMap mapKeys GUARDED_BY(cs_KeyStore);
+ ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
+
+ void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
+
+public:
+ virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
+ virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ virtual bool HaveKey(const CKeyID &address) const override;
+ virtual std::set<CKeyID> GetKeys() const;
+ virtual bool GetKey(const CKeyID &address, CKey &keyOut) const override;
+ virtual bool AddCScript(const CScript& redeemScript);
+ virtual bool HaveCScript(const CScriptID &hash) const override;
+ virtual std::set<CScriptID> GetCScripts() const;
+ virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
+};
+
+/** Return the CKeyID of the key involved in a script (if there is a unique one). */
+CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest);
+
+#endif // BITCOIN_SCRIPT_SIGNINGPROVIDER_H
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index b7d6cd925c..fc6898f444 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -9,7 +9,6 @@
#include <pubkey.h>
#include <script/script.h>
-
typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index eeb54b4cf0..da0abd495a 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -533,9 +533,6 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{
CAddrManTest addrman;
- // Set addrman addr placement to be deterministic.
- addrman.MakeDeterministic();
-
BOOST_CHECK(addrman.size() == 0);
// Empty addrman should return blank addrman info.
@@ -568,9 +565,6 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
{
CAddrManTest addrman;
- // Set addrman addr placement to be deterministic.
- addrman.MakeDeterministic();
-
// Add twenty two addresses.
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) {
@@ -627,9 +621,6 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{
CAddrManTest addrman;
- // Set addrman addr placement to be deterministic.
- addrman.MakeDeterministic();
-
BOOST_CHECK(addrman.size() == 0);
// Empty addrman should return blank addrman info.
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index dac201a35f..5ce8e6feb0 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
size_t poolSize = pool.size();
- pool.removeRecursive(*block.vtx[2]);
+ pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
CBlock block2;
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 4e2acca4c3..4ac12bf969 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -4,6 +4,7 @@
#include <crypto/aes.h>
#include <crypto/chacha20.h>
+#include <crypto/chacha_poly_aead.h>
#include <crypto/poly1305.h>
#include <crypto/hkdf_sha256_32.h>
#include <crypto/hmac_sha256.h>
@@ -585,6 +586,131 @@ BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests)
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d");
}
+static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aad_length, const std::string& hex_m, const std::string& hex_k1, const std::string& hex_k2, const std::string& hex_aad_keystream, const std::string& hex_encrypted_message, const std::string& hex_encrypted_message_seq_999)
+{
+ // we need two sequence numbers, one for the payload cipher instance...
+ uint32_t seqnr_payload = 0;
+ // ... and one for the AAD (length) cipher instance
+ uint32_t seqnr_aad = 0;
+ // we need to keep track of the position in the AAD cipher instance
+ // keystream since we use the same 64byte output 21 times
+ // (21 times 3 bytes length < 64)
+ int aad_pos = 0;
+
+ std::vector<unsigned char> aead_K_1 = ParseHex(hex_k1);
+ std::vector<unsigned char> aead_K_2 = ParseHex(hex_k2);
+ std::vector<unsigned char> plaintext_buf = ParseHex(hex_m);
+ std::vector<unsigned char> expected_aad_keystream = ParseHex(hex_aad_keystream);
+ std::vector<unsigned char> expected_ciphertext_and_mac = ParseHex(hex_encrypted_message);
+ std::vector<unsigned char> expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999);
+
+ std::vector<unsigned char> ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0);
+ std::vector<unsigned char> plaintext_buf_new(plaintext_buf.size(), 0);
+ std::vector<unsigned char> cmp_ctx_buffer(64);
+ uint32_t out_len = 0;
+
+ // create the AEAD instance
+ ChaCha20Poly1305AEAD aead(aead_K_1.data(), aead_K_1.size(), aead_K_2.data(), aead_K_2.size());
+
+ // create a chacha20 instance to compare against
+ ChaCha20 cmp_ctx(aead_K_2.data(), 32);
+
+ // encipher
+ bool res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true);
+ // make sure the operation succeeded if expected to succeed
+ BOOST_CHECK_EQUAL(res, must_succeed);
+ if (!res) return;
+
+ // verify ciphertext & mac against the test vector
+ BOOST_CHECK_EQUAL(expected_ciphertext_and_mac.size(), ciphertext_buf.size());
+ BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0);
+
+ // manually construct the AAD keystream
+ cmp_ctx.SetIV(seqnr_aad);
+ cmp_ctx.Seek(0);
+ cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64);
+ BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0);
+ // crypt the 3 length bytes and compare the length
+ uint32_t len_cmp = 0;
+ len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) |
+ (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 |
+ (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16;
+ BOOST_CHECK_EQUAL(len_cmp, expected_aad_length);
+
+ // encrypt / decrypt 1000 packets
+ for (size_t i = 0; i < 1000; ++i) {
+ res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true);
+ BOOST_CHECK(res);
+ BOOST_CHECK(aead.GetLength(&out_len, seqnr_aad, aad_pos, ciphertext_buf.data()));
+ BOOST_CHECK_EQUAL(out_len, expected_aad_length);
+ res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, plaintext_buf_new.data(), plaintext_buf_new.size(), ciphertext_buf.data(), ciphertext_buf.size(), false);
+ BOOST_CHECK(res);
+
+ // make sure we repetitive get the same plaintext
+ BOOST_CHECK(memcmp(plaintext_buf.data(), plaintext_buf_new.data(), plaintext_buf.size()) == 0);
+
+ // compare sequence number 999 against the test vector
+ if (seqnr_payload == 999) {
+ BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac_sequence999.data(), expected_ciphertext_and_mac_sequence999.size()) == 0);
+ }
+ // set nonce and block counter, output the keystream
+ cmp_ctx.SetIV(seqnr_aad);
+ cmp_ctx.Seek(0);
+ cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64);
+
+ // crypt the 3 length bytes and compare the length
+ len_cmp = 0;
+ len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) |
+ (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 |
+ (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16;
+ BOOST_CHECK_EQUAL(len_cmp, expected_aad_length);
+
+ // increment the sequence number(s)
+ // always increment the payload sequence number
+ // increment the AAD keystream position by its size (3)
+ // increment the AAD sequence number if we would hit the 64 byte limit
+ seqnr_payload++;
+ aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
+ if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
+ aad_pos = 0;
+ seqnr_aad++;
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector)
+{
+ /* test chacha20poly1305@bitcoin AEAD */
+
+ // must fail with no message
+ TestChaCha20Poly1305AEAD(false, 0,
+ "",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000", "", "", "");
+
+ TestChaCha20Poly1305AEAD(true, 0,
+ /* m */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k1 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k2 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
+ /* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11829c1b6c1df1f551cd6131ff08",
+ /* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa16ec62c5e24f040c08bb20c3598");
+ TestChaCha20Poly1305AEAD(true, 1,
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
+ "77b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32baf0c85b6dff8602b06cf52a6aefc62e",
+ "b1a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3a8bd94d54b5ecabbc41ffbb0c90924080");
+ TestChaCha20Poly1305AEAD(true, 255,
+ "ff0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9",
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ "ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ "c640c1711e3ee904ac35c57ab9791c8a1c408603a90b77a83b54f6c844cb4b06d94e7fc6c800e165acd66147e80ec45a567f6ce66d05ec0cae679dceeb890017",
+ "3940c1e92da4582ff6f92a776aeb14d014d384eeb30f660dacf70a14a23fd31e91212701334e2ce1acf5199dc84f4d61ddbe6571bca5af874b4c9226c26e650995d157644e1848b96ed6c2102d5489a050e71d29a5a66ece11de5fb5c9558d54da28fe45b0bc4db4e5b88030bfc4a352b4b7068eccf656bae7ad6a35615315fc7c49d4200388d5eca67c2e822e069336c69b40db67e0f3c81209c50f3216a4b89fb3ae1b984b7851a2ec6f68ab12b101ab120e1ea7313bb93b5a0f71185c7fea017ddb92769861c29dba4fbc432280d5dff21b36d1c4c790128b22699950bb18bf74c448cdfe547d8ed4f657d8005fdc0cd7a050c2d46050a44c4376355858981fbe8b184288276e7a93eabc899c4a",
+ "f039c6689eaeef0456685200feaab9d54bbd9acde4410a3b6f4321296f4a8ca2604b49727d8892c57e005d799b2a38e85e809f20146e08eec75169691c8d4f54a0d51a1e1c7b381e0474eb02f994be9415ef3ffcbd2343f0601e1f3b172a1d494f838824e4df570f8e3b0c04e27966e36c82abd352d07054ef7bd36b84c63f9369afe7ed79b94f953873006b920c3fa251a771de1b63da927058ade119aa898b8c97e42a606b2f6df1e2d957c22f7593c1e2002f4252f4c9ae4bf773499e5cfcfe14dfc1ede26508953f88553bf4a76a802f6a0068d59295b01503fd9a600067624203e880fdf53933b96e1f4d9eb3f4e363dd8165a278ff667a41ee42b9892b077cefff92b93441f7be74cf10e6cd");
+}
+
BOOST_AUTO_TEST_CASE(countbits_tests)
{
FastRandomContext ctx;
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 93883d1d98..a50d6854f8 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -6,10 +6,11 @@
#include <banman.h>
#include <chainparams.h>
-#include <keystore.h>
#include <net.h>
#include <net_processing.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
#include <serialize.h>
#include <util/memory.h>
#include <util/system.h>
@@ -369,7 +370,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
{
CKey key;
key.MakeNewKey(true);
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
// 50 orphan transactions:
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 8a42344642..77304fe918 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -7,6 +7,7 @@
#include <test/setup_common.h>
#include <string>
+#include <utility>
#include <vector>
#include <boost/algorithm/string.hpp>
@@ -32,17 +33,18 @@ static void ResetArgs(const std::string& strArg)
BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}
-static void SetupArgs(const std::vector<std::string>& args)
+static void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
gArgs.ClearArgs();
- for (const std::string& arg : args) {
- gArgs.AddArg(arg, "", false, OptionsCategory::OPTIONS);
+ for (const auto& arg : args) {
+ gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
BOOST_AUTO_TEST_CASE(boolarg)
{
- SetupArgs({"-foo"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ SetupArgs({foo});
ResetArgs("-foo");
BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
@@ -95,7 +97,9 @@ BOOST_AUTO_TEST_CASE(boolarg)
BOOST_AUTO_TEST_CASE(stringarg)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_STRING);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_STRING);
+ SetupArgs({foo, bar});
ResetArgs("");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
@@ -120,7 +124,9 @@ BOOST_AUTO_TEST_CASE(stringarg)
BOOST_AUTO_TEST_CASE(intarg)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_INT);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_INT);
+ SetupArgs({foo, bar});
ResetArgs("");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);
@@ -140,7 +146,9 @@ BOOST_AUTO_TEST_CASE(intarg)
BOOST_AUTO_TEST_CASE(doubledash)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
+ SetupArgs({foo, bar});
ResetArgs("--foo");
BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);
@@ -151,7 +159,9 @@ BOOST_AUTO_TEST_CASE(doubledash)
BOOST_AUTO_TEST_CASE(boolargno)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_BOOL);
+ SetupArgs({foo, bar});
ResetArgs("-nofoo");
BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index c6a3de2285..fe5d31b7d3 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -14,6 +14,8 @@
BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
+static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::REPLACED;
+
BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
{
// Test CTxMemPool::remove functionality
@@ -59,13 +61,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
// Nothing in pool, remove should do nothing:
unsigned int poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txParent));
+ testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Just the parent:
testPool.addUnchecked(entry.FromTx(txParent));
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txParent));
+ testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
// Parent, children, grandchildren:
@@ -77,18 +79,18 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
}
// Remove Child[0], GrandChild[0] should be removed:
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txChild[0]));
+ testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
// ... make sure grandchild and child are gone:
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txGrandChild[0]));
+ testPool.removeRecursive(CTransaction(txGrandChild[0]), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txChild[0]));
+ testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize);
// Remove parent, all children/grandchildren should go:
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txParent));
+ testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
BOOST_CHECK_EQUAL(testPool.size(), 0U);
@@ -101,7 +103,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
// put into the mempool (maybe because it is non-standard):
poolSize = testPool.size();
- testPool.removeRecursive(CTransaction(txParent));
+ testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
BOOST_CHECK_EQUAL(testPool.size(), 0U);
}
@@ -283,11 +285,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 10U);
// Now try removing tx10 and verify the sort order returns to normal
- pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx());
+ pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
CheckSort<descendant_score>(pool, snapshotOrder);
- pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
- pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
+ pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
+ pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
}
BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 4bd40687a6..05d7f76983 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -158,7 +158,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- mempool.removeRecursive(CTransaction(tx));
+ mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 11e79937be..7c60abb93f 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -3,12 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
-#include <keystore.h>
#include <policy/policy.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/interpreter.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <tinyformat.h>
#include <uint256.h>
#include <test/setup_common.h>
@@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
BOOST_AUTO_TEST_CASE(multisig_Sign)
{
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 735b67c06e..f451d80984 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -4,13 +4,13 @@
#include <consensus/tx_verify.h>
#include <key.h>
-#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
#include <script/script.h>
#include <script/script_error.h>
#include <policy/settings.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <test/setup_common.h>
#include <vector>
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(sign)
// scriptPubKey: HASH160 <hash> EQUAL
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
@@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(set)
{
LOCK(cs_main);
// Test the CScript::Set* methods
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CKey key[4];
std::vector<CPubKey> keys;
for (int i = 0; i < 4; i++)
@@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
LOCK(cs_main);
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CKey key[6];
std::vector<CPubKey> keys;
for (int i = 0; i < 6; i++)
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 046b220e3f..412a57dd9d 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -3,8 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
-#include <keystore.h>
#include <script/script.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <test/setup_common.h>
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index ae903df0ad..84a70fe78b 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -6,10 +6,10 @@
#include <core_io.h>
#include <key.h>
-#include <keystore.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
@@ -1199,7 +1199,7 @@ SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction&
BOOST_AUTO_TEST_CASE(script_combineSigs)
{
// Test the ProduceSignature's ability to combine signatures function
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
std::vector<CKey> keys;
std::vector<CPubKey> pubkeys;
for (int i = 0; i < 3; i++)
diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp
index 24c7d51898..de877fd167 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/setup_common.cpp
@@ -23,10 +23,13 @@
#include <util/memory.h>
#include <util/strencodings.h>
#include <util/time.h>
+#include <util/translation.h>
#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
+#include <functional>
+
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
FastRandomContext g_insecure_rand_ctx;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index f77b77a972..34192c6b6a 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -12,12 +12,12 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
-#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <script/script.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <script/script_error.h>
#include <script/standard.h>
#include <streams.h>
@@ -289,7 +289,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
-SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
+SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
@@ -322,7 +322,7 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
BOOST_AUTO_TEST_CASE(test_Get)
{
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
@@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_CHECK_EQUAL(coins.GetValueIn(CTransaction(t1)), (50+21+22)*CENT);
}
-static void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
+static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
{
CMutableTransaction outputm;
outputm.nVersion = 1;
@@ -423,7 +423,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
CKey key;
key.MakeNewKey(true); // Need to use compressed keys in segwit or the signing will fail
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKeyPubKey(key, key.GetPubKey()));
CKeyID hash = key.GetPubKey().GetID();
CScript scriptPubKey = CScript() << OP_0 << std::vector<unsigned char>(hash.begin(), hash.end());
@@ -507,7 +507,7 @@ SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutabl
BOOST_AUTO_TEST_CASE(test_witness)
{
- CBasicKeyStore keystore, keystore2;
+ FillableSigningProvider keystore, keystore2;
CKey key1, key2, key3, key1L, key2L;
CPubKey pubkey1, pubkey2, pubkey3, pubkey1L, pubkey2L;
key1.MakeNewKey(true);
@@ -682,7 +682,7 @@ BOOST_AUTO_TEST_CASE(test_witness)
BOOST_AUTO_TEST_CASE(test_IsStandard)
{
LOCK(cs_main);
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 45c97fa2aa..f99a3748c9 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -8,8 +8,8 @@
#include <txmempool.h>
#include <script/standard.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <test/setup_common.h>
-#include <keystore.h>
#include <boost/test/unit_test.hpp>
@@ -161,7 +161,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(coinbaseKey));
BOOST_CHECK(keystore.AddCScript(p2pk_scriptPubKey));
diff --git a/src/test/util.cpp b/src/test/util.cpp
index a2ea648324..b7bb6deeaa 100644
--- a/src/test/util.cpp
+++ b/src/test/util.cpp
@@ -23,14 +23,9 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
std::string getnewaddress(CWallet& w)
{
constexpr auto output_type = OutputType::BECH32;
-
- CPubKey new_key;
- if (!w.GetKeyFromPool(new_key)) assert(false);
-
- w.LearnRelatedScripts(new_key, output_type);
- const auto dest = GetDestinationForKey(new_key, output_type);
-
- w.SetAddressBook(dest, /* label */ "", "receive");
+ CTxDestination dest;
+ std::string error;
+ if (!w.GetNewDestination(output_type, "", dest, error)) assert(false);
return EncodeDestination(dest);
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 9960573b33..15fe1148fe 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -14,6 +14,7 @@
#include <stdint.h>
#include <thread>
+#include <utility>
#include <vector>
#ifndef WIN32
#include <signal.h>
@@ -154,10 +155,10 @@ struct TestArgsManager : public ArgsManager
LOCK(cs_args);
m_network_only_args.insert(arg);
}
- void SetupArgs(int argv, const char* args[])
+ void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
- for (int i = 0; i < argv; ++i) {
- AddArg(args[i], "", false, OptionsCategory::OPTIONS);
+ for (const auto& arg : args) {
+ AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
using ArgsManager::ReadConfigStream;
@@ -168,11 +169,15 @@ struct TestArgsManager : public ArgsManager
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
TestArgsManager testArgs;
- const char* avail_args[] = {"-a", "-b", "-ccc", "-d"};
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
+ const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
+
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
std::string error;
- testArgs.SetupArgs(4, avail_args);
+ testArgs.SetupArgs({a, b, ccc, d});
BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
@@ -200,11 +205,17 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
BOOST_AUTO_TEST_CASE(util_GetBoolArg)
{
TestArgsManager testArgs;
- const char* avail_args[] = {"-a", "-b", "-c", "-d", "-e", "-f"};
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
+ const auto c = std::make_pair("-c", ArgsManager::ALLOW_BOOL);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_BOOL);
+ const auto e = std::make_pair("-e", ArgsManager::ALLOW_BOOL);
+ const auto f = std::make_pair("-f", ArgsManager::ALLOW_BOOL);
+
const char *argv_test[] = {
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error;
- testArgs.SetupArgs(6, avail_args);
+ testArgs.SetupArgs({a, b, c, d, e, f});
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// Each letter should be set.
@@ -237,9 +248,10 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
TestArgsManager testArgs;
// Params test
- const char* avail_args[] = {"-foo", "-bar"};
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_BOOL);
const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
- testArgs.SetupArgs(2, avail_args);
+ testArgs.SetupArgs({foo, bar});
std::string error;
BOOST_CHECK(testArgs.ParseParameters(4, (char**)argv_test, error));
@@ -308,8 +320,17 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
"iii=2\n";
TestArgsManager test_args;
- const char* avail_args[] = {"-a", "-b", "-ccc", "-d", "-e", "-fff", "-ggg", "-h", "-i", "-iii"};
- test_args.SetupArgs(10, avail_args);
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
+ const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_STRING);
+ const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
+ const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_BOOL);
+ const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_BOOL);
+ const auto h = std::make_pair("-h", ArgsManager::ALLOW_BOOL);
+ const auto i = std::make_pair("-i", ArgsManager::ALLOW_BOOL);
+ const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_INT);
+ test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii});
test_args.ReadConfigString(str_config);
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map
@@ -507,8 +528,9 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_AUTO_TEST_CASE(util_GetChainName)
{
TestArgsManager test_args;
- const char* avail_args[] = {"-testnet", "-regtest"};
- test_args.SetupArgs(2, avail_args);
+ const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_BOOL);
+ const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_BOOL);
+ test_args.SetupArgs({testnet, regtest});
const char* argv_testnet[] = {"cmd", "-testnet"};
const char* argv_regtest[] = {"cmd", "-regtest"};
@@ -682,7 +704,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
const std::string& name = net_specific ? "wallet" : "server";
const std::string key = "-" + name;
- parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
+ parser.AddArg(key, name, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
if (net_specific) parser.SetNetworkOnlyArg(key);
auto args = GetValues(arg_actions, section, name, "a");
@@ -809,8 +831,8 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
TestArgsManager parser;
LOCK(parser.cs_args);
- parser.AddArg("-regtest", "regtest", false, OptionsCategory::OPTIONS);
- parser.AddArg("-testnet", "testnet", false, OptionsCategory::OPTIONS);
+ parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
action == DISABLE_TEST ? "-testnet=0" :
diff --git a/src/timedata.cpp b/src/timedata.cpp
index f4613eeec8..9458b9ae0c 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -12,6 +12,7 @@
#include <sync.h>
#include <ui_interface.h>
#include <util/system.h>
+#include <util/translation.h>
#include <warnings.h>
@@ -100,7 +101,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
if (!fMatch)
{
fDone = true;
- std::string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME);
+ std::string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.").translated, PACKAGE_NAME);
SetMiscWarning(strMessage);
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index a1c730ba08..3f40785c21 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -759,7 +759,9 @@ void InterruptTorControl()
{
if (gBase) {
LogPrintf("tor: Thread interrupt\n");
- event_base_loopbreak(gBase);
+ event_base_once(gBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) {
+ event_base_loopbreak(gBase);
+ }, nullptr, nullptr);
}
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 73fe2a8ee4..df9851396e 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -5,12 +5,13 @@
#include <txdb.h>
-#include <random.h>
#include <pow.h>
+#include <random.h>
#include <shutdown.h>
+#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
-#include <ui_interface.h>
+#include <util/translation.h>
#include <stdint.h>
@@ -250,7 +251,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
- // Load mapBlockIndex
+ // Load m_block_index
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
if (ShutdownRequested()) return false;
@@ -357,7 +358,7 @@ bool CCoinsViewDB::Upgrade() {
int64_t count = 0;
LogPrintf("Upgrading utxo-set database...\n");
LogPrintf("[0%%]..."); /* Continued */
- uiInterface.ShowProgress(_("Upgrading UTXO database"), 0, true);
+ uiInterface.ShowProgress(_("Upgrading UTXO database").translated, 0, true);
size_t batch_size = 1 << 24;
CDBBatch batch(db);
int reportDone = 0;
@@ -372,7 +373,7 @@ bool CCoinsViewDB::Upgrade() {
if (count++ % 256 == 0) {
uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
- uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true);
+ uiInterface.ShowProgress(_("Upgrading UTXO database").translated, percentageDone, true);
if (reportDone < percentageDone/10) {
// report max. every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
diff --git a/src/txmempool.h b/src/txmempool.h
index 565dd61f0f..7169e80da2 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -345,7 +345,6 @@ struct TxMempoolInfo
* this is passed to the notification signal.
*/
enum class MemPoolRemovalReason {
- UNKNOWN = 0, //!< Manually removed or unknown reason
EXPIRY, //!< Expired from mempool
SIZELIMIT, //!< Removed in size limiting
REORG, //!< Removed for reorganization
@@ -574,7 +573,7 @@ public:
void addUnchecked(const CTxMemPoolEntry& entry, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
- void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -613,7 +612,7 @@ public:
* Set updateDescendants to true when removing a tx that was in a block, so
* that any in-mempool descendants have their ancestor state updated.
*/
- void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** When adding transactions from a disconnected block back to the mempool,
* new mempool entries may have children in the mempool (which is generally
@@ -735,7 +734,7 @@ private:
* transactions in a chain before we've updated all the state for the
* removal.
*/
- void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
};
/**
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 9331a92ad7..9edb7dc533 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -4,7 +4,9 @@
#include <util/error.h>
+#include <tinyformat.h>
#include <util/system.h>
+#include <util/translation.h>
std::string TransactionErrorString(const TransactionError err)
{
@@ -36,10 +38,10 @@ std::string TransactionErrorString(const TransactionError err)
std::string AmountHighWarn(const std::string& optname)
{
- return strprintf(_("%s is set very high!"), optname);
+ return strprintf(_("%s is set very high!").translated, optname);
}
std::string AmountErrMsg(const char* const optname, const std::string& strValue)
{
- return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
+ return strprintf(_("Invalid amount for -%s=<amount>: '%s'").translated, optname, strValue);
}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 72b37b9187..f8fcbc1206 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -7,6 +7,7 @@
#include <chainparamsbase.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <stdarg.h>
@@ -267,22 +268,24 @@ public:
* This method also tracks when the -no form was supplied, and if so,
* checks whether there was a double-negative (-nofoo=0 -> -foo=1).
*
- * If there was not a double negative, it removes the "no" from the key,
- * and returns true, indicating the caller should clear the args vector
- * to indicate a negated option.
+ * If there was not a double negative, it removes the "no" from the key
+ * and clears the args vector to indicate a negated option.
*
* If there was a double negative, it removes "no" from the key, sets the
- * value to "1" and returns false.
+ * value to "1" and pushes the key and the updated value to the args vector.
*
- * If there was no "no", it leaves key and value untouched and returns
- * false.
+ * If there was no "no", it leaves key and value untouched and pushes them
+ * to the args vector.
*
* Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable
* options that are not normally boolean (e.g. using -nodebuglogfile to request
* that debug log output is not sent to any file at all).
*/
-static bool InterpretNegatedOption(std::string& key, std::string& val)
+
+NODISCARD static bool InterpretOption(std::string key, std::string val, unsigned int flags,
+ std::map<std::string, std::vector<std::string>>& args,
+ std::string& error)
{
assert(key[0] == '-');
@@ -293,31 +296,25 @@ static bool InterpretNegatedOption(std::string& key, std::string& val)
++option_index;
}
if (key.substr(option_index, 2) == "no") {
- bool bool_val = InterpretBool(val);
key.erase(option_index, 2);
- if (!bool_val ) {
+ if (flags & ArgsManager::ALLOW_BOOL) {
+ if (InterpretBool(val)) {
+ args[key].clear();
+ return true;
+ }
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
val = "1";
} else {
- return true;
+ error = strprintf("Negating of %s is meaningless and therefore forbidden", key.c_str());
+ return false;
}
}
- return false;
+ args[key].push_back(val);
+ return true;
}
-ArgsManager::ArgsManager() :
- /* These options would cause cross-contamination if values for
- * mainnet were used while running on regtest/testnet (or vice-versa).
- * Setting them as section_only_args ensures that sharing a config file
- * between mainnet and regtest/testnet won't cause problems due to these
- * parameters by accident. */
- m_network_only_args{
- "-addnode", "-connect",
- "-port", "-bind",
- "-rpcport", "-rpcbind",
- "-wallet",
- }
+ArgsManager::ArgsManager()
{
// nothing to do
}
@@ -383,6 +380,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
+ if (key == "-") break; //bitcoin-tx using stdin
std::string val;
size_t is_index = key.find('=');
if (is_index != std::string::npos) {
@@ -402,19 +400,14 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
if (key.length() > 1 && key[1] == '-')
key.erase(0, 1);
- // Check for -nofoo
- if (InterpretNegatedOption(key, val)) {
- m_override_args[key].clear();
- } else {
- m_override_args[key].push_back(val);
- }
-
- // Check that the arg is known
- if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
- if (!IsArgKnown(key)) {
- error = strprintf("Invalid parameter %s", key.c_str());
+ const unsigned int flags = FlagsOfKnownArg(key);
+ if (flags) {
+ if (!InterpretOption(key, val, flags, m_override_args, error)) {
return false;
}
+ } else {
+ error = strprintf("Invalid parameter %s", key.c_str());
+ return false;
}
}
@@ -431,21 +424,30 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
return true;
}
-bool ArgsManager::IsArgKnown(const std::string& key) const
+unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
{
+ assert(key[0] == '-');
+
size_t option_index = key.find('.');
- std::string arg_no_net;
if (option_index == std::string::npos) {
- arg_no_net = key;
+ option_index = 1;
} else {
- arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos);
+ ++option_index;
}
+ if (key.substr(option_index, 2) == "no") {
+ option_index += 2;
+ }
+
+ const std::string base_arg_name = '-' + key.substr(option_index);
LOCK(cs_args);
for (const auto& arg_map : m_available_args) {
- if (arg_map.second.count(arg_no_net)) return true;
+ const auto search = arg_map.second.find(base_arg_name);
+ if (search != arg_map.second.end()) {
+ return search->second.m_flags;
+ }
}
- return false;
+ return ArgsManager::NONE;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
@@ -537,24 +539,29 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
m_override_args[strArg] = {strValue};
}
-void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat)
+void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
{
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
eq_index = name.size();
}
+ std::string arg_name = name.substr(0, eq_index);
LOCK(cs_args);
std::map<std::string, Arg>& arg_map = m_available_args[cat];
- auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only));
+ auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
assert(ret.second); // Make sure an insertion actually happened
+
+ if (flags & ArgsManager::NETWORK_ONLY) {
+ m_network_only_args.emplace(arg_name);
+ }
}
void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
{
for (const std::string& name : names) {
- AddArg(name, "", false, OptionsCategory::HIDDEN);
+ AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
}
}
@@ -613,7 +620,7 @@ std::string ArgsManager::GetHelpMessage() const
if (arg_map.first == OptionsCategory::HIDDEN) break;
for (const auto& arg : arg_map.second) {
- if (show_debug || !arg.second.m_debug_only) {
+ if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
std::string name;
if (arg.second.m_help_param.empty()) {
name = arg.first;
@@ -634,7 +641,7 @@ bool HelpRequested(const ArgsManager& args)
void SetupHelpOptions(ArgsManager& args)
{
- args.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
+ args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
args.AddHiddenArgs({"-h", "-help"});
}
@@ -838,22 +845,18 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
- std::string strKey = std::string("-") + option.first;
- std::string strValue = option.second;
-
- if (InterpretNegatedOption(strKey, strValue)) {
- m_config_args[strKey].clear();
+ const std::string strKey = std::string("-") + option.first;
+ const unsigned int flags = FlagsOfKnownArg(strKey);
+ if (flags) {
+ if (!InterpretOption(strKey, option.second, flags, m_config_args, error)) {
+ return false;
+ }
} else {
- m_config_args[strKey].push_back(strValue);
- }
-
- // Check that the arg is known
- if (!IsArgKnown(strKey)) {
- if (!ignore_invalid_keys) {
+ if (ignore_invalid_keys) {
+ LogPrintf("Ignoring unknown configuration value %s\n", option.first);
+ } else {
error = strprintf("Invalid configuration value %s", option.first.c_str());
return false;
- } else {
- LogPrintf("Ignoring unknown configuration value %s\n", option.first);
}
}
}
@@ -1184,7 +1187,7 @@ int GetNumCores()
std::string CopyrightHolders(const std::string& strPrefix)
{
- const auto copyright_devs = strprintf(_(COPYRIGHT_HOLDERS), COPYRIGHT_HOLDERS_SUBSTITUTION);
+ const auto copyright_devs = strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION);
std::string strCopyrightHolders = strPrefix + copyright_devs;
// Make sure Bitcoin Core copyright is not removed by accident
diff --git a/src/util/system.h b/src/util/system.h
index dda9156488..75e8096826 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -40,18 +40,6 @@ int64_t GetStartupTime();
extern const char * const BITCOIN_CONF_FILENAME;
-/** Translate a message to the native language of the user. */
-const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
-
-/**
- * Translation function.
- * If no translation function is set, simply return the input.
- */
-inline std::string _(const char* psz)
-{
- return G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(psz) : psz;
-}
-
void SetupEnvironment();
bool SetupNetworking();
@@ -139,6 +127,23 @@ struct SectionInfo
class ArgsManager
{
+public:
+ enum Flags {
+ NONE = 0x00,
+ // Boolean options can accept negation syntax -noOPTION or -noOPTION=1
+ ALLOW_BOOL = 0x01,
+ ALLOW_INT = 0x02,
+ ALLOW_STRING = 0x04,
+ ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING,
+ DEBUG_ONLY = 0x100,
+ /* Some options would cause cross-contamination if values for
+ * mainnet were used while running on regtest/testnet (or vice-versa).
+ * Setting them as NETWORK_ONLY ensures that sharing a config file
+ * between mainnet and regtest/testnet won't cause problems due to these
+ * parameters by accident. */
+ NETWORK_ONLY = 0x200,
+ };
+
protected:
friend class ArgsManagerHelper;
@@ -146,9 +151,7 @@ protected:
{
std::string m_help_param;
std::string m_help_text;
- bool m_debug_only;
-
- Arg(const std::string& help_param, const std::string& help_text, bool debug_only) : m_help_param(help_param), m_help_text(help_text), m_debug_only(debug_only) {};
+ unsigned int m_flags;
};
mutable CCriticalSection cs_args;
@@ -268,7 +271,7 @@ public:
/**
* Add argument
*/
- void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat);
+ void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
* Add many hidden arguments
@@ -281,6 +284,7 @@ public:
void ClearArgs() {
LOCK(cs_args);
m_available_args.clear();
+ m_network_only_args.clear();
}
/**
@@ -289,9 +293,10 @@ public:
std::string GetHelpMessage() const;
/**
- * Check whether we know of this arg
+ * Return Flags for known arg.
+ * Return ArgsManager::NONE for unknown arg.
*/
- bool IsArgKnown(const std::string& key) const;
+ unsigned int FlagsOfKnownArg(const std::string& key) const;
};
extern ArgsManager gArgs;
diff --git a/src/util/translation.h b/src/util/translation.h
new file mode 100644
index 0000000000..f100dab20d
--- /dev/null
+++ b/src/util/translation.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef BITCOIN_UTIL_TRANSLATION_H
+#define BITCOIN_UTIL_TRANSLATION_H
+
+#include <tinyformat.h>
+
+#include <utility>
+
+/**
+ * Bilingual messages:
+ * - in GUI: user's native language + untranslated (i.e. English)
+ * - in log and stderr: untranslated only
+ */
+struct bilingual_str {
+ std::string original;
+ std::string translated;
+};
+
+namespace tinyformat {
+template <typename... Args>
+bilingual_str format(const bilingual_str& fmt, const Args&... args)
+{
+ return bilingual_str{format(fmt.original, args...), format(fmt.translated, args...)};
+}
+} // namespace tinyformat
+
+/** Translate a message to the native language of the user. */
+const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
+
+/**
+ * Translation function.
+ * If no translation function is set, simply return the input.
+ */
+inline bilingual_str _(const char* psz)
+{
+ return bilingual_str{psz, G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(psz) : psz};
+}
+
+#endif // BITCOIN_UTIL_TRANSLATION_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 262b6856a4..b4677df62f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -41,6 +41,7 @@
#include <util/rbf.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/translation.h>
#include <util/validation.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -77,7 +78,11 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-static CChainState g_chainstate;
+namespace {
+BlockManager g_blockman;
+} // anon namespace
+
+static CChainState g_chainstate(g_blockman);
CChainState& ChainstateActive() { return g_chainstate; }
@@ -95,7 +100,6 @@ CChain& ChainActive() { return g_chainstate.m_chain; }
*/
RecursiveMutex cs_main;
-BlockMap& mapBlockIndex = ::ChainstateActive().mapBlockIndex;
CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
@@ -125,12 +129,7 @@ CScript COINBASE_FLAGS;
// Internal stuff
namespace {
- CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid;
-
- /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
- * Pruned nodes may have entries where B is missing data.
- */
- std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = ::ChainstateActive().mapBlocksUnlinked;
+ CBlockIndex* pindexBestInvalid = nullptr;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
@@ -148,6 +147,13 @@ namespace {
std::set<int> setDirtyFileInfo;
} // anon namespace
+CBlockIndex* LookupBlockIndex(const uint256& hash)
+{
+ AssertLockHeld(cs_main);
+ BlockMap::const_iterator it = g_blockman.m_block_index.find(hash);
+ return it == g_blockman.m_block_index.end() ? nullptr : it->second;
+}
+
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
AssertLockHeld(cs_main);
@@ -611,7 +617,23 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
std::string errString;
if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
+ setAncestors.clear();
+ // If CalculateMemPoolAncestors fails second time, we want the original error string.
+ std::string dummy_err_string;
+ // If the new transaction is relatively small (up to 40k weight)
+ // and has at most one ancestor (ie ancestor limit of 2, including
+ // the new transaction), allow it if its parent has exactly the
+ // descendant limit descendants.
+ //
+ // This allows protocols which rely on distrusting counterparties
+ // being able to broadcast descendants of an unconfirmed transaction
+ // to be secure by simply only having two immediately-spendable
+ // outputs - one for each counterparty. For more info on the uses for
+ // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
+ if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
+ !pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
+ }
}
// A transaction that spends outputs that would be replaced by it is invalid. Now
@@ -1047,6 +1069,11 @@ bool CChainState::IsInitialBlockDownload() const
static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
+BlockMap& BlockIndex()
+{
+ return g_blockman.m_block_index;
+}
+
static void AlertNotify(const std::string& strMessage)
{
uiInterface.NotifyAlertChanged();
@@ -1160,7 +1187,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- m_failed_blocks.insert(pindex);
+ m_blockman.m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1384,7 +1411,7 @@ static bool AbortNode(const std::string& strMessage, const std::string& userMess
if (!userMessage.empty()) {
uiInterface.ThreadSafeMessageBox(userMessage, "", CClientUIInterface::MSG_ERROR | prefix);
} else {
- uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
+ uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details").translated, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
}
StartShutdown();
return false;
@@ -1695,8 +1722,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
// This setting doesn't force the selection of any particular chain but makes validating some faster by
// effectively caching the result of part of the verification.
- BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid);
- if (it != mapBlockIndex.end()) {
+ BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
+ if (it != m_blockman.m_block_index.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
@@ -2003,7 +2030,7 @@ bool CChainState::FlushStateToDisk(
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(GetBlocksDir())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
@@ -2038,7 +2065,7 @@ bool CChainState::FlushStateToDisk(
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
@@ -2112,7 +2139,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)").translated, bit);
if (state == ThresholdState::ACTIVE) {
DoWarning(strWarning);
} else {
@@ -2129,7 +2156,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
+ AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
@@ -2160,7 +2187,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
- return AbortNode(state, "Failed to read block");
+ return error("DisconnectTip(): Failed to read block");
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
@@ -2366,10 +2393,11 @@ CBlockIndex* CChainState::FindMostWorkChain() {
if (fFailedChain) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
} else if (fMissingData) {
- // If we're missing data, then add back to mapBlocksUnlinked,
+ // If we're missing data, then add back to m_blocks_unlinked,
// so that if the block arrives in the future we can try adding
// to setBlockIndexCandidates again.
- mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed));
+ m_blockman.m_blocks_unlinked.insert(
+ std::make_pair(pindexFailed->pprev, pindexFailed));
}
setBlockIndexCandidates.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
@@ -2416,6 +2444,11 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
UpdateMempoolForReorg(disconnectpool, false);
+
+ // If we're unable to disconnect a block during normal operation,
+ // then that is a failure of our local system -- we should abort
+ // rather than stay on a less work chain.
+ AbortNode(state, "Failed to disconnect block; see debug.log for details");
return false;
}
fBlocksDisconnected = true;
@@ -2720,12 +2753,12 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(to_mark_failed);
setBlockIndexCandidates.erase(to_mark_failed);
- m_failed_blocks.insert(to_mark_failed);
+ m_blockman.m_failed_blocks.insert(to_mark_failed);
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
- BlockMap::iterator it = mapBlockIndex.begin();
- while (it != mapBlockIndex.end()) {
+ BlockMap::iterator it = m_blockman.m_block_index.begin();
+ while (it != m_blockman.m_block_index.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
@@ -2752,8 +2785,8 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
int nHeight = pindex->nHeight;
// Remove the invalidity flag from this block and all its descendants.
- BlockMap::iterator it = mapBlockIndex.begin();
- while (it != mapBlockIndex.end()) {
+ BlockMap::iterator it = m_blockman.m_block_index.begin();
+ while (it != m_blockman.m_block_index.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
@@ -2764,7 +2797,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
- m_failed_blocks.erase(it->second);
+ m_blockman.m_failed_blocks.erase(it->second);
}
it++;
}
@@ -2774,7 +2807,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex);
- m_failed_blocks.erase(pindex);
+ m_blockman.m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
}
@@ -2784,14 +2817,14 @@ void ResetBlockFailureFlags(CBlockIndex *pindex) {
return ::ChainstateActive().ResetBlockFailureFlags(pindex);
}
-CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
+CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
- BlockMap::iterator it = mapBlockIndex.find(hash);
- if (it != mapBlockIndex.end())
+ BlockMap::iterator it = m_block_index.find(hash);
+ if (it != m_block_index.end())
return it->second;
// Construct new block index object
@@ -2800,10 +2833,10 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
pindexNew->nSequenceId = 0;
- BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first;
+ BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
- BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
- if (miPrev != mapBlockIndex.end())
+ BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
+ if (miPrev != m_block_index.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
@@ -2852,17 +2885,17 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
- std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex);
+ std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = m_blockman.m_blocks_unlinked.equal_range(pindex);
while (range.first != range.second) {
std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first;
queue.push_back(it->second);
range.first++;
- mapBlocksUnlinked.erase(it);
+ m_blockman.m_blocks_unlinked.erase(it);
}
}
} else {
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) {
- mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
+ m_blockman.m_blocks_unlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
}
}
@@ -2905,7 +2938,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
bool out_of_space;
size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode("Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode("Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -2929,7 +2962,7 @@ static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, un
bool out_of_space;
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!"), CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3117,7 +3150,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
if (fCheckpointsEnabled) {
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
- // MapBlockIndex.
+ // g_blockman.m_block_index.
CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
return state.Invalid(ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
@@ -3230,15 +3263,15 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
return true;
}
-bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
+bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
- BlockMap::iterator miSelf = mapBlockIndex.find(hash);
+ BlockMap::iterator miSelf = m_block_index.find(hash);
CBlockIndex *pindex = nullptr;
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
- if (miSelf != mapBlockIndex.end()) {
+ if (miSelf != m_block_index.end()) {
// Block header is already known.
pindex = miSelf->second;
if (ppindex)
@@ -3253,8 +3286,8 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
// Get prev block index
CBlockIndex* pindexPrev = nullptr;
- BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
- if (mi == mapBlockIndex.end())
+ BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock);
+ if (mi == m_block_index.end())
return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
@@ -3306,8 +3339,6 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (ppindex)
*ppindex = pindex;
- CheckBlockIndex(chainparams.GetConsensus());
-
return true;
}
@@ -3319,7 +3350,10 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- if (!::ChainstateActive().AcceptBlockHeader(header, state, chainparams, &pindex)) {
+ bool accepted = g_blockman.AcceptBlockHeader(header, state, chainparams, &pindex);
+ ::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
+
+ if (!accepted) {
if (first_invalid) *first_invalid = header;
return false;
}
@@ -3362,7 +3396,10 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
- if (!AcceptBlockHeader(block, state, chainparams, &pindex))
+ bool accepted_header = m_blockman.AcceptBlockHeader(block, state, chainparams, &pindex);
+ CheckBlockIndex(chainparams.GetConsensus());
+
+ if (!accepted_header)
return false;
// Try to process all requested blocks that we don't have, but only
@@ -3513,7 +3550,7 @@ void PruneOneBlockFile(const int fileNumber)
{
LOCK(cs_LastBlockFile);
- for (const auto& entry : mapBlockIndex) {
+ for (const auto& entry : g_blockman.m_block_index) {
CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -3523,16 +3560,16 @@ void PruneOneBlockFile(const int fileNumber)
pindex->nUndoPos = 0;
setDirtyBlockIndex.insert(pindex);
- // Prune from mapBlocksUnlinked -- any block we prune would have
+ // Prune from m_blocks_unlinked -- any block we prune would have
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
- // mapBlocksUnlinked or setBlockIndexCandidates.
- std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev);
+ // m_blocks_unlinked or setBlockIndexCandidates.
+ auto range = g_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
if (_it->second == pindex) {
- mapBlocksUnlinked.erase(_it);
+ g_blockman.m_blocks_unlinked.erase(_it);
}
}
}
@@ -3681,7 +3718,7 @@ fs::path GetBlockPosFilename(const FlatFilePos &pos)
return BlockFileSeq().FileName(pos);
}
-CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
+CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
{
AssertLockHeld(cs_main);
@@ -3689,27 +3726,30 @@ CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
return nullptr;
// Return existing
- BlockMap::iterator mi = mapBlockIndex.find(hash);
- if (mi != mapBlockIndex.end())
+ BlockMap::iterator mi = m_block_index.find(hash);
+ if (mi != m_block_index.end())
return (*mi).second;
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
- mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first;
+ mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
}
-bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree)
+bool BlockManager::LoadBlockIndex(
+ const Consensus::Params& consensus_params,
+ CBlockTreeDB& blocktree,
+ std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
{
if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
return false;
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
- vSortedByHeight.reserve(mapBlockIndex.size());
- for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
+ vSortedByHeight.reserve(m_block_index.size());
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
@@ -3729,7 +3769,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
} else {
pindex->nChainTx = 0;
- mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex));
+ m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
}
} else {
pindex->nChainTx = pindex->nTx;
@@ -3739,8 +3779,9 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))
- setBlockIndexCandidates.insert(pindex);
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) {
+ block_index_candidates.insert(pindex);
+ }
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex;
if (pindex->pprev)
@@ -3752,9 +3793,21 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
return true;
}
+void BlockManager::Unload() {
+ m_failed_blocks.clear();
+ m_blocks_unlinked.clear();
+
+ for (const BlockMap::value_type& entry : m_block_index) {
+ delete entry.second;
+ }
+
+ m_block_index.clear();
+}
+
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!::ChainstateActive().LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
+ if (!g_blockman.LoadBlockIndex(
+ chainparams.GetConsensus(), *pblocktree, ::ChainstateActive().setBlockIndexCandidates))
return false;
// Load block file info
@@ -3777,7 +3830,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : g_blockman.m_block_index)
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
@@ -3830,7 +3883,7 @@ bool LoadChainTip(const CChainParams& chainparams)
CVerifyDB::CVerifyDB()
{
- uiInterface.ShowProgress(_("Verifying blocks..."), 0, false);
+ uiInterface.ShowProgress(_("Verifying blocks...").translated, 0, false);
}
CVerifyDB::~CVerifyDB()
@@ -3864,7 +3917,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
- uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
+ uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
@@ -3922,7 +3975,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
- uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
+ uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
pindex = ::ChainActive().Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
@@ -3969,23 +4022,23 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
if (hashHeads.empty()) return true; // We're already in a consistent state.
if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state");
- uiInterface.ShowProgress(_("Replaying blocks..."), 0, false);
+ uiInterface.ShowProgress(_("Replaying blocks...").translated, 0, false);
LogPrintf("Replaying blocks\n");
const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush.
const CBlockIndex* pindexNew; // New tip during the interrupted flush.
const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip.
- if (mapBlockIndex.count(hashHeads[0]) == 0) {
+ if (m_blockman.m_block_index.count(hashHeads[0]) == 0) {
return error("ReplayBlocks(): reorganization to unknown block requested");
}
- pindexNew = mapBlockIndex[hashHeads[0]];
+ pindexNew = m_blockman.m_block_index[hashHeads[0]];
if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
- if (mapBlockIndex.count(hashHeads[1]) == 0) {
+ if (m_blockman.m_block_index.count(hashHeads[1]) == 0) {
return error("ReplayBlocks(): reorganization from unknown block requested");
}
- pindexOld = mapBlockIndex[hashHeads[1]];
+ pindexOld = m_blockman.m_block_index[hashHeads[1]];
pindexFork = LastCommonAncestor(pindexOld, pindexNew);
assert(pindexFork != nullptr);
}
@@ -4015,7 +4068,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
- uiInterface.ShowProgress(_("Replaying blocks..."), (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
+ uiInterface.ShowProgress(_("Replaying blocks...").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
if (!RollforwardBlock(pindex, cache, params)) return false;
}
@@ -4051,10 +4104,10 @@ void CChainState::EraseBlockData(CBlockIndex* index)
setDirtyBlockIndex.insert(index);
// Update indexes
setBlockIndexCandidates.erase(index);
- std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(index->pprev);
+ auto ret = m_blockman.m_blocks_unlinked.equal_range(index->pprev);
while (ret.first != ret.second) {
if (ret.first->second == index) {
- mapBlocksUnlinked.erase(ret.first++);
+ m_blockman.m_blocks_unlinked.erase(ret.first++);
} else {
++ret.first;
}
@@ -4074,7 +4127,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// blocks will be dealt with below (releasing cs_main in between).
{
LOCK(cs_main);
- for (const auto& entry : mapBlockIndex) {
+ for (const auto& entry : m_blockman.m_block_index) {
if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
EraseBlockData(entry.second);
}
@@ -4180,7 +4233,6 @@ bool RewindBlockIndex(const CChainParams& params) {
void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1;
- m_failed_blocks.clear();
setBlockIndexCandidates.clear();
}
@@ -4191,10 +4243,10 @@ void UnloadBlockIndex()
{
LOCK(cs_main);
::ChainActive().SetTip(nullptr);
+ g_blockman.Unload();
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
mempool.clear();
- mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
nLastBlockFile = 0;
setDirtyBlockIndex.clear();
@@ -4203,11 +4255,6 @@ void UnloadBlockIndex()
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
-
- for (const BlockMap::value_type& entry : mapBlockIndex) {
- delete entry.second;
- }
- mapBlockIndex.clear();
fHavePruned = false;
::ChainstateActive().UnloadBlockIndex();
@@ -4220,7 +4267,7 @@ bool LoadBlockIndex(const CChainParams& chainparams)
if (!fReindex) {
bool ret = LoadBlockIndexDB(chainparams);
if (!ret) return false;
- needs_init = mapBlockIndex.empty();
+ needs_init = g_blockman.m_block_index.empty();
}
if (needs_init) {
@@ -4240,10 +4287,10 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
LOCK(cs_main);
// Check whether we're already initialized by checking for genesis in
- // mapBlockIndex. Note that we can't use m_chain here, since it is
+ // m_blockman.m_block_index. Note that we can't use m_chain here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
- if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash()))
+ if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
try {
@@ -4251,7 +4298,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
- CBlockIndex *pindex = AddToBlockIndex(block);
+ CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
@@ -4396,20 +4443,20 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
LOCK(cs_main);
// During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain,
- // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when
- // iterating the block tree require that m_chain has been initialized.)
+ // so we have the genesis block in m_blockman.m_block_index but no active chain. (A few of the
+ // tests when iterating the block tree require that m_chain has been initialized.)
if (m_chain.Height() < 0) {
- assert(mapBlockIndex.size() <= 1);
+ assert(m_blockman.m_block_index.size() <= 1);
return;
}
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward;
- for (const std::pair<const uint256, CBlockIndex*>& entry : mapBlockIndex) {
+ for (const std::pair<const uint256, CBlockIndex*>& entry : m_blockman.m_block_index) {
forward.insert(std::make_pair(entry.second->pprev, entry.second));
}
- assert(forward.size() == mapBlockIndex.size());
+ assert(forward.size() == m_blockman.m_block_index.size());
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(nullptr);
CBlockIndex *pindex = rangeGenesis.first->second;
@@ -4463,7 +4510,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
- assert(pindexFirstNotTreeValid == nullptr); // All mapBlockIndex entries must at least be TREE valid
+ assert(pindexFirstNotTreeValid == nullptr); // All m_blockman.m_block_index entries must at least be TREE valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == nullptr); // TREE valid implies all parents are TREE valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == nullptr); // CHAIN valid implies all parents are CHAIN valid
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == nullptr); // SCRIPTS valid implies all parents are SCRIPTS valid
@@ -4482,13 +4529,13 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
}
// If some parent is missing, then it could be that this block was in
// setBlockIndexCandidates but had to be removed because of the missing data.
- // In this case it must be in mapBlocksUnlinked -- see test below.
+ // In this case it must be in m_blocks_unlinked -- see test below.
}
} else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates.
assert(setBlockIndexCandidates.count(pindex) == 0);
}
- // Check whether this block is in mapBlocksUnlinked.
- std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev);
+ // Check whether this block is in m_blocks_unlinked.
+ std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
bool foundInUnlinked = false;
while (rangeUnlinked.first != rangeUnlinked.second) {
assert(rangeUnlinked.first->first == pindex->pprev);
@@ -4499,22 +4546,22 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
rangeUnlinked.first++;
}
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) {
- // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked.
+ // If this block has block data available, some parent was never received, and has no invalid parents, it must be in m_blocks_unlinked.
assert(foundInUnlinked);
}
- if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA
- if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked.
+ if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in m_blocks_unlinked if we don't HAVE_DATA
+ if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
assert(fHavePruned); // We must have pruned.
- // This block may have entered mapBlocksUnlinked if:
+ // This block may have entered m_blocks_unlinked if:
// - it has a descendant that at some point had more work than the
// tip, and
// - we tried switching to that descendant but were missing
// data for some intermediate block between m_chain and the
// tip.
// So if this block is itself better than m_chain.Tip() and it wasn't in
- // setBlockIndexCandidates, then it must be in mapBlocksUnlinked.
+ // setBlockIndexCandidates, then it must be in m_blocks_unlinked.
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
if (pindexFirstInvalid == nullptr) {
assert(foundInUnlinked);
@@ -4758,10 +4805,10 @@ public:
CMainCleanup() {}
~CMainCleanup() {
// block headers
- BlockMap::iterator it1 = mapBlockIndex.begin();
- for (; it1 != mapBlockIndex.end(); it1++)
+ BlockMap::iterator it1 = g_blockman.m_block_index.begin();
+ for (; it1 != g_blockman.m_block_index.end(); it1++)
delete (*it1).second;
- mapBlockIndex.clear();
+ g_blockman.m_block_index.clear();
}
};
static CMainCleanup instance_of_cmaincleanup;
diff --git a/src/validation.h b/src/validation.h
index 9573d62048..d747fdbf27 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -64,6 +64,12 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101;
static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25;
/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
+/**
+ * An extra transaction can be added to a package, as long as it only has one
+ * ancestor and is no larger than this. Not really any reason to make this
+ * configurable as it doesn't materially change DoS parameters.
+ */
+static const unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT = 10000;
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336;
/** Maximum kilobytes for transactions to store for processing during reorg */
@@ -126,8 +132,6 @@ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
/** Maximum number of unconnecting headers announcements before DoS score */
static const int MAX_UNCONNECTING_HEADERS = 10;
-static const bool DEFAULT_PEERBLOOMFILTERS = true;
-
/** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0;
@@ -144,7 +148,6 @@ extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
-extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
extern uint256 g_best_block;
@@ -406,12 +409,7 @@ public:
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
-inline CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- AssertLockHeld(cs_main);
- BlockMap::const_iterator it = mapBlockIndex.find(hash);
- return it == mapBlockIndex.end() ? nullptr : it->second;
-}
+CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -439,27 +437,90 @@ struct CBlockIndexWorkComparator
};
/**
- * CChainState stores and provides an API to update our local knowledge of the
- * current best chain and header tree.
+ * Maintains a tree of blocks (stored in `m_block_index`) which is consulted
+ * to determine where the most-work tip is.
*
- * It generally provides access to the current block tree, as well as functions
- * to provide new data, which it will appropriately validate and incorporate in
- * its state as necessary.
+ * This data is used mostly in `CChainState` - information about, e.g.,
+ * candidate tips is not maintained here.
+ */
+class BlockManager {
+public:
+ BlockMap m_block_index GUARDED_BY(cs_main);
+
+ /** In order to efficiently track invalidity of headers, we keep the set of
+ * blocks which we tried to connect and found to be invalid here (ie which
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
+ * walk this set and check if a new header is a descendant of something in
+ * this set, preventing us from having to walk m_block_index when we try
+ * to connect a bad block and fail.
+ *
+ * While this is more complicated than marking everything which descends
+ * from an invalid block as invalid at the time we discover it to be
+ * invalid, doing so would require walking all of m_block_index to find all
+ * descendants. Since this case should be very rare, keeping track of all
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
+ * well.
+ *
+ * Because we already walk m_block_index in height-order at startup, we go
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
+ * instead of putting things in this set.
+ */
+ std::set<CBlockIndex*> m_failed_blocks;
+
+ /**
+ * All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
+ * Pruned nodes may have entries where B is missing data.
+ */
+ std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
+
+ /**
+ * Load the blocktree off disk and into memory. Populate certain metadata
+ * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
+ * collections like setDirtyBlockIndex.
+ *
+ * @param[out] block_index_candidates Fill this set with any valid blocks for
+ * which we've downloaded all transactions.
+ */
+ bool LoadBlockIndex(
+ const Consensus::Params& consensus_params,
+ CBlockTreeDB& blocktree,
+ std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Clear all data members. */
+ void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Create a new block index entry for a given block hash */
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /**
+ * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
+ * that it doesn't descend from an invalid block, and then add it to m_block_index.
+ */
+ bool AcceptBlockHeader(
+ const CBlockHeader& block,
+ CValidationState& state,
+ const CChainParams& chainparams,
+ CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+};
+
+/**
+ * CChainState stores and provides an API to update our local knowledge of the
+ * current best chain.
*
* Eventually, the API here is targeted at being exposed externally as a
* consumable libconsensus library, so any functions added must only call
* other class member functions, pure functions in other parts of the consensus
* library, callbacks via the validation interface, or read/write-to-disk
* functions (eventually this will also be via callbacks).
+ *
+ * Anything that is contingent on the current tip of the chain is stored here,
+ * whereas block information and metadata independent of the current tip is
+ * kept in `BlockMetadataManager`.
*/
class CChainState {
private:
- /**
- * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
- * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
- * missing the data for the block.
- */
- std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/**
* Every received block is assigned a unique and increasing identifier, so we
@@ -473,26 +534,6 @@ private:
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;
- /** In order to efficiently track invalidity of headers, we keep the set of
- * blocks which we tried to connect and found to be invalid here (ie which
- * were set to BLOCK_FAILED_VALID since the last restart). We can then
- * walk this set and check if a new header is a descendant of something in
- * this set, preventing us from having to walk mapBlockIndex when we try
- * to connect a bad block and fail.
- *
- * While this is more complicated than marking everything which descends
- * from an invalid block as invalid at the time we discover it to be
- * invalid, doing so would require walking all of mapBlockIndex to find all
- * descendants. Since this case should be very rare, keeping track of all
- * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
- * well.
- *
- * Because we already walk mapBlockIndex in height-order at startup, we go
- * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
- * instead of putting things in this set.
- */
- std::set<CBlockIndex*> m_failed_blocks;
-
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
@@ -507,15 +548,23 @@ private:
*/
mutable std::atomic<bool> m_cached_finished_ibd{false};
+ //! Reference to a BlockManager instance which itself is shared across all
+ //! CChainState instances. Keeping a local reference allows us to test more
+ //! easily as opposed to referencing a global.
+ BlockManager& m_blockman;
+
public:
+ CChainState(BlockManager& blockman) : m_blockman(blockman) { }
+
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
- BlockMap mapBlockIndex GUARDED_BY(cs_main);
- std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
- CBlockIndex *pindexBestInvalid = nullptr;
-
- bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
+ * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
+ * missing the data for the block.
+ */
+ std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/**
* Update the on-disk chain state.
@@ -541,11 +590,6 @@ public:
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
- /**
- * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
- * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
- */
- bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
@@ -572,13 +616,6 @@ public:
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload() const;
-private:
- bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
- bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
-
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Create a new block index entry for a given block hash */
- CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Make various assertions about the state of the block index.
*
@@ -586,6 +623,10 @@ private:
*/
void CheckBlockIndex(const Consensus::Params& consensusParams);
+private:
+ bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -615,6 +656,9 @@ CChainState& ChainstateActive();
/** @returns the most-work chain. */
CChain& ChainActive();
+/** @returns the global block index map. */
+BlockMap& BlockIndex();
+
/** Global variable that points to the coins database (protected by cs_main) */
extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index 60bce66839..14513bc9e9 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -20,5 +20,7 @@ void CCoinControl::SetNull()
m_confirm_target.reset();
m_signal_bip125_rbf.reset();
m_fee_mode = FeeEstimateMode::UNSET;
+ m_min_depth = DEFAULT_MIN_DEPTH;
+ m_max_depth = DEFAULT_MAX_DEPTH;
}
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 249c402e4d..92a290530c 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -12,6 +12,9 @@
#include <boost/optional.hpp>
+const int DEFAULT_MIN_DEPTH = 0;
+const int DEFAULT_MAX_DEPTH = 9999999;
+
/** Coin Control Features. */
class CCoinControl
{
@@ -39,7 +42,9 @@ public:
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
//! Minimum chain depth value for coin availability
- int m_min_depth{0};
+ int m_min_depth = DEFAULT_MIN_DEPTH;
+ //! Maximum chain depth value for coin availability
+ int m_max_depth = DEFAULT_MAX_DEPTH;
CCoinControl()
{
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index dd56ea10ab..0b76c1a0eb 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -107,8 +107,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
return true;
}
-
-static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
+bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
@@ -118,7 +117,7 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
}
-static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
@@ -128,7 +127,7 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<u
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
}
-static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
+bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
{
CKeyingMaterial vchSecret;
if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
@@ -140,188 +139,3 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsi
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
return key.VerifyPubKey(vchPubKey);
}
-
-bool CCryptoKeyStore::SetCrypted()
-{
- LOCK(cs_KeyStore);
- if (fUseCrypto)
- return true;
- if (!mapKeys.empty())
- return false;
- fUseCrypto = true;
- return true;
-}
-
-bool CCryptoKeyStore::IsLocked() const
-{
- if (!IsCrypted()) {
- return false;
- }
- LOCK(cs_KeyStore);
- return vMasterKey.empty();
-}
-
-bool CCryptoKeyStore::Lock()
-{
- if (!SetCrypted())
- return false;
-
- {
- LOCK(cs_KeyStore);
- vMasterKey.clear();
- }
-
- NotifyStatusChanged(this);
- return true;
-}
-
-bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
-{
- {
- LOCK(cs_KeyStore);
- if (!SetCrypted())
- return false;
-
- bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
- bool keyFail = false;
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
- for (; mi != mapCryptedKeys.end(); ++mi)
- {
- const CPubKey &vchPubKey = (*mi).second.first;
- const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- CKey key;
- if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
- {
- keyFail = true;
- break;
- }
- keyPass = true;
- if (fDecryptionThoroughlyChecked)
- break;
- }
- if (keyPass && keyFail)
- {
- LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
- throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
- }
- if (keyFail || (!keyPass && !accept_no_keys))
- return false;
- vMasterKey = vMasterKeyIn;
- fDecryptionThoroughlyChecked = true;
- }
- NotifyStatusChanged(this);
- return true;
-}
-
-bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return CBasicKeyStore::AddKeyPubKey(key, pubkey);
- }
-
- if (IsLocked()) {
- return false;
- }
-
- std::vector<unsigned char> vchCryptedSecret;
- CKeyingMaterial vchSecret(key.begin(), key.end());
- if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
- return false;
- }
-
- if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
- return false;
- }
- return true;
-}
-
-
-bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
-{
- LOCK(cs_KeyStore);
- if (!SetCrypted()) {
- return false;
- }
-
- mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
- ImplicitlyLearnRelatedKeyScripts(vchPubKey);
- return true;
-}
-
-bool CCryptoKeyStore::HaveKey(const CKeyID &address) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return CBasicKeyStore::HaveKey(address);
- }
- return mapCryptedKeys.count(address) > 0;
-}
-
-bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return CBasicKeyStore::GetKey(address, keyOut);
- }
-
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- const CPubKey &vchPubKey = (*mi).second.first;
- const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
- }
- return false;
-}
-
-bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted())
- return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
-
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- vchPubKeyOut = (*mi).second.first;
- return true;
- }
- // Check for watch-only pubkeys
- return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
-}
-
-std::set<CKeyID> CCryptoKeyStore::GetKeys() const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return CBasicKeyStore::GetKeys();
- }
- std::set<CKeyID> set_address;
- for (const auto& mi : mapCryptedKeys) {
- set_address.insert(mi.first);
- }
- return set_address;
-}
-
-bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
-{
- LOCK(cs_KeyStore);
- if (!mapCryptedKeys.empty() || IsCrypted())
- return false;
-
- fUseCrypto = true;
- for (const KeyMap::value_type& mKey : mapKeys)
- {
- const CKey &key = mKey.second;
- CPubKey vchPubKey = key.GetPubKey();
- CKeyingMaterial vchSecret(key.begin(), key.end());
- std::vector<unsigned char> vchCryptedSecret;
- if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
- return false;
- if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
- return false;
- }
- mapKeys.clear();
- return true;
-}
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index 8e195ca8fa..17a4e9820c 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -5,9 +5,9 @@
#ifndef BITCOIN_WALLET_CRYPTER_H
#define BITCOIN_WALLET_CRYPTER_H
-#include <keystore.h>
#include <serialize.h>
#include <support/allocators/secure.h>
+#include <script/signingprovider.h>
#include <atomic>
@@ -109,54 +109,8 @@ public:
}
};
-/** Keystore which keeps the private keys encrypted.
- * It derives from the basic key store, which is used if no encryption is active.
- */
-class CCryptoKeyStore : public CBasicKeyStore
-{
-private:
-
- CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
-
- //! if fUseCrypto is true, mapKeys must be empty
- //! if fUseCrypto is false, vMasterKey must be empty
- std::atomic<bool> fUseCrypto;
-
- //! keeps track of whether Unlock has run a thorough check before
- bool fDecryptionThoroughlyChecked;
-
-protected:
- using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
-
- bool SetCrypted();
-
- //! will encrypt previously unencrypted keys
- bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
-
- bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
- CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
-
-public:
- CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false)
- {
- }
-
- bool IsCrypted() const { return fUseCrypto; }
- bool IsLocked() const;
- bool Lock();
-
- virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
- bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
- bool HaveKey(const CKeyID &address) const override;
- bool GetKey(const CKeyID &address, CKey& keyOut) const override;
- bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
- std::set<CKeyID> GetKeys() const override;
-
- /**
- * Wallet status (encrypted, locked) changed.
- * Note: Called without locks held.
- */
- boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
-};
+bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext);
+bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key);
#endif // BITCOIN_WALLET_CRYPTER_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index b5f90deabd..26aeb754ad 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -6,6 +6,7 @@
#include <wallet/db.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <stdint.h>
@@ -404,7 +405,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er
LogPrintf("Using wallet %s\n", file_path.string());
if (!env->Open(true /* retry */)) {
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
+ errorStr = strprintf(_("Error initializing wallet database environment %s!").translated, walletDir);
return false;
}
@@ -426,12 +427,12 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& w
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
" Original %s saved as %s in %s; if"
" your balance or transactions are incorrect you should"
- " restore from a backup."),
+ " restore from a backup.").translated,
walletFile, backup_filename, walletDir);
}
if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL)
{
- errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
+ errorStr = strprintf(_("%s corrupt, salvage failed").translated, walletFile);
return false;
}
}
@@ -584,7 +585,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
if (fCreate && !Exists(std::string("version"))) {
bool fTmp = fReadOnly;
fReadOnly = false;
- WriteVersion(CLIENT_VERSION);
+ Write(std::string("version"), CLIENT_VERSION);
fReadOnly = fTmp;
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index b3856fbaf9..94f41eaf16 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -399,17 +399,6 @@ public:
return (ret == 0);
}
- bool ReadVersion(int& nVersion)
- {
- nVersion = 0;
- return Read(std::string("version"), nVersion);
- }
-
- bool WriteVersion(int nVersion)
- {
- return Write(std::string("version"), nVersion);
- }
-
bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
};
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 46cf6b7616..619197a57a 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -272,18 +272,14 @@ Result CreateRateBumpTransaction(CWallet* wallet, const uint256& txid, const CCo
new_coin_control.m_min_depth = 1;
CTransactionRef tx_new = MakeTransactionRef();
- CReserveKey reservekey(wallet);
CAmount fee_ret;
int change_pos_in_out = -1; // No requested location for change
std::string fail_reason;
- if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, reservekey, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
errors.push_back("Unable to create transaction: " + fail_reason);
return Result::WALLET_ERROR;
}
- // If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
- reservekey.KeepKey();
-
// Write back new fee if successful
new_fee = fee_ret;
@@ -330,9 +326,8 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
mapValue_t mapValue = oldWtx.mapValue;
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
- CReserveKey reservekey(wallet);
CValidationState state;
- if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, state)) {
+ if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 4c327c77ae..e766deadb7 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -7,11 +7,12 @@
#include <interfaces/chain.h>
#include <net.h>
#include <outputtype.h>
-#include <util/system.h>
#include <util/moneystr.h>
-#include <walletinitinterface.h>
+#include <util/system.h>
+#include <util/translation.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
+#include <walletinitinterface.h>
class WalletInit : public WalletInitInterface {
public:
@@ -33,41 +34,41 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions() const
{
- gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
- gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
- gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
- gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
+ gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
"Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target",
- CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
- gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
- CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET);
- gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", false, OptionsCategory::WALLET);
- gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", false, OptionsCategory::WALLET);
- gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET);
- gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET);
- gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", false, OptionsCategory::WALLET);
- gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", false, OptionsCategory::WALLET);
- gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET);
- gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", false, OptionsCategory::WALLET);
+ CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
- gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
- gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
- " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET);
+ " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST);
- gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST);
- gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST);
- gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
}
bool WalletInit::ParameterInteraction() const
@@ -121,7 +122,7 @@ bool WalletInit::ParameterInteraction() const
if (gArgs.GetBoolArg("-sysperms", false))
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
- return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
+ return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.").translated);
return true;
}
diff --git a/src/wallet/ismine.cpp b/src/wallet/ismine.cpp
index 6138d4ae44..b7ef2d4490 100644
--- a/src/wallet/ismine.cpp
+++ b/src/wallet/ismine.cpp
@@ -8,6 +8,7 @@
#include <key.h>
#include <script/script.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <wallet/wallet.h>
typedef std::vector<unsigned char> valtype;
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 54aa12dba8..b5d3b8c305 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -8,6 +8,7 @@
#include <interfaces/chain.h>
#include <scheduler.h>
#include <util/system.h>
+#include <util/translation.h>
#include <wallet/wallet.h>
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
@@ -18,14 +19,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist").translated, wallet_dir.string()));
return false;
} else if (!fs::is_directory(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory").translated, wallet_dir.string()));
return false;
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path").translated, wallet_dir.string()));
return false;
}
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
@@ -33,7 +34,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
- chain.initMessage(_("Verifying wallet(s)..."));
+ chain.initMessage(_("Verifying wallet(s)...").translated);
// Parameter interaction code should have thrown an error if -salvagewallet
// was enabled with more than wallet file, so the wallet_files size check
@@ -47,7 +48,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
WalletLocation location(wallet_file);
if (!wallet_paths.insert(location.GetPath()).second) {
- chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
+ chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified.").translated, wallet_file));
return false;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 3112dca9f5..a905cc0c55 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -16,9 +16,9 @@
#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
-#include <wallet/wallet.h>
-
+#include <util/translation.h>
#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
#include <stdint.h>
#include <tuple>
@@ -109,8 +109,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"importprivkey",
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
@@ -135,7 +133,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
},
- }.ToString());
+ }.Check(request);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
@@ -187,19 +185,15 @@ UniValue importprivkey(const JSONRPCRequest& request)
}
}
- // Don't throw error in case a key is already there
- if (pwallet->HaveKey(vchAddress)) {
- return NullUniValue;
+ // Use timestamp of 1 to scan the whole chain
+ if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- // whenever a key is imported, we need to scan the whole chain
- pwallet->UpdateTimeFirstKey(1);
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
-
- if (!pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ // Add the wpkh script for this key if possible
+ if (pubkey.IsCompressed()) {
+ pwallet->ImportScripts({GetScriptForDestination(WitnessV0KeyHash(vchAddress))}, 0 /* timestamp */);
}
- pwallet->LearnAllRelatedScripts(pubkey);
}
}
if (fRescan) {
@@ -217,8 +211,6 @@ UniValue abortrescan(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 0)
- throw std::runtime_error(
RPCHelpMan{"abortrescan",
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -232,49 +224,13 @@ UniValue abortrescan(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("abortrescan", "")
},
- }.ToString());
+ }.Check(request);
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
}
-static void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
-static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
-{
- if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
-
- pwallet->MarkDirty();
-
- if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script, 0 /* nCreateTime */)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
-
- if (isRedeemScript) {
- const CScriptID id(script);
- if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
- }
- ImportAddress(pwallet, ScriptHash(id), strLabel);
- } else {
- CTxDestination destination;
- if (ExtractDestination(script, destination)) {
- pwallet->SetAddressBook(destination, strLabel, "receive");
- }
- }
-}
-
-static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
-{
- CScript script = GetScriptForDestination(dest);
- ImportScript(pwallet, script, strLabel, false);
- // add to address book or update label
- if (IsValidDestination(dest))
- pwallet->SetAddressBook(dest, strLabel, "receive");
-}
-
UniValue importaddress(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -283,8 +239,6 @@ UniValue importaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"importaddress",
"\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -309,7 +263,7 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
},
- }.ToString());
+ }.Check(request);
std::string strLabel;
@@ -347,10 +301,22 @@ UniValue importaddress(const JSONRPCRequest& request)
if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
}
- ImportAddress(pwallet, dest, strLabel);
+
+ pwallet->MarkDirty();
+
+ pwallet->ImportScriptPubKeys(strLabel, {GetScriptForDestination(dest)}, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
} else if (IsHex(request.params[0].get_str())) {
std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
- ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
+ CScript redeem_script(data.begin(), data.end());
+
+ std::set<CScript> scripts = {redeem_script};
+ pwallet->ImportScripts(scripts, 0 /* timestamp */);
+
+ if (fP2SH) {
+ scripts.insert(GetScriptForDestination(ScriptHash(CScriptID(redeem_script))));
+ }
+
+ pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
}
@@ -376,8 +342,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 2)
- throw std::runtime_error(
RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
@@ -386,8 +350,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
},
RPCResults{},
RPCExamples{""},
- }.ToString()
- );
+ }.Check(request);
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
@@ -443,8 +406,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
@@ -456,7 +417,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
},
- }.ToString());
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -485,8 +446,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one public key.\n"
@@ -507,7 +466,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
},
- }.ToString());
+ }.Check(request);
std::string strLabel;
@@ -542,11 +501,16 @@ UniValue importpubkey(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
+ std::set<CScript> script_pub_keys;
for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
- ImportAddress(pwallet, dest, strLabel);
+ script_pub_keys.insert(GetScriptForDestination(dest));
}
- ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
- pwallet->LearnAllRelatedScripts(pubKey);
+
+ pwallet->MarkDirty();
+
+ pwallet->ImportScriptPubKeys(strLabel, script_pub_keys, true /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
+
+ pwallet->ImportPubKeys({pubKey.GetID()}, {{pubKey.GetID(), pubKey}} , {} /* key_origins */, false /* add_keypool */, false /* internal */, 1 /* timestamp */);
}
if (fRescan)
{
@@ -570,8 +534,6 @@ UniValue importwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -587,7 +549,7 @@ UniValue importwallet(const JSONRPCRequest& request)
"\nImport using the json rpc call\n"
+ HelpExampleRpc("importwallet", "\"test\"")
},
- }.ToString());
+ }.Check(request);
if (pwallet->chain().havePruned()) {
// Exit early and print an error.
@@ -622,7 +584,7 @@ UniValue importwallet(const JSONRPCRequest& request)
// Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
// we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
- pwallet->chain().showProgress(strprintf("%s " + _("Importing..."), pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
+ pwallet->chain().showProgress(strprintf("%s " + _("Importing...").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
std::vector<std::pair<CScript, int64_t>> scripts;
while (file.good()) {
@@ -679,18 +641,18 @@ UniValue importwallet(const JSONRPCRequest& request)
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
- if (pwallet->HaveKey(keyid)) {
- pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(PKHash(keyid)));
- continue;
- }
+
pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
- if (!pwallet->AddKeyPubKey(key, pubkey)) {
+
+ if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
+ pwallet->WalletLogPrintf("Error importing key for %s\n", EncodeDestination(PKHash(keyid)));
fGood = false;
continue;
}
- pwallet->mapKeyMetadata[keyid].nCreateTime = time;
+
if (has_label)
pwallet->SetAddressBook(PKHash(keyid), label, "receive");
+
nTimeBegin = std::min(nTimeBegin, time);
progress++;
}
@@ -698,24 +660,19 @@ UniValue importwallet(const JSONRPCRequest& request)
pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
const CScript& script = script_pair.first;
int64_t time = script_pair.second;
- CScriptID id(script);
- if (pwallet->HaveCScript(id)) {
- pwallet->WalletLogPrintf("Skipping import of %s (script already present)\n", HexStr(script));
- continue;
- }
- if(!pwallet->AddCScript(script)) {
+
+ if (!pwallet->ImportScripts({script}, time)) {
pwallet->WalletLogPrintf("Error importing script %s\n", HexStr(script));
fGood = false;
continue;
}
if (time > 0) {
- pwallet->m_script_metadata[id].nCreateTime = time;
nTimeBegin = std::min(nTimeBegin, time);
}
+
progress++;
}
pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
- pwallet->UpdateTimeFirstKey(nTimeBegin);
}
pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
@@ -735,8 +692,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
@@ -751,7 +706,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"")
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"")
},
- }.ToString());
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -783,8 +738,6 @@ UniValue dumpwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
@@ -802,7 +755,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
HelpExampleCli("dumpwallet", "\"test\"")
+ HelpExampleRpc("dumpwallet", "\"test\"")
},
- }.ToString());
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -1274,7 +1227,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// All good, time to import
pwallet->MarkDirty();
- if (!pwallet->ImportScripts(import_data.import_scripts)) {
+ if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
@@ -1283,7 +1236,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, internal, timestamp)) {
+ if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
@@ -1322,8 +1275,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
return NullUniValue;
}
- if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
@@ -1384,8 +1335,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
},
- }.ToString()
- );
+ }.Check(mainRequest);
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index eae5f876ea..f94214b6ee 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -52,6 +52,14 @@ static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& pa
return avoid_reuse;
}
+/** Checks if a CKey is in the given CWallet compressed or otherwise*/
+bool HaveKey(const CWallet& wallet, const CKey& key)
+{
+ CKey key2;
+ key2.Set(key.begin(), key.end(), !key.IsCompressed());
+ return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
+}
+
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
{
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
@@ -159,8 +167,6 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
@@ -176,7 +182,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
HelpExampleCli("getnewaddress", "")
+ HelpExampleRpc("getnewaddress", "")
},
- }.ToString());
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -196,19 +202,11 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
}
}
- if (!pwallet->IsLocked()) {
- pwallet->TopUpKeyPool();
- }
-
- // Generate a new key that is added to wallet
- CPubKey newKey;
- if (!pwallet->GetKeyFromPool(newKey)) {
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
+ CTxDestination dest;
+ std::string error;
+ if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
}
- pwallet->LearnRelatedScripts(newKey, output_type);
- CTxDestination dest = GetDestinationForKey(newKey, output_type);
-
- pwallet->SetAddressBook(dest, label, "receive");
return EncodeDestination(dest);
}
@@ -222,8 +220,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
@@ -237,7 +233,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
HelpExampleCli("getrawchangeaddress", "")
+ HelpExampleRpc("getrawchangeaddress", "")
},
- }.ToString());
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -245,10 +241,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
}
- if (!pwallet->IsLocked()) {
- pwallet->TopUpKeyPool();
- }
-
OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
if (!request.params[0].isNull()) {
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
@@ -256,16 +248,11 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
}
}
- CReserveKey reservekey(pwallet);
- CPubKey vchPubKey;
- if (!reservekey.GetReservedKey(vchPubKey, true))
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
-
- reservekey.KeepKey();
-
- pwallet->LearnRelatedScripts(vchPubKey, output_type);
- CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
-
+ CTxDestination dest;
+ std::string error;
+ if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
+ }
return EncodeDestination(dest);
}
@@ -279,8 +266,6 @@ static UniValue setlabel(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 2)
- throw std::runtime_error(
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
@@ -292,7 +277,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")
},
- }.ToString());
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -332,7 +317,6 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
- CReserveKey reservekey(pwallet);
CAmount nFeeRequired;
std::string strError;
std::vector<CRecipient> vecSend;
@@ -340,13 +324,13 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CTransactionRef tx;
- if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
+ if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, state)) {
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
@@ -362,8 +346,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 9)
- throw std::runtime_error(
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HelpRequiringPassphrase(pwallet) + "\n",
@@ -377,8 +359,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" transaction, just kept in your wallet."},
{"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
" The recipient will receive less bitcoins than you enter in the amount field."},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -395,7 +377,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true")
+ HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -460,8 +442,6 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"listaddressgroupings",
"\nLists groups of addresses which have had their common ownership\n"
"made public by common use as inputs or as the resulting change\n"
@@ -484,7 +464,7 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
HelpExampleCli("listaddressgroupings", "")
+ HelpExampleRpc("listaddressgroupings", "")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -523,8 +503,6 @@ static UniValue signmessage(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 2)
- throw std::runtime_error(
RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HelpRequiringPassphrase(pwallet) + "\n",
@@ -545,7 +523,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
},
- }.ToString());
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -591,8 +569,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
@@ -612,7 +588,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -663,8 +639,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
@@ -684,7 +658,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -733,8 +707,6 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -756,7 +728,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -796,14 +768,12 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 0)
- throw std::runtime_error(
RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
RPCResults{},
RPCExamples{""},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -825,7 +795,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
return NullUniValue;
}
- const RPCHelpMan help{"sendmany",
+ RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HelpRequiringPassphrase(pwallet) + "\n",
{
@@ -845,8 +815,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -866,11 +836,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -948,16 +914,15 @@ static UniValue sendmany(const JSONRPCRequest& request)
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
// Send
- CReserveKey keyChange(pwallet);
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
std::string strFailReason;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
+ bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strFailReason, coin_control);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, state)) {
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@@ -974,8 +939,6 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- std::string msg =
RPCHelpMan{"addmultisigaddress",
"\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
@@ -1004,9 +967,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
},
- }.ToString();
- throw std::runtime_error(msg);
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -1211,8 +1172,6 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
@@ -1243,7 +1202,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1264,8 +1223,6 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
@@ -1289,7 +1246,7 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("listreceivedbylabel", "6 true")
+ HelpExampleRpc("listreceivedbylabel", "6, true, true")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1404,8 +1361,6 @@ UniValue listtransactions(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"listtransactions",
"\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
@@ -1457,7 +1412,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1538,8 +1493,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"listsinceblock",
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
@@ -1592,7 +1545,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1683,8 +1636,6 @@ static UniValue gettransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
@@ -1732,7 +1683,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1785,8 +1736,6 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"abandontransaction",
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
@@ -1801,8 +1750,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.ToString());
- }
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1833,8 +1781,6 @@ static UniValue backupwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
@@ -1845,7 +1791,7 @@ static UniValue backupwallet(const JSONRPCRequest& request)
HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1872,8 +1818,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HelpRequiringPassphrase(pwallet) + "\n",
@@ -1885,7 +1829,7 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
},
- }.ToString());
+ }.Check(request);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
@@ -1922,8 +1866,6 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 2) {
- throw std::runtime_error(
RPCHelpMan{"walletpassphrase",
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
@@ -1943,8 +1885,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
},
- }.ToString());
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2009,8 +1950,6 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 2) {
- throw std::runtime_error(
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
@@ -2022,8 +1961,7 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
},
- }.ToString());
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2063,8 +2001,6 @@ static UniValue walletlock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 0) {
- throw std::runtime_error(
RPCHelpMan{"walletlock",
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
"After calling this method, you will need to call walletpassphrase again\n"
@@ -2081,8 +2017,7 @@ static UniValue walletlock(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
},
- }.ToString());
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2107,8 +2042,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"encryptwallet",
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
"After this, any calls that interact with private keys such as sending or signing \n"
@@ -2131,8 +2064,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
},
- }.ToString());
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2171,8 +2103,6 @@ static UniValue lockunspent(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
RPCHelpMan{"lockunspent",
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
@@ -2209,7 +2139,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
},
- }.ToString());
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2300,8 +2230,6 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 0)
- throw std::runtime_error(
RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
@@ -2327,7 +2255,7 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
},
- }.ToString());
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2357,8 +2285,6 @@ static UniValue settxfee(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) {
- throw std::runtime_error(
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
{
@@ -2371,8 +2297,7 @@ static UniValue settxfee(const JSONRPCRequest& request)
HelpExampleCli("settxfee", "0.00001")
+ HelpExampleRpc("settxfee", "0.00001")
},
- }.ToString());
- }
+ }.Check(request);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -2399,7 +2324,7 @@ static UniValue getbalances(const JSONRPCRequest& request)
}
CWallet& wallet = *rpc_wallet;
- const RPCHelpMan help{
+ RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
@@ -2420,11 +2345,7 @@ static UniValue getbalances(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2469,7 +2390,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
return NullUniValue;
}
- const RPCHelpMan help{"getwalletinfo",
+ RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
@@ -2499,11 +2420,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
HelpExampleCli("getwalletinfo", "")
+ HelpExampleRpc("getwalletinfo", "")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2550,8 +2467,6 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
static UniValue listwalletdir(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0) {
- throw std::runtime_error(
RPCHelpMan{"listwalletdir",
"Returns a list of wallets in the wallet directory.\n",
{},
@@ -2569,8 +2484,7 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
HelpExampleCli("listwalletdir", "")
+ HelpExampleRpc("listwalletdir", "")
},
- }.ToString());
- }
+ }.Check(request);
UniValue wallets(UniValue::VARR);
for (const auto& path : ListWalletDir()) {
@@ -2586,8 +2500,6 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
static UniValue listwallets(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
RPCHelpMan{"listwallets",
"Returns a list of currently loaded wallets.\n"
"For full information on the wallet, use \"getwalletinfo\"\n",
@@ -2602,7 +2514,7 @@ static UniValue listwallets(const JSONRPCRequest& request)
HelpExampleCli("listwallets", "")
+ HelpExampleRpc("listwallets", "")
},
- }.ToString());
+ }.Check(request);
UniValue obj(UniValue::VARR);
@@ -2621,8 +2533,6 @@ static UniValue listwallets(const JSONRPCRequest& request)
static UniValue loadwallet(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting bitcoind will be"
@@ -2640,7 +2550,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
HelpExampleCli("loadwallet", "\"test.dat\"")
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
},
- }.ToString());
+ }.Check(request);
WalletLocation location(request.params[0].get_str());
@@ -2674,12 +2584,10 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- std::string flags = "";
- for (auto& it : WALLET_FLAG_MAP)
- if (it.second & MUTABLE_WALLET_FLAGS)
- flags += (flags == "" ? "" : ", ") + it.first;
- throw std::runtime_error(
+ std::string flags = "";
+ for (auto& it : WALLET_FLAG_MAP)
+ if (it.second & MUTABLE_WALLET_FLAGS)
+ flags += (flags == "" ? "" : ", ") + it.first;
RPCHelpMan{"setwalletflag",
"\nChange the state of the given wallet flag for a wallet.\n",
{
@@ -2697,8 +2605,7 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
HelpExampleCli("setwalletflag", "avoid_reuse")
+ HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
},
- }.ToString());
- }
+ }.Check(request);
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2737,7 +2644,7 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
static UniValue createwallet(const JSONRPCRequest& request)
{
- const RPCHelpMan help{
+ RPCHelpMan{
"createwallet",
"\nCreates and loads a new wallet.\n",
{
@@ -2757,81 +2664,51 @@ static UniValue createwallet(const JSONRPCRequest& request)
HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
- std::string error;
- std::string warning;
+ }.Check(request);
uint64_t flags = 0;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
}
- bool create_blank = false; // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
if (!request.params[2].isNull() && request.params[2].get_bool()) {
- create_blank = true;
flags |= WALLET_FLAG_BLANK_WALLET;
}
SecureString passphrase;
passphrase.reserve(100);
+ std::string warning;
if (!request.params[3].isNull()) {
passphrase = request.params[3].get_str().c_str();
if (passphrase.empty()) {
- // Empty string is invalid
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Cannot encrypt a wallet with a blank password");
+ // Empty string means unencrypted
+ warning = "Empty string given as passphrase, wallet will not be encrypted.";
}
- // Born encrypted wallets need to be blank first so that wallet creation doesn't make any unencrypted keys
- flags |= WALLET_FLAG_BLANK_WALLET;
}
if (!request.params[4].isNull() && request.params[4].get_bool()) {
flags |= WALLET_FLAG_AVOID_REUSE;
}
- WalletLocation location(request.params[0].get_str());
- if (location.Exists()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + location.GetName() + " already exists.");
- }
-
- // Wallet::Verify will check if we're trying to create a wallet with a duplication name.
- if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
+ std::string error;
+ std::string create_warning;
+ std::shared_ptr<CWallet> wallet;
+ WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, create_warning, wallet);
+ switch (status) {
+ case WalletCreationStatus::CREATION_FAILED:
+ throw JSONRPCError(RPC_WALLET_ERROR, error);
+ case WalletCreationStatus::ENCRYPTION_FAILED:
+ throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
+ case WalletCreationStatus::SUCCESS:
+ break;
+ // no default case, so the compiler can warn about missing cases
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, flags);
- if (!wallet) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
+ if (warning.empty()) {
+ warning = create_warning;
+ } else if (!warning.empty() && !create_warning.empty()){
+ warning += "; " + create_warning;
}
- // Encrypt the wallet if there's a passphrase
- if (!passphrase.empty() && !(flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- if (!wallet->EncryptWallet(passphrase)) {
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet created but failed to encrypt.");
- }
-
- if (!create_blank) {
- // Unlock the wallet
- if (!wallet->Unlock(passphrase)) {
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Wallet was encrypted but could not be unlocked");
- }
-
- // Set a seed for the wallet
- CPubKey master_pub_key = wallet->GenerateNewSeed();
- wallet->SetHDSeed(master_pub_key);
- wallet->NewKeyPool();
-
- // Relock the wallet
- wallet->Lock();
- }
- }
-
- AddWallet(wallet);
-
- wallet->postInitProcess();
-
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", warning);
@@ -2841,8 +2718,6 @@ static UniValue createwallet(const JSONRPCRequest& request)
static UniValue unloadwallet(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() > 1) {
- throw std::runtime_error(
RPCHelpMan{"unloadwallet",
"Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
"Specifying the wallet name on a wallet endpoint is invalid.",
@@ -2854,8 +2729,7 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
HelpExampleCli("unloadwallet", "wallet_name")
+ HelpExampleRpc("unloadwallet", "wallet_name")
},
- }.ToString());
- }
+ }.Check(request);
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
@@ -2892,7 +2766,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
return NullUniValue;
}
- const RPCHelpMan help{
+ RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
@@ -2946,11 +2820,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
int nMinDepth = 1;
if (!request.params[0].isNull()) {
@@ -3016,9 +2886,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
{
CCoinControl cctl;
cctl.m_avoid_address_reuse = false;
+ cctl.m_min_depth = nMinDepth;
+ cctl.m_max_depth = nMaxDepth;
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
@@ -3219,7 +3091,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
return NullUniValue;
}
- const RPCHelpMan help{"fundrawtransaction",
+ RPCHelpMan{"fundrawtransaction",
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@@ -3249,9 +3121,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -3283,11 +3155,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
- };
-
- if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(help.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
@@ -3320,8 +3188,6 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
RPCHelpMan{"signrawtransactionwithwallet",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
@@ -3371,7 +3237,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
+ HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
@@ -3404,26 +3270,24 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"bumpfee",
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
"The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
- "If `totalFee` is given, adding inputs is not supported, so there must be a single change output that is big enough or it will fail.\n"
+ "If `totalFee` (DEPRECATED) is given, adding inputs is not supported, so there must be a single change output that is big enough or it will fail.\n"
"All inputs in the original transaction will be included in the replacement transaction.\n"
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
"By default, the new fee will be calculated automatically using estimatesmartfee.\n"
"The user can specify a confirmation target for estimatesmartfee.\n"
- "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
+ "Alternatively, the user can specify totalFee (DEPRECATED), or use RPC settxfee to set a higher fee rate.\n"
"At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
"returned by getnetworkinfo) to enter the node's mempool.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"confTarget", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
- {"totalFee", RPCArg::Type::NUM, /* default */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis.\n"
+ {"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
+ {"totalFee", RPCArg::Type::NUM, /* default */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis. (DEPRECATED)\n"
" In rare cases, the actual fee paid might be slightly higher than the specified\n"
" totalFee if the tx change output has to be removed because it is too close to\n"
" the dust threshold."},
@@ -3453,8 +3317,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
"\nBump the fee, get the new transaction\'s txid\n" +
HelpExampleCli("bumpfee", "<txid>")
},
- }.ToString());
- }
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -3479,6 +3342,9 @@ static UniValue bumpfee(const JSONRPCRequest& request)
} else if (options.exists("confTarget")) { // TODO: alias this to conf_target
coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
} else if (options.exists("totalFee")) {
+ if (!pwallet->chain().rpcEnableDeprecated("totalFee")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "totalFee argument has been deprecated and will be removed in 0.20. Please use -deprecatedrpc=totalFee to continue using this argument until removal.");
+ }
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid totalFee %s (must be greater than 0)", FormatMoney(totalFee)));
@@ -3567,8 +3433,6 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -3586,8 +3450,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
HelpExampleCli("rescanblockchain", "100000 120000")
+ HelpExampleRpc("rescanblockchain", "100000, 120000")
},
- }.ToString());
- }
+ }.Check(request);
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {
@@ -3774,8 +3637,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address. Some information requires the address\n"
"to be in the wallet.\n",
@@ -3824,8 +3685,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
},
- }.ToString());
- }
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -3902,8 +3762,6 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
@@ -3920,7 +3778,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
HelpExampleCli("getaddressesbylabel", "\"tabby\"")
+ HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
},
- }.ToString());
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -3961,8 +3819,6 @@ static UniValue listlabels(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 1)
- throw std::runtime_error(
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
@@ -3984,7 +3840,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
},
- }.ToString());
+ }.Check(request);
LOCK(pwallet->cs_wallet);
@@ -4018,8 +3874,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 2) {
- throw std::runtime_error(
RPCHelpMan{"sethdseed",
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
@@ -4040,8 +3894,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
},
- }.ToString());
- }
+ }.Check(request);
if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
@@ -4097,8 +3950,6 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
- throw std::runtime_error(
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
@@ -4125,7 +3976,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("walletprocesspsbt", "\"psbt\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
@@ -4166,8 +4017,6 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
- throw std::runtime_error(
RPCHelpMan{"walletcreatefundedpsbt",
"\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
"Implements the Creator and Updater roles.\n",
@@ -4217,7 +4066,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
@@ -4239,7 +4088,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\nCreate a transaction with no inputs\n"
+ HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -4252,7 +4101,13 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
CAmount fee;
int change_position;
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"]);
+ bool rbf = pwallet->m_signal_rbf;
+ const UniValue &replaceable_arg = request.params[3]["replaceable"];
+ if (!replaceable_arg.isNull()) {
+ RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
+ rbf = replaceable_arg.isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
// Make a blank psbt
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 1816fca257..279542ffad 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -4,6 +4,7 @@
#include <boost/test/unit_test.hpp>
+#include <noui.h>
#include <test/setup_common.h>
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
@@ -33,21 +34,27 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
{
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
{
SetWalletDir(m_walletdir_path_cases["file"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
{
SetWalletDir(m_walletdir_path_cases["relative"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index ef95d0544f..8af05dea45 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -272,7 +272,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
if (blockTime > 0) {
auto locked_chain = wallet.chain().lock();
LockAssertion lock(::cs_main);
- auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
+ auto inserted = ::BlockIndex().emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
block = inserted.first->second;
@@ -361,17 +361,16 @@ public:
CWalletTx& AddTx(CRecipient recipient)
{
CTransactionRef tx;
- CReserveKey reservekey(wallet.get());
CAmount fee;
int changePos = -1;
std::string error;
CCoinControl dummy;
{
auto locked_chain = m_chain->lock();
- BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
+ BOOST_CHECK(wallet->CreateTransaction(*locked_chain, {recipient}, tx, fee, changePos, error, dummy));
}
CValidationState state;
- BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, state));
+ BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, state));
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);
@@ -464,8 +463,9 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
- CPubKey pubkey;
- BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
+ CTxDestination dest;
+ std::string error;
+ BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
}
// Explicit calculation which is used to test the wallet constant
@@ -488,7 +488,7 @@ static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
// Add inner-script to key store and key to watchonly
- CBasicKeyStore keystore;
+ FillableSigningProvider keystore;
keystore.AddCScript(inner_script);
keystore.AddKeyPubKey(key, pubkey);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7f14a4a7c3..b3269083ec 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -13,18 +13,19 @@
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
-#include <keystore.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/descriptor.h>
#include <script/script.h>
+#include <script/signingprovider.h>
#include <util/bip32.h>
#include <util/error.h>
#include <util/fees.h>
#include <util/moneystr.h>
#include <util/rbf.h>
+#include <util/translation.h>
#include <util/validation.h>
#include <validation.h>
#include <wallet/coincontrol.h>
@@ -160,9 +161,74 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
return LoadWallet(chain, WalletLocation(name), error, warning);
}
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result)
+{
+ // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
+ bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
+
+ // Born encrypted wallets need to be created blank first.
+ if (!passphrase.empty()) {
+ wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
+ }
+
+ // Check the wallet file location
+ WalletLocation location(name);
+ if (location.Exists()) {
+ error = "Wallet " + location.GetName() + " already exists.";
+ return WalletCreationStatus::CREATION_FAILED;
+ }
+
+ // Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
+ std::string wallet_error;
+ if (!CWallet::Verify(chain, location, false, wallet_error, warning)) {
+ error = "Wallet file verification failed: " + wallet_error;
+ return WalletCreationStatus::CREATION_FAILED;
+ }
+
+ // Do not allow a passphrase when private keys are disabled
+ if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ error = "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.";
+ return WalletCreationStatus::CREATION_FAILED;
+ }
+
+ // Make the wallet
+ std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, wallet_creation_flags);
+ if (!wallet) {
+ error = "Wallet creation failed";
+ return WalletCreationStatus::CREATION_FAILED;
+ }
+
+ // Encrypt the wallet
+ if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!wallet->EncryptWallet(passphrase)) {
+ error = "Error: Wallet created but failed to encrypt.";
+ return WalletCreationStatus::ENCRYPTION_FAILED;
+ }
+ if (!create_blank) {
+ // Unlock the wallet
+ if (!wallet->Unlock(passphrase)) {
+ error = "Error: Wallet was encrypted but could not be unlocked";
+ return WalletCreationStatus::ENCRYPTION_FAILED;
+ }
+
+ // Set a seed for the wallet
+ CPubKey master_pub_key = wallet->GenerateNewSeed();
+ wallet->SetHDSeed(master_pub_key);
+ wallet->NewKeyPool();
+
+ // Relock the wallet
+ wallet->Lock();
+ }
+ }
+ AddWallet(wallet);
+ wallet->postInitProcess();
+ result = wallet;
+ return WalletCreationStatus::SUCCESS;
+}
+
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
-const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
+const uint256 CWalletTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
*
@@ -294,14 +360,14 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
// Make sure we aren't adding private keys to private key disabled wallets
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
- // CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey
+ // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
// which is overridden below. To avoid flushes, the database handle is
// tunneled through to it.
bool needsDB = !encrypted_batch;
if (needsDB) {
encrypted_batch = &batch;
}
- if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
+ if (!AddKeyPubKeyInner(secret, pubkey)) {
if (needsDB) encrypted_batch = nullptr;
return false;
}
@@ -336,7 +402,7 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<unsigned char> &vchCryptedSecret)
{
- if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
+ if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret))
return false;
{
LOCK(cs_wallet);
@@ -404,7 +470,7 @@ void CWallet::UpgradeKeyMetadata()
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
- return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
+ return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
}
/**
@@ -431,7 +497,7 @@ bool CWallet::AddCScript(const CScript& redeemScript)
bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
{
- if (!CCryptoKeyStore::AddCScript(redeemScript))
+ if (!FillableSigningProvider::AddCScript(redeemScript))
return false;
if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
@@ -452,12 +518,40 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return true;
}
- return CCryptoKeyStore::AddCScript(redeemScript);
+ return FillableSigningProvider::AddCScript(redeemScript);
+}
+
+static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
+{
+ //TODO: Use Solver to extract this?
+ CScript::const_iterator pc = dest.begin();
+ opcodetype opcode;
+ std::vector<unsigned char> vch;
+ if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch))
+ return false;
+ pubKeyOut = CPubKey(vch);
+ if (!pubKeyOut.IsFullyValid())
+ return false;
+ if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch))
+ return false;
+ return true;
+}
+
+bool CWallet::AddWatchOnlyInMem(const CScript &dest)
+{
+ LOCK(cs_KeyStore);
+ setWatchOnly.insert(dest);
+ CPubKey pubKey;
+ if (ExtractPubKey(dest, pubKey)) {
+ mapWatchKeys[pubKey.GetID()] = pubKey;
+ ImplicitlyLearnRelatedKeyScripts(pubKey);
+ }
+ return true;
}
bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
{
- if (!CCryptoKeyStore::AddWatchOnly(dest))
+ if (!AddWatchOnlyInMem(dest))
return false;
const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
@@ -490,8 +584,17 @@ bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
bool CWallet::RemoveWatchOnly(const CScript &dest)
{
AssertLockHeld(cs_wallet);
- if (!CCryptoKeyStore::RemoveWatchOnly(dest))
- return false;
+ {
+ LOCK(cs_KeyStore);
+ setWatchOnly.erase(dest);
+ CPubKey pubKey;
+ if (ExtractPubKey(dest, pubKey)) {
+ mapWatchKeys.erase(pubKey.GetID());
+ }
+ // Related CScripts are not removed; having superfluous scripts around is
+ // harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
+ }
+
if (!HaveWatchOnly())
NotifyWatchonlyChanged(false);
if (!WalletBatch(*database).EraseWatchOnly(dest))
@@ -502,7 +605,19 @@ bool CWallet::RemoveWatchOnly(const CScript &dest)
bool CWallet::LoadWatchOnly(const CScript &dest)
{
- return CCryptoKeyStore::AddWatchOnly(dest);
+ return AddWatchOnlyInMem(dest);
+}
+
+bool CWallet::HaveWatchOnly(const CScript &dest) const
+{
+ LOCK(cs_KeyStore);
+ return setWatchOnly.count(dest) > 0;
+}
+
+bool CWallet::HaveWatchOnly() const
+{
+ LOCK(cs_KeyStore);
+ return (!setWatchOnly.empty());
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys)
@@ -518,7 +633,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key
- if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys)) {
+ if (Unlock(_vMasterKey, accept_no_keys)) {
// Now that we've unlocked, upgrade the key metadata
UpgradeKeyMetadata();
return true;
@@ -544,7 +659,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
return false;
- if (CCryptoKeyStore::Unlock(_vMasterKey))
+ if (Unlock(_vMasterKey))
{
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
@@ -1663,14 +1778,27 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}
-bool CWallet::ImportScripts(const std::set<CScript> scripts)
+bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
{
WalletBatch batch(*database);
for (const auto& entry : scripts) {
- if (!HaveCScript(CScriptID(entry)) && !AddCScriptWithDB(batch, entry)) {
+ CScriptID id(entry);
+ if (HaveCScript(id)) {
+ WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry));
+ continue;
+ }
+ if (!AddCScriptWithDB(batch, entry)) {
return false;
}
+
+ if (timestamp > 0) {
+ m_script_metadata[CScriptID(entry)].nCreateTime = timestamp;
+ }
+ }
+ if (timestamp > 0) {
+ UpdateTimeFirstKey(timestamp);
}
+
return true;
}
@@ -1682,9 +1810,14 @@ bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const in
CPubKey pubkey = key.GetPubKey();
const CKeyID& id = entry.first;
assert(key.VerifyPubKey(pubkey));
+ // Skip if we already have the key
+ if (HaveKey(id)) {
+ WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey));
+ continue;
+ }
mapKeyMetadata[id].nCreateTime = timestamp;
// If the private key is not present in the wallet, insert it.
- if (!HaveKey(id) && !AddKeyPubKeyWithDB(batch, key, pubkey)) {
+ if (!AddKeyPubKeyWithDB(batch, key, pubkey)) {
return false;
}
UpdateTimeFirstKey(timestamp);
@@ -1705,7 +1838,12 @@ bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const st
}
const CPubKey& pubkey = entry->second;
CPubKey temp;
- if (!GetPubKey(id, temp) && !AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
+ if (GetPubKey(id, temp)) {
+ // Already have pubkey, skipping
+ WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp));
+ continue;
+ }
+ if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
return false;
}
mapKeyMetadata[id].nCreateTime = timestamp;
@@ -1719,7 +1857,7 @@ bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const st
return true;
}
-bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp)
+bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp)
{
WalletBatch batch(*database);
for (const CScript& script : script_pub_keys) {
@@ -1730,7 +1868,7 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
}
CTxDestination dest;
ExtractDestination(script, dest);
- if (!internal && IsValidDestination(dest)) {
+ if (apply_label && IsValidDestination(dest)) {
SetAddressBookWithDB(batch, dest, label, "receive");
}
}
@@ -1900,7 +2038,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
fAbortRescan = false;
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
+ ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
uint256 tip_hash;
// The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
Optional<int> block_height = MakeOptional(false, int());
@@ -1919,7 +2057,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
while (block_height && !fAbortRescan && !chain().shutdownRequested()) {
m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin);
if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
+ ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
@@ -1975,7 +2113,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}
}
}
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
+ ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 100); // hide progress dialog in GUI
if (block_height && fAbortRescan) {
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
result.status = ScanResult::USER_ABORT;
@@ -1996,8 +2134,7 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order
- for (std::pair<const uint256, CWalletTx>& item : mapWallet)
- {
+ for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
const uint256& wtxid = item.first;
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
@@ -2012,32 +2149,32 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
// Try to add wallet transactions to memory pool
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
- CValidationState state;
- wtx.AcceptToMemoryPool(locked_chain, state);
+ std::string unused_err_string;
+ wtx.SubmitMemoryPoolAndRelay(unused_err_string, false);
}
}
-bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain)
+bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
{
// Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false;
- // Don't relay coinbase transactions outside blocks
- if (IsCoinBase()) return false;
// Don't relay abandoned transactions
if (isAbandoned()) return false;
- // Don't relay conflicted or already confirmed transactions
- if (GetDepthInMainChain(locked_chain) != 0) return false;
- // Don't relay transactions that aren't accepted to the mempool
- CValidationState unused_state;
- if (!InMempool() && !AcceptToMemoryPool(locked_chain, unused_state)) return false;
- // Don't try to relay if the node is not connected to the p2p network
- if (!pwallet->chain().p2pEnabled()) return false;
-
- // Try to relay the transaction
- pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
- pwallet->chain().relayTransaction(GetHash());
- return true;
+ // Submit transaction to mempool for relay
+ pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString());
+ // We must set fInMempool here - while it will be re-set to true by the
+ // entered-mempool callback, if we did not there would be a race where a
+ // user could call sendmoney in a loop and hit spurious out of funds errors
+ // because we think that this newly generated transaction's change is
+ // unavailable as we're not yet aware that it is in the mempool.
+ //
+ // Irrespective of the failure reason, un-marking fInMempool
+ // out-of-order is incorrect - it should be unmarked when
+ // TransactionRemovedFromMempool fires.
+ bool ret = pwallet->chain().broadcastTransaction(tx, err_string, pwallet->m_default_max_tx_fee, relay);
+ fInMempool |= ret;
+ return ret;
}
std::set<uint256> CWalletTx::GetConflicts() const
@@ -2228,7 +2365,7 @@ void CWallet::ResendWalletTransactions()
if (m_best_block_time < nLastResend) return;
nLastResend = GetTime();
- int relayed_tx_count = 0;
+ int submitted_tx_count = 0;
{ // locked_chain and cs_wallet scope
auto locked_chain = chain().lock();
@@ -2240,12 +2377,13 @@ void CWallet::ResendWalletTransactions()
// only rebroadcast unconfirmed txes older than 5 minutes before the
// last block was found
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
- if (wtx.RelayWalletTransaction(*locked_chain)) ++relayed_tx_count;
+ std::string unused_err_string;
+ if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
}
} // locked_chain and cs_wallet
- if (relayed_tx_count > 0) {
- WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
+ if (submitted_tx_count > 0) {
+ WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count);
}
}
@@ -2310,7 +2448,7 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
-void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
+void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
{
AssertLockHeld(cs_wallet);
@@ -2319,6 +2457,8 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
// Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
// a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
+ const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
+ const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
for (const auto& entry : mapWallet)
{
@@ -2378,8 +2518,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- if (nDepth < nMinDepth || nDepth > nMaxDepth)
+ if (nDepth < min_depth || nDepth > max_depth) {
continue;
+ }
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
@@ -2666,17 +2807,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- CReserveKey reservekey(this);
CTransactionRef tx_new;
- if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
+ if (!CreateTransaction(*locked_chain, vecSend, tx_new, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false;
}
if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
- // We don't have the normal Create/Commit cycle, and don't want to risk
- // reusing change, so just remove the key from the keypool here.
- reservekey.KeepKey();
}
// Copy output sizes from new transaction; they may have had the fee
@@ -2696,11 +2833,6 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
}
- if (nFeeRet > this->m_default_max_tx_fee) {
- strFailReason = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
- return false;
- }
-
return true;
}
@@ -2792,17 +2924,18 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
return m_default_address_type;
}
-bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
+bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
+ ReserveDestination reservedest(this);
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
for (const auto& recipient : vecSend)
{
if (nValue < 0 || recipient.nAmount < 0)
{
- strFailReason = _("Transaction amounts must not be negative");
+ strFailReason = _("Transaction amounts must not be negative").translated;
return false;
}
nValue += recipient.nAmount;
@@ -2812,7 +2945,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
if (vecSend.empty())
{
- strFailReason = _("Transaction must have at least one recipient");
+ strFailReason = _("Transaction must have at least one recipient").translated;
return false;
}
@@ -2829,11 +2962,11 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
LOCK(cs_wallet);
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0, coin_control.m_min_depth);
+ AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change
- // TODO: pass in scriptChange instead of reservekey so
+ // TODO: pass in scriptChange instead of reservedest so
// change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
@@ -2850,22 +2983,19 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// Reserve a new key pair from key pool
if (!CanGetAddresses(true)) {
- strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.");
+ strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.").translated;
return false;
}
- CPubKey vchPubKey;
- bool ret;
- ret = reservekey.GetReservedKey(vchPubKey, true);
+ CTxDestination dest;
+ const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
+ bool ret = reservedest.GetReservedDestination(change_type, dest, true);
if (!ret)
{
- strFailReason = _("Keypool ran out, please call keypoolrefill first");
+ strFailReason = "Keypool ran out, please call keypoolrefill first";
return false;
}
- const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
-
- LearnRelatedScripts(vchPubKey, change_type);
- scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));
+ scriptChange = GetScriptForDestination(dest);
}
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
@@ -2919,12 +3049,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
if (txout.nValue < 0)
- strFailReason = _("The transaction amount is too small to pay the fee");
+ strFailReason = _("The transaction amount is too small to pay the fee").translated;
else
- strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+ strFailReason = _("The transaction amount is too small to send after the fee has been deducted").translated;
}
else
- strFailReason = _("Transaction amount too small");
+ strFailReason = _("Transaction amount too small").translated;
return false;
}
txNew.vout.push_back(txout);
@@ -2952,7 +3082,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
continue;
}
else {
- strFailReason = _("Insufficient funds");
+ strFailReason = _("Insufficient funds").translated;
return false;
}
}
@@ -2983,7 +3113,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
{
- strFailReason = _("Change index out of range");
+ strFailReason = _("Change index out of range").translated;
return false;
}
@@ -3002,22 +3132,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
if (nBytes < 0) {
- strFailReason = _("Signing transaction failed");
+ strFailReason = _("Signing transaction failed").translated;
return false;
}
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
- strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
- }
-
- // If we made it here and we aren't even able to meet the relay fee on the next pass, give up
- // because we must be at the maximum allowed fee.
- if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes))
- {
- strFailReason = _("Transaction too large for fee policy");
+ strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.").translated;
return false;
}
@@ -3057,7 +3179,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// fee to pay for the new output and still meet nFeeNeeded
// Or we should have just subtracted fee from recipients and
// nFeeNeeded should not have changed
- strFailReason = _("Transaction fee and change calculation failed");
+ strFailReason = _("Transaction fee and change calculation failed").translated;
return false;
}
@@ -3086,8 +3208,6 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
}
- if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change
-
// Shuffle selected coins and fill in final vin
txNew.vin.clear();
std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
@@ -3116,7 +3236,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
- strFailReason = _("Signing transaction failed");
+ strFailReason = _("Signing transaction failed").translated;
return false;
} else {
UpdateInput(txNew.vin.at(nIn), sigdata);
@@ -3132,19 +3252,28 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// Limit size
if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
{
- strFailReason = _("Transaction too large");
+ strFailReason = _("Transaction too large").translated;
return false;
}
}
+ if (nFeeRet > m_default_max_tx_fee) {
+ strFailReason = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
+ return false;
+ }
+
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
if (!chain().checkChainLimits(tx)) {
- strFailReason = _("Transaction has too long of a mempool chain");
+ strFailReason = _("Transaction has too long of a mempool chain").translated;
return false;
}
}
+ // Before we return success, we assume any change key will be used to prevent
+ // accidental re-use.
+ reservedest.KeepDestination();
+
WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
feeCalc.est.pass.start, feeCalc.est.pass.end,
@@ -3159,7 +3288,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state)
+bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CValidationState& state)
{
{
auto locked_chain = chain().lock();
@@ -3173,8 +3302,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */
{
- // Take key pair from key pool so it won't be used again
- reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
@@ -3195,12 +3322,10 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
- // Broadcast
- if (!wtx.AcceptToMemoryPool(*locked_chain, state)) {
- WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
+ std::string err_string;
+ if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) {
+ WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
- } else {
- wtx.RelayWalletTransaction(*locked_chain);
}
}
}
@@ -3425,8 +3550,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
LOCK(cs_wallet);
- if (IsLocked())
- return false;
+ if (IsLocked()) return false;
// Top up key pool
unsigned int nTargetSize;
@@ -3487,8 +3611,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
{
LOCK(cs_wallet);
- if (!IsLocked())
- TopUpKeyPool();
+ TopUpKeyPool();
bool fReturningInternal = fRequestedInternal;
fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
@@ -3575,6 +3698,42 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
return true;
}
+bool CWallet::GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error)
+{
+ LOCK(cs_wallet);
+ error.clear();
+
+ TopUpKeyPool();
+
+ // Generate a new key that is added to wallet
+ CPubKey new_key;
+ if (!GetKeyFromPool(new_key)) {
+ error = "Error: Keypool ran out, please call keypoolrefill first";
+ return false;
+ }
+ LearnRelatedScripts(new_key, type);
+ dest = GetDestinationForKey(new_key, type);
+
+ SetAddressBook(dest, label, "receive");
+ return true;
+}
+
+bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error)
+{
+ error.clear();
+
+ TopUpKeyPool();
+
+ ReserveDestination reservedest(this);
+ if (!reservedest.GetReservedDestination(type, dest, true)) {
+ error = "Error: Keypool ran out, please call keypoolrefill first";
+ return false;
+ }
+
+ reservedest.KeepDestination();
+ return true;
+}
+
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
if (setKeyPool.empty()) {
return GetTime();
@@ -3754,7 +3913,7 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
return result;
}
-bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
+bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination& dest, bool internal)
{
if (!pwallet->CanGetAddresses(internal)) {
return false;
@@ -3770,25 +3929,29 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
fInternal = keypool.fInternal;
}
assert(vchPubKey.IsValid());
- pubkey = vchPubKey;
+ pwallet->LearnRelatedScripts(vchPubKey, type);
+ address = GetDestinationForKey(vchPubKey, type);
+ dest = address;
return true;
}
-void CReserveKey::KeepKey()
+void ReserveDestination::KeepDestination()
{
if (nIndex != -1)
pwallet->KeepKey(nIndex);
nIndex = -1;
vchPubKey = CPubKey();
+ address = CNoDestination();
}
-void CReserveKey::ReturnKey()
+void ReserveDestination::ReturnDestination()
{
if (nIndex != -1) {
pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
}
nIndex = -1;
vchPubKey = CPubKey();
+ address = CNoDestination();
}
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
@@ -4088,17 +4251,17 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- chain.initMessage(_("Zapping all transactions from wallet..."));
+ chain.initMessage(_("Zapping all transactions from wallet...").translated);
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
- chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted").translated, walletFile));
return nullptr;
}
}
- chain.initMessage(_("Loading wallet..."));
+ chain.initMessage(_("Loading wallet...").translated);
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
@@ -4109,26 +4272,26 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
if (nLoadWalletRet == DBErrors::CORRUPT) {
- chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted").translated, walletFile));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
chain.initWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
- " or address book entries might be missing or incorrect."),
+ " or address book entries might be missing or incorrect.").translated,
walletFile));
}
else if (nLoadWalletRet == DBErrors::TOO_NEW) {
- chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, PACKAGE_NAME));
+ chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s").translated, walletFile, PACKAGE_NAME));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
- chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME));
+ chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete").translated, PACKAGE_NAME));
return nullptr;
}
else {
- chain.initError(strprintf(_("Error loading %s"), walletFile));
+ chain.initError(strprintf(_("Error loading %s").translated, walletFile));
return nullptr;
}
}
@@ -4147,7 +4310,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
- chain.initError(_("Cannot downgrade wallet"));
+ chain.initError(_("Cannot downgrade wallet").translated);
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
@@ -4160,7 +4323,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
int max_version = walletInstance->GetVersion();
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
- chain.initError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
+ chain.initError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.").translated);
return nullptr;
}
@@ -4188,7 +4351,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
- chain.initError(_("Unable to generate keys"));
+ chain.initError(_("Unable to generate keys").translated);
return nullptr;
}
}
@@ -4208,7 +4371,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Top up the keypool
if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
- chain.initError(_("Unable to generate initial keys"));
+ chain.initError(_("Unable to generate initial keys").translated);
return nullptr;
}
@@ -4216,12 +4379,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
- chain.initError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Private keys can only be disabled during creation").translated, walletFile));
return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
LOCK(walletInstance->cs_KeyStore);
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
- chain.initWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
+ chain.initWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys").translated, walletFile));
}
}
@@ -4243,21 +4406,21 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (n > HIGH_TX_FEE_PER_KB) {
chain.initWarning(AmountHighWarn("-mintxfee") + " " +
- _("This is the minimum transaction fee you pay on every transaction."));
+ _("This is the minimum transaction fee you pay on every transaction.").translated);
}
walletInstance->m_min_fee = CFeeRate(n);
}
- walletInstance->m_allow_fallback_fee = Params().IsFallbackFeeEnabled();
+ walletInstance->m_allow_fallback_fee = Params().IsTestChain();
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
- chain.initError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'").translated, gArgs.GetArg("-fallbackfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
chain.initWarning(AmountHighWarn("-fallbackfee") + " " +
- _("This is the transaction fee you may pay when fee estimates are not available."));
+ _("This is the transaction fee you may pay when fee estimates are not available.").translated);
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
@@ -4265,12 +4428,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-discardfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
- chain.initError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'").translated, gArgs.GetArg("-discardfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
chain.initWarning(AmountHighWarn("-discardfee") + " " +
- _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ _("This is the transaction fee you may discard if change is smaller than dust at this level").translated);
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
}
@@ -4282,11 +4445,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
chain.initWarning(AmountHighWarn("-paytxfee") + " " +
- _("This is the transaction fee you will pay if you send a transaction."));
+ _("This is the transaction fee you will pay if you send a transaction.").translated);
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) {
- chain.initError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
+ chain.initError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)").translated,
gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString()));
return nullptr;
}
@@ -4300,10 +4463,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return nullptr;
}
if (nMaxFee > HIGH_MAX_TX_FEE) {
- chain.initWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
+ chain.initWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.").translated);
}
if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) {
- chain.initError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
+ chain.initError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)").translated,
gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString()));
return nullptr;
}
@@ -4312,7 +4475,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
chain.initWarning(AmountHighWarn("-minrelaytxfee") + " " +
- _("The wallet will avoid paying less than the minimum relay fee."));
+ _("The wallet will avoid paying less than the minimum relay fee.").translated);
walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
@@ -4360,12 +4523,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (rescan_height != block_height) {
- chain.initError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ chain.initError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)").translated);
return nullptr;
}
}
- chain.initMessage(_("Rescanning..."));
+ chain.initMessage(_("Rescanning...").translated);
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
// No need to read and scan block if block was created before
@@ -4379,7 +4542,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
- chain.initError(_("Failed to rescan the wallet during initialization"));
+ chain.initError(_("Failed to rescan the wallet during initialization").translated);
return nullptr;
}
}
@@ -4465,13 +4628,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
m_pre_split = false;
}
-CWalletKey::CWalletKey(int64_t nExpires)
-{
- nTimeCreated = (nExpires ? GetTime() : 0);
- nTimeExpires = nExpires;
-}
-
-void CMerkleTx::SetMerkleBranch(const uint256& block_hash, int posInBlock)
+void CWalletTx::SetMerkleBranch(const uint256& block_hash, int posInBlock)
{
// Update the tx's hashBlock
hashBlock = block_hash;
@@ -4480,7 +4637,7 @@ void CMerkleTx::SetMerkleBranch(const uint256& block_hash, int posInBlock)
nIndex = posInBlock;
}
-int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
+int CWalletTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
{
if (hashUnset())
return 0;
@@ -4488,7 +4645,7 @@ int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1);
}
-int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
+int CWalletTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
{
if (!IsCoinBase())
return 0;
@@ -4497,24 +4654,12 @@ int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
}
-bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
+bool CWalletTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
{
// note GetBlocksToMaturity is 0 for non-coinbase tx
return GetBlocksToMaturity(locked_chain) > 0;
}
-bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state)
-{
- // We must set fInMempool here - while it will be re-set to true by the
- // entered-mempool callback, if we did not there would be a race where a
- // user could call sendmoney in a loop and hit spurious out of funds errors
- // because we think that this newly generated transaction's change is
- // unavailable as we're not yet aware that it is in the mempool.
- bool ret = locked_chain.submitToMemoryPool(tx, pwallet->m_default_max_tx_fee, state);
- fInMempool |= ret;
- return ret;
-}
-
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
@@ -4590,3 +4735,203 @@ bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, cons
mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
}
+
+bool CWallet::SetCrypted()
+{
+ LOCK(cs_KeyStore);
+ if (fUseCrypto)
+ return true;
+ if (!mapKeys.empty())
+ return false;
+ fUseCrypto = true;
+ return true;
+}
+
+bool CWallet::IsLocked() const
+{
+ if (!IsCrypted()) {
+ return false;
+ }
+ LOCK(cs_KeyStore);
+ return vMasterKey.empty();
+}
+
+bool CWallet::Lock()
+{
+ if (!SetCrypted())
+ return false;
+
+ {
+ LOCK(cs_KeyStore);
+ vMasterKey.clear();
+ }
+
+ NotifyStatusChanged(this);
+ return true;
+}
+
+bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
+{
+ {
+ LOCK(cs_KeyStore);
+ if (!SetCrypted())
+ return false;
+
+ bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
+ bool keyFail = false;
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
+ for (; mi != mapCryptedKeys.end(); ++mi)
+ {
+ const CPubKey &vchPubKey = (*mi).second.first;
+ const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+ CKey key;
+ if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
+ {
+ keyFail = true;
+ break;
+ }
+ keyPass = true;
+ if (fDecryptionThoroughlyChecked)
+ break;
+ }
+ if (keyPass && keyFail)
+ {
+ LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
+ throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
+ }
+ if (keyFail || (!keyPass && !accept_no_keys))
+ return false;
+ vMasterKey = vMasterKeyIn;
+ fDecryptionThoroughlyChecked = true;
+ }
+ NotifyStatusChanged(this);
+ return true;
+}
+
+bool CWallet::HaveKey(const CKeyID &address) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::HaveKey(address);
+ }
+ return mapCryptedKeys.count(address) > 0;
+}
+
+bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::GetKey(address, keyOut);
+ }
+
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
+ {
+ const CPubKey &vchPubKey = (*mi).second.first;
+ const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+ return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
+ }
+ return false;
+}
+
+bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
+{
+ LOCK(cs_KeyStore);
+ WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
+ if (it != mapWatchKeys.end()) {
+ pubkey_out = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) {
+ return GetWatchPubKey(address, vchPubKeyOut);
+ }
+ return true;
+ }
+
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
+ {
+ vchPubKeyOut = (*mi).second.first;
+ return true;
+ }
+ // Check for watch-only pubkeys
+ return GetWatchPubKey(address, vchPubKeyOut);
+}
+
+std::set<CKeyID> CWallet::GetKeys() const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::GetKeys();
+ }
+ std::set<CKeyID> set_address;
+ for (const auto& mi : mapCryptedKeys) {
+ set_address.insert(mi.first);
+ }
+ return set_address;
+}
+
+bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
+{
+ LOCK(cs_KeyStore);
+ if (!mapCryptedKeys.empty() || IsCrypted())
+ return false;
+
+ fUseCrypto = true;
+ for (const KeyMap::value_type& mKey : mapKeys)
+ {
+ const CKey &key = mKey.second;
+ CPubKey vchPubKey = key.GetPubKey();
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ std::vector<unsigned char> vchCryptedSecret;
+ if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
+ return false;
+ if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
+ return false;
+ }
+ mapKeys.clear();
+ return true;
+}
+
+bool CWallet::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::AddKeyPubKey(key, pubkey);
+ }
+
+ if (IsLocked()) {
+ return false;
+ }
+
+ std::vector<unsigned char> vchCryptedSecret;
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
+ return false;
+ }
+
+ if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
+ return false;
+ }
+ return true;
+}
+
+
+bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+{
+ LOCK(cs_KeyStore);
+ if (!SetCrypted()) {
+ return false;
+ }
+
+ mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
+ ImplicitlyLearnRelatedKeyScripts(vchPubKey);
+ return true;
+}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 7b5465c219..3a45c1ccc5 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -12,7 +12,6 @@
#include <outputtype.h>
#include <policy/feerate.h>
#include <script/sign.h>
-#include <streams.h>
#include <tinyformat.h>
#include <ui_interface.h>
#include <util/strencodings.h>
@@ -35,6 +34,8 @@
#include <utility>
#include <vector>
+#include <boost/signals2/signal.hpp>
+
//! Explicitly unload and delete the wallet.
//! Blocks the current thread after signaling the unload intent so that all
//! wallet clients release the wallet.
@@ -49,6 +50,14 @@ std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning);
+enum class WalletCreationStatus {
+ SUCCESS,
+ CREATION_FAILED,
+ ENCRYPTION_FAILED
+};
+
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result);
+
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
@@ -85,11 +94,11 @@ static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
class CCoinControl;
class COutput;
-class CReserveKey;
class CScript;
class CWalletTx;
struct FeeCalculation;
enum class FeeEstimateMode;
+class ReserveDestination;
/** (client) version numbers for particular wallet features */
enum WalletFeature
@@ -254,55 +263,57 @@ public:
}
};
-/** A wrapper to reserve a key from a wallet keypool
+/** A wrapper to reserve an address from a wallet
*
- * CReserveKey is used to reserve a key from the keypool. It is passed around
- * during the CreateTransaction/CommitTransaction procedure.
+ * ReserveDestination is used to reserve an address.
+ * It is currently only used inside of CreateTransaction.
*
- * Instantiating a CReserveKey does not reserve a keypool key. To do so,
- * GetReservedKey() needs to be called on the object. Once a key has been
- * reserved, call KeepKey() on the CReserveKey object to make sure it is not
- * returned to the keypool. Call ReturnKey() to return the key to the keypool
- * so it can be re-used (for example, if the key was used in a new transaction
+ * Instantiating a ReserveDestination does not reserve an address. To do so,
+ * GetReservedDestination() needs to be called on the object. Once an address has been
+ * reserved, call KeepDestination() on the ReserveDestination object to make sure it is not
+ * returned. Call ReturnDestination() to return the address so it can be re-used (for
+ * example, if the address was used in a new transaction
* and that transaction was not completed and needed to be aborted).
*
- * If a key is reserved and KeepKey() is not called, then the key will be
- * returned to the keypool when the CReserveObject goes out of scope.
+ * If an address is reserved and KeepDestination() is not called, then the address will be
+ * returned when the ReserveDestination goes out of scope.
*/
-class CReserveKey
+class ReserveDestination
{
protected:
- //! The wallet to reserve the keypool key from
+ //! The wallet to reserve from
CWallet* pwallet;
- //! The index of the key in the keypool
+ //! The index of the address's key in the keypool
int64_t nIndex{-1};
- //! The public key
+ //! The public key for the address
CPubKey vchPubKey;
+ //! The destination
+ CTxDestination address;
//! Whether this is from the internal (change output) keypool
bool fInternal{false};
public:
- //! Construct a CReserveKey object. This does NOT reserve a key from the keypool yet
- explicit CReserveKey(CWallet* pwalletIn)
+ //! Construct a ReserveDestination object. This does NOT reserve an address yet
+ explicit ReserveDestination(CWallet* pwalletIn)
{
pwallet = pwalletIn;
}
- CReserveKey(const CReserveKey&) = delete;
- CReserveKey& operator=(const CReserveKey&) = delete;
+ ReserveDestination(const ReserveDestination&) = delete;
+ ReserveDestination& operator=(const ReserveDestination&) = delete;
//! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
- ~CReserveKey()
+ ~ReserveDestination()
{
- ReturnKey();
+ ReturnDestination();
}
- //! Reserve a key from the keypool
- bool GetReservedKey(CPubKey &pubkey, bool internal = false);
- //! Return a key to the keypool
- void ReturnKey();
- //! Keep the key. Do not return it to the keypool when this object goes out of scope
- void KeepKey();
+ //! Reserve an address
+ bool GetReservedDestination(const OutputType type, CTxDestination& pubkey, bool internal);
+ //! Return reserved address
+ void ReturnDestination();
+ //! Keep the address. Do not return it's key to the keypool when this object goes out of scope
+ void KeepDestination();
};
/** Address book data */
@@ -353,82 +364,24 @@ struct COutputEntry
int vout;
};
-/** A transaction with a merkle branch linking it to the block chain. */
+/** Legacy class used for deserializing vtxPrev for backwards compatibility.
+ * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
+ * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
+ * These need to get deserialized for field alignment when deserializing
+ * a CWalletTx, but the deserialized values are discarded.**/
class CMerkleTx
{
-private:
- /** Constant used in hashBlock to indicate tx has been abandoned */
- static const uint256 ABANDON_HASH;
-
public:
- CTransactionRef tx;
- uint256 hashBlock;
-
- /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
- * block in the chain we know this or any in-wallet dependency conflicts
- * with. Older clients interpret nIndex == -1 as unconfirmed for backward
- * compatibility.
- */
- int nIndex;
-
- CMerkleTx()
- {
- SetTx(MakeTransactionRef());
- Init();
- }
-
- explicit CMerkleTx(CTransactionRef arg)
- {
- SetTx(std::move(arg));
- Init();
- }
-
- void Init()
- {
- hashBlock = uint256();
- nIndex = -1;
- }
-
- void SetTx(CTransactionRef arg)
+ template<typename Stream>
+ void Unserialize(Stream& s)
{
- tx = std::move(arg);
- }
-
- ADD_SERIALIZE_METHODS;
+ CTransactionRef tx;
+ uint256 hashBlock;
+ std::vector<uint256> vMerkleBranch;
+ int nIndex;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
- READWRITE(tx);
- READWRITE(hashBlock);
- READWRITE(vMerkleBranch);
- READWRITE(nIndex);
+ s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
}
-
- void SetMerkleBranch(const uint256& block_hash, int posInBlock);
-
- /**
- * Return depth of transaction in blockchain:
- * <0 : conflicts with a transaction this deep in the blockchain
- * 0 : in memory pool, waiting to be included in a block
- * >=1 : this many blocks deep in the main chain
- */
- int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const;
- bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; }
-
- /**
- * @return number of blocks to maturity for this transaction:
- * 0 : is not a coinbase transaction, or is a mature coinbase transaction
- * >0 : is a coinbase transaction which matures in this many blocks
- */
- int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
- bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
- bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
- void setAbandoned() { hashBlock = ABANDON_HASH; }
-
- const uint256& GetHash() const { return tx->GetHash(); }
- bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
};
//Get the marginal bytes of spending the specified output
@@ -438,11 +391,14 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet,
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.
*/
-class CWalletTx : public CMerkleTx
+class CWalletTx
{
private:
const CWallet* pwallet;
+ /** Constant used in hashBlock to indicate tx has been abandoned */
+ static const uint256 ABANDON_HASH;
+
public:
/**
* Key/value map with information about the transaction.
@@ -500,7 +456,10 @@ public:
mutable bool fInMempool;
mutable CAmount nChangeCached;
- CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
+ CWalletTx(const CWallet* pwalletIn, CTransactionRef arg)
+ : tx(std::move(arg)),
+ hashBlock(uint256()),
+ nIndex(-1)
{
Init(pwalletIn);
}
@@ -520,10 +479,18 @@ public:
nOrderPos = -1;
}
+ CTransactionRef tx;
+ uint256 hashBlock;
+ /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
+ * block in the chain we know this or any in-wallet dependency conflicts
+ * with. Older clients interpret nIndex == -1 as unconfirmed for backward
+ * compatibility.
+ */
+ int nIndex;
+
template<typename Stream>
void Serialize(Stream& s) const
{
- char fSpent = false;
mapValue_t mapValueCopy = mapValue;
mapValueCopy["fromaccount"] = "";
@@ -532,20 +499,21 @@ public:
mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
}
- s << static_cast<const CMerkleTx&>(*this);
- std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev
- s << vUnused << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << fSpent;
+ std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch
+ std::vector<char> dummy_vector2; //!< Used to be vtxPrev
+ char dummy_char = false; //!< Used to be fSpent
+ s << tx << hashBlock << dummy_vector1 << nIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_char;
}
template<typename Stream>
void Unserialize(Stream& s)
{
Init(nullptr);
- char fSpent;
- s >> static_cast<CMerkleTx&>(*this);
- std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev
- s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> fSpent;
+ std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
+ std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
+ char dummy_char; //! Used to be fSpent
+ s >> tx >> hashBlock >> dummy_vector1 >> nIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_char;
ReadOrderPos(nOrderPos, mapValue);
nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
@@ -556,6 +524,11 @@ public:
mapValue.erase("timesmart");
}
+ void SetTx(CTransactionRef arg)
+ {
+ tx = std::move(arg);
+ }
+
//! make sure balances are recalculated
void MarkDirty()
{
@@ -606,11 +579,8 @@ public:
int64_t GetTxTime() const;
- // Pass this transaction to the node to relay to its peers
- bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
-
- /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state);
+ // Pass this transaction to node for mempool insertion and relay to peers if flag set to true
+ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@@ -619,6 +589,31 @@ public:
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
+
+ void SetMerkleBranch(const uint256& block_hash, int posInBlock);
+
+ /**
+ * Return depth of transaction in blockchain:
+ * <0 : conflicts with a transaction this deep in the blockchain
+ * 0 : in memory pool, waiting to be included in a block
+ * >=1 : this many blocks deep in the main chain
+ */
+ int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const;
+ bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; }
+
+ /**
+ * @return number of blocks to maturity for this transaction:
+ * 0 : is not a coinbase transaction, or is a mature coinbase transaction
+ * >0 : is a coinbase transaction which matures in this many blocks
+ */
+ int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
+ bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
+ bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
+ void setAbandoned() { hashBlock = ABANDON_HASH; }
+
+ const uint256& GetHash() const { return tx->GetHash(); }
+ bool IsCoinBase() const { return tx->IsCoinBase(); }
+ bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
};
class COutput
@@ -665,33 +660,6 @@ public:
}
};
-/** Private key that includes an expiration date in case it never gets used. */
-class CWalletKey
-{
-public:
- CPrivKey vchPrivKey;
- int64_t nTimeCreated;
- int64_t nTimeExpires;
- std::string strComment;
- // todo: add something to note what created it (user, getnewaddress, change)
- // maybe should have a map<string, string> property map
-
- explicit CWalletKey(int64_t nExpires=0);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- int nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(vchPrivKey);
- READWRITE(nTimeCreated);
- READWRITE(nTimeExpires);
- READWRITE(LIMITED_STRING(strComment, 65536));
- }
-};
-
struct CoinSelectionParams
{
bool use_bnb = true;
@@ -709,9 +677,35 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
-class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notifications
+class CWallet final : public FillableSigningProvider, private interfaces::Chain::Notifications
{
private:
+ CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
+
+ //! if fUseCrypto is true, mapKeys must be empty
+ //! if fUseCrypto is false, vMasterKey must be empty
+ std::atomic<bool> fUseCrypto;
+
+ //! keeps track of whether Unlock has run a thorough check before
+ bool fDecryptionThoroughlyChecked;
+
+ using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
+ using WatchOnlySet = std::set<CScript>;
+ using WatchKeyMap = std::map<CKeyID, CPubKey>;
+
+ bool SetCrypted();
+
+ //! will encrypt previously unencrypted keys
+ bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
+
+ bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
+ CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
+ WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
+ WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
+
+ bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
+
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<int64_t> m_scanning_start{0};
@@ -794,8 +788,9 @@ private:
* of the other AddWatchOnly which accepts a timestamp and sets
* nTimeFirstKey more intelligently for more efficient rescans.
*/
- bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddWatchOnlyInMem(const CScript &dest);
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
@@ -835,6 +830,9 @@ private:
*/
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
+ //! Fetches a key from the keypool
+ bool GetKeyFromPool(CPubKey &key, bool internal = false);
+
public:
/*
* Main wallet lock.
@@ -879,7 +877,9 @@ public:
/** Construct wallet with specified name and database implementation. */
CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
- : m_chain(chain),
+ : fUseCrypto(false),
+ fDecryptionThoroughlyChecked(false),
+ m_chain(chain),
m_location(location),
database(std::move(database))
{
@@ -893,6 +893,10 @@ public:
encrypted_batch = nullptr;
}
+ bool IsCrypted() const { return fUseCrypto; }
+ bool IsLocked() const;
+ bool Lock();
+
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
@@ -922,7 +926,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe = true, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
@@ -975,7 +979,7 @@ public:
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
- bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
+ bool LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); }
//! Load metadata (used by LoadWallet)
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -986,9 +990,13 @@ public:
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds an encrypted key to the store, and saves it to disk.
- bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override;
+ bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ bool GetKey(const CKeyID &address, CKey& keyOut) const override;
+ bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ bool HaveKey(const CKeyID &address) const override;
+ std::set<CKeyID> GetKeys() const override;
bool AddCScript(const CScript& redeemScript) override;
bool LoadCScript(const CScript& redeemScript);
@@ -1005,9 +1013,15 @@ public:
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool RemoveWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
+ //! Returns whether the watch-only script is in the wallet
+ bool HaveWatchOnly(const CScript &dest) const;
+ //! Returns whether there are any watch-only things in the wallet
+ bool HaveWatchOnly() const;
+ //! Fetches a pubkey from mapWatchKeys if it exists there
+ bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime = 0;
@@ -1079,9 +1093,9 @@ public:
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
- bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
+ bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
- bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state);
+ bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CValidationState& state);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{
@@ -1092,10 +1106,10 @@ public:
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
- bool ImportScripts(const std::set<CScript> scripts) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
@@ -1136,7 +1150,6 @@ public:
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
- bool GetKeyFromPool(CPubKey &key, bool internal = false);
int64_t GetOldestKeyPoolTime();
/**
* Marks all keys in the keypool up to and including reserve_key as used.
@@ -1149,6 +1162,9 @@ public:
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
+ bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
+ bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
+
isminetype IsMine(const CTxIn& txin) const;
/**
* Returns amount of debit if the input matches the
@@ -1232,6 +1248,12 @@ public:
/** Keypool has new keys */
boost::signals2::signal<void ()> NotifyCanGetAddressesChanged;
+ /**
+ * Wallet status (encrypted, locked) changed.
+ * Note: Called without locks held.
+ */
+ boost::signals2::signal<void (CWallet* wallet)> NotifyStatusChanged;
+
/** Inquire whether this wallet broadcasts transactions. */
bool GetBroadcastTransactions() const { return fBroadcastTransactions; }
/** Set whether this wallet broadcasts transactions. */
@@ -1296,7 +1318,7 @@ public:
/**
* Explicitly make the wallet learn the related scripts for outputs to the
* given key. This is purely to make the wallet file compatible with older
- * software, as CBasicKeyStore automatically does this implicitly for all
+ * software, as FillableSigningProvider automatically does this implicitly for all
* keys now.
*/
void LearnRelatedScripts(const CPubKey& key, OutputType);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index ece97e2a75..635997afc9 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -21,45 +21,71 @@
#include <boost/thread.hpp>
+namespace DBKeys {
+const std::string ACENTRY{"acentry"};
+const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
+const std::string BESTBLOCK{"bestblock"};
+const std::string CRYPTED_KEY{"ckey"};
+const std::string CSCRIPT{"cscript"};
+const std::string DEFAULTKEY{"defaultkey"};
+const std::string DESTDATA{"destdata"};
+const std::string FLAGS{"flags"};
+const std::string HDCHAIN{"hdchain"};
+const std::string KEYMETA{"keymeta"};
+const std::string KEY{"key"};
+const std::string MASTER_KEY{"mkey"};
+const std::string MINVERSION{"minversion"};
+const std::string NAME{"name"};
+const std::string OLD_KEY{"wkey"};
+const std::string ORDERPOSNEXT{"orderposnext"};
+const std::string POOL{"pool"};
+const std::string PURPOSE{"purpose"};
+const std::string SETTINGS{"settings"};
+const std::string TX{"tx"};
+const std::string VERSION{"version"};
+const std::string WATCHMETA{"watchmeta"};
+const std::string WATCHS{"watchs"};
+} // namespace DBKeys
+
//
// WalletBatch
//
bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
{
- return WriteIC(std::make_pair(std::string("name"), strAddress), strName);
+ return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
}
bool WalletBatch::EraseName(const std::string& strAddress)
{
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
- return EraseIC(std::make_pair(std::string("name"), strAddress));
+ return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
}
bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
{
- return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose);
+ return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
}
bool WalletBatch::ErasePurpose(const std::string& strAddress)
{
- return EraseIC(std::make_pair(std::string("purpose"), strAddress));
+ return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
}
bool WalletBatch::WriteTx(const CWalletTx& wtx)
{
- return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
+ return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
}
bool WalletBatch::EraseTx(uint256 hash)
{
- return EraseIC(std::make_pair(std::string("tx"), hash));
+ return EraseIC(std::make_pair(DBKeys::TX, hash));
}
bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
{
- return WriteIC(std::make_pair(std::string("keymeta"), pubkey), meta, overwrite);
+ return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite);
}
bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
@@ -74,7 +100,7 @@ bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey,
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
- return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
+ return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
@@ -85,75 +111,74 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
return false;
}
- if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) {
+ if (!WriteIC(std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey), vchCryptedSecret, false)) {
return false;
}
- EraseIC(std::make_pair(std::string("key"), vchPubKey));
- EraseIC(std::make_pair(std::string("wkey"), vchPubKey));
+ EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
return true;
}
bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{
- return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
+ return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
}
bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
- return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, false);
+ return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
}
bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
{
- if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) {
+ if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
return false;
}
- return WriteIC(std::make_pair(std::string("watchs"), dest), '1');
+ return WriteIC(std::make_pair(DBKeys::WATCHS, dest), '1');
}
bool WalletBatch::EraseWatchOnly(const CScript &dest)
{
- if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) {
+ if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
return false;
}
- return EraseIC(std::make_pair(std::string("watchs"), dest));
+ return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
}
bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
{
- WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
- return WriteIC(std::string("bestblock_nomerkle"), locator);
+ WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
+ return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
}
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
{
- if (m_batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
- return m_batch.Read(std::string("bestblock_nomerkle"), locator);
+ if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
+ return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
{
- return WriteIC(std::string("orderposnext"), nOrderPosNext);
+ return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
}
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return m_batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
+ return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
}
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
{
- return WriteIC(std::make_pair(std::string("pool"), nPool), keypool);
+ return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
}
bool WalletBatch::ErasePool(int64_t nPool)
{
- return EraseIC(std::make_pair(std::string("pool"), nPool));
+ return EraseIC(std::make_pair(DBKeys::POOL, nPool));
}
bool WalletBatch::WriteMinVersion(int nVersion)
{
- return WriteIC(std::string("minversion"), nVersion);
+ return WriteIC(DBKeys::MINVERSION, nVersion);
}
class CWalletScanState {
@@ -165,7 +190,6 @@ public:
unsigned int m_unknown_records{0};
bool fIsEncrypted{false};
bool fAnyUnordered{false};
- int nFileVersion{0};
std::vector<uint256> vWalletUpgrade;
CWalletScanState() {
@@ -181,20 +205,15 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
// Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other
ssKey >> strType;
- if (strType == "name")
- {
+ if (strType == DBKeys::NAME) {
std::string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
- }
- else if (strType == "purpose")
- {
+ } else if (strType == DBKeys::PURPOSE) {
std::string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
- }
- else if (strType == "tx")
- {
+ } else if (strType == DBKeys::TX) {
uint256 hash;
ssKey >> hash;
CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
@@ -228,9 +247,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fAnyUnordered = true;
pwallet->LoadToWallet(wtx);
- }
- else if (strType == "watchs")
- {
+ } else if (strType == DBKeys::WATCHS) {
wss.nWatchKeys++;
CScript script;
ssKey >> script;
@@ -238,9 +255,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadWatchOnly(script);
- }
- else if (strType == "key" || strType == "wkey")
- {
+ } else if (strType == DBKeys::KEY) {
CPubKey vchPubKey;
ssKey >> vchPubKey;
if (!vchPubKey.IsValid())
@@ -252,20 +267,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CPrivKey pkey;
uint256 hash;
- if (strType == "key")
- {
- wss.nKeys++;
- ssValue >> pkey;
- } else {
- CWalletKey wkey;
- ssValue >> wkey;
- pkey = wkey.vchPrivKey;
- }
+ wss.nKeys++;
+ ssValue >> pkey;
- // Old wallets store keys as "key" [pubkey] => [privkey]
+ // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
// ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
// using EC operations as a checksum.
- // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
+ // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
// remaining backwards-compatible.
try
{
@@ -302,9 +310,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Error reading wallet database: LoadKey failed";
return false;
}
- }
- else if (strType == "mkey")
- {
+ } else if (strType == DBKeys::MASTER_KEY) {
unsigned int nID;
ssKey >> nID;
CMasterKey kMasterKey;
@@ -317,9 +323,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
pwallet->mapMasterKeys[nID] = kMasterKey;
if (pwallet->nMasterKeyMaxID < nID)
pwallet->nMasterKeyMaxID = nID;
- }
- else if (strType == "ckey")
- {
+ } else if (strType == DBKeys::CRYPTED_KEY) {
CPubKey vchPubKey;
ssKey >> vchPubKey;
if (!vchPubKey.IsValid())
@@ -337,27 +341,21 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false;
}
wss.fIsEncrypted = true;
- }
- else if (strType == "keymeta")
- {
+ } else if (strType == DBKeys::KEYMETA) {
CPubKey vchPubKey;
ssKey >> vchPubKey;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
- }
- else if (strType == "watchmeta")
- {
+ } else if (strType == DBKeys::WATCHMETA) {
CScript script;
ssKey >> script;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->LoadScriptMetadata(CScriptID(script), keyMeta);
- }
- else if (strType == "defaultkey")
- {
+ } else if (strType == DBKeys::DEFAULTKEY) {
// We don't want or need the default key, but if there is one set,
// we want to make sure that it is valid so that we can detect corruption
CPubKey vchPubKey;
@@ -366,24 +364,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Error reading wallet database: Default Key corrupt";
return false;
}
- }
- else if (strType == "pool")
- {
+ } else if (strType == DBKeys::POOL) {
int64_t nIndex;
ssKey >> nIndex;
CKeyPool keypool;
ssValue >> keypool;
pwallet->LoadKeyPool(nIndex, keypool);
- }
- else if (strType == "version")
- {
- ssValue >> wss.nFileVersion;
- if (wss.nFileVersion == 10300)
- wss.nFileVersion = 300;
- }
- else if (strType == "cscript")
- {
+ } else if (strType == DBKeys::CSCRIPT) {
uint160 hash;
ssKey >> hash;
CScript script;
@@ -393,33 +381,31 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Error reading wallet database: LoadCScript failed";
return false;
}
- }
- else if (strType == "orderposnext")
- {
+ } else if (strType == DBKeys::ORDERPOSNEXT) {
ssValue >> pwallet->nOrderPosNext;
- }
- else if (strType == "destdata")
- {
+ } else if (strType == DBKeys::DESTDATA) {
std::string strAddress, strKey, strValue;
ssKey >> strAddress;
ssKey >> strKey;
ssValue >> strValue;
pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue);
- }
- else if (strType == "hdchain")
- {
+ } else if (strType == DBKeys::HDCHAIN) {
CHDChain chain;
ssValue >> chain;
pwallet->SetHDChain(chain, true);
- } else if (strType == "flags") {
+ } else if (strType == DBKeys::FLAGS) {
uint64_t flags;
ssValue >> flags;
if (!pwallet->SetWalletFlags(flags, true)) {
strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
return false;
}
- } else if (strType != "bestblock" && strType != "bestblock_nomerkle" &&
- strType != "minversion" && strType != "acentry") {
+ } else if (strType == DBKeys::OLD_KEY) {
+ strErr = "Found unsupported 'wkey' record, try loading with version 0.18";
+ return false;
+ } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
+ strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
+ strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
wss.m_unknown_records++;
}
} catch (const std::exception& e) {
@@ -438,8 +424,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
bool WalletBatch::IsKeyType(const std::string& strType)
{
- return (strType== "key" || strType == "wkey" ||
- strType == "mkey" || strType == "ckey");
+ return (strType == DBKeys::KEY ||
+ strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY);
}
DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
@@ -451,8 +437,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (m_batch.Read((std::string)"minversion", nMinVersion))
- {
+ if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
@@ -486,15 +471,15 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
{
// losing keys is considered a catastrophic error, anything else
// we assume the user can live with:
- if (IsKeyType(strType) || strType == "defaultkey") {
+ if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) {
result = DBErrors::CORRUPT;
- } else if(strType == "flags") {
+ } else if (strType == DBKeys::FLAGS) {
// reading the wallet flags can only fail if unknown flags are present
result = DBErrors::TOO_NEW;
} else {
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
- if (strType == "tx")
+ if (strType == DBKeys::TX)
// Rescan if there is a bad transaction record:
gArgs.SoftSetBoolArg("-rescan", true);
}
@@ -519,7 +504,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (result != DBErrors::LOAD_OK)
return result;
- pwallet->WalletLogPrintf("nFileVersion = %d\n", wss.nFileVersion);
+ // Last client version to open this wallet, was previously the file version number
+ int last_client = CLIENT_VERSION;
+ m_batch.Read(DBKeys::VERSION, last_client);
+
+ int wallet_version = pwallet->GetVersion();
+ pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
@@ -532,11 +522,11 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
WriteTx(pwallet->mapWallet.at(hash));
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
- if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
+ if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000))
return DBErrors::NEED_REWRITE;
- if (wss.nFileVersion < CLIENT_VERSION) // Update
- WriteVersion(CLIENT_VERSION);
+ if (last_client < CLIENT_VERSION) // Update
+ m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
@@ -558,8 +548,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CW
try {
int nMinVersion = 0;
- if (m_batch.Read((std::string)"minversion", nMinVersion))
- {
+ if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}
@@ -588,7 +577,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CW
std::string strType;
ssKey >> strType;
- if (strType == "tx") {
+ if (strType == DBKeys::TX) {
uint256 hash;
ssKey >> hash;
@@ -723,8 +712,9 @@ bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, C
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
dummyWss, strType, strErr);
}
- if (!IsKeyType(strType) && strType != "hdchain")
+ if (!IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
return false;
+ }
if (!fReadOK)
{
LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
@@ -746,23 +736,23 @@ bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::string& w
bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
- return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
+ return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)), value);
}
bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
{
- return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
+ return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(address, key)));
}
bool WalletBatch::WriteHDChain(const CHDChain& chain)
{
- return WriteIC(std::string("hdchain"), chain);
+ return WriteIC(DBKeys::HDCHAIN, chain);
}
bool WalletBatch::WriteWalletFlags(const uint64_t flags)
{
- return WriteIC(std::string("flags"), flags);
+ return WriteIC(DBKeys::FLAGS, flags);
}
bool WalletBatch::TxnBegin()
@@ -779,13 +769,3 @@ bool WalletBatch::TxnAbort()
{
return m_batch.TxnAbort();
}
-
-bool WalletBatch::ReadVersion(int& nVersion)
-{
- return m_batch.ReadVersion(nVersion);
-}
-
-bool WalletBatch::WriteVersion(int nVersion)
-{
- return m_batch.WriteVersion(nVersion);
-}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index d4a3bba97a..0fee35934d 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -55,6 +55,32 @@ enum class DBErrors
NEED_REWRITE
};
+namespace DBKeys {
+extern const std::string ACENTRY;
+extern const std::string BESTBLOCK;
+extern const std::string BESTBLOCK_NOMERKLE;
+extern const std::string CRYPTED_KEY;
+extern const std::string CSCRIPT;
+extern const std::string DEFAULTKEY;
+extern const std::string DESTDATA;
+extern const std::string FLAGS;
+extern const std::string HDCHAIN;
+extern const std::string KEY;
+extern const std::string KEYMETA;
+extern const std::string MASTER_KEY;
+extern const std::string MINVERSION;
+extern const std::string NAME;
+extern const std::string OLD_KEY;
+extern const std::string ORDERPOSNEXT;
+extern const std::string POOL;
+extern const std::string PURPOSE;
+extern const std::string SETTINGS;
+extern const std::string TX;
+extern const std::string VERSION;
+extern const std::string WATCHMETA;
+extern const std::string WATCHS;
+} // namespace DBKeys
+
/* simple HD chain data model */
class CHDChain
{
@@ -249,10 +275,6 @@ public:
bool TxnCommit();
//! Abort current transaction
bool TxnAbort();
- //! Read wallet version
- bool ReadVersion(int& nVersion);
- //! Write wallet version
- bool WriteVersion(int nVersion);
private:
BerkeleyBatch m_batch;
WalletDatabase& m_database;
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 5542412a7f..35d2033ba8 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -3,9 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <warnings.h>
+
#include <sync.h>
#include <util/system.h>
-#include <warnings.h>
+#include <util/translation.h>
static RecursiveMutex cs_warnings;
static std::string strMiscWarning GUARDED_BY(cs_warnings);
@@ -46,7 +48,7 @@ std::string GetWarnings(const std::string& strFor)
if (!CLIENT_VERSION_IS_RELEASE) {
strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications";
- strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated;
}
// Misc warnings like out of disk space and clock is wrong
@@ -59,12 +61,12 @@ std::string GetWarnings(const std::string& strFor)
if (fLargeWorkForkFound)
{
strStatusBar = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
- strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated;
}
else if (fLargeWorkInvalidChainFound)
{
strStatusBar = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
- strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated;
}
if (strFor == "gui")
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index a34968ef7d..cf97b7ecce 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -15,8 +15,6 @@ namespace {
UniValue getzmqnotifications(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 0) {
- throw std::runtime_error(
RPCHelpMan{"getzmqnotifications",
"\nReturns information about the active ZeroMQ notifications.\n",
{},
@@ -34,8 +32,7 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
HelpExampleCli("getzmqnotifications", "")
+ HelpExampleRpc("getzmqnotifications", "")
},
- }.ToString());
- }
+ }.Check(request);
UniValue result(UniValue::VARR);
if (g_zmq_notification_interface != nullptr) {
diff --git a/test/README.md b/test/README.md
index ecea3213ab..8f08b7afe4 100644
--- a/test/README.md
+++ b/test/README.md
@@ -49,6 +49,29 @@ You can run any combination (incl. duplicates) of tests by calling:
test/functional/test_runner.py <testname1> <testname2> <testname3> ...
```
+Wildcard test names can be passed, if the paths are coherent and the test runner
+is called from a `bash` shell or similar that does the globbing. For example,
+to run all the wallet tests:
+
+```
+test/functional/test_runner.py test/functional/wallet*
+functional/test_runner.py functional/wallet* (called from the test/ directory)
+test_runner.py wallet* (called from the test/functional/ directory)
+```
+
+but not
+
+```
+test/functional/test_runner.py wallet*
+```
+
+Combinations of wildcards can be passed:
+
+```
+test/functional/test_runner.py ./test/functional/tool* test/functional/mempool*
+test_runner.py tool* mempool*
+```
+
Run the regression test suite with:
```
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 45ecaabe14..5364ac4b8c 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -8,6 +8,7 @@ If no argument is provided, the most recent test directory will be used."""
import argparse
from collections import defaultdict, namedtuple
+import glob
import heapq
import itertools
import os
@@ -76,9 +77,17 @@ def read_logs(tmp_dir):
Delegates to generator function get_log_events() to provide individual log events
for each of the input log files."""
+ # Find out what the folder is called that holds the debug.log file
+ chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir))
+ if chain:
+ chain = chain[0] # pick the first one if more than one chain was found (should never happen)
+ chain = re.search('node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name
+ else:
+ chain = 'regtest' # fallback to regtest (should only happen when none exists)
+
files = [("test", "%s/test_framework.log" % tmp_dir)]
for i in itertools.count():
- logfile = "{}/node{}/regtest/debug.log".format(tmp_dir, i)
+ logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain)
if not os.path.isfile(logfile):
break
files.append(("node%d" % i, logfile))
@@ -164,25 +173,26 @@ def get_log_events(source, logfile):
def print_logs_plain(log_events, colors):
- """Renders the iterator of log events into text."""
- for event in log_events:
- lines = event.event.splitlines()
- print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
- if len(lines) > 1:
- for line in lines[1:]:
- print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
+ """Renders the iterator of log events into text."""
+ for event in log_events:
+ lines = event.event.splitlines()
+ print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
+ if len(lines) > 1:
+ for line in lines[1:]:
+ print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
def print_logs_html(log_events):
- """Renders the iterator of log events into html."""
- try:
- import jinja2
- except ImportError:
- print("jinja2 not found. Try `pip install jinja2`")
- sys.exit(1)
- print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
+ """Renders the iterator of log events into html."""
+ try:
+ import jinja2
+ except ImportError:
+ print("jinja2 not found. Try `pip install jinja2`")
+ sys.exit(1)
+ print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
.get_template('combined_log_template.html')
.render(title="Combined Logs from testcase", log_events=[event._asdict() for event in log_events]))
+
if __name__ == '__main__':
main()
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index a2726763d0..70dfe81d4e 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -186,12 +186,15 @@ class ExampleTest(BitcoinTestFramework):
self.log.info("Connect node2 and node1")
connect_nodes(self.nodes[1], 2)
+ self.log.info("Wait for node2 to receive all the blocks from node1")
+ self.sync_all()
+
self.log.info("Add P2P connection to node2")
self.nodes[0].disconnect_p2ps()
self.nodes[2].add_p2p_connection(BaseNode())
- self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
+ self.log.info("Test that node2 propagates all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
new file mode 100755
index 0000000000..62c3eca07d
--- /dev/null
+++ b/test/functional/feature_abortnode.py
@@ -0,0 +1,48 @@
+#!/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 bitcoind aborts if can't disconnect a block.
+
+- Start a single node and generate 3 blocks.
+- Delete the undo data.
+- Mine a fork that requires disconnecting the tip.
+- Verify that bitcoind AbortNode's.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import wait_until, get_datadir_path, connect_nodes
+import os
+
+class AbortNodeTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def setup_network(self):
+ self.setup_nodes()
+ # We'll connect the nodes later
+
+ def run_test(self):
+ self.nodes[0].generate(3)
+ datadir = get_datadir_path(self.options.tmpdir, 0)
+
+ # Deleting the undo file will result in reorg failure
+ os.unlink(os.path.join(datadir, 'regtest', 'blocks', 'rev00000.dat'))
+
+ # Connecting to a node with a more work chain will trigger a reorg
+ # attempt.
+ self.nodes[1].generate(3)
+ with self.nodes[0].assert_debug_log(["Failed to disconnect block"]):
+ connect_nodes(self.nodes[0], 1)
+ self.nodes[1].generate(1)
+
+ # Check that node0 aborted
+ self.log.info("Waiting for crash")
+ wait_until(lambda: self.nodes[0].is_node_stopped(), timeout=60)
+ self.log.info("Node crashed - now verifying restart fails")
+ self.nodes[0].assert_start_raises_init_error()
+
+if __name__ == '__main__':
+ AbortNodeTest().main()
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index b7814bf33e..420a3a7688 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -72,7 +72,7 @@ class AssumeValidTest(BitcoinTestFramework):
break
try:
p2p_conn.send_message(msg_block(self.blocks[i]))
- except IOError as e:
+ except IOError:
assert not p2p_conn.is_connected
break
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index d38eca6cbe..f0bf09e172 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -29,7 +29,10 @@ NOT_FINAL_ERROR = "non-BIP68-final (code 64)"
class BIP68Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.extra_args = [[], ["-acceptnonstdtxn=0"]]
+ self.extra_args = [
+ ["-acceptnonstdtxn=1"],
+ ["-acceptnonstdtxn=0"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 2f57d99f7c..12a52935ef 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -82,7 +82,7 @@ class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [[]]
+ self.extra_args = [['-acceptnonstdtxn=1']] # This is a consensus block test, we don't care about tx policy
def run_test(self):
node = self.nodes[0] # convenience reference to the node
@@ -497,6 +497,14 @@ class FullBlockTest(BitcoinTestFramework):
tx_last = tx_new
b39_outputs += 1
+ # The accounting in the loop above can be off, because it misses the
+ # compact size encoding of the number of transactions in the block.
+ # Make sure we didn't accidentally make too big a block. Note that the
+ # size of the block has non-determinism due to the ECDSA signature in
+ # the first transaction.
+ while (len(b39.serialize()) >= MAX_BLOCK_BASE_SIZE):
+ del b39.vtx[-1]
+
b39 = self.update_block(39, [])
self.send_blocks([b39], True)
self.save_spendable_output()
diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py
index 3a4889bbe9..6f01f97ea2 100755
--- a/test/functional/feature_blocksdir.py
+++ b/test/functional/feature_blocksdir.py
@@ -18,10 +18,10 @@ class BlocksdirTest(BitcoinTestFramework):
def run_test(self):
self.stop_node(0)
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks"))
assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "blocks"))
shutil.rmtree(self.nodes[0].datadir)
- initialize_datadir(self.options.tmpdir, 0)
+ initialize_datadir(self.options.tmpdir, 0, self.chain)
self.log.info("Starting with nonexistent blocksdir ...")
blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir')
self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], 'Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path))
@@ -30,8 +30,8 @@ class BlocksdirTest(BitcoinTestFramework):
self.start_node(0, ["-blocksdir=" + blocksdir_path])
self.log.info("mining blocks..")
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
- assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat"))
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index"))
+ assert os.path.isfile(os.path.join(blocksdir_path, self.chain, "blocks", "blk00000.dat"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks", "index"))
if __name__ == '__main__':
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 7712e8bdf6..af34f9f0db 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -57,7 +57,11 @@ def cltv_validate(node, tx, height):
class BIP65Test(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 reject reason for testing
+ self.extra_args = [[
+ '-whitelist=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
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index f0d6bc21e6..70a824b863 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -22,7 +22,7 @@ class ConfArgsTest(BitcoinTestFramework):
conf.write('includeconf={}\n'.format(inc_conf_file_path))
self.nodes[0].assert_start_raises_init_error(
- expected_msg='Error parsing command line arguments: Invalid parameter -dash_cli',
+ expected_msg='Error: Error parsing command line arguments: Invalid parameter -dash_cli',
extra_args=['-dash_cli=1'],
)
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
@@ -33,27 +33,33 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('-dash=1\n')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
- with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
- conf.write("wallet=foo\n")
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+ if self.is_wallet_compiled():
+ with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
+ conf.write("wallet=foo\n")
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+
+ with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
+ conf.write('regtest=0\n') # mainnet
+ conf.write('acceptnonstdtxn=1\n')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: acceptnonstdtxn is not currently supported for main chain')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('nono\n')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\nmain.rpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass')
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
inc_conf_file2_path = os.path.join(self.nodes[0].datadir, 'include2.conf')
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 62062926a6..ff014de0e0 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -30,17 +30,27 @@ import http.client
import random
import time
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ ToHex,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, create_confirmed_utxos, hex_str_to_bytes
+from test_framework.util import (
+ assert_equal,
+ create_confirmed_utxos,
+ hex_str_to_bytes,
+)
class ChainstateWriteCrashTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = False
- # Need a bit of extra time for the nodes to start up for this test
- self.rpc_timeout = 90
+ self.rpc_timeout = 180
# Set -maxmempool=0 to turn off mempool memory sharing with dbcache
# Set -rpcservertimeout=900 to reduce socket disconnects in this
@@ -54,7 +64,8 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args
# Node3 is a normal node with default args, except will mine full blocks
- self.node3_args = ["-blockmaxweight=4000000"]
+ # and non-standard txs (e.g. txs with "dust" outputs)
+ self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
def skip_test_if_missing_module(self):
@@ -267,7 +278,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
# Warn if any of the nodes escaped restart.
for i in range(3):
if self.restart_counts[i] == 0:
- self.log.warn("Node %d never crashed during utxo flush!", i)
+ self.log.warning("Node %d never crashed during utxo flush!", i)
if __name__ == "__main__":
ChainstateWriteCrashTest().main()
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index a4b9f213a1..071f7d5cca 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -120,9 +120,16 @@ def check_estimates(node, fees_seen):
else:
assert_greater_than_or_equal(i + 1, e["blocks"])
+
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
+ # mine non-standard txs (e.g. txs with "dust" outputs)
+ self.extra_args = [
+ ["-acceptnonstdtxn", "-maxorphantx=1000", "-whitelist=127.0.0.1"],
+ ["-acceptnonstdtxn", "-blockmaxweight=68000", "-maxorphantx=1000"],
+ ["-acceptnonstdtxn", "-blockmaxweight=32000", "-maxorphantx=1000"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -133,9 +140,7 @@ class EstimateFeeTest(BitcoinTestFramework):
But first we need to use one node to create a lot of outputs
which we will use to generate our transactions.
"""
- self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-blockmaxweight=68000", "-maxorphantx=1000"],
- ["-blockmaxweight=32000", "-maxorphantx=1000"]])
+ self.add_nodes(3, extra_args=self.extra_args)
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
# NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py
index d06f6826f0..2cd6a05d08 100755
--- a/test/functional/feature_includeconf.py
+++ b/test/functional/feature_includeconf.py
@@ -43,7 +43,7 @@ class IncludeConfTest(BitcoinTestFramework):
self.log.info("-includeconf cannot be used as command-line arg")
self.stop_node(0)
- self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf")
+ self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf")
self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'")
with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f:
@@ -59,11 +59,11 @@ class IncludeConfTest(BitcoinTestFramework):
# Commented out as long as we ignore invalid arguments in configuration files
#with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
# f.write("foo=bar\n")
- #self.nodes[0].assert_start_raises_init_error(expected_msg="Error reading configuration file: Invalid configuration value foo")
+ #self.nodes[0].assert_start_raises_init_error(expected_msg="Error: Error reading configuration file: Invalid configuration value foo")
self.log.info("-includeconf cannot be invalid path")
os.remove(os.path.join(self.options.tmpdir, "node0", "relative.conf"))
- self.nodes[0].assert_start_raises_init_error(expected_msg="Error reading configuration file: Failed to include configuration file relative.conf")
+ self.nodes[0].assert_start_raises_init_error(expected_msg="Error: Error reading configuration file: Failed to include configuration file relative.conf")
self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'")
with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 87c318de9a..180ea0e51d 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -35,7 +35,7 @@ class MaxUploadTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-maxuploadtarget=800"]]
+ self.extra_args = [["-maxuploadtarget=800", "-acceptnonstdtxn=1"]]
# Cache for utxos, as the listunspent may take a long time later in the test
self.utxo_cache = []
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 66c395d7a2..727f4b9589 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -204,6 +204,7 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Mine 220 more large blocks so we have requisite history")
mine_large_blocks(self.nodes[0], 220)
+ self.sync_blocks(self.nodes[0:3], timeout=120)
usage = calc_usage(self.prunedir)
self.log.info("Usage should be below target: %d" % usage)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 1496c5d958..fd79df0b07 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -67,8 +67,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [
[
+ "-acceptnonstdtxn=1",
"-maxorphantx=1000",
- "-whitelist=127.0.0.1",
"-limitancestorcount=50",
"-limitancestorsize=101",
"-limitdescendantcount=200",
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2d4dd96a1d..a71d4071d5 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -53,17 +53,20 @@ class SegWitTest(BitcoinTestFramework):
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
[
+ "-acceptnonstdtxn=1",
"-rpcserialversion=0",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
],
[
+ "-acceptnonstdtxn=1",
"-blockversion=4",
"-rpcserialversion=1",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
],
[
+ "-acceptnonstdtxn=1",
"-blockversion=536870915",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 7bb7044cc0..0a378c5ef5 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -29,7 +29,7 @@ class TestBitcoinCli(BitcoinTestFramework):
rpc_response = self.nodes[0].getblockchaininfo()
assert_equal(cli_response, rpc_response)
- user, password = get_auth_cookie(self.nodes[0].datadir)
+ user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
self.log.info("Test -stdinrpcpass option")
assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 2bb5d8ab7d..209a222004 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -36,7 +36,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [[
'-txindex',
- '-acceptnonstdtxn=0', # Try to mimic main-net
]] * self.num_nodes
def skip_test_if_missing_module(self):
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index 351b27e94a..edf2069933 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -13,7 +13,11 @@ class MempoolLimitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]]
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ "-maxmempool=5",
+ "-spendzeroconfchange=0",
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
new file mode 100755
index 0000000000..30f851fb8e
--- /dev/null
+++ b/test/functional/mempool_package_onemore.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-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 descendant package tracking carve-out allowing one final transaction in
+ an otherwise-full package as long as it has only one parent and is <= 10k in
+ size.
+"""
+
+from decimal import Decimal
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
+
+MAX_ANCESTORS = 25
+MAX_DESCENDANTS = 25
+
+class MempoolPackagesTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.extra_args = [["-maxorphantx=1000"]]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ # Build a transaction that spends parent_txid:vout
+ # Return amount sent
+ def chain_transaction(self, node, parent_txids, vouts, value, fee, num_outputs):
+ send_value = satoshi_round((value - fee)/num_outputs)
+ inputs = []
+ for (txid, vout) in zip(parent_txids, vouts):
+ inputs.append({'txid' : txid, 'vout' : vout})
+ outputs = {}
+ for i in range(num_outputs):
+ outputs[node.getnewaddress()] = send_value
+ rawtx = node.createrawtransaction(inputs, outputs)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx['hex'])
+ fulltx = node.getrawtransaction(txid, 1)
+ assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
+ return (txid, send_value)
+
+ def run_test(self):
+ # Mine some blocks and have them mature.
+ self.nodes[0].generate(101)
+ utxo = self.nodes[0].listunspent(10)
+ txid = utxo[0]['txid']
+ vout = utxo[0]['vout']
+ value = utxo[0]['amount']
+
+ fee = Decimal("0.0002")
+ # MAX_ANCESTORS transactions off a confirmed tx should be fine
+ chain = []
+ for _ in range(4):
+ (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
+ vout = 0
+ value = sent_value
+ chain.append([txid, value])
+ for _ in range(MAX_ANCESTORS - 4):
+ (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
+ value = sent_value
+ chain.append([txid, value])
+ (second_chain, second_chain_value) = self.chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
+
+ # Check mempool has MAX_ANCESTORS + 1 transactions in it
+ assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 1)
+
+ # Adding one more transaction on to the chain should fail.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
+ # ...even if it chains on from some point in the middle of the chain.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
+ # ...even if it chains on to two parent transactions with one in the chain.
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
+ # ...especially if its > 40k weight
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
+ # But not if it chains directly off the first transaction
+ self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ # and the second chain should work just fine
+ self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+
+ # Finally, check that we added two transactions
+ assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 3)
+
+if __name__ == '__main__':
+ MempoolPackagesTest().main()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index b0a069be81..7e05a8e6c8 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -14,7 +14,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [["-printpriority=1"], ["-printpriority=1"]]
+ self.extra_args = [[
+ "-printpriority=1",
+ "-acceptnonstdtxn=1",
+ ]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 0994857912..eb3336bd3b 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -95,6 +95,9 @@ class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]]
self.utxos = []
def skip_test_if_missing_module(self):
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 481d697e63..d5c68e622b 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -6,6 +6,7 @@
import asyncio
import os
import struct
+import sys
from test_framework import messages
from test_framework.mininode import P2PDataStore, NetworkThread
@@ -92,18 +93,25 @@ class InvalidMessagesTest(BitcoinTestFramework):
#
# Send an oversized message, ensure we're disconnected.
#
- msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
- assert len(msg_over_size.serialize()) == (msg_limit + 1)
+ # Under macOS this test is skipped due to an unexpected error code
+ # returned from the closing socket which python/asyncio does not
+ # yet know how to handle.
+ #
+ if sys.platform != 'darwin':
+ msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1))
+ assert len(msg_over_size.serialize()) == (msg_limit + 1)
- with node.assert_debug_log(["Oversized message from peer=4, disconnecting"]):
- # An unknown message type (or *any* message type) over
- # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
- node.p2p.send_message(msg_over_size)
- node.p2p.wait_for_disconnect(timeout=4)
+ with node.assert_debug_log(["Oversized message from peer=4, disconnecting"]):
+ # An unknown message type (or *any* message type) over
+ # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
+ node.p2p.send_message(msg_over_size)
+ node.p2p.wait_for_disconnect(timeout=4)
- node.disconnect_p2ps()
- conn = node.add_p2p_connection(P2PDataStore())
- conn.wait_for_verack()
+ node.disconnect_p2ps()
+ conn = node.add_p2p_connection(P2PDataStore())
+ conn.wait_for_verack()
+ else:
+ self.log.info("Skipping test p2p_invalid_messages/1 (oversized message) under macOS")
#
# 2.
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index 1b18dd3e58..3cca2d78db 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -25,6 +25,9 @@ from data import invalid_txs
class InvalidTxRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]]
self.setup_clean_chain = True
def bootstrap_p2p(self, *, num_connections=1):
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 573d5f5a5f..a4650df8ee 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -8,7 +8,7 @@ Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correc
and that it responds to getdata requests for blocks correctly:
- send a block within 288 + 2 of the tip
- disconnect peers who request blocks older than that."""
-from test_framework.messages import CInv, msg_getdata, msg_verack, NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS
+from test_framework.messages import CInv, msg_getdata, msg_verack, NODE_NETWORK_LIMITED, NODE_WITNESS
from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -55,7 +55,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
- expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED
+ expected_services = NODE_WITNESS | NODE_NETWORK_LIMITED
self.log.info("Check that node has signalled expected services.")
assert_equal(node.nServices, expected_services)
@@ -83,7 +83,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
node1.wait_for_addr()
#must relay address with NODE_NETWORK_LIMITED
- assert_equal(node1.firstAddrnServices, 1036)
+ assert_equal(node1.firstAddrnServices, expected_services)
self.nodes[0].disconnect_p2ps()
node1.wait_for_disconnect()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index b7fa42f593..dca71aec43 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -184,7 +184,11 @@ class SegWitTest(BitcoinTestFramework):
self.setup_clean_chain = True
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", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
+ self.extra_args = [
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:999999999999"],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:0"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
new file mode 100755
index 0000000000..19d78ff303
--- /dev/null
+++ b/test/functional/p2p_tx_download.py
@@ -0,0 +1,175 @@
+#!/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 transaction download behavior
+"""
+
+from test_framework.messages import (
+ CInv,
+ CTransaction,
+ FromHex,
+ MSG_TX,
+ MSG_TYPE_MASK,
+ msg_inv,
+ msg_notfound,
+)
+from test_framework.mininode import (
+ P2PInterface,
+ mininode_lock,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ wait_until,
+)
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+
+import time
+
+
+class TestP2PConn(P2PInterface):
+ def __init__(self):
+ super().__init__()
+ self.tx_getdata_count = 0
+
+ def on_getdata(self, message):
+ for i in message.inv:
+ if i.type & MSG_TYPE_MASK == MSG_TX:
+ self.tx_getdata_count += 1
+
+
+# Constants from net_processing
+GETDATA_TX_INTERVAL = 60 # seconds
+MAX_GETDATA_RANDOM_DELAY = 2 # seconds
+INBOUND_PEER_TX_DELAY = 2 # seconds
+MAX_GETDATA_IN_FLIGHT = 100
+TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
+
+# Python test constants
+NUM_INBOUND = 10
+MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
+
+
+class TxDownloadTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 2
+
+ def test_tx_requests(self):
+ self.log.info("Test that we request transactions from all our peers, eventually")
+
+ txid = 0xdeadbeef
+
+ self.log.info("Announce the txid from each incoming peer to node 0")
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.nodes[0].p2ps:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ outstanding_peer_index = [i for i in range(len(self.nodes[0].p2ps))]
+
+ def getdata_found(peer_index):
+ p = self.nodes[0].p2ps[peer_index]
+ with mininode_lock:
+ return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid
+
+ node_0_mocktime = int(time.time())
+ while outstanding_peer_index:
+ node_0_mocktime += MAX_GETDATA_INBOUND_WAIT
+ self.nodes[0].setmocktime(node_0_mocktime)
+ wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
+ for i in outstanding_peer_index:
+ if getdata_found(i):
+ outstanding_peer_index.remove(i)
+
+ self.nodes[0].setmocktime(0)
+ self.log.info("All outstanding peers received a getdata")
+
+ def test_inv_block(self):
+ self.log.info("Generate a transaction on node 0")
+ tx = self.nodes[0].createrawtransaction(
+ inputs=[{ # coinbase
+ "txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0],
+ "vout": 0
+ }],
+ outputs={ADDRESS_BCRT1_UNSPENDABLE: 50 - 0.00025},
+ )
+ tx = self.nodes[0].signrawtransactionwithkey(
+ hexstring=tx,
+ privkeys=[self.nodes[0].get_deterministic_priv_key().key],
+ )['hex']
+ ctx = FromHex(CTransaction(), tx)
+ txid = int(ctx.rehash(), 16)
+
+ self.log.info(
+ "Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND))
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.peers:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ self.log.info("Put the tx in node 0's mempool")
+ self.nodes[0].sendrawtransaction(tx)
+
+ # Since node 1 is connected outbound to an honest peer (node 0), it
+ # should get the tx within a timeout. (Assuming that node 0
+ # announced the tx within the timeout)
+ # The timeout is the sum of
+ # * the worst case until the tx is first requested from an inbound
+ # peer, plus
+ # * the first time it is re-requested from the outbound peer, plus
+ # * 2 seconds to avoid races
+ timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + (
+ GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY)
+ self.log.info("Tx should be received at node 1 after {} seconds".format(timeout))
+ self.sync_mempools(timeout=timeout)
+
+ def test_in_flight_max(self):
+ self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format(
+ MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60))
+ txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)]
+
+ p = self.nodes[0].p2ps[0]
+
+ with mininode_lock:
+ p.tx_getdata_count = 0
+
+ p.send_message(msg_inv([CInv(t=1, h=i) for i in txids]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT)
+
+ self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request")
+ p.send_message(msg_notfound(vec=[CInv(t=1, h=txids[0])]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT + 1, timeout=10, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1)
+
+ WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL
+ self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60))
+ self.nodes[0].setmocktime(int(time.time() + WAIT_TIME))
+ wait_until(lambda: p.tx_getdata_count == MAX_GETDATA_IN_FLIGHT + 2)
+ self.nodes[0].setmocktime(0)
+
+ def run_test(self):
+ # Setup the p2p connections
+ self.peers = []
+ for node in self.nodes:
+ for i in range(NUM_INBOUND):
+ self.peers.append(node.add_p2p_connection(TestP2PConn()))
+
+ self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
+
+ # Test the in-flight max first, because we want no transactions in
+ # flight ahead of this test.
+ self.test_in_flight_max()
+
+ self.test_inv_block()
+
+ self.test_tx_requests()
+
+
+if __name__ == '__main__':
+ TxDownloadTest().main()
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index acc7cd811c..8979251a26 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -55,7 +55,7 @@ class RPCBindTest(BitcoinTestFramework):
self.nodes[0].rpchost = None
self.start_nodes([node_args])
# connect to node through non-loopback interface
- node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
+ node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, self.chain, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
node.getnetworkinfo()
self.stop_nodes()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index cdf636e200..b621081752 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -41,11 +41,11 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 3)
def run_test(self):
- min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
+ self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
# if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests.
@@ -53,13 +53,43 @@ class RawTransactionsTest(BitcoinTestFramework):
# than a minimum sized signature.
# = 2 bytes * minRelayTxFeePerByte
- feeTolerance = 2 * min_relay_tx_fee/1000
+ self.fee_tolerance = 2 * self.min_relay_tx_fee / 1000
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(121)
self.sync_all()
+ self.test_change_position()
+ self.test_simple()
+ self.test_simple_two_coins()
+ self.test_simple_two_outputs()
+ self.test_change()
+ self.test_no_change()
+ self.test_invalid_option()
+ self.test_invalid_change_address()
+ self.test_valid_change_address()
+ self.test_change_type()
+ self.test_coin_selection()
+ self.test_two_vin()
+ self.test_two_vin_two_vout()
+ self.test_invalid_input()
+ self.test_fee_p2pkh()
+ self.test_fee_p2pkh_multi_out()
+ self.test_fee_p2sh()
+ self.test_fee_4of5()
+ self.test_spend_2of2()
+ self.test_locked_wallet()
+ self.test_many_inputs_fee()
+ self.test_many_inputs_send()
+ self.test_op_return()
+ self.test_watchonly()
+ self.test_all_watched_funds()
+ self.test_option_feerate()
+ self.test_address_reuse()
+ self.test_option_subtract_fee_from_outputs()
+
+ def test_change_position(self):
# ensure that setting changePosition in fundraw with an exact match is handled properly
rawmatch = self.nodes[2].createrawtransaction([], {self.nodes[2].getnewaddress():50})
rawmatch = self.nodes[2].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]})
@@ -67,15 +97,15 @@ class RawTransactionsTest(BitcoinTestFramework):
watchonly_address = self.nodes[0].getnewaddress()
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
- watchonly_amount = Decimal(200)
+ self.watchonly_amount = Decimal(200)
self.nodes[3].importpubkey(watchonly_pubkey, "", True)
- watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount)
+ self.watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, self.watchonly_amount)
# Lock UTXO so nodes[0] doesn't accidentally spend it
- watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address)
- self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
+ self.watchonly_vout = find_vout_for_address(self.nodes[0], self.watchonly_txid, watchonly_address)
+ self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
- self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10)
+ self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), self.watchonly_amount / 10)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
@@ -84,6 +114,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_simple(self):
###############
# simple test #
###############
@@ -92,10 +123,10 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test that we have enough inputs
+ def test_simple_two_coins(self):
##############################
# simple test with two coins #
##############################
@@ -105,25 +136,11 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test if we have enough inputs
-
- ##############################
- # simple test with two coins #
- ##############################
- inputs = [ ]
- outputs = { self.nodes[0].getnewaddress() : 2.6 }
- rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
- rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
- dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_simple_two_outputs(self):
################################
# simple test with two outputs #
################################
@@ -133,7 +150,6 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -142,7 +158,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_change(self):
#########################################################################
# test a fundrawtransaction with a VIN greater than the required amount #
#########################################################################
@@ -156,6 +172,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
+ self.test_no_change_fee = fee # Use the same fee for the next tx
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -163,14 +180,14 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_no_change(self):
#####################################################################
# test a fundrawtransaction with which will not get a change output #
#####################################################################
utx = get_unspent(self.nodes[2].listunspent(), 5)
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
- outputs = { self.nodes[0].getnewaddress() : Decimal(5.0) - fee - feeTolerance }
+ outputs = {self.nodes[0].getnewaddress(): Decimal(5.0) - self.test_no_change_fee - self.fee_tolerance}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
@@ -185,7 +202,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(rawtxfund['changepos'], -1)
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_invalid_option(self):
####################################################
# test a fundrawtransaction with an invalid option #
####################################################
@@ -202,6 +219,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# reserveChangeKey was deprecated and is now removed
assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True}))
+ def test_invalid_change_address(self):
############################################################
# test a fundrawtransaction with an invalid change address #
############################################################
@@ -215,6 +233,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ def test_valid_change_address(self):
############################################################
# test a fundrawtransaction with a provided change address #
############################################################
@@ -233,6 +252,7 @@ class RawTransactionsTest(BitcoinTestFramework):
out = dec_tx['vout'][0]
assert_equal(change, out['scriptPubKey']['addresses'][0])
+ def test_change_type(self):
#########################################################
# test a fundrawtransaction with a provided change type #
#########################################################
@@ -247,6 +267,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
+ def test_coin_selection(self):
#########################################################################
# test a fundrawtransaction with a VIN smaller than the required amount #
#########################################################################
@@ -264,7 +285,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -281,7 +301,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 1)
assert_equal(len(dec_tx['vout']), 2)
-
+ def test_two_vin(self):
###########################################
# test a fundrawtransaction with two VINs #
###########################################
@@ -295,7 +315,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -315,6 +334,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params
+ def test_two_vin_two_vout(self):
#########################################################
# test a fundrawtransaction with two VINs and two vOUTs #
#########################################################
@@ -328,7 +348,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -340,16 +359,16 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 2)
assert_equal(len(dec_tx['vout']), 3)
+ def test_invalid_input(self):
##############################################
# test a fundrawtransaction with invalid vin #
##############################################
inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin!
outputs = { self.nodes[0].getnewaddress() : 1.0}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
+ def test_fee_p2pkh(self):
############################################################
#compare fee of a standard pubkeyhash transaction
inputs = []
@@ -363,9 +382,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
+ def test_fee_p2pkh_multi_out(self):
############################################################
#compare fee of a standard pubkeyhash transaction with multiple outputs
inputs = []
@@ -378,10 +398,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_p2sh(self):
############################################################
#compare fee of a 2of2 multisig p2sh transaction
@@ -405,10 +425,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_4of5(self):
############################################################
#compare fee of a standard pubkeyhash transaction
@@ -438,10 +458,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_spend_2of2(self):
############################################################
# spend a 2of2 multisig transaction over fundraw
@@ -456,7 +476,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# send 1.2 BTC to msig addr
- txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
+ self.nodes[0].sendtoaddress(mSigObj, 1.2)
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -468,7 +488,7 @@ class RawTransactionsTest(BitcoinTestFramework):
fundedTx = self.nodes[2].fundrawtransaction(rawtx)
signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
+ self.nodes[2].sendrawtransaction(signedTx['hex'])
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -476,6 +496,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())
+ def test_locked_wallet(self):
############################################################
# locked wallet test
self.nodes[1].encryptwallet("test")
@@ -485,7 +506,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -493,7 +514,7 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes,0,3)
# Again lock the watchonly UTXO or nodes[0] may spend it, because
# lockunspent is memory-only and thus lost on restart
- self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
+ self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
self.sync_all()
# drain the keypool
@@ -523,14 +544,14 @@ class RawTransactionsTest(BitcoinTestFramework):
#now we need to unlock
self.nodes[1].walletpassphrase("test", 600)
signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
+ self.nodes[1].sendrawtransaction(signedTx['hex'])
self.nodes[1].generate(1)
self.sync_all()
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())
-
+ def test_many_inputs_fee(self):
###############################################
# multiple (~19) inputs tx test | Compare fee #
###############################################
@@ -558,9 +579,9 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance*19 #~19 inputs
-
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance * 19 #~19 inputs
+ def test_many_inputs_send(self):
#############################################
# multiple (~19) inputs tx test | sign/send #
#############################################
@@ -584,12 +605,13 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawtx)
fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
+ self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
assert_equal(oldBalance+Decimal('50.19000000'), self.nodes[0].getbalance()) #0.19+block reward
+ def test_op_return(self):
#####################################################
# test fundrawtransaction with OP_RETURN and no vin #
#####################################################
@@ -606,40 +628,41 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_greater_than(len(dec_tx['vin']), 0) # at least one vin
assert_equal(len(dec_tx['vout']), 2) # one change output added
-
+ def test_watchonly(self):
##################################################
# test a fundrawtransaction using only watchonly #
##################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount / 2}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 1)
- assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
+ assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid)
assert "fee" in result.keys()
assert_greater_than(result["changepos"], -1)
+ def test_all_watched_funds(self):
###############################################################
# test fundrawtransaction using the entirety of watched funds #
###############################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
# Backward compatibility test (2nd param is includeWatching)
result = self.nodes[3].fundrawtransaction(rawtx, True)
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 2)
- assert res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid
+ assert res_dec["vin"][0]["txid"] == self.watchonly_txid or res_dec["vin"][1]["txid"] == self.watchonly_txid
assert_greater_than(result["fee"], 0)
assert_greater_than(result["changepos"], -1)
- assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10)
+ assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], self.watchonly_amount / 10)
signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
assert not signedtx["complete"]
@@ -649,6 +672,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_option_feerate(self):
#######################
# Test feeRate option #
#######################
@@ -659,18 +683,20 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {self.nodes[3].getnewaddress() : 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee)
- result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee})
- result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10*min_relay_tx_fee})
+ result = self.nodes[3].fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee)
+ result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
+ result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
+ def test_address_reuse(self):
################################
# Test no address reuse occurs #
################################
+ rawtx = self.nodes[3].createrawtransaction(inputs=[], outputs={self.nodes[3].getnewaddress(): 1})
result3 = self.nodes[3].fundrawtransaction(rawtx)
res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
changeaddress = ""
@@ -682,6 +708,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# Now the change address key should be removed from the keypool
assert changeaddress != nextaddr
+ def test_option_subtract_fee_from_outputs(self):
######################################
# Test subtractFeeFromOutputs option #
######################################
@@ -693,11 +720,11 @@ class RawTransactionsTest(BitcoinTestFramework):
outputs = {self.nodes[2].getnewaddress(): 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}),
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})]
+ result = [self.nodes[3].fundrawtransaction(rawtx), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee}),
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee, "subtractFeeFromOutputs": [0]}),]
dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]
output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index dc113da530..5a04e0c8d8 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -27,6 +27,11 @@ class PSBTTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = False
self.num_nodes = 3
+ self.extra_args = [
+ ["-walletrbf=1"],
+ ["-walletrbf=0"],
+ []
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -136,7 +141,7 @@ class PSBTTest(BitcoinTestFramework):
assert_greater_than(0.06, res["fee"])
# feeRate of 10 BTC / KB produces a total fee well above -maxtxfee
- # previously this was silenty capped at -maxtxfee
+ # previously this was silently capped at -maxtxfee
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 10})
# partially sign multisig things with node 1
@@ -207,18 +212,18 @@ class PSBTTest(BitcoinTestFramework):
# replaceable arg
block_height = self.nodes[0].getblockcount()
unspent = self.nodes[0].listunspent()[0]
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False}, False)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" not in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
- # Same construction with only locktime set
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, True)
+ # Same construction with only locktime set and RBF explicitly enabled
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True}, True)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height)
@@ -226,9 +231,16 @@ class PSBTTest(BitcoinTestFramework):
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"]:
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
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"]:
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+
# Make sure change address wallet does not have P2SH innerscript access to results in success
# when attempting BnB coin selection
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 102dd22594..8bbb3c04fa 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -20,6 +20,17 @@ import string
import configparser
import sys
+def call_with_auth(node, user, password):
+ url = urllib.parse.urlparse(node.url)
+ headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))}
+
+ conn = http.client.HTTPConnection(url.hostname, url.port)
+ conn.connect()
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ resp = conn.getresponse()
+ conn.close()
+ return resp
+
class HTTPBasicsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -28,15 +39,24 @@ class HTTPBasicsTest(BitcoinTestFramework):
def setup_chain(self):
super().setup_chain()
#Append rpcauth to bitcoin.conf before initialization
+ self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"
- rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
- rpcuser = "rpcuser=rpcuser💻"
- rpcpassword = "rpcpassword=rpcpassword🔑"
- self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
+ self.rpcuser = "rpcuser💻"
+ self.rpcpassword = "rpcpassword🔑"
+
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
gen_rpcauth = config['environment']['RPCAUTH']
+
+ # Generate RPCAUTH with specified password
+ self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
+ p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True)
+ lines = p.stdout.read().splitlines()
+ rpcauth2 = lines[1]
+
+ # Generate RPCAUTH without specifying password
+ self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True)
lines = p.stdout.read().splitlines()
rpcauth3 = lines[1]
@@ -47,160 +67,40 @@ class HTTPBasicsTest(BitcoinTestFramework):
f.write(rpcauth2+"\n")
f.write(rpcauth3+"\n")
with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f:
- f.write(rpcuser+"\n")
- f.write(rpcpassword+"\n")
-
- def run_test(self):
-
- ##################################################
- # Check correctness of the rpcauth config option #
- ##################################################
- url = urllib.parse.urlparse(self.nodes[0].url)
-
- #Old authpair
- authpair = url.username + ':' + url.password
-
- #New authpair generated via share/rpcauth tool
- password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
-
- #Second authpair with different username
- password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
- authpairnew = "rt:"+password
+ f.write("rpcuser={}\n".format(self.rpcuser))
+ f.write("rpcpassword={}\n".format(self.rpcpassword))
+ def test_auth(self, node, user, password):
self.log.info('Correct...')
- headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
+ assert_equal(200, call_with_auth(node, user, password).status)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Use new authpair to confirm both work
- self.log.info('Correct...')
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong login name with rt's password
self.log.info('Wrong...')
- authpairnew = "rtwrong:"+password
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ assert_equal(401, call_with_auth(node, user, password+'wrong').status)
- #Wrong password for rt
self.log.info('Wrong...')
- authpairnew = "rt:"+password+"wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+ assert_equal(401, call_with_auth(node, user+'wrong', password).status)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
- #Correct for rt2
- self.log.info('Correct...')
- authpairnew = "rt2:"+password2
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong password for rt2
self.log.info('Wrong...')
- authpairnew = "rt2:"+password2+"wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ assert_equal(401, call_with_auth(node, user+'wrong', password+'wrong').status)
- #Correct for randomly generated user
- self.log.info('Correct...')
- authpairnew = self.user+":"+self.password
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
+ def run_test(self):
- #Wrong password for randomly generated user
- self.log.info('Wrong...')
- authpairnew = self.user+":"+self.password+"Wrong"
- headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+ ##################################################
+ # Check correctness of the rpcauth config option #
+ ##################################################
+ url = urllib.parse.urlparse(self.nodes[0].url)
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
+ self.test_auth(self.nodes[0], url.username, url.password)
+ self.test_auth(self.nodes[0], 'rt', self.rtpassword)
+ self.test_auth(self.nodes[0], 'rt2', self.rt2password)
+ self.test_auth(self.nodes[0], self.user, self.password)
###############################################################
# Check correctness of the rpcuser/rpcpassword config options #
###############################################################
url = urllib.parse.urlparse(self.nodes[1].url)
- # rpcuser and rpcpassword authpair
- self.log.info('Correct...')
- rpcuserauthpair = "rpcuser💻:rpcpassword🔑"
-
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 200)
- conn.close()
-
- #Wrong login name with rpcuser's password
- rpcuserauthpair = "rpcuserwrong:rpcpassword"
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
- #Wrong password for rpcuser
- self.log.info('Wrong...')
- rpcuserauthpair = "rpcuser:rpcpasswordwrong"
- headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
- conn = http.client.HTTPConnection(url.hostname, url.port)
- conn.connect()
- conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
- resp = conn.getresponse()
- assert_equal(resp.status, 401)
- conn.close()
-
+ self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword)
if __name__ == '__main__':
HTTPBasicsTest ().main ()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index e454ed5987..89a5a65e64 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -44,7 +44,7 @@ BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in an
NODE_NETWORK = (1 << 0)
# NODE_GETUTXO = (1 << 1)
-NODE_BLOOM = (1 << 2)
+# NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
NODE_NETWORK_LIMITED = (1 << 10)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index cc3a4cc72a..779863df79 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -363,6 +363,7 @@ class P2PInterface(P2PConnection):
def wait_for_tx(self, txid, timeout=60):
def test_function():
+ assert self.is_connected
if not self.last_message.get('tx'):
return False
return self.last_message['tx'].tx.rehash() == txid
@@ -370,11 +371,15 @@ class P2PInterface(P2PConnection):
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_block(self, blockhash, timeout=60):
- test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_header(self, blockhash, timeout=60):
def test_function():
+ assert self.is_connected
last_headers = self.last_message.get('headers')
if not last_headers:
return False
@@ -389,7 +394,11 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block/tx has been requested."""
- test_function = lambda: self.last_message.get("getdata")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getdata")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_getheaders(self, timeout=60):
@@ -399,20 +408,30 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block header has been requested."""
- test_function = lambda: self.last_message.get("getheaders")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getheaders")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_inv(self, expected_inv, timeout=60):
"""Waits for an INV message and checks that the first inv object in the message was as expected."""
if len(expected_inv) > 1:
raise NotImplementedError("wait_for_inv() will only verify the first inv object")
- test_function = lambda: self.last_message.get("inv") and \
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("inv") and \
self.last_message["inv"].inv[0].type == expected_inv[0].type and \
self.last_message["inv"].inv[0].hash == expected_inv[0].hash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_verack(self, timeout=60):
- test_function = lambda: self.message_count["verack"]
+ def test_function():
+ return self.message_count["verack"]
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message sending helper functions
@@ -424,7 +443,11 @@ class P2PInterface(P2PConnection):
# Sync up with the node
def sync_with_ping(self, timeout=60):
self.send_message(msg_ping(nonce=self.ping_counter))
- test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index aa674f9ebe..9aff08fdc7 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -91,6 +91,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def __init__(self):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
+ self.chain = 'regtest'
self.setup_clean_chain = False
self.nodes = []
self.network_thread = None
@@ -192,18 +193,18 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.setup_network()
self.run_test()
success = TestStatus.PASSED
- except JSONRPCException as e:
+ except JSONRPCException:
self.log.exception("JSONRPC error")
except SkipTest as e:
self.log.warning("Test Skipped: %s" % e.message)
success = TestStatus.SKIPPED
- except AssertionError as e:
+ except AssertionError:
self.log.exception("Assertion failed")
- except KeyError as e:
+ except KeyError:
self.log.exception("Key error")
- except Exception as e:
+ except Exception:
self.log.exception("Unexpected exception caught during testing")
- except KeyboardInterrupt as e:
+ except KeyboardInterrupt:
self.log.warning("Exiting after keyboard interrupt")
if success == TestStatus.FAILED and self.options.pdbonfailure:
@@ -342,6 +343,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes.append(TestNode(
i,
get_datadir_path(self.options.tmpdir, i),
+ chain=self.chain,
rpchost=rpchost,
timewait=self.rpc_timeout,
bitcoind=binary[i],
@@ -477,11 +479,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not os.path.isdir(cache_node_dir):
self.log.debug("Creating cache directory {}".format(cache_node_dir))
- initialize_datadir(self.options.cachedir, CACHE_NODE_ID)
+ initialize_datadir(self.options.cachedir, CACHE_NODE_ID, self.chain)
self.nodes.append(
TestNode(
CACHE_NODE_ID,
cache_node_dir,
+ chain=self.chain,
extra_conf=["bind=127.0.0.1"],
extra_args=['-disablewallet'],
rpchost=None,
@@ -515,7 +518,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes = []
def cache_path(*paths):
- return os.path.join(cache_node_dir, "regtest", *paths)
+ return os.path.join(cache_node_dir, self.chain, *paths)
os.rmdir(cache_path('wallets')) # Remove empty wallets dir
for entry in os.listdir(cache_path()):
@@ -526,7 +529,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.log.debug("Copy cache directory {} to node {}".format(cache_node_dir, i))
to_dir = get_datadir_path(self.options.tmpdir, i)
shutil.copytree(cache_node_dir, to_dir)
- initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf
+ initialize_datadir(self.options.tmpdir, i, self.chain) # Overwrite port/rpcport in bitcoin.conf
def _initialize_chain_clean(self):
"""Initialize empty blockchain for use by the test.
@@ -534,7 +537,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
Create an empty blockchain and num_nodes wallets.
Useful if a test case wants complete control over initialization."""
for i in range(self.num_nodes):
- initialize_datadir(self.options.tmpdir, i)
+ initialize_datadir(self.options.tmpdir, i, self.chain)
def skip_if_no_py3_zmq(self):
"""Attempt to import the zmq package and skip the test if the import fails."""
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 3311377090..cac5281764 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -59,7 +59,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, *, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
+ def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -70,6 +70,7 @@ class TestNode():
self.datadir = datadir
self.stdout_dir = os.path.join(self.datadir, "stdout")
self.stderr_dir = os.path.join(self.datadir, "stderr")
+ self.chain = chain
self.rpchost = rpchost
self.rpc_timeout = timewait
self.binary = bitcoind
@@ -197,7 +198,7 @@ class TestNode():
# Delete any existing cookie file -- if such a file exists (eg due to
# unclean shutdown), it will get overwritten anyway by bitcoind, and
# potentially interfere with our attempt to authenticate
- delete_cookie_file(self.datadir)
+ delete_cookie_file(self.datadir, self.chain)
# add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal
subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")
@@ -219,7 +220,7 @@ class TestNode():
raise FailedToStartError(self._node_msg(
'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
try:
- rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
+ rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.chain, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
self.log.debug("RPC successfully started")
@@ -306,7 +307,7 @@ class TestNode():
@contextlib.contextmanager
def assert_debug_log(self, expected_msgs):
- debug_log = os.path.join(self.datadir, 'regtest', 'debug.log')
+ debug_log = os.path.join(self.datadir, self.chain, 'debug.log')
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
prev_size = dl.tell()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 26215083fb..8730157c74 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -19,6 +19,7 @@ import time
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
+from io import BytesIO
logger = logging.getLogger("TestFramework.utils")
@@ -270,8 +271,8 @@ def p2p_port(n):
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
-def rpc_url(datadir, i, rpchost=None):
- rpc_u, rpc_p = get_auth_cookie(datadir)
+def rpc_url(datadir, i, chain, rpchost):
+ rpc_u, rpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
port = rpc_port(i)
if rpchost:
@@ -285,13 +286,13 @@ def rpc_url(datadir, i, rpchost=None):
# Node functions
################
-def initialize_datadir(dirname, n):
+def initialize_datadir(dirname, n, chain):
datadir = get_datadir_path(dirname, n)
if not os.path.isdir(datadir):
os.makedirs(datadir)
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
- f.write("regtest=1\n")
- f.write("[regtest]\n")
+ f.write("{}=1\n".format(chain))
+ f.write("[{}]\n".format(chain))
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("server=1\n")
@@ -311,7 +312,7 @@ def append_config(datadir, options):
for option in options:
f.write(option + "\n")
-def get_auth_cookie(datadir):
+def get_auth_cookie(datadir, chain):
user = None
password = None
if os.path.isfile(os.path.join(datadir, "bitcoin.conf")):
@@ -324,7 +325,7 @@ def get_auth_cookie(datadir):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
try:
- with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
+ with open(os.path.join(datadir, chain, ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
user = split_userpass[0]
@@ -336,10 +337,10 @@ def get_auth_cookie(datadir):
return user, password
# If a cookie file exists in the given datadir, delete it.
-def delete_cookie_file(datadir):
- if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
+def delete_cookie_file(datadir, chain):
+ if os.path.isfile(os.path.join(datadir, chain, ".cookie")):
logger.debug("Deleting leftover cookie file")
- os.remove(os.path.join(datadir, "regtest", ".cookie"))
+ os.remove(os.path.join(datadir, chain, ".cookie"))
def get_bip9_status(node, key):
info = node.getblockchaininfo()
@@ -515,14 +516,13 @@ def gen_return_txouts():
for i in range(512):
script_pubkey = script_pubkey + "01"
# concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- txouts = "81"
+ txouts = []
+ from .messages import CTxOut
+ txout = CTxOut()
+ txout.nValue = 0
+ txout.scriptPubKey = hex_str_to_bytes(script_pubkey)
for k in range(128):
- # add txout value
- txouts = txouts + "0000000000000000"
- # add length of script_pubkey
- txouts = txouts + "fd0402"
- # add script_pubkey
- txouts = txouts + script_pubkey
+ txouts.append(txout)
return txouts
# Create a spend of each passed-in utxo, splicing in "txouts" to each raw
@@ -530,6 +530,7 @@ def gen_return_txouts():
def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
addr = node.getnewaddress()
txids = []
+ from .messages import CTransaction
for _ in range(num):
t = utxos.pop()
inputs = [{"txid": t["txid"], "vout": t["vout"]}]
@@ -537,9 +538,11 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
change = t['amount'] - fee
outputs[addr] = satoshi_round(change)
rawtx = node.createrawtransaction(inputs, outputs)
- newtx = rawtx[0:92]
- newtx = newtx + txouts
- newtx = newtx + rawtx[94:]
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex_str_to_bytes(rawtx)))
+ for txout in txouts:
+ tx.vout.append(txout)
+ newtx = tx.serialize().hex()
signresult = node.signrawtransactionwithwallet(newtx, None, "NONE")
txid = node.sendrawtransaction(signresult["hex"], 0)
txids.append(txid)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 462547f44f..0e0e6ed18e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -91,6 +91,7 @@ BASE_SCRIPTS = [
'wallet_labels.py',
'p2p_segwit.py',
'p2p_timeouts.py',
+ 'p2p_tx_download.py',
'wallet_dump.py',
'wallet_listtransactions.py',
# vv Tests less than 60s vv
@@ -107,6 +108,7 @@ BASE_SCRIPTS = [
'feature_bip68_sequence.py',
'p2p_feefilter.py',
'feature_reindex.py',
+ 'feature_abortnode.py',
# vv Tests less than 30s vv
'wallet_keypool_topup.py',
'interface_zmq.py',
@@ -157,6 +159,7 @@ BASE_SCRIPTS = [
'rpc_invalidateblock.py',
'feature_rbf.py',
'mempool_packages.py',
+ 'mempool_package_onemore.py',
'rpc_createmultisig.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
@@ -173,6 +176,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --nonloopback',
'mining_basic.py',
'wallet_bumpfee.py',
+ 'wallet_bumpfee_totalfee_deprecation.py',
'rpc_named_arguments.py',
'wallet_listsinceblock.py',
'p2p_leak.py',
@@ -234,6 +238,7 @@ def main():
parser.add_argument('--quiet', '-q', action='store_true', help='only print dots, results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
+ parser.add_argument('--filter', help='filter scripts to run by regular expression')
args, unknown_args = parser.parse_known_args()
# args to be passed on always start with two dashes; tests are the remaining unknown args
@@ -269,11 +274,22 @@ def main():
test_list = []
if tests:
# Individual tests have been specified. Run specified tests that exist
- # in the ALL_SCRIPTS list. Accept the name with or without .py extension.
- tests = [test + ".py" if ".py" not in test else test for test in tests]
+ # in the ALL_SCRIPTS list. Accept names with or without a .py extension.
+ # Specified tests can contain wildcards, but in that case the supplied
+ # paths should be coherent, e.g. the same path as that provided to call
+ # test_runner.py. Examples:
+ # `test/functional/test_runner.py test/functional/wallet*`
+ # `test/functional/test_runner.py ./test/functional/wallet*`
+ # `test_runner.py wallet*`
+ # but not:
+ # `test/functional/test_runner.py wallet*`
+ # Multiple wildcards can be passed:
+ # `test_runner.py tool* mempool*`
for test in tests:
- if test in ALL_SCRIPTS:
- test_list.append(test)
+ script = test.split("/")[-1]
+ script = script + ".py" if ".py" not in script else script
+ if script in ALL_SCRIPTS:
+ test_list.append(script)
else:
print("{}WARNING!{} Test '{}' not found in full test list.".format(BOLD[1], BOLD[0], test))
elif args.extended:
@@ -294,6 +310,9 @@ def main():
if not exclude_list:
print("{}WARNING!{} Test '{}' not found in current test list.".format(BOLD[1], BOLD[0], exclude_test))
+ if args.filter:
+ test_list = list(filter(re.compile(args.filter).search, test_list))
+
if not test_list:
print("No valid test scripts specified. Check that your test is in one "
"of the test lists in test_runner.py, or run test_runner.py with no arguments to run all tests")
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index daa834b5b8..34e84fcf55 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -20,6 +20,9 @@ from test_framework.util import (
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.extra_args = [[
+ "-acceptnonstdtxn=1",
+ ]] * self.num_nodes
self.setup_clean_chain = True
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 1fe029a6fb..bfc01e3f5e 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -37,6 +37,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.extra_args = [[
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
+ "-deprecatedrpc=totalFee",
] for i in range(self.num_nodes)]
def skip_test_if_missing_module(self):
@@ -80,6 +81,7 @@ class BumpFeeTest(BitcoinTestFramework):
test_bumpfee_metadata(rbf_node, dest_address)
test_locked_wallet_fails(rbf_node, dest_address)
test_change_script_match(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)
@@ -248,6 +250,15 @@ def test_settxfee(rbf_node, dest_address):
rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee
+def test_maxtxfee_fails(test, rbf_node, dest_address):
+ test.restart_node(1, ['-maxtxfee=0.00003'] + test.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])
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+
+
def test_rebumping(rbf_node, dest_address):
# check that re-bumping the original tx fails, but bumping the bumper succeeds
rbfid = spend_one_input(rbf_node, dest_address)
diff --git a/test/functional/wallet_bumpfee_totalfee_deprecation.py b/test/functional/wallet_bumpfee_totalfee_deprecation.py
new file mode 100755
index 0000000000..b8e097c32e
--- /dev/null
+++ b/test/functional/wallet_bumpfee_totalfee_deprecation.py
@@ -0,0 +1,54 @@
+#!/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 deprecation of passing `totalFee` to the bumpfee RPC."""
+from decimal import Decimal
+
+from test_framework.messages import BIP125_SEQUENCE_NUMBER
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
+
+class BumpFeeWithTotalFeeArgumentDeprecationTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.extra_args = [[
+ "-walletrbf={}".format(i),
+ "-mintxfee=0.00002",
+ ] for i in range(self.num_nodes)]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ peer_node, rbf_node = self.nodes
+ peer_node.generate(110)
+ self.sync_all()
+ peer_node.sendtoaddress(rbf_node.getnewaddress(), 0.001)
+ self.sync_all()
+ peer_node.generate(1)
+ self.sync_all()
+ rbfid = spend_one_input(rbf_node, peer_node.getnewaddress())
+
+ self.log.info("Testing bumpfee with totalFee argument raises RPC error with deprecation message")
+ assert_raises_rpc_error(
+ -8,
+ "totalFee argument has been deprecated and will be removed in 0.20. " +
+ "Please use -deprecatedrpc=totalFee to continue using this argument until removal.",
+ rbf_node.bumpfee, rbfid, {"totalFee": 2000})
+
+ self.log.info("Testing bumpfee without totalFee argument does not raise")
+ rbf_node.bumpfee(rbfid)
+
+def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
+ tx_input = dict(sequence=BIP125_SEQUENCE_NUMBER,
+ **next(u for u in node.listunspent() if u["amount"] == Decimal("0.00100000")))
+ destinations = {dest_address: Decimal("0.00050000")}
+ destinations[node.getrawchangeaddress()] = change_size
+ rawtx = node.createrawtransaction([tx_input], destinations)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx["hex"])
+ return txid
+
+if __name__ == "__main__":
+ BumpFeeWithTotalFeeArgumentDeprecationTest().main()
diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py
index 0b584a0bb2..330de8b0fc 100755
--- a/test/functional/wallet_create_tx.py
+++ b/test/functional/wallet_create_tx.py
@@ -6,6 +6,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_raises_rpc_error,
)
from test_framework.blocktools import (
TIME_GENESIS_BLOCK,
@@ -26,6 +27,10 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.nodes[0].generate(200)
self.nodes[0].setmocktime(0)
+ self.test_anti_fee_sniping()
+ self.test_tx_size_too_large()
+
+ def test_anti_fee_sniping(self):
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
@@ -38,6 +43,40 @@ class CreateTxWalletTest(BitcoinTestFramework):
tx = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(txid)['hex'])
assert 0 < tx['locktime'] <= 201
+ def test_tx_size_too_large(self):
+ # More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
+ outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for i in range(400)}
+ raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
+
+ for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
+ self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
+ self.restart_node(0, extra_args=[fee_setting])
+ assert_raises_rpc_error(
+ -6,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
+ )
+ assert_raises_rpc_error(
+ -4,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
+ )
+
+ self.log.info('Check maxtxfee in combination with settxfee')
+ self.restart_node(0)
+ self.nodes[0].settxfee(0.01)
+ assert_raises_rpc_error(
+ -6,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
+ )
+ assert_raises_rpc_error(
+ -4,
+ "Fee exceeds maximum configured by -maxtxfee",
+ lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
+ )
+ self.nodes[0].settxfee(0)
+
if __name__ == '__main__':
CreateTxWalletTest().main()
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index c17949a2f6..e302e499f4 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -116,8 +116,20 @@ class CreateWalletTest(BitcoinTestFramework):
walletinfo = w6.getwalletinfo()
assert_equal(walletinfo['keypoolsize'], 1)
assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
- # Empty passphrase, error
- assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
+ # Allow empty passphrase, but there should be a warning
+ resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
+ assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.')
+ w7 = node.get_wallet_rpc('w7')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+
+ self.log.info('Test making a wallet with avoid reuse flag')
+ self.nodes[0].createwallet('w8', False, False, '', True) # Use positional arguments to check for bug where avoid_reuse could not be set for wallets without needing them to be encrypted
+ w8 = node.get_wallet_rpc('w8')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+ assert_equal(w8.getwalletinfo()["avoid_reuse"], True)
+
+ self.log.info('Using a passphrase with private keys disabled returns error')
+ assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w9', disable_private_keys=True, passphrase='thisisapassphrase')
if __name__ == '__main__':
CreateWalletTest().main()
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index c514b7e0b4..fbcb4e75ba 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -49,7 +49,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_equal(privkey, self.nodes[0].dumpprivkey(address))
# Check that the timeout is right
- time.sleep(2)
+ time.sleep(3)
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Test wrong passphrase
diff --git a/test/lint/extended-lint-all.sh b/test/lint/extended-lint-all.sh
new file mode 100755
index 0000000000..65c51e02f5
--- /dev/null
+++ b/test/lint/extended-lint-all.sh
@@ -0,0 +1,26 @@
+#!/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.
+#
+# This script runs all contrib/devtools/extended-lint-*.sh files, and fails if
+# any exit with a non-zero status code.
+
+# This script is intentionally locale dependent by not setting "export LC_ALL=C"
+# in order to allow for the executed lint scripts to opt in or opt out of locale
+# dependence themselves.
+
+set -u
+
+SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
+LINTALL=$(basename "${BASH_SOURCE[0]}")
+
+for f in "${SCRIPTDIR}"/extended-lint-*.sh; do
+ if [ "$(basename "$f")" != "$LINTALL" ]; then
+ if ! "$f"; then
+ echo "^---- failure generated from $f"
+ exit 1
+ fi
+ fi
+done
diff --git a/test/lint/extended-lint-cppcheck.sh b/test/lint/extended-lint-cppcheck.sh
new file mode 100755
index 0000000000..47df25ba6b
--- /dev/null
+++ b/test/lint/extended-lint-cppcheck.sh
@@ -0,0 +1,80 @@
+#!/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
+
+ENABLED_CHECKS=(
+ "Class '.*' has a constructor with 1 argument that is not explicit."
+ "Struct '.*' has a constructor with 1 argument that is not explicit."
+)
+
+IGNORED_WARNINGS=(
+ "src/arith_uint256.h:.* Class 'arith_uint256' has a constructor with 1 argument that is not explicit."
+ "src/arith_uint256.h:.* Class 'base_uint < 256 >' has a constructor with 1 argument that is not explicit."
+ "src/arith_uint256.h:.* Class 'base_uint' has a constructor with 1 argument that is not explicit."
+ "src/coins.h:.* Class 'CCoinsViewBacked' has a constructor with 1 argument that is not explicit."
+ "src/coins.h:.* Class 'CCoinsViewCache' has a constructor with 1 argument that is not explicit."
+ "src/coins.h:.* Class 'CCoinsViewCursor' has a constructor with 1 argument that is not explicit."
+ "src/net.h:.* Class 'CNetMessage' has a constructor with 1 argument that is not explicit."
+ "src/policy/feerate.h:.* Class 'CFeeRate' has a constructor with 1 argument that is not explicit."
+ "src/prevector.h:.* Class 'const_iterator' has a constructor with 1 argument that is not explicit."
+ "src/prevector.h:.* Class 'const_reverse_iterator' has a constructor with 1 argument that is not explicit."
+ "src/prevector.h:.* Class 'iterator' has a constructor with 1 argument that is not explicit."
+ "src/prevector.h:.* Class 'reverse_iterator' has a constructor with 1 argument that is not explicit."
+ "src/primitives/block.h:.* Class 'CBlock' has a constructor with 1 argument that is not explicit."
+ "src/primitives/transaction.h:.* Class 'CTransaction' has a constructor with 1 argument that is not explicit."
+ "src/protocol.h:.* Class 'CMessageHeader' has a constructor with 1 argument that is not explicit."
+ "src/qt/guiutil.h:.* Class 'ItemDelegate' has a constructor with 1 argument that is not explicit."
+ "src/rpc/util.h:.* Struct 'RPCResults' has a constructor with 1 argument that is not explicit."
+ "src/rpc/util.h:.* style: Struct 'UniValueType' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'AddressDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'ComboDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'ConstPubkeyProvider' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'PKDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'PKHDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'RawDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'SHDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'WPKHDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/descriptor.cpp:.* Class 'WSHDescriptor' has a constructor with 1 argument that is not explicit."
+ "src/script/script.h:.* Class 'CScript' has a constructor with 1 argument that is not explicit."
+ "src/script/standard.h:.* Class 'CScriptID' has a constructor with 1 argument that is not explicit."
+ "src/support/allocators/secure.h:.* Struct 'secure_allocator < char >' has a constructor with 1 argument that is not explicit."
+ "src/support/allocators/secure.h:.* Struct 'secure_allocator < RNGState >' has a constructor with 1 argument that is not explicit."
+ "src/support/allocators/secure.h:.* Struct 'secure_allocator < unsigned char >' has a constructor with 1 argument that is not explicit."
+ "src/support/allocators/zeroafterfree.h:.* Struct 'zero_after_free_allocator < char >' has a constructor with 1 argument that is not explicit."
+ "src/test/checkqueue_tests.cpp:.* Struct 'FailingCheck' has a constructor with 1 argument that is not explicit."
+ "src/test/checkqueue_tests.cpp:.* Struct 'MemoryCheck' has a constructor with 1 argument that is not explicit."
+ "src/test/checkqueue_tests.cpp:.* Struct 'UniqueCheck' has a constructor with 1 argument that is not explicit."
+ "src/wallet/db.h:.* Class 'BerkeleyEnvironment' has a constructor with 1 argument that is not explicit."
+)
+
+if ! command -v cppcheck > /dev/null; then
+ echo "Skipping cppcheck linting since cppcheck is not installed. Install by running \"apt install cppcheck\""
+ exit 0
+fi
+
+function join_array {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
+
+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/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 | \
+ grep -E "${ENABLED_CHECKS_REGEXP}" | \
+ grep -vE "${IGNORED_WARNINGS_REGEXP}")
+if [[ ${WARNINGS} != "" ]]; then
+ echo "${WARNINGS}"
+ echo
+ echo "Advice not applicable in this specific case? Add an exception by updating"
+ echo "IGNORED_WARNINGS in $0"
+ # Uncomment to enforce the developer note policy "By default, declare single-argument constructors `explicit`"
+ # exit 1
+fi
+exit 0
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 99b0eaa38e..47ad896589 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -16,7 +16,7 @@ FALSE_POSITIVES = [
("src/dbwrapper.cpp", "vsnprintf(p, limit - p, format, backup_ap)"),
("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"),
("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"),
- ("src/util/system.cpp", "strprintf(_(COPYRIGHT_HOLDERS), COPYRIGHT_HOLDERS_SUBSTITUTION)"),
+ ("src/util/system.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
("src/wallet/wallet.h", "WalletLogPrintf(std::string fmt, Params... parameters)"),
("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"),
("src/logging.h", "LogPrintf(const char* fmt, const Args&... args)"),