aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml15
-rw-r--r--.gitignore4
-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/lint_06_script.sh2
-rwxr-xr-x.travis/test_04_install.sh3
-rwxr-xr-x.travis/test_05_before_script.sh5
-rw-r--r--CONTRIBUTING.md24
-rwxr-xr-xautogen.sh4
-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.vcxproj5
-rw-r--r--build_msvc/bitcoin-cli/bitcoin-cli.vcxproj1
-rw-r--r--build_msvc/bitcoin-tx/bitcoin-tx.vcxproj1
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj1
-rw-r--r--build_msvc/bitcoin_config.h10
-rw-r--r--build_msvc/bitcoind/bitcoind.vcxproj27
-rw-r--r--build_msvc/common.init.vcxproj2
-rw-r--r--build_msvc/common.vcxproj12
-rw-r--r--build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in1
-rw-r--r--build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in1
-rw-r--r--build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj1
-rw-r--r--build_msvc/libleveldb/libleveldb.vcxproj1
-rw-r--r--build_msvc/libsecp256k1/libsecp256k1.vcxproj1
-rw-r--r--build_msvc/libunivalue/libunivalue.vcxproj1
-rw-r--r--build_msvc/msbuild/tasks/replaceinfile.targets35
-rw-r--r--build_msvc/test_bitcoin/test_bitcoin.vcxproj1
-rw-r--r--build_msvc/testconsensus/testconsensus.vcxproj1
-rw-r--r--configure.ac79
-rwxr-xr-xcontrib/devtools/copyright_header.py4
-rwxr-xr-xcontrib/devtools/gen-manpages.sh2
-rwxr-xr-xcontrib/devtools/github-merge.py71
-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
-rwxr-xr-xcontrib/install_db4.sh6
-rwxr-xr-xcontrib/macdeploy/detached-sig-apply.sh6
-rwxr-xr-xcontrib/macdeploy/detached-sig-create.sh14
-rw-r--r--contrib/qos/tc.sh4
-rwxr-xr-xcontrib/verify-commits/gpg.sh4
-rw-r--r--contrib/verifybinaries/README.md2
-rwxr-xr-xcontrib/verifybinaries/verify.sh15
-rwxr-xr-xcontrib/windeploy/detached-sig-create.sh4
-rw-r--r--depends/packages/dbus.mk27
-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/rapidcheck.mk4
-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/Doxyfile.in2
-rw-r--r--doc/README.md1
-rw-r--r--doc/bips.md4
-rw-r--r--doc/bitcoin-conf.md15
-rw-r--r--doc/build-osx.md263
-rw-r--r--doc/dependencies.md14
-rw-r--r--doc/developer-notes.md2
-rw-r--r--doc/fuzzing.md4
-rw-r--r--doc/reduce-memory.md49
-rw-r--r--doc/release-notes-0.18.1-16257.md6
-rw-r--r--doc/release-notes-15427.md9
-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
-rwxr-xr-xshare/genbuild.sh4
-rwxr-xr-xshare/qt/extract_strings_qt.py3
-rw-r--r--src/Makefile.am17
-rw-r--r--src/Makefile.bench.include8
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/addrdb.cpp20
-rw-r--r--src/banman.cpp3
-rw-r--r--src/bench/ccoins_caching.cpp6
-rw-r--r--src/bench/chacha_poly_aead.cpp123
-rw-r--r--src/bench/checkblock.cpp17
-rw-r--r--src/bench/data.cpp14
-rw-r--r--src/bench/data.h19
-rw-r--r--src/bench/rpc_blockchain.cpp33
-rw-r--r--src/bitcoin-cli.cpp5
-rw-r--r--src/bitcoin-tx.cpp12
-rw-r--r--src/bitcoin-wallet.cpp4
-rw-r--r--src/bitcoind.cpp31
-rw-r--r--src/chainparams.cpp17
-rw-r--r--src/chainparams.h11
-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.cpp187
-rw-r--r--src/interfaces/chain.cpp6
-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/logging.cpp8
-rw-r--r--src/logging.h2
-rw-r--r--src/net.cpp65
-rw-r--r--src/net_processing.cpp18
-rw-r--r--src/net_processing.h4
-rw-r--r--src/netbase.cpp224
-rw-r--r--src/node/transaction.cpp6
-rw-r--r--src/noui.cpp36
-rw-r--r--src/outputtype.cpp6
-rw-r--r--src/outputtype.h4
-rw-r--r--src/policy/fees.h1
-rw-r--r--src/protocol.h5
-rw-r--r--src/psbt.cpp19
-rw-r--r--src/psbt.h7
-rw-r--r--src/qt/addresstablemodel.cpp16
-rw-r--r--src/qt/bitcoin.cpp75
-rw-r--r--src/qt/bitcoin.h1
-rw-r--r--src/qt/bitcoingui.cpp59
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/bitcoinstrings.cpp17
-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/intro.cpp12
-rw-r--r--src/qt/locale/bitcoin_en.ts455
-rw-r--r--src/qt/main.cpp2
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/paymentserver.cpp8
-rwxr-xr-xsrc/qt/res/movies/makespinner.sh2
-rw-r--r--src/qt/rpcconsole.cpp9
-rw-r--r--src/qt/rpcconsole.h1
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/splashscreen.cpp10
-rw-r--r--src/qt/test/addressbooktests.cpp1
-rw-r--r--src/qt/test/apptests.cpp10
-rw-r--r--src/qt/test/paymentservertests.cpp4
-rw-r--r--src/qt/test/rpcnestedtests.cpp1
-rw-r--r--src/qt/test/rpcnestedtests.h3
-rw-r--r--src/qt/test/test_main.cpp25
-rw-r--r--src/qt/test/wallettests.cpp3
-rw-r--r--src/qt/trafficgraphwidget.cpp1
-rw-r--r--src/qt/transactiontablemodel.cpp15
-rw-r--r--src/qt/utilitydialog.cpp6
-rw-r--r--src/qt/walletcontroller.cpp16
-rw-r--r--src/qt/walletcontroller.h10
-rw-r--r--src/qt/walletmodel.cpp34
-rw-r--r--src/qt/walletmodeltransaction.cpp6
-rw-r--r--src/qt/walletmodeltransaction.h5
-rw-r--r--src/rest.cpp1
-rw-r--r--src/rpc/blockchain.cpp185
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp51
-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.cpp151
-rw-r--r--src/rpc/rawtransaction_util.cpp10
-rw-r--r--src/rpc/rawtransaction_util.h4
-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.cpp50
-rw-r--r--src/rpc/server.h18
-rw-r--r--src/rpc/util.cpp46
-rw-r--r--src/rpc/util.h23
-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.cpp70
-rw-r--r--src/script/sign.h73
-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/support/cleanse.cpp36
-rw-r--r--src/support/cleanse.h3
-rw-r--r--src/test/addrman_tests.cpp9
-rw-r--r--src/test/allocator_tests.cpp2
-rw-r--r--src/test/arith_uint256_tests.cpp1
-rw-r--r--src/test/bip32_tests.cpp3
-rw-r--r--src/test/blockchain_tests.cpp1
-rw-r--r--src/test/blockencodings_tests.cpp4
-rw-r--r--src/test/blockfilter_index_tests.cpp3
-rw-r--r--src/test/checkqueue_tests.cpp3
-rw-r--r--src/test/coins_tests.cpp2
-rw-r--r--src/test/crypto_tests.cpp126
-rw-r--r--src/test/dbwrapper_tests.cpp16
-rw-r--r--src/test/denialofservice_tests.cpp7
-rw-r--r--src/test/flatfile_tests.cpp11
-rw-r--r--src/test/fs_tests.cpp3
-rw-r--r--src/test/fuzz/deserialize.cpp5
-rw-r--r--src/test/fuzz/fuzz.h1
-rw-r--r--src/test/fuzz/script_flags.cpp1
-rw-r--r--src/test/hash_tests.cpp3
-rw-r--r--src/test/key_properties.cpp4
-rw-r--r--src/test/key_tests.cpp1
-rw-r--r--src/test/mempool_tests.cpp24
-rw-r--r--src/test/miner_tests.cpp4
-rw-r--r--src/test/multisig_tests.cpp5
-rw-r--r--src/test/net_tests.cpp7
-rw-r--r--src/test/policyestimator_tests.cpp1
-rw-r--r--src/test/pow_tests.cpp1
-rw-r--r--src/test/raii_event_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp2
-rw-r--r--src/test/script_p2sh_tests.cpp8
-rw-r--r--src/test/script_standard_tests.cpp3
-rw-r--r--src/test/script_tests.cpp6
-rw-r--r--src/test/scriptnum10.h1
-rw-r--r--src/test/serialize_tests.cpp1
-rw-r--r--src/test/setup_common.cpp31
-rw-r--r--src/test/setup_common.h9
-rw-r--r--src/test/sigopcount_tests.cpp2
-rw-r--r--src/test/streams_tests.cpp1
-rw-r--r--src/test/transaction_tests.cpp15
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp8
-rw-r--r--src/test/uint256_tests.cpp6
-rw-r--r--src/test/util.cpp13
-rw-r--r--src/test/util_tests.cpp7
-rw-r--r--src/test/validation_block_tests.cpp155
-rw-r--r--src/timedata.cpp3
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txdb.cpp11
-rw-r--r--src/txmempool.cpp20
-rw-r--r--src/txmempool.h41
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/uint256.cpp15
-rw-r--r--src/util/error.cpp8
-rw-r--r--src/util/error.h1
-rw-r--r--src/util/fees.cpp1
-rw-r--r--src/util/system.cpp32
-rw-r--r--src/util/system.h21
-rw-r--r--src/util/translation.h42
-rw-r--r--src/validation.cpp281
-rw-r--r--src/validation.h161
-rw-r--r--src/validationinterface.h1
-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/fees.cpp9
-rw-r--r--src/wallet/init.cpp9
-rw-r--r--src/wallet/ismine.cpp1
-rw-r--r--src/wallet/load.cpp11
-rw-r--r--src/wallet/psbtwallet.cpp11
-rw-r--r--src/wallet/rpcdump.cpp154
-rw-r--r--src/wallet/rpcwallet.cpp361
-rw-r--r--src/wallet/test/db_tests.cpp12
-rw-r--r--src/wallet/test/init_test_fixture.cpp5
-rw-r--r--src/wallet/test/init_tests.cpp1
-rw-r--r--src/wallet/test/ismine_tests.cpp1
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp18
-rw-r--r--src/wallet/wallet.cpp596
-rw-r--r--src/wallet/wallet.h321
-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/example_test.py5
-rwxr-xr-xtest/functional/feature_abortnode.py48
-rwxr-xr-xtest/functional/feature_bip68_sequence.py5
-rwxr-xr-xtest/functional/feature_block.py10
-rwxr-xr-xtest/functional/feature_cltv.py24
-rwxr-xr-xtest/functional/feature_config_args.py17
-rwxr-xr-xtest/functional/feature_dbcrash.py2
-rwxr-xr-xtest/functional/feature_dersig.py19
-rwxr-xr-xtest/functional/feature_includeconf.py6
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py2
-rwxr-xr-xtest/functional/feature_rbf.py2
-rwxr-xr-xtest/functional/feature_segwit.py3
-rwxr-xr-xtest/functional/interface_rpc.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/rpc_createmultisig.py5
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py158
-rwxr-xr-xtest/functional/rpc_psbt.py40
-rwxr-xr-xtest/functional/rpc_users.py182
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py35
-rw-r--r--test/functional/test_framework/util.py23
-rwxr-xr-xtest/functional/test_runner.py26
-rwxr-xr-xtest/functional/tool_wallet.py128
-rwxr-xr-xtest/functional/wallet_balance.py2
-rwxr-xr-xtest/functional/wallet_basic.py3
-rwxr-xr-xtest/functional/wallet_bumpfee.py15
-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/functional/wallet_resendwallettransactions.py3
-rwxr-xr-xtest/lint/commit-script-check.sh8
-rwxr-xr-xtest/lint/extended-lint-all.sh26
-rwxr-xr-xtest/lint/extended-lint-cppcheck.sh80
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh2
-rwxr-xr-xtest/lint/lint-format-strings.py3
-rwxr-xr-xtest/lint/lint-logs.sh1
-rwxr-xr-xtest/lint/lint-shell.sh16
321 files changed, 6011 insertions, 3693 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 0c43e61592..ac17e2eeb6 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -33,23 +33,10 @@ 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
-before_test:
-- ps: ${conf_ini} = (Get-Content([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini.in")))
-- ps: ${conf_ini} = ${conf_ini}.Replace("@PACKAGE_NAME@", "Bitcoin Core")
-- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER})
-- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER})
-- ps: ${conf_ini} = ${conf_ini}.Replace("@EXEEXT@", ".exe")
-- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_WALLET_TRUE@", "")
-- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIN_CLI_TRUE@", "")
-- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIND_TRUE@", "")
-- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_ZMQ_TRUE@", "")
-- ps: ${utf8} = New-Object System.Text.UTF8Encoding ${false}
-- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), ${conf_ini}, ${utf8})'
-- ps: move "build_msvc\${env:PLATFORM}\${env:CONFIGURATION}\*.exe" src
test_script:
- cmd: src\test_bitcoin.exe -k stdout -e stdout 2> NUL
- cmd: src\bench_bitcoin.exe -evals=1 -scaling=0 > NUL
diff --git a/.gitignore b/.gitignore
index 3ebf00ed52..cec1fb3dec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.tar.gz
*.exe
+*.pdb
src/bitcoin
src/bitcoind
src/bitcoin-cli
@@ -76,7 +77,7 @@ src/qt/bitcoin-qt.includes
# Only ignore unexpected patches
*.patch
-!depends/patches/*.patch
+!depends/patches/**/*.patch
#libtool object files
*.lo
@@ -85,6 +86,7 @@ src/qt/bitcoin-qt.includes
# Compilation and Qt preprocessor part
*.qm
Makefile
+!depends/Makefile
bitcoin-qt
Bitcoin-Qt.app
background.tiff*
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/lint_06_script.sh b/.travis/lint_06_script.sh
index eeebc80ec0..c7dea599dc 100755
--- a/.travis/lint_06_script.sh
+++ b/.travis/lint_06_script.sh
@@ -18,7 +18,7 @@ test/lint/check-doc.py
test/lint/check-rpc-mappings.py .
test/lint/lint-all.sh
-if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then
+if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" ] && [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then
git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit
travis_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $(<contrib/verify-commits/trusted-keys) &&
./contrib/verify-commits/verify-commits.py --clean-merge=2;
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/.travis/test_05_before_script.sh b/.travis/test_05_before_script.sh
index 70caee979c..516d3fc042 100755
--- a/.travis/test_05_before_script.sh
+++ b/.travis/test_05_before_script.sh
@@ -10,10 +10,10 @@ DOCKER_EXEC echo \> \$HOME/.bitcoin # Make sure default datadir does not exist
mkdir -p depends/SDKs depends/sdk-sources
-if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then
+if [ -n "$OSX_SDK" ] && [ ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then
curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz
fi
-if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then
+if [ -n "$OSX_SDK" ] && [ -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then
tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz
fi
if [[ $HOST = *-mingw32 ]]; then
@@ -22,4 +22,3 @@ fi
if [ -z "$NO_DEPENDS" ]; then
DOCKER_EXEC CONFIG_SHELL= make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS
fi
-
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/autogen.sh b/autogen.sh
index 0c05626ccc..2c434e9ef0 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -7,10 +7,10 @@ export LC_ALL=C
set -e
srcdir="$(dirname $0)"
cd "$srcdir"
-if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then
+if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="$(command -v glibtoolize)"; then
LIBTOOLIZE="${GLIBTOOLIZE}"
export LIBTOOLIZE
fi
-which autoreconf >/dev/null || \
+command -v autoreconf >/dev/null || \
(echo "configuration failed, please install autoconf first" && exit 1)
autoreconf --install --force --warnings=all
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 6ded6895cd..e64614c09d 100644
--- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
+++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
@@ -20,9 +20,11 @@
<ClCompile Include="..\..\src\bench\checkqueue.cpp" />
<ClCompile Include="..\..\src\bench\coin_selection.cpp" />
<ClCompile Include="..\..\src\bench\crypto_hash.cpp" />
+ <ClCompile Include="..\..\src\bench\data.cpp" />
<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" />
@@ -68,9 +70,10 @@
<ItemGroup>
<RawBenchFile Include="..\..\src\bench\data\*.raw" />
</ItemGroup>
- <HeaderFromHexdump RawFilePath="%(RawBenchFile.FullPath)" HeaderFilePath="%(RawBenchFile.FullPath).h" SourceHeader="static unsigned const char %(RawBenchFile.Filename)[] = {" SourceFooter="};" />
+ <HeaderFromHexdump RawFilePath="%(RawBenchFile.FullPath)" HeaderFilePath="%(RawBenchFile.FullPath).h" SourceHeader="static unsigned const char %(RawBenchFile.Filename)_raw[] = {" SourceFooter="};" />
</Target>
<Import Label="hexdumpTarget" Project="..\msbuild\tasks\hexdump.targets" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project> \ No newline at end of file
diff --git a/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj b/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
index 72c55e47cc..e5e0e978f8 100644
--- a/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
+++ b/build_msvc/bitcoin-cli/bitcoin-cli.vcxproj
@@ -27,4 +27,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj b/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
index 6891a3e5ad..4e9b4916a0 100644
--- a/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
+++ b/build_msvc/bitcoin-tx/bitcoin-tx.vcxproj
@@ -33,4 +33,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
index 996676baf9..40c5db5522 100644
--- a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
+++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
@@ -51,4 +51,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h
index ab13f73539..66cc1208a1 100644
--- a/build_msvc/bitcoin_config.h
+++ b/build_msvc/bitcoin_config.h
@@ -421,4 +421,14 @@
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
+/* Windows Universal Platform constraints */
+#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
+/* Either a desktop application without API restrictions, or and older system
+ before these macros were defined. */
+
+/* ::wsystem is available */
+#define HAVE_SYSTEM 1
+
+#endif // !WINAPI_FAMILY || WINAPI_FAMILY_DESKTOP_APP
+
#endif //BITCOIN_BITCOIN_CONFIG_H
diff --git a/build_msvc/bitcoind/bitcoind.vcxproj b/build_msvc/bitcoind/bitcoind.vcxproj
index 2c5dde3015..ae24cb100e 100644
--- a/build_msvc/bitcoind/bitcoind.vcxproj
+++ b/build_msvc/bitcoind/bitcoind.vcxproj
@@ -45,4 +45,31 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Label="ReplaceInFile" Project="..\msbuild\tasks\replaceinfile.targets" />
+ <PropertyGroup>
+ <ConfigIniIn>..\..\test\config.ini.in</ConfigIniIn>
+ <ConfigIniOut>..\..\test\config.ini</ConfigIniOut>
+ </PropertyGroup>
+ <Target Name="AfterBuild">
+ <Copy SourceFiles="$(ConfigIniIn)" DestinationFiles="$(ConfigIniOut)" ></Copy>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@PACKAGE_NAME@" By="Bitcoin Core"></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@abs_top_srcdir@" By="..\.." ToFullPath="true"></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@abs_top_builddir@" By="..\.." ToFullPath="true"></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@EXEEXT@" By=".exe"></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@ENABLE_WALLET_TRUE@" By=""></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@BUILD_BITCOIN_CLI_TRUE@" By=""></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@BUILD_BITCOIND_TRUE@" By=""></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@ENABLE_FUZZ_TRUE@" By=""></ReplaceInFile>
+ <ReplaceInFile FilePath="$(ConfigIniOut)"
+ Replace="@ENABLE_ZMQ_TRUE@" By=""></ReplaceInFile>
+ </Target>
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index 8b6217e2b0..0d186b5af2 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -7,7 +7,6 @@
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
</PropertyGroup>
-
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' and !Exists('$(WindowsSdkDir)\DesignTime\CommonConfiguration\Neutral\Windows.props')">
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
@@ -115,4 +114,5 @@
<AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
+ <Import Project="common.init.vcxproj.user" Condition="Exists('common.init.vcxproj.user')" />
</Project>
diff --git a/build_msvc/common.vcxproj b/build_msvc/common.vcxproj
new file mode 100644
index 0000000000..4bbcc3767f
--- /dev/null
+++ b/build_msvc/common.vcxproj
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<PropertyGroup><BuildDependsOn>$(BuildDependsOn);CopyBuildArtifacts</BuildDependsOn></PropertyGroup>
+ <Target Name="CopyBuildArtifacts" Condition="'$(ConfigurationType)' != 'StaticLibrary'">
+ <ItemGroup>
+ <BuildArtifacts Include="$(OutDir)$(TargetName)$(TargetExt)"></BuildArtifacts>
+ <BuildArtifacts Include="$(OutDir)$(TargetName).pdb"></BuildArtifacts>
+ </ItemGroup>
+ <Copy SourceFiles="@(BuildArtifacts)" SkipUnchangedFiles="true" DestinationFolder="..\..\src\" Condition="'$(OutDir)' != ''"></Copy>
+ </Target>
+ <Import Project="common.vcxproj.user" Condition="Exists('common.vcxproj.user')" />
+</Project>
diff --git a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
index 5f6cf608af..620df72a2f 100644
--- a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
+++ b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
index 0bd9b3cd89..b47d62b295 100644
--- a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
+++ b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
index abf1a7ac02..32cb75bf87 100644
--- a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
+++ b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
index 8e2ed394f8..58e90dbaeb 100644
--- a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
+++ b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in
@@ -15,4 +15,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project> \ No newline at end of file
diff --git a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
index 71364d96b1..adf4fa0354 100644
--- a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
+++ b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
index b67f3904a4..9c8279c72a 100644
--- a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
+++ b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
index b8d0f4b442..1a6b7b6b92 100644
--- a/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
+++ b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
index 1b2c27e53f..e86eea81e6 100644
--- a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
+++ b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in
@@ -12,4 +12,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
index 9c8931021b..4cb0bdc902 100644
--- a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
+++ b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj
@@ -33,4 +33,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project> \ No newline at end of file
diff --git a/build_msvc/libleveldb/libleveldb.vcxproj b/build_msvc/libleveldb/libleveldb.vcxproj
index 4689bdc45e..f855923c62 100644
--- a/build_msvc/libleveldb/libleveldb.vcxproj
+++ b/build_msvc/libleveldb/libleveldb.vcxproj
@@ -59,4 +59,5 @@
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
index 644f44350e..99fb63fb02 100644
--- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj
+++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
@@ -18,4 +18,5 @@
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project> \ No newline at end of file
diff --git a/build_msvc/libunivalue/libunivalue.vcxproj b/build_msvc/libunivalue/libunivalue.vcxproj
index 92a9115711..0f13a57241 100644
--- a/build_msvc/libunivalue/libunivalue.vcxproj
+++ b/build_msvc/libunivalue/libunivalue.vcxproj
@@ -15,4 +15,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/msbuild/tasks/replaceinfile.targets b/build_msvc/msbuild/tasks/replaceinfile.targets
new file mode 100644
index 0000000000..2ccb8b30e0
--- /dev/null
+++ b/build_msvc/msbuild/tasks/replaceinfile.targets
@@ -0,0 +1,35 @@
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <UsingTask
+ TaskName="ReplaceInFile"
+ TaskFactory="CodeTaskFactory"
+ AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >
+ <ParameterGroup>
+ <FilePath Required="true" />
+ <Replace Required="true" />
+ <By Required="false" />
+ <ToFullPath Required="false" />
+ </ParameterGroup>
+ <Task>
+ <Using Namespace="System"/>
+ <Using Namespace="System.IO"/>
+ <Code Type="Fragment" Language="cs">
+<![CDATA[
+if(File.Exists(FilePath) == false) {
+ Log.LogError("replaceinfile task could not locate " + FilePath + ".");
+}
+else {
+ var data = File.ReadAllText(FilePath);
+ var by = By;
+ if (ToFullPath == "true")
+ {
+ by = Path.GetFullPath(by);
+ }
+ data = data.Replace(Replace, by);
+ Log.LogMessage("Replace '" + Replace + "' by '" + by + "' in " + FilePath);
+ File.WriteAllText(FilePath, data, new System.Text.UTF8Encoding(false));
+}
+]]>
+ </Code>
+ </Task>
+ </UsingTask>
+</Project> \ No newline at end of file
diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
index 64bdb5f492..47e87b59e4 100644
--- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj
+++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
@@ -64,4 +64,5 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Label="hexdumpTarget" Project="..\msbuild\tasks\hexdump.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/build_msvc/testconsensus/testconsensus.vcxproj b/build_msvc/testconsensus/testconsensus.vcxproj
index 9a6da3ecae..776c40920a 100644
--- a/build_msvc/testconsensus/testconsensus.vcxproj
+++ b/build_msvc/testconsensus/testconsensus.vcxproj
@@ -24,4 +24,5 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <Import Project="..\common.vcxproj" />
</Project>
diff --git a/configure.ac b/configure.ac
index 5e1a5e14a1..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]])
@@ -925,6 +928,29 @@ if test x$use_reduce_exports = xyes; then
[AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])])
fi
+AC_MSG_CHECKING([for std::system])
+AC_LINK_IFELSE(
+ [ AC_LANG_PROGRAM(
+ [[ #include <cstdlib> ]],
+ [[ int nErr = std::system(""); ]]
+ )],
+ [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STD__SYSTEM, 1, Define to 1 if you have the `std::system' function.)],
+ [ AC_MSG_RESULT(no) ]
+)
+
+AC_MSG_CHECKING([for ::_wsystem])
+AC_LINK_IFELSE(
+ [ AC_LANG_PROGRAM(
+ [[ ]],
+ [[ int nErr = ::_wsystem(""); ]]
+ )],
+ [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_WSYSTEM, 1, Define to 1 if you have the `::wsystem' function.)],
+ [ AC_MSG_RESULT(no) ]
+)
+
+# Define to 1 if std::system or ::wsystem (Windows) is available
+AC_DEFINE([HAVE_SYSTEM], [HAVE_STD__SYSTEM || HAVE_WSYSTEM], [std::system or ::wsystem])
+
LEVELDB_CPPFLAGS=
LIBLEVELDB=
LIBMEMENV=
@@ -933,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
@@ -945,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
@@ -1343,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/copyright_header.py b/contrib/devtools/copyright_header.py
index f2987f2260..fc01e570aa 100755
--- a/contrib/devtools/copyright_header.py
+++ b/contrib/devtools/copyright_header.py
@@ -34,7 +34,7 @@ EXCLUDE_DIRS = [
"src/univalue/",
]
-INCLUDE = ['*.h', '*.cpp', '*.cc', '*.c', '*.py']
+INCLUDE = ['*.h', '*.cpp', '*.cc', '*.c', '*.mm', '*.py']
INCLUDE_COMPILED = re.compile('|'.join([fnmatch.translate(m) for m in INCLUDE]))
def applies_to_file(filename):
@@ -90,14 +90,12 @@ def compile_copyright_regex(copyright_style, year_style, name):
EXPECTED_HOLDER_NAMES = [
"Satoshi Nakamoto\n",
"The Bitcoin Core developers\n",
- "Bitcoin Core Developers\n",
"BitPay Inc\.\n",
"University of Illinois at Urbana-Champaign\.\n",
"Pieter Wuille\n",
"Wladimir J. van der Laan\n",
"Jeff Garzik\n",
"Jan-Klaas Kollhof\n",
- "Sam Rushing\n",
"ArtForz -- public domain half-a-node\n",
"Intel Corporation",
"The Zcash developers",
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh
index 63b9847100..dbdb622877 100755
--- a/contrib/devtools/gen-manpages.sh
+++ b/contrib/devtools/gen-manpages.sh
@@ -16,7 +16,7 @@ BITCOINQT=${BITCOINQT:-$BINDIR/qt/bitcoin-qt}
[ ! -x $BITCOIND ] && echo "$BITCOIND not found or not executable." && exit 1
# The autodetected version git tag can screw up manpage output a little bit
-BTCVER=($($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ print $6, $7 }'))
+read -r -a BTCVER <<< "$($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ print $6, $7 }')"
# Create a footer file with copyright content.
# This gets autodetected fine for bitcoind if --version-string is not set,
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
index 03179cec24..78ac671bfe 100755
--- a/contrib/devtools/github-merge.py
+++ b/contrib/devtools/github-merge.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016-2017 Bitcoin Core Developers
+# Copyright (c) 2016-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -32,10 +32,14 @@ BASH = os.getenv('BASH','bash')
# OS specific configuration for terminal attributes
ATTR_RESET = ''
ATTR_PR = ''
+ATTR_NAME = ''
+ATTR_WARN = ''
COMMIT_FORMAT = '%H %s (%an)%d'
if os.name == 'posix': # if posix, assume we can use basic terminal escapes
ATTR_RESET = '\033[0m'
ATTR_PR = '\033[1;36m'
+ ATTR_NAME = '\033[0;36m'
+ ATTR_WARN = '\033[1;31m'
COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
def git_config_get(option, default=None):
@@ -164,18 +168,36 @@ def tree_sha512sum(commit='HEAD'):
return overall.hexdigest()
def get_acks_from_comments(head_commit, comments):
- assert len(head_commit) == 6
- ack_str ='\n\nACKs for commit {}:\n'.format(head_commit)
+ # Look for abbreviated commit id, because not everyone wants to type/paste
+ # the whole thing and the chance of collisions within a PR is small enough
+ head_abbrev = head_commit[0:6]
+ acks = []
for c in comments:
- review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_commit in l]
+ review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_abbrev in l]
if review:
- ack_str += ' {}:\n'.format(c['user']['login'])
- ack_str += ' {}\n'.format(review[0])
+ acks.append((c['user']['login'], review[0]))
+ return acks
+
+def make_acks_message(head_commit, acks):
+ if acks:
+ ack_str ='\n\nACKs for top commit:\n'.format(head_commit)
+ for name, msg in acks:
+ ack_str += ' {}:\n'.format(name)
+ ack_str += ' {}\n'.format(msg)
+ else:
+ ack_str ='\n\nTop commit has no ACKs.\n'
return ack_str
-def print_merge_details(pull, title, branch, base_branch, head_branch):
+def print_merge_details(pull, title, branch, base_branch, head_branch, acks):
print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
+ if acks is not None:
+ if acks:
+ print('{}ACKs:{}'.format(ATTR_PR, ATTR_RESET))
+ for (name, message) in acks:
+ print('* {} {}({}){}'.format(message, ATTR_NAME, name, ATTR_RESET))
+ else:
+ print('{}Top commit has no ACKs!{}'.format(ATTR_WARN, ATTR_RESET))
def parse_arguments():
epilog = '''
@@ -225,9 +247,6 @@ def main():
info = retrieve_pr_info(repo,pull,ghtoken)
if info is None:
sys.exit(1)
- comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
- if comments is None:
- sys.exit(1)
title = info['title'].strip()
body = info['body'].strip()
# precedence order for destination branch argument:
@@ -257,6 +276,8 @@ def main():
sys.exit(3)
try:
subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout)
+ head_commit = subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')
+ assert len(head_commit) == 40
except subprocess.CalledProcessError:
print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr)
sys.exit(3)
@@ -281,7 +302,6 @@ def main():
message = firstline + '\n\n'
message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
- message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments)
try:
subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
except subprocess.CalledProcessError:
@@ -299,20 +319,14 @@ def main():
if len(symlink_files) > 0:
sys.exit(4)
- # Put tree SHA512 into the message
+ # Compute SHA512 of git tree (to be able to detect changes before sign-off)
try:
first_sha512 = tree_sha512sum()
- message += '\n\nTree-SHA512: ' + first_sha512
except subprocess.CalledProcessError:
print("ERROR: Unable to compute tree hash")
sys.exit(4)
- try:
- subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
- except subprocess.CalledProcessError:
- print("ERROR: Cannot update message.", file=stderr)
- sys.exit(4)
- print_merge_details(pull, title, branch, base_branch, head_branch)
+ print_merge_details(pull, title, branch, base_branch, head_branch, None)
print()
# Run test command if configured.
@@ -345,8 +359,24 @@ def main():
print("ERROR: Tree hash changed unexpectedly",file=stderr)
sys.exit(8)
+ # Retrieve PR comments and ACKs and add to commit message, store ACKs to print them with commit
+ # description
+ comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
+ if comments is None:
+ print("ERROR: Could not fetch PR comments and reviews",file=stderr)
+ sys.exit(1)
+ acks = get_acks_from_comments(head_commit=head_commit, comments=comments)
+ message += make_acks_message(head_commit=head_commit, acks=acks)
+ # end message with SHA512 tree hash, then update message
+ message += '\n\nTree-SHA512: ' + first_sha512
+ try:
+ subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
+ except subprocess.CalledProcessError:
+ print("ERROR: Cannot update message.", file=stderr)
+ sys.exit(4)
+
# Sign the merge commit.
- print_merge_details(pull, title, branch, base_branch, head_branch)
+ print_merge_details(pull, title, branch, base_branch, head_branch, acks)
while True:
reply = ask_prompt("Type 's' to sign off on the above merge, or 'x' to reject and exit.").lower()
if reply == 's':
@@ -381,4 +411,3 @@ def main():
if __name__ == '__main__':
main()
-
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/contrib/install_db4.sh b/contrib/install_db4.sh
index 47594a5b0a..4b848dda99 100755
--- a/contrib/install_db4.sh
+++ b/contrib/install_db4.sh
@@ -14,7 +14,7 @@ if [ -z "${1}" ]; then
fi
expand_path() {
- echo "$(cd "${1}" && pwd -P)"
+ cd "${1}" && pwd -P
}
BDB_PREFIX="$(expand_path ${1})/db4"; shift;
@@ -23,7 +23,7 @@ BDB_HASH='12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef'
BDB_URL="https://download.oracle.com/berkeley-db/${BDB_VERSION}.tar.gz"
check_exists() {
- which "$1" >/dev/null 2>&1
+ command -v "$1" >/dev/null
}
sha256_check() {
@@ -95,7 +95,9 @@ make install
echo
echo "db4 build complete."
echo
+# shellcheck disable=SC2016
echo 'When compiling bitcoind, run `./configure` in the following way:'
echo
echo " export BDB_PREFIX='${BDB_PREFIX}'"
+# shellcheck disable=SC2016
echo ' ./configure BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include" ...'
diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh
index f8503e4de8..af2b11fa0d 100755
--- a/contrib/macdeploy/detached-sig-apply.sh
+++ b/contrib/macdeploy/detached-sig-apply.sh
@@ -36,13 +36,13 @@ if [ -z "${CODESIGN_ALLOCATE}" ]; then
fi
find ${TEMPDIR} -name "*.sign" | while read i; do
- SIZE=`stat -c %s "${i}"`
- TARGET_FILE="`echo "${i}" | sed 's/\.sign$//'`"
+ SIZE=$(stat -c %s "${i}")
+ TARGET_FILE="$(echo "${i}" | sed 's/\.sign$//')"
echo "Allocating space for the signature of size ${SIZE} in ${TARGET_FILE}"
${CODESIGN_ALLOCATE} -i "${TARGET_FILE}" -a ${ARCH} ${SIZE} -o "${i}.tmp"
- OFFSET=`${PAGESTUFF} "${i}.tmp" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'`
+ OFFSET=$(${PAGESTUFF} "${i}.tmp" -p | tail -2 | grep offset | sed 's/[^0-9]*//g')
if [ -z ${QUIET} ]; then
echo "Attaching signature at offset ${OFFSET}"
fi
diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh
index 5281ebcc47..938bcd1638 100755
--- a/contrib/macdeploy/detached-sig-create.sh
+++ b/contrib/macdeploy/detached-sig-create.sh
@@ -14,7 +14,7 @@ TEMPLIST=${TEMPDIR}/signatures.txt
OUT=signature-osx.tar.gz
OUTROOT=osx
-if [ ! -n "$1" ]; then
+if [ -z "$1" ]; then
echo "usage: $0 <codesign args>"
echo "example: $0 -s MyIdentity"
exit 1
@@ -26,20 +26,20 @@ mkdir -p ${TEMPDIR}
${CODESIGN} -f --file-list ${TEMPLIST} "$@" "${BUNDLE}"
grep -v CodeResources < "${TEMPLIST}" | while read i; do
- TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`"
- SIZE=`pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g'`
- OFFSET=`pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'`
+ TARGETFILE="${BUNDLE}/$(echo "${i}" | sed "s|.*${BUNDLE}/||")"
+ SIZE=$(pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g')
+ OFFSET=$(pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g')
SIGNFILE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}.sign"
- DIRNAME="`dirname "${SIGNFILE}"`"
+ DIRNAME="$(dirname "${SIGNFILE}")"
mkdir -p "${DIRNAME}"
echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}"
dd if="$i" of="${SIGNFILE}" bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null
done
grep CodeResources < "${TEMPLIST}" | while read i; do
- TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`"
+ TARGETFILE="${BUNDLE}/$(echo "${i}" | sed "s|.*${BUNDLE}/||")"
RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}"
- DIRNAME="`dirname "${RESOURCE}"`"
+ DIRNAME="$(dirname "${RESOURCE}")"
mkdir -p "${DIRNAME}"
echo "Adding resource for: \"${TARGETFILE}\""
cp "${i}" "${RESOURCE}"
diff --git a/contrib/qos/tc.sh b/contrib/qos/tc.sh
index 5f9b87d9b2..ccb0f4f895 100644
--- a/contrib/qos/tc.sh
+++ b/contrib/qos/tc.sh
@@ -33,7 +33,7 @@ tc class add dev ${IF} parent 1:1 classid 1:11 htb rate ${LIMIT} ceil ${LIMIT} p
tc filter add dev ${IF} parent 1: protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev ${IF} parent 1: protocol ip prio 2 handle 2 fw classid 1:11
-if [ ! -z "${LOCALNET_V6}" ] ; then
+if [ -n "${LOCALNET_V6}" ] ; then
# v6 cannot have the same priority value as v4
tc filter add dev ${IF} parent 1: protocol ipv6 prio 3 handle 1 fw classid 1:10
tc filter add dev ${IF} parent 1: protocol ipv6 prio 4 handle 2 fw classid 1:11
@@ -56,7 +56,7 @@ fi
iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 8333 ! -d ${LOCALNET_V4} -j MARK --set-mark 0x2
iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 8333 ! -d ${LOCALNET_V4} -j MARK --set-mark 0x2
-if [ ! -z "${LOCALNET_V6}" ] ; then
+if [ -n "${LOCALNET_V6}" ] ; then
ip6tables -t mangle -A OUTPUT -p tcp -m tcp --dport 8333 ! -d ${LOCALNET_V6} -j MARK --set-mark 0x4
ip6tables -t mangle -A OUTPUT -p tcp -m tcp --sport 8333 ! -d ${LOCALNET_V6} -j MARK --set-mark 0x4
fi
diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh
index 7a10ba7d7d..288e4ccacb 100755
--- a/contrib/verify-commits/gpg.sh
+++ b/contrib/verify-commits/gpg.sh
@@ -21,7 +21,7 @@ else
# they've created a collision for. Not the most likely attack, but preventing
# it is pretty easy so we do so as a "belt-and-suspenders" measure.
GPG_RES=""
- for LINE in "$(gpg --version)"; do
+ for LINE in $(gpg --version); do
case "$LINE" in
"gpg (GnuPG) 1.4.1"*|"gpg (GnuPG) 2.0."*)
echo "Please upgrade to at least gpg 2.1.10 to check for weak signatures" > /dev/stderr
@@ -35,7 +35,7 @@ else
done
[ "$GPG_RES" = "" ] && GPG_RES="$(printf '%s\n' "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
fi
-for LINE in $(echo "$GPG_RES"); do
+for LINE in $GPG_RES; do
case "$LINE" in
"[GNUPG:] VALIDSIG "*)
while read KEY; do
diff --git a/contrib/verifybinaries/README.md b/contrib/verifybinaries/README.md
index 3ffe0a2f28..4209fdb364 100644
--- a/contrib/verifybinaries/README.md
+++ b/contrib/verifybinaries/README.md
@@ -6,7 +6,7 @@ Make sure you obtain the proper release signing key and verify the fingerprint w
```sh
$ gpg --fingerprint "Bitcoin Core binary release signing key"
-pub 4096R/36C2E964 2015-06-24 [expires: 2017-02-13]
+pub 4096R/36C2E964 2015-06-24 [expires: YYYY-MM-DD]
Key fingerprint = 01EA 5486 DE18 A882 D4C2 6845 90C8 019E 36C2 E964
uid Wladimir J. van der Laan (Bitcoin Core binary release signing key) <laanwj@gmail.com>
```
diff --git a/contrib/verifybinaries/verify.sh b/contrib/verifybinaries/verify.sh
index fc7492ad3b..bfe74aa4fa 100755
--- a/contrib/verifybinaries/verify.sh
+++ b/contrib/verifybinaries/verify.sh
@@ -13,7 +13,7 @@
export LC_ALL=C
function clean_up {
- for file in $*
+ for file in "$@"
do
rm "$file" 2> /dev/null
done
@@ -82,22 +82,20 @@ else
exit 2
fi
-#first we fetch the file containing the signature
-WGETOUT=$(wget -N "$HOST1$BASEDIR$SIGNATUREFILENAME" 2>&1)
-
-#and then see if wget completed successfully
-if [ $? -ne 0 ]; then
+if ! WGETOUT=$(wget -N "$HOST1$BASEDIR$SIGNATUREFILENAME" 2>&1); then
echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?"
+ # shellcheck disable=SC1087
echo "[$VERSIONPREFIX]<version>-[$RCVERSIONSTRING[0-9]] (example: ${VERSIONPREFIX}0.10.4-${RCVERSIONSTRING}1)"
echo "wget output:"
+ # shellcheck disable=SC2001
echo "$WGETOUT"|sed 's/^/\t/g'
exit 2
fi
-WGETOUT=$(wget -N -O "$SIGNATUREFILENAME.2" "$HOST2$BASEDIR$SIGNATUREFILENAME" 2>&1)
-if [ $? -ne 0 ]; then
+if ! WGETOUT=$(wget -N -O "$SIGNATUREFILENAME.2" "$HOST2$BASEDIR$SIGNATUREFILENAME" 2>&1); then
echo "bitcoin.org failed to provide signature file, but bitcoincore.org did?"
echo "wget output:"
+ # shellcheck disable=SC2001
echo "$WGETOUT"|sed 's/^/\t/g'
clean_up $SIGNATUREFILENAME
exit 3
@@ -128,6 +126,7 @@ if [ $RET -ne 0 ]; then
fi
echo "gpg output:"
+ # shellcheck disable=SC2001
echo "$GPGOUT"|sed 's/^/\t/g'
clean_up $SIGNATUREFILENAME $SIGNATUREFILENAME.2 $TMPFILE
exit "$RET"
diff --git a/contrib/windeploy/detached-sig-create.sh b/contrib/windeploy/detached-sig-create.sh
index 15f8108cf0..cc42422b23 100755
--- a/contrib/windeploy/detached-sig-create.sh
+++ b/contrib/windeploy/detached-sig-create.sh
@@ -8,7 +8,7 @@ if [ -z "$OSSLSIGNCODE" ]; then
OSSLSIGNCODE=osslsigncode
fi
-if [ ! -n "$1" ]; then
+if [ -z "$1" ]; then
echo "usage: $0 <osslcodesign args>"
echo "example: $0 -key codesign.key"
exit 1
@@ -23,7 +23,7 @@ TIMESERVER=http://timestamp.comodoca.com
CERTFILE="win-codesign.cert"
mkdir -p "${OUTSUBDIR}"
-basename -a `ls -1 "${SRCDIR}"/*-unsigned.exe` | while read UNSIGNED; do
+basename -a $(ls -1 "${SRCDIR}"/*-unsigned.exe) | while read UNSIGNED; do
echo Signing "${UNSIGNED}"
"${OSSLSIGNCODE}" sign -certs "${CERTFILE}" -t "${TIMESERVER}" -in "${SRCDIR}/${UNSIGNED}" -out "${WORKDIR}/${UNSIGNED}" "$@"
"${OSSLSIGNCODE}" extract-signature -pem -in "${WORKDIR}/${UNSIGNED}" -out "${OUTSUBDIR}/${UNSIGNED}.pem" && rm "${WORKDIR}/${UNSIGNED}"
diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk
deleted file mode 100644
index ad10b0fdd7..0000000000
--- a/depends/packages/dbus.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-package=dbus
-$(package)_version=1.10.18
-$(package)_download_path=https://dbus.freedesktop.org/releases/dbus
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a
-$(package)_dependencies=expat
-
-define $(package)_set_vars
- $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-shared --without-x
-endef
-
-define $(package)_config_cmds
- $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
- $(MAKE) -C dbus libdbus-1.la
-endef
-
-define $(package)_stage_cmds
- $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \
- $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA
-endef
-
-define $(package)_postprocess_cmds
- rm lib/*.la
-endef
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 9a08e30892..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 dbus 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/rapidcheck.mk b/depends/packages/rapidcheck.mk
index a35e091c80..fa4fb3c782 100644
--- a/depends/packages/rapidcheck.mk
+++ b/depends/packages/rapidcheck.mk
@@ -8,6 +8,10 @@ define $(package)_config_cmds
cmake -DCMAKE_INSTALL_PREFIX=$($(package)_staging_dir)$(host_prefix) -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DRC_INSTALL_ALL_EXTRAS=ON
endef
+define $(package)_preprocess_cmds
+ sed -i.old 's/ -Wall//' CMakeLists.txt
+endef
+
define $(package)_build_cmds
$(MAKE) rapidcheck
endef
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 1993ff8344..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=--with-pic --disable-shared
-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/Doxyfile.in b/doc/Doxyfile.in
index 58c65fb7e2..399d54eb85 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -453,7 +453,7 @@ EXTRACT_PACKAGE = NO
# included in the documentation.
# The default value is: NO.
-EXTRACT_STATIC = NO
+EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
diff --git a/doc/README.md b/doc/README.md
index b4fa933c8e..d3017de2ab 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -74,6 +74,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
- [bitcoin.conf Configuration File](bitcoin-conf.md)
- [Files](files.md)
- [Fuzz-testing](fuzzing.md)
+- [Reduce Memory](reduce-memory.md)
- [Reduce Traffic](reduce-traffic.md)
- [Tor Support](tor.md)
- [Init Scripts (systemd/upstart/openrc)](init.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/build-osx.md b/doc/build-osx.md
index d28a3d97aa..65cfce6b15 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -1,33 +1,37 @@
-macOS Build Instructions and Notes
-====================================
+# macOS Build Instructions and Notes
+
The commands in this guide should be executed in a Terminal application.
-The built-in one is located in `/Applications/Utilities/Terminal.app`.
+The built-in one is located in
+```
+/Applications/Utilities/Terminal.app
+```
-Preparation
------------
+## Preparation
Install the macOS command line tools:
-`xcode-select --install`
+```shell
+xcode-select --install
+```
When the popup appears, click `Install`.
Then install [Homebrew](https://brew.sh).
-Dependencies
-----------------------
-
- brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode
+## Dependencies
+```shell
+brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode
+```
See [dependencies.md](dependencies.md) for a complete overview.
If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG:
+```shell
+brew install librsvg
+```
- brew install librsvg
-
-Berkeley DB
------------
+## Berkeley DB
It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
-you can use [the installation script included in contrib/](/contrib/install_db4.sh)
+you can use [this](/contrib/install_db4.sh) script to install it
like so:
```shell
@@ -38,172 +42,167 @@ from the root of the repository.
**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](/doc/build-osx.md#disable-wallet-mode)).
-Build Bitcoin Core
-------------------------
+## Build Bitcoin Core
1. Clone the Bitcoin Core source code:
-
- git clone https://github.com/bitcoin/bitcoin
- cd bitcoin
+ ```shell
+ git clone https://github.com/bitcoin/bitcoin
+ cd bitcoin
+ ```
2. Build Bitcoin Core:
Configure and build the headless Bitcoin Core binaries as well as the GUI (if Qt is found).
You can disable the GUI build by passing `--without-gui` to configure.
-
- ./autogen.sh
- ./configure
- make
+ ```shell
+ ./autogen.sh
+ ./configure
+ make
+ ```
3. It is recommended to build and run the unit tests:
-
- make check
-
-4. You can also create a .dmg that contains the .app bundle (optional):
-
- make deploy
-
-Disable-wallet mode
---------------------
-When the intention is to run only a P2P node without a wallet, Bitcoin Core may be compiled in
-disable-wallet mode with:
-
- ./configure --disable-wallet
+ ```shell
+ make check
+ ```
+
+4. You can also create a `.dmg` that contains the `.app` bundle (optional):
+ ```shell
+ make deploy
+ ```
+
+## `disable-wallet` mode
+When the intention is to run only a P2P node without a wallet, Bitcoin Core may be
+compiled in `disable-wallet` mode with:
+```shell
+./configure --disable-wallet
+```
In this case there is no dependency on Berkeley DB 4.8.
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.
-Running
--------
-
+## Running
Bitcoin Core is now available at `./src/bitcoind`
Before running, you may create an empty configuration file:
+```shell
+mkdir -p "/Users/${USER}/Library/Application Support/Bitcoin"
- mkdir -p "/Users/${USER}/Library/Application Support/Bitcoin"
-
- touch "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
+touch "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
- chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
+chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
+```
-The first time you run bitcoind, it will start downloading the blockchain. This process could take many hours, or even days on slower than average systems.
+The first time you run bitcoind, it will start downloading the blockchain. This process could
+take many hours, or even days on slower than average systems.
You can monitor the download process by looking at the debug.log file:
+```shell
+tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log
+```
- tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log
-
-Other commands:
--------
-
- ./src/bitcoind -daemon # Starts the bitcoin daemon.
- ./src/bitcoin-cli --help # Outputs a list of command-line options.
- ./src/bitcoin-cli help # Outputs a list of RPC commands when the daemon is running.
-
-Notes
------
-
-* Tested on OS X 10.10 Yosemite through macOS 10.13 High Sierra on 64-bit Intel processors only.
-
-* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714)
+## Other commands:
+```shell
+./src/bitcoind -daemon # Starts the bitcoin daemon.
+./src/bitcoin-cli --help # Outputs a list of command-line options.
+./src/bitcoin-cli help # Outputs a list of RPC commands when the daemon is running.
+```
-Deterministic macOS DMG Notes
------------------------------
+## Notes
+* Tested on OS X 10.10 Yosemite through macOS 10.14 Mojave on 64-bit Intel
+processors only.
+* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714).
-Working macOS DMGs are created in Linux by combining a recent clang,
-the Apple binutils (ld, ar, etc) and DMG authoring tools.
+## Deterministic macOS DMG Notes
+Working macOS DMGs are created in Linux by combining a recent `clang`, the Apple
+`binutils` (`ld`, `ar`, etc) and DMG authoring tools.
-Apple uses clang extensively for development and has upstreamed the necessary
-functionality so that a vanilla clang can take advantage. It supports the use
-of -F, -target, -mmacosx-version-min, and --sysroot, which are all necessary
-when building for macOS.
+Apple uses `clang` extensively for development and has upstreamed the necessary
+functionality so that a vanilla clang can take advantage. It supports the use of `-F`,
+`-target`, `-mmacosx-version-min`, and `--sysroot`, which are all necessary when
+building for macOS.
-Apple's version of binutils (called cctools) contains lots of functionality
-missing in the FSF's binutils. In addition to extra linker options for
-frameworks and sysroots, several other tools are needed as well such as
-install_name_tool, lipo, and nmedit. These do not build under linux, so they
-have been patched to do so. The work here was used as a starting point:
-[mingwandroid/toolchain4](https://github.com/mingwandroid/toolchain4).
+Apple's version of `binutils` (called `cctools`) contains lots of functionality missing in the
+FSF's `binutils`. In addition to extra linker options for frameworks and sysroots, several
+other tools are needed as well such as `install_name_tool`, `lipo`, and `nmedit`. These
+do not build under Linux, so they have been patched to do so. The work here was used as
+a starting point: [mingwandroid/toolchain4](https://github.com/mingwandroid/toolchain4).
-In order to build a working toolchain, the following source packages are needed
-from Apple: cctools, dyld, and ld64.
+In order to build a working toolchain, the following source packages are needed from
+Apple: `cctools`, `dyld`, and `ld64`.
-These tools inject timestamps by default, which produce non-deterministic
-binaries. The ZERO_AR_DATE environment variable is used to disable that.
+These tools inject timestamps by default, which produce non-deterministic binaries. The
+`ZERO_AR_DATE` environment variable is used to disable that.
-This version of cctools has been patched to use the current version of clang's
-headers and its libLTO.so rather than those from llvmgcc, as it was
-originally done in toolchain4.
+This version of `cctools` has been patched to use the current version of `clang`'s headers
+and its `libLTO.so` rather than those from `llvmgcc`, as it was originally done in `toolchain4`.
-To complicate things further, all builds must target an Apple SDK. These SDKs
-are free to download, but not redistributable.
-To obtain it, register for a developer account, then download the [Xcode 7.3.1 dmg](https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/Xcode_7.3.1/Xcode_7.3.1.dmg).
+To complicate things further, all builds must target an Apple SDK. These SDKs are free to
+download, but not redistributable. To obtain it, register for an Apple Developer Account,
+then download the [Xcode 7.3.1 dmg](https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/Xcode_7.3.1/Xcode_7.3.1.dmg).
-This file is several gigabytes in size, but only a single directory inside is
-needed:
+This file is several gigabytes in size, but only a single directory inside is needed:
```
Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk
```
-Unfortunately, the usual linux tools (7zip, hpmount, loopback mount) are incapable of opening this file.
-To create a tarball suitable for Gitian input, there are two options:
+Unfortunately, the usual Linux tools (7zip, hpmount, loopback mount) are incapable of
+opening this file. To create a tarball suitable for Gitian input, there are two options:
-Using macOS, you can mount the dmg, and then create it with:
-```
- $ hdiutil attach Xcode_7.3.1.dmg
- $ tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.11.sdk.tar.gz MacOSX10.11.sdk
+Using macOS, you can mount the DMG, and then create it with:
+```shell
+hdiutil attach Xcode_7.3.1.dmg
+tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.11.sdk.tar.gz MacOSX10.11.sdk
```
-Alternatively, you can use 7zip and SleuthKit to extract the files one by one.
-The script contrib/macdeploy/extract-osx-sdk.sh automates this. First ensure
-the dmg file is in the current directory, and then run the script. You may wish
-to delete the intermediate 5.hfs file and MacOSX10.11.sdk (the directory) when
-you've confirmed the extraction succeeded.
+Alternatively, you can use 7zip and SleuthKit to extract the files one by one. The script
+[`extract-osx-sdk.sh`](./../contrib/macdeploy/extract-osx-sdk.sh) automates this. First
+ensure the DMG file is in the current directory, and then run the script. You may wish to
+delete the `intermediate 5.hfs` file and `MacOSX10.11.sdk` (the directory) when you've
+confirmed the extraction succeeded.
-```bash
+```shell
apt-get install p7zip-full sleuthkit
contrib/macdeploy/extract-osx-sdk.sh
rm -rf 5.hfs MacOSX10.11.sdk
```
-The Gitian descriptors build 2 sets of files: Linux tools, then Apple binaries
-which are created using these tools. The build process has been designed to
-avoid including the SDK's files in Gitian's outputs. All interim tarballs are
-fully deterministic and may be freely redistributed.
+The Gitian descriptors build 2 sets of files: Linux tools, then Apple binaries which are
+created using these tools. The build process has been designed to avoid including the
+SDK's files in Gitian's outputs. All interim tarballs are fully deterministic and may be freely
+redistributed.
-genisoimage is used to create the initial DMG. It is not deterministic as-is,
-so it has been patched. A system genisoimage will work fine, but it will not
-be deterministic because the file-order will change between invocations.
-The patch can be seen here: [theuni/osx-cross-depends](https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff).
-No effort was made to fix this cleanly, so it likely leaks memory badly. But
-it's only used for a single invocation, so that's no real concern.
+`genisoimage` is used to create the initial DMG. It is not deterministic as-is, so it has been
+patched. A system `genisoimage` will work fine, but it will not be deterministic because
+the file-order will change between invocations. The patch can be seen here: [theuni/osx-cross-depends](https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff).
+No effort was made to fix this cleanly, so it likely leaks memory badly. But it's only used for
+a single invocation, so that's no real concern.
-genisoimage cannot compress DMGs, so afterwards, the 'dmg' tool from the
-libdmg-hfsplus project is used to compress it. There are several bugs in this
-tool and its maintainer has seemingly abandoned the project. It has been forked
-and is available (with fixes) here: [theuni/libdmg-hfsplus](https://github.com/theuni/libdmg-hfsplus).
+`genisoimage` cannot compress DMGs, so afterwards, the DMG tool from the
+`libdmg-hfsplus` project is used to compress it. There are several bugs in this tool and its
+maintainer has seemingly abandoned the project. It has been forked and is available
+(with fixes) here: [theuni/libdmg-hfsplus](https://github.com/theuni/libdmg-hfsplus).
-The 'dmg' tool has the ability to create DMGs from scratch as well, but this
-functionality is broken. Only the compression feature is currently used.
-Ideally, the creation could be fixed and genisoimage would no longer be necessary.
+The DMG tool has the ability to create DMGs from scratch as well, but this functionality is
+broken. Only the compression feature is currently used. Ideally, the creation could be fixed
+and `genisoimage` would no longer be necessary.
Background images and other features can be added to DMG files by inserting a
-.DS_Store before creation. This is generated by the script
-contrib/macdeploy/custom_dsstore.py.
-
-As of OS X 10.9 Mavericks, using an Apple-blessed key to sign binaries is a
-requirement in order to satisfy the new Gatekeeper requirements. Because this
-private key cannot be shared, we'll have to be a bit creative in order for the
-build process to remain somewhat deterministic. Here's how it works:
-
-- Builders use Gitian to create an unsigned release. This outputs an unsigned
- dmg which users may choose to bless and run. It also outputs an unsigned app
- structure in the form of a tarball, which also contains all of the tools
- that have been previously (deterministically) built in order to create a
- final dmg.
-- The Apple keyholder uses this unsigned app to create a detached signature,
- using the script that is also included there. Detached signatures are available from this [repository](https://github.com/bitcoin-core/bitcoin-detached-sigs).
-- Builders feed the unsigned app + detached signature back into Gitian. It
- uses the pre-built tools to recombine the pieces into a deterministic dmg.
+`.DS_Store` before creation. This is generated by the script
+`contrib/macdeploy/custom_dsstore.py`.
+
+As of OS X 10.9 Mavericks, using an Apple-blessed key to sign binaries is a requirement in
+order to satisfy the new Gatekeeper requirements. Because this private key cannot be
+shared, we'll have to be a bit creative in order for the build process to remain somewhat
+deterministic. Here's how it works:
+
+- Builders use Gitian to create an unsigned release. This outputs an unsigned DMG which
+ users may choose to bless and run. It also outputs an unsigned app structure in the form
+ of a tarball, which also contains all of the tools that have been previously (deterministically)
+ built in order to create a final DMG.
+- The Apple keyholder uses this unsigned app to create a detached signature, using the
+ script that is also included there. Detached signatures are available from this [repository](https://github.com/bitcoin-core/bitcoin-detached-sigs).
+- Builders feed the unsigned app + detached signature back into Gitian. It uses the
+ pre-built tools to recombine the pieces into a deterministic DMG.
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 4c0e8d2567..0b23ca0a2d 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -7,26 +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) | | | |
-| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | |
-| 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/reduce-memory.md b/doc/reduce-memory.md
new file mode 100644
index 0000000000..8d8ccdfedc
--- /dev/null
+++ b/doc/reduce-memory.md
@@ -0,0 +1,49 @@
+# Reduce Memory
+
+There are a few parameters that can be dialed down to reduce the memory usage of `bitcoind`. This can be useful on embedded systems or small VPSes.
+
+## In-memory caches
+
+The size of some in-memory caches can be reduced. As caches trade off memory usage for performance, reducing these will usually have a negative effect on performance.
+
+- `-dbcache=<n>` - the UTXO database cache size, this defaults to `450`. The unit is MiB (1024).
+ - The minimum value for `-dbcache` is 4.
+ - A lower `-dbcache` makes initial sync time much longer. After the initial sync, the effect is less pronounced for most use-cases, unless fast validation of blocks is important, such as for mining.
+
+## Memory pool
+
+- In Bitcoin Core there is a memory pool limiter which can be configured with `-maxmempool=<n>`, where `<n>` is the size in MB (1000). The default value is `300`.
+ - The minimum value for `-maxmempool` is 5.
+ - A lower maximum mempool size means that transactions will be evicted sooner. This will affect any uses of `bitcoind` that process unconfirmed transactions.
+
+- To completely disable mempool functionality there is the option `-blocksonly`. This will make the client opt out of receiving (and thus relaying) transactions completely, except as part of blocks.
+
+ - Do not use this when using the client to broadcast transactions as any transaction sent will stick out like a sore thumb, affecting privacy. When used with the wallet it should be combined with `-walletbroadcast=0` and `-spendzeroconfchange=0`. Another mechanism for broadcasting outgoing transactions (if any) should be used.
+
+- Since `0.14.0`, unused memory allocated to the mempool (default: 300MB) is shared with the UTXO cache, so when trying to reduce memory usage you should limit the mempool, with the `-maxmempool` command line argument.
+
+## Number of peers
+
+- `-maxconnections=<n>` - the maximum number of connections, this defaults to `125`. Each active connection takes up some memory. Only significant if incoming
+ connections are enabled, otherwise the number of connections will never be more than `8`.
+
+## Thread configuration
+
+For each thread a thread stack needs to be allocated. By default on Linux,
+threads take up 8MiB for the thread stack on a 64-bit system, and 4MiB in a
+32-bit system.
+
+- `-par=<n>` - the number of script verification threads, defaults to the number of cores in the system minus one.
+- `-rpcthreads=<n>` - the number of threads used for processing RPC requests, defaults to `4`.
+
+## Linux specific
+
+By default, since glibc `2.10`, the C library will create up to two heap arenas per core. This is known to cause excessive memory usage in some scenarios. To avoid this make a script that sets `MALLOC_ARENA_MAX` before starting bitcoind:
+
+```bash
+#!/bin/bash
+export MALLOC_ARENA_MAX=1
+bitcoind
+```
+
+The behavior was introduced to increase CPU locality of allocated memory and performance with concurrent allocation, so this setting could in theory reduce performance. However, in Bitcoin Core very little parallel allocation happens, so the impact is expected to be small or absent.
diff --git a/doc/release-notes-0.18.1-16257.md b/doc/release-notes-0.18.1-16257.md
new file mode 100644
index 0000000000..21867b7fb2
--- /dev/null
+++ b/doc/release-notes-0.18.1-16257.md
@@ -0,0 +1,6 @@
+Wallet changes
+--------------
+When creating a transaction with a fee above `-maxtxfee` (default 0.1 BTC),
+the RPC commands `walletcreatefundedpsbt` and `fundrawtransaction` will now fail
+instead of rounding down the fee. Beware that the `feeRate` argument is specified
+in BTC per kilobyte, not satoshi per byte.
diff --git a/doc/release-notes-15427.md b/doc/release-notes-15427.md
new file mode 100644
index 0000000000..25edfd4402
--- /dev/null
+++ b/doc/release-notes-15427.md
@@ -0,0 +1,9 @@
+Updated RPCs
+------------
+
+The `utxoupdatepsbt` RPC method has been updated to take a `descriptors`
+argument. When provided, input and output scripts and keys will be filled in
+when known, and P2SH-witness inputs will be filled in from the UTXO set when a
+descriptor is provided that shows they're spending segwit outputs.
+
+See the RPC help text for full details.
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/share/genbuild.sh b/share/genbuild.sh
index 38c9ba176c..cd6214d5fa 100755
--- a/share/genbuild.sh
+++ b/share/genbuild.sh
@@ -24,9 +24,9 @@ git_check_in_repo() {
DESC=""
SUFFIX=""
-if [ "${BITCOIN_GENBUILD_NO_GIT}" != "1" -a -e "$(which git 2>/dev/null)" -a "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] && git_check_in_repo share/genbuild.sh; then
+if [ "${BITCOIN_GENBUILD_NO_GIT}" != "1" ] && [ -e "$(command -v git)" ] && [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] && git_check_in_repo share/genbuild.sh; then
# clean 'dirty' status of touched files that haven't been modified
- git diff >/dev/null 2>/dev/null
+ git diff >/dev/null 2>/dev/null
# if latest commit is tagged and not dirty, then override using the tag name
RAWDESC=$(git describe --abbrev=0 2>/dev/null)
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index 76ef12eeec..3cc0fa1b1d 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -76,10 +76,7 @@ f.write("""
#endif
""")
f.write('static const char UNUSED *bitcoin_strings[] = {\n')
-f.write('QT_TRANSLATE_NOOP("bitcoin-core", "%s"),\n' % (os.getenv('PACKAGE_NAME'),))
f.write('QT_TRANSLATE_NOOP("bitcoin-core", "%s"),\n' % (os.getenv('COPYRIGHT_HOLDERS'),))
-if os.getenv('COPYRIGHT_HOLDERS_SUBSTITUTION') != os.getenv('PACKAGE_NAME'):
- f.write('QT_TRANSLATE_NOOP("bitcoin-core", "%s"),\n' % (os.getenv('COPYRIGHT_HOLDERS_SUBSTITUTION'),))
messages.sort(key=operator.itemgetter(0))
for (msgid, msgstr) in messages:
if msgid != EMPTY:
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 c6162b5caa..e421b377a0 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -18,15 +18,19 @@ bench_bench_bitcoin_SOURCES = \
bench/block_assemble.cpp \
bench/checkblock.cpp \
bench/checkqueue.cpp \
+ bench/data.h \
+ bench/data.cpp \
bench/duplicate_inputs.cpp \
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 \
@@ -76,7 +80,7 @@ CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
-bench/checkblock.cpp: bench/data/block413567.raw.h
+bench/data.cpp: bench/data/block413567.raw.h
bitcoin_bench: $(BENCH_BINARY)
@@ -89,7 +93,7 @@ bitcoin_bench_clean : FORCE
%.raw.h: %.raw
@$(MKDIR_P) $(@D)
@{ \
- echo "static unsigned const char $(*F)[] = {" && \
+ echo "static unsigned const char $(*F)_raw[] = {" && \
$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \
echo "};"; \
} > "$@.new" && mv -f "$@.new" "$@"
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index c4c08487f3..3ae8498a87 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -447,7 +447,7 @@ SECONDARY: $(QT_QM)
$(srcdir)/qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
- $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) PACKAGE_NAME="$(PACKAGE_NAME)" COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" COPYRIGHT_HOLDERS_SUBSTITUTION="$(COPYRIGHT_HOLDERS_SUBSTITUTION)" $(PYTHON) ../share/qt/extract_strings_qt.py $^
+ $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $^
translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index c6083f5554..db936486b6 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -44,18 +44,30 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
fs::path pathTmp = GetDataDir() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
- if (fileout.IsNull())
+ if (fileout.IsNull()) {
+ fileout.fclose();
+ remove(pathTmp);
return error("%s: Failed to open file %s", __func__, pathTmp.string());
+ }
// Serialize
- if (!SerializeDB(fileout, data)) return false;
- if (!FileCommit(fileout.Get()))
+ if (!SerializeDB(fileout, data)) {
+ fileout.fclose();
+ remove(pathTmp);
+ return false;
+ }
+ if (!FileCommit(fileout.Get())) {
+ fileout.fclose();
+ remove(pathTmp);
return error("%s: Failed to flush file %s", __func__, pathTmp.string());
+ }
fileout.fclose();
// replace existing file, if any, with new file
- if (!RenameOver(pathTmp, path))
+ if (!RenameOver(pathTmp, path)) {
+ remove(pathTmp);
return error("%s: Rename-into-place failed", __func__);
+ }
return true;
}
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/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/checkblock.cpp b/src/bench/checkblock.cpp
index e325333c01..4b13381e16 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -3,41 +3,34 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
+#include <bench/data.h>
#include <chainparams.h>
#include <validation.h>
#include <streams.h>
#include <consensus/validation.h>
-namespace block_bench {
-#include <bench/data/block413567.raw.h>
-} // namespace block_bench
-
// These are the two major time-sinks which happen after we have fully received
// a block off the wire, but before we can relay the block on to peers using
// compact block relay.
static void DeserializeBlockTest(benchmark::State& state)
{
- CDataStream stream((const char*)block_bench::block413567,
- (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
- SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
while (state.KeepRunning()) {
CBlock block;
stream >> block;
- bool rewound = stream.Rewind(sizeof(block_bench::block413567));
+ bool rewound = stream.Rewind(benchmark::data::block413567.size());
assert(rewound);
}
}
static void DeserializeAndCheckBlockTest(benchmark::State& state)
{
- CDataStream stream((const char*)block_bench::block413567,
- (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
- SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
@@ -46,7 +39,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
while (state.KeepRunning()) {
CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
stream >> block;
- bool rewound = stream.Rewind(sizeof(block_bench::block413567));
+ bool rewound = stream.Rewind(benchmark::data::block413567.size());
assert(rewound);
CValidationState validationState;
diff --git a/src/bench/data.cpp b/src/bench/data.cpp
new file mode 100644
index 0000000000..0ae4c7cad4
--- /dev/null
+++ b/src/bench/data.cpp
@@ -0,0 +1,14 @@
+// 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 <bench/data.h>
+
+namespace benchmark {
+namespace data {
+
+#include <bench/data/block413567.raw.h>
+const std::vector<uint8_t> block413567{block413567_raw, block413567_raw + sizeof(block413567_raw) / sizeof(block413567_raw[0])};
+
+} // namespace data
+} // namespace benchmark
diff --git a/src/bench/data.h b/src/bench/data.h
new file mode 100644
index 0000000000..5f13d766ea
--- /dev/null
+++ b/src/bench/data.h
@@ -0,0 +1,19 @@
+// 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_BENCH_DATA_H
+#define BITCOIN_BENCH_DATA_H
+
+#include <cstdint>
+#include <vector>
+
+namespace benchmark {
+namespace data {
+
+extern const std::vector<uint8_t> block413567;
+
+} // namespace data
+} // namespace benchmark
+
+#endif // BITCOIN_BENCH_DATA_H
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..8ca985458d 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>
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 933b34744d..89e2ab305b 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>
@@ -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..a690e2facb 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;
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.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 6625080c6e..b84c7dc93d 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;
@@ -376,10 +354,14 @@ void SetupServerArgs()
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"};
gArgs.AddArg("-version", "Print version and exit", false, 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);
+#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);
+#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);
+#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);
@@ -488,7 +470,7 @@ void SetupServerArgs()
"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("-checkblockindex", strprintf("Do a full consistency check for the block tree, 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);
@@ -562,23 +544,24 @@ 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";
}
+#if HAVE_SYSTEM
static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex)
{
if (initialSync || !pBlockIndex)
@@ -591,6 +574,7 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
t.detach(); // thread runs free
}
}
+#endif
static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
@@ -839,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);
}
/**
@@ -945,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
@@ -967,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);
}
@@ -976,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);
}
}
@@ -1003,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")) {
@@ -1018,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));
}
}
}
@@ -1027,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));
}
}
@@ -1063,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"))
@@ -1086,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
@@ -1095,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;
@@ -1144,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;
@@ -1176,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;
}
@@ -1197,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,
@@ -1248,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());
@@ -1308,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
@@ -1336,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));
}
@@ -1350,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++) {
@@ -1371,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);
@@ -1395,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);
}
@@ -1474,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();
@@ -1505,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;
}
@@ -1527,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;
}
@@ -1557,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;
}
}
@@ -1582,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);
@@ -1593,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;
}
@@ -1617,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) {
@@ -1674,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();
}
}
@@ -1691,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;
}
@@ -1708,8 +1699,10 @@ bool AppInitMain(InitInterfaces& interfaces)
fHaveGenesis = true;
}
+#if HAVE_SYSTEM
if (gArgs.IsArgSet("-blocknotify"))
uiInterface.NotifyBlockTip_connect(BlockNotifyCallback);
+#endif
std::vector<fs::path> vImportFiles;
for (const std::string& strFile : gArgs.GetArgs("-loadblock")) {
@@ -1741,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);
@@ -1787,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);
}
@@ -1796,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);
}
@@ -1817,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..22e4aaedaf 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -9,6 +9,7 @@
#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <net.h>
+#include <net_processing.h>
#include <node/coin.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -205,7 +206,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;
@@ -292,8 +293,7 @@ public:
}
void relayTransaction(const uint256& txid) override
{
- CInv inv(MSG_TX, txid);
- g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
+ RelayTransaction(txid, *g_connman);
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
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/logging.cpp b/src/logging.cpp
index 418a701179..dc2d130a2a 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -75,6 +75,14 @@ bool BCLog::Logger::StartLogging()
return true;
}
+void BCLog::Logger::DisconnectTestLogger()
+{
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ m_buffering = true;
+ if (m_fileout != nullptr) fclose(m_fileout);
+ m_fileout = nullptr;
+}
+
void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
{
m_categories |= flag;
diff --git a/src/logging.h b/src/logging.h
index 36b1a4045d..75cd5353c0 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -100,6 +100,8 @@ namespace BCLog {
/** Start logging (and flush all buffered messages) */
bool StartLogging();
+ /** Only for testing */
+ void DisconnectTestLogger();
void ShrinkDebugFile();
diff --git a/src/net.cpp b/src/net.cpp
index 75a47d7ad2..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 5ff456fcb0..4e631fd00e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -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);
});
@@ -1555,6 +1555,11 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
}
}
+ // Unknown types in the GetData stay in vRecvGetData and block any future
+ // message from this peer, see vRecvGetData check in ProcessMessages().
+ // Depending on future p2p changes, we might either drop unknown getdata on
+ // the floor or disconnect the peer.
+
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
@@ -1806,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()) {
@@ -2493,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()) {
@@ -2572,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);
}
}
}
@@ -3260,6 +3265,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return false;
// this maintains the order of responses
+ // and prevents vRecvGetData to grow unbounded
if (!pfrom->vRecvGetData.empty()) return true;
if (!pfrom->orphan_work_set.empty()) return true;
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..5cd567a5c4 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>
@@ -69,10 +70,7 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx,
return TransactionError::P2P_DISABLED;
}
- CInv inv(MSG_TX, hashTx);
- g_connman->ForEachNode([&inv](CNode* pnode) {
- pnode->PushInventory(inv);
- });
+ RelayTransaction(hashTx, *g_connman);
return TransactionError::OK;
}
diff --git a/src/noui.cpp b/src/noui.cpp
index 2be89a7468..c07939cc79 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -24,26 +24,30 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca
{
bool fSecure = style & CClientUIInterface::SECURE;
style &= ~CClientUIInterface::SECURE;
+ bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
+ style &= ~CClientUIInterface::MSG_NOPREFIX;
std::string strCaption;
- // Check for usage of predefined caption
- switch (style) {
- case CClientUIInterface::MSG_ERROR:
- strCaption += _("Error");
- break;
- case CClientUIInterface::MSG_WARNING:
- strCaption += _("Warning");
- break;
- case CClientUIInterface::MSG_INFORMATION:
- strCaption += _("Information");
- break;
- default:
- strCaption += caption; // Use supplied caption (can be empty)
+ if (prefix) {
+ switch (style) {
+ case CClientUIInterface::MSG_ERROR:
+ strCaption = "Error: ";
+ break;
+ case CClientUIInterface::MSG_WARNING:
+ strCaption = "Warning: ";
+ break;
+ case CClientUIInterface::MSG_INFORMATION:
+ strCaption = "Information: ";
+ break;
+ default:
+ strCaption = caption + ": "; // Use supplied caption (can be empty)
+ }
}
- if (!fSecure)
- LogPrintf("%s: %s\n", strCaption, message);
- tfm::format(std::cerr, "%s: %s\n", strCaption.c_str(), message.c_str());
+ if (!fSecure) {
+ LogPrintf("%s%s\n", strCaption, message);
+ }
+ tfm::format(std::cerr, "%s%s\n", strCaption.c_str(), message.c_str());
return false;
}
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/policy/fees.h b/src/policy/fees.h
index 6e61f76178..16683bf5ad 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -43,7 +43,6 @@ enum class FeeReason {
PAYTXFEE,
FALLBACK,
REQUIRED,
- MAXTXFEE,
};
/* Used to determine type of fee estimation requested */
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.cpp b/src/psbt.cpp
index d765133190..fe74002e82 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -212,6 +212,25 @@ bool PSBTInputSigned(const PSBTInput& input)
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
+void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index)
+{
+ const CTxOut& out = psbt.tx->vout.at(index);
+ PSBTOutput& psbt_out = psbt.outputs.at(index);
+
+ // Fill a SignatureData with output info
+ SignatureData sigdata;
+ psbt_out.FillSignatureData(sigdata);
+
+ // Construct a would-be spend of this output, to update sigdata with.
+ // Note that ProduceSignature is used to fill in metadata (not actual signatures),
+ // so provider does not need to provide any private keys (it can be a HidingSigningProvider).
+ MutableTransactionSignatureCreator creator(psbt.tx.get_ptr(), /* index */ 0, out.nValue, SIGHASH_ALL);
+ ProduceSignature(provider, creator, out.scriptPubKey, sigdata);
+
+ // Put redeem_script, witness_script, key paths, into PSBTOutput.
+ psbt_out.FromSignatureData(sigdata);
+}
+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
{
PSBTInput& input = psbt.inputs.at(index);
diff --git a/src/psbt.h b/src/psbt.h
index 1bc1e91a84..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};
@@ -565,6 +566,12 @@ bool PSBTInputSigned(const PSBTInput& input);
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+/** Updates a PSBTOutput with information from provider.
+ *
+ * This fills in the redeem_script, witness_script, and hd_keypaths where possible.
+ */
+void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index);
+
/**
* Finalizes a PSBT if possible, combining partial signatures.
*
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 f6ed88a1e6..a3b6a92855 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)) {
@@ -413,7 +402,6 @@ static void SetupUIArgs()
gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI);
}
-#ifndef BITCOIN_QT_TEST
int GuiMain(int argc, char* argv[])
{
#ifdef WIN32
@@ -436,18 +424,22 @@ int GuiMain(int argc, char* argv[])
Q_INIT_RESOURCE(bitcoin);
Q_INIT_RESOURCE(bitcoin_locale);
- BitcoinApplication app(*node, argc, argv);
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if QT_VERSION >= 0x050600
- QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
+ BitcoinApplication app(*node, argc, 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)) {
- QMessageBox::critical(nullptr, QObject::tr(PACKAGE_NAME),
- QObject::tr("Error parsing command line arguments: %1.").arg(QString::fromStdString(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,
+ // 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,12 +491,14 @@ int GuiMain(int argc, char* argv[])
/// - Do not call GetDataDir(true) before this step finishes
if (!fs::is_directory(GetDataDir(false)))
{
- QMessageBox::critical(nullptr, QObject::tr(PACKAGE_NAME),
+ 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)) {
- QMessageBox::critical(nullptr, QObject::tr(PACKAGE_NAME),
+ 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,7 +513,8 @@ int GuiMain(int argc, char* argv[])
try {
node->selectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- QMessageBox::critical(nullptr, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what()));
+ node->initError(strprintf("%s\n", e.what()));
+ QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
@@ -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
@@ -573,7 +573,7 @@ int GuiMain(int argc, char* argv[])
if (app.baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN)
- WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
+ WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
app.requestShutdown();
@@ -592,4 +592,3 @@ int GuiMain(int argc, char* argv[])
}
return rv;
}
-#endif // BITCOIN_QT_TEST
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 8388e8d36e..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>
@@ -294,15 +291,15 @@ void BitcoinGUI::createActions()
quitAction->setStatusTip(tr("Quit application"));
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
quitAction->setMenuRole(QAction::QuitRole);
- aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(tr(PACKAGE_NAME)), this);
- aboutAction->setStatusTip(tr("Show information about %1").arg(tr(PACKAGE_NAME)));
+ aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(PACKAGE_NAME), this);
+ aboutAction->setStatusTip(tr("Show information about %1").arg(PACKAGE_NAME));
aboutAction->setMenuRole(QAction::AboutRole);
aboutAction->setEnabled(false);
aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this);
- optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME)));
+ optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(PACKAGE_NAME));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this);
@@ -344,7 +341,7 @@ void BitcoinGUI::createActions()
showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
- showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(tr(PACKAGE_NAME)));
+ showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
@@ -371,13 +368,12 @@ void BitcoinGUI::createActions()
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] {
m_open_wallet_menu->clear();
- std::vector<std::string> available_wallets = m_wallet_controller->getWalletsAvailableToOpen();
- std::vector<std::string> wallets = m_node.listWalletDir();
- for (const auto& path : wallets) {
+ for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
+ const std::string& path = i.first;
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
QAction* action = m_open_wallet_menu->addAction(name);
- if (std::find(available_wallets.begin(), available_wallets.end(), path) == available_wallets.end()) {
+ if (i.second) {
// This wallet is already loaded
action->setEnabled(false);
continue;
@@ -410,7 +406,7 @@ void BitcoinGUI::createActions()
assert(invoked);
});
}
- if (wallets.empty()) {
+ if (m_open_wallet_menu->isEmpty()) {
QAction* action = m_open_wallet_menu->addAction(tr("No wallets available"));
action->setEnabled(false);
}
@@ -640,7 +636,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
- for (WalletModel* wallet_model : m_wallet_controller->getWallets()) {
+ for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) {
addWallet(wallet_model);
}
}
@@ -729,7 +725,7 @@ void BitcoinGUI::createTrayIcon()
#ifndef Q_OS_MAC
if (QSystemTrayIcon::isSystemTrayAvailable()) {
trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this);
- QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + m_network_style->getTitleAddText();
+ QString toolTip = tr("%1 client").arg(PACKAGE_NAME) + " " + m_network_style->getTitleAddText();
trayIcon->setToolTip(toolTip);
}
#endif
@@ -1039,49 +1035,51 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
progressBar->setToolTip(tooltip);
}
-void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret)
+void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret)
{
- QString strTitle = tr("Bitcoin"); // default title
+ // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines).
+ QString strTitle{PACKAGE_NAME};
// Default to information icon
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
- QString msgType;
+ bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
+ style &= ~CClientUIInterface::MSG_NOPREFIX;
- // Prefer supplied title over style based title
+ QString msgType;
if (!title.isEmpty()) {
msgType = title;
- }
- else {
+ } else {
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
+ if (prefix) message = tr("Error: %1").arg(message);
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
+ if (prefix) message = tr("Warning: %1").arg(message);
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
+ // No need to prepend the prefix here.
break;
default:
break;
}
}
- // Append title to "Bitcoin - "
- if (!msgType.isEmpty())
+
+ if (!msgType.isEmpty()) {
strTitle += " - " + msgType;
+ }
- // Check for error/warning icon
if (style & CClientUIInterface::ICON_ERROR) {
nMBoxIcon = QMessageBox::Critical;
nNotifyIcon = Notificator::Critical;
- }
- else if (style & CClientUIInterface::ICON_WARNING) {
+ } else if (style & CClientUIInterface::ICON_WARNING) {
nMBoxIcon = QMessageBox::Warning;
nNotifyIcon = Notificator::Warning;
}
- // Display message
if (style & CClientUIInterface::MODAL) {
// Check for buttons, use OK as default, if none was supplied
QMessageBox::StandardButton buttons;
@@ -1094,9 +1092,9 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
int r = mBox.exec();
if (ret != nullptr)
*ret = r == QMessageBox::Ok;
- }
- else
+ } else {
notificator->notify(static_cast<Notificator::Class>(nNotifyIcon), strTitle, message);
+ }
}
void BitcoinGUI::changeEvent(QEvent *e)
@@ -1290,7 +1288,7 @@ void BitcoinGUI::updateProxyIcon()
void BitcoinGUI::updateWindowTitle()
{
- QString window_title = tr(PACKAGE_NAME);
+ QString window_title = PACKAGE_NAME;
#ifdef ENABLE_WALLET
if (walletFrame) {
WalletModel* const wallet_model = walletFrame->currentWalletModel();
@@ -1374,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/bitcoingui.h b/src/qt/bitcoingui.h
index 608f10750a..46ced79007 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -220,7 +220,7 @@ public Q_SLOTS:
@see CClientUIInterface::MessageBoxFlags
@param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only)
*/
- void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr);
+ void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr);
#ifdef ENABLE_WALLET
void setCurrentWallet(WalletModel* wallet_model);
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index dc997e96cc..87736cd185 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -9,14 +9,13 @@
#define UNUSED
#endif
static const char UNUSED *bitcoin_strings[] = {
-QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core"),
QT_TRANSLATE_NOOP("bitcoin-core", "The %s developers"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"-maxtxfee is set very high! Fees this large could be paid on a single "
"transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Can't generate a change-address key. Private keys are disabled for this "
-"wallet."),
+"Can't generate a change-address key. No keys in the internal keypool and "
+"can't generate any keys."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. %s is probably already running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -88,9 +87,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: The network does not appear to fully agree! Some miners appear to "
"be experiencing issues."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Warning: Unknown block versions being mined! It's possible unknown rules are "
-"in effect"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"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."),
@@ -124,15 +120,13 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. Duplicate -wallet fi
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low for %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is too low!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to rescan the wallet during initialization"),
QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"),
@@ -150,6 +144,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Need to specify a port with -whitebind: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune cannot be configured with a negative value."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -blockfilterindex."),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -txindex."),
QT_TRANSLATE_NOOP("bitcoin-core", "Pruning blockstore..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."),
@@ -179,9 +174,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large for fee policy"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unable to create the PID file '%s': %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
@@ -189,9 +186,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading txindex database"),
QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside wallet directory %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Warning"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: unknown new rules activated (versionbit %i)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."),
};
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/intro.cpp b/src/qt/intro.cpp
index c595361934..102e37e471 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -119,16 +119,16 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz
m_chain_state_size(chain_state_size)
{
ui->setupUi(this);
- ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME)));
- ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME)));
+ ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME));
+ ui->storageLabel->setText(ui->storageLabel->text().arg(PACKAGE_NAME));
ui->lblExplanation1->setText(ui->lblExplanation1->text()
- .arg(tr(PACKAGE_NAME))
+ .arg(PACKAGE_NAME)
.arg(m_blockchain_size)
.arg(2009)
.arg(tr("Bitcoin"))
);
- ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(tr(PACKAGE_NAME)));
+ ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
uint64_t pruneTarget = std::max<int64_t>(0, gArgs.GetArg("-prune", 0));
requiredSpace = m_blockchain_size;
@@ -145,7 +145,7 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz
}
requiredSpace += m_chain_state_size;
ui->sizeWarningLabel->setText(
- tr("%1 will download and store a copy of the Bitcoin block chain.").arg(tr(PACKAGE_NAME)) + " " +
+ tr("%1 will download and store a copy of the Bitcoin block chain.").arg(PACKAGE_NAME) + " " +
storageRequiresMsg.arg(requiredSpace) + " " +
tr("The wallet will also be stored in this directory.")
);
@@ -221,7 +221,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
}
break;
} catch (const fs::filesystem_error&) {
- QMessageBox::critical(nullptr, tr(PACKAGE_NAME),
+ QMessageBox::critical(nullptr, PACKAGE_NAME,
tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
/* fall through, back to choosing screen */
}
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index c6ecbc3f87..bff7469071 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -59,7 +59,7 @@
<translation>&amp;Delete</translation>
</message>
<message>
- <location filename="../addressbookpage.cpp" line="+85"/>
+ <location filename="../addressbookpage.cpp" line="+84"/>
<source>Choose the address to send coins to</source>
<translation type="unfinished"></translation>
</message>
@@ -90,7 +90,7 @@
</message>
<message>
<location line="+5"/>
- <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
+ <source>These are your Bitcoin addresses for receiving payments. Use the &apos;Create new receiving address&apos; button in the receive tab to create new addresses.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -132,7 +132,7 @@
<context>
<name>AddressTableModel</name>
<message>
- <location filename="../addresstablemodel.cpp" line="+164"/>
+ <location filename="../addresstablemodel.cpp" line="+163"/>
<source>Label</source>
<translation type="unfinished"></translation>
</message>
@@ -297,7 +297,7 @@
<context>
<name>BanTableModel</name>
<message>
- <location filename="../bantablemodel.cpp" line="+88"/>
+ <location filename="../bantablemodel.cpp" line="+86"/>
<source>IP/Netmask</source>
<translation type="unfinished"></translation>
</message>
@@ -315,12 +315,12 @@
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+574"/>
+ <location line="+638"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-652"/>
+ <location line="-716"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -400,12 +400,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+157"/>
+ <location line="+217"/>
<source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+330"/>
+ <location line="+334"/>
<source>Click to disable network activity.</source>
<translation type="unfinished"></translation>
</message>
@@ -430,12 +430,12 @@
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="+315"/>
+ <location line="+317"/>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-970"/>
+ <location line="-1036"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -465,12 +465,7 @@
<translation>&amp;Verify message...</translation>
</message>
<message>
- <location line="+660"/>
- <source>Bitcoin</source>
- <translation>Bitcoin</translation>
- </message>
- <message>
- <location line="-733"/>
+ <location line="-73"/>
<source>&amp;Send</source>
<translation>&amp;Send</translation>
</message>
@@ -505,12 +500,12 @@
<translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
- <location line="+61"/>
+ <location line="+118"/>
<source>&amp;File</source>
<translation>&amp;File</translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+14"/>
<source>&amp;Settings</source>
<translation>&amp;Settings</translation>
</message>
@@ -525,7 +520,7 @@
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="-211"/>
+ <location line="-271"/>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
<translation type="unfinished"></translation>
</message>
@@ -545,12 +540,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+10"/>
<source>&amp;Command-line options</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+484"/>
+ <location line="+540"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -591,27 +586,27 @@
<translation>Transactions after this will not yet be visible.</translation>
</message>
<message>
- <location line="+27"/>
+ <location line="+28"/>
<source>Error</source>
<translation>Error</translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+4"/>
<source>Warning</source>
<translation>Warning</translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+4"/>
<source>Information</source>
<translation>Information</translation>
</message>
<message>
- <location line="-78"/>
+ <location line="-81"/>
<source>Up to date</source>
<translation>Up to date</translation>
</message>
<message>
- <location line="-593"/>
+ <location line="-657"/>
<source>&amp;Sending addresses</source>
<translation type="unfinished"></translation>
</message>
@@ -621,12 +616,52 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+6"/>
+ <source>Open Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Open a wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Close Wallet...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Close wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+63"/>
+ <location line="+30"/>
+ <source>default wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Open Wallet Failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>No wallets available</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+48"/>
<source>&amp;Window</source>
<translation type="unfinished">&amp;Window</translation>
</message>
@@ -651,7 +686,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+228"/>
+ <location line="+232"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
@@ -666,7 +701,17 @@
<translation>Catching up...</translation>
</message>
<message>
- <location line="+151"/>
+ <location line="+50"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Warning: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+99"/>
<source>Date: %1
</source>
<translation type="unfinished"></translation>
@@ -737,7 +782,7 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+395"/>
+ <location filename="../bitcoin.cpp" line="+390"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation type="unfinished"></translation>
</message>
@@ -830,7 +875,7 @@
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+58"/>
+ <location filename="../coincontroldialog.cpp" line="+54"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -1031,7 +1076,7 @@
<context>
<name>HelpMessageDialog</name>
<message>
- <location filename="../utilitydialog.cpp" line="+44"/>
+ <location filename="../utilitydialog.cpp" line="+39"/>
<source>version</source>
<translation type="unfinished">version</translation>
</message>
@@ -1120,7 +1165,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+80"/>
+ <location line="+75"/>
<source>Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<translation type="unfinished"></translation>
</message>
@@ -1171,7 +1216,7 @@
<message>
<location line="+7"/>
<location line="+26"/>
- <location filename="../modaloverlay.cpp" line="+140"/>
+ <location filename="../modaloverlay.cpp" line="+141"/>
<source>Unknown...</source>
<translation type="unfinished"></translation>
</message>
@@ -1207,8 +1252,8 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../modaloverlay.cpp" line="-1"/>
- <source>Unknown. Syncing Headers (%1)...</source>
+ <location filename="../modaloverlay.cpp" line="+6"/>
+ <source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1540,12 +1585,12 @@
<translation>default</translation>
</message>
<message>
- <location line="+63"/>
+ <location line="+67"/>
<source>none</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+93"/>
+ <location line="+89"/>
<source>Confirm options reset</source>
<translation>Confirm options reset</translation>
</message>
@@ -1689,7 +1734,7 @@
<name>PaymentServer</name>
<message>
<location filename="../paymentserver.cpp" line="+226"/>
- <location line="+338"/>
+ <location line="+346"/>
<location line="+42"/>
<location line="+110"/>
<location line="+14"/>
@@ -1698,7 +1743,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-521"/>
+ <location line="-529"/>
<source>Cannot start bitcoin: click-to-pay handler</source>
<translation type="unfinished"></translation>
</message>
@@ -1706,14 +1751,14 @@
<location line="+62"/>
<location line="+9"/>
<location line="+16"/>
+ <location line="+16"/>
<location line="+5"/>
- <location line="+12"/>
<location line="+7"/>
<source>URI handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-49"/>
+ <location line="-53"/>
<source>&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
<translation type="unfinished"></translation>
</message>
@@ -1728,12 +1773,13 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+16"/>
+ <location line="+36"/>
<source>Cannot process payment request because BIP70 support was not compiled in.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="-32"/>
<source>Invalid payment address %1</source>
<translation type="unfinished"></translation>
</message>
@@ -1744,16 +1790,17 @@
</message>
<message>
<location line="+14"/>
+ <location line="+9"/>
<source>Payment request file handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="-8"/>
<source>Payment request file cannot be read! This can be caused by an invalid payment request file.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+195"/>
+ <location line="+199"/>
<location line="+9"/>
<location line="+31"/>
<location line="+10"/>
@@ -1832,7 +1879,7 @@
<context>
<name>PeerTableModel</name>
<message>
- <location filename="../peertablemodel.cpp" line="+109"/>
+ <location filename="../peertablemodel.cpp" line="+108"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
@@ -1865,17 +1912,17 @@
<context>
<name>QObject</name>
<message>
- <location filename="../bitcoinunits.cpp" line="+197"/>
+ <location filename="../bitcoinunits.cpp" line="+195"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+111"/>
+ <location filename="../guiutil.cpp" line="+108"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+687"/>
+ <location line="+702"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1985,20 +2032,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+185"/>
- <source>%1 didn&apos;t yet exit safely...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../modaloverlay.cpp" line="-29"/>
- <source>unknown</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>QObject::QObject</name>
- <message>
- <location filename="../bitcoin.cpp" line="-113"/>
+ <location filename="../bitcoin.cpp" line="+74"/>
<source>Error parsing command line arguments: %1.</source>
<translation type="unfinished"></translation>
</message>
@@ -2017,11 +2051,21 @@
<source>Error: %1</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+57"/>
+ <source>%1 didn&apos;t yet exit safely...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../modaloverlay.cpp" line="-36"/>
+ <source>unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>QRImageWidget</name>
<message>
- <location filename="../receiverequestdialog.cpp" line="+32"/>
+ <location filename="../qrimagewidget.cpp" line="+29"/>
<source>&amp;Save Image...</source>
<translation type="unfinished"></translation>
</message>
@@ -2031,6 +2075,21 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+13"/>
+ <source>Resulting URI too long, try to reduce the text for label / message.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Error encoding URI into QR Code.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+38"/>
+ <source>QR code support not available.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+32"/>
<source>Save QR Code</source>
<translation type="unfinished"></translation>
@@ -2056,7 +2115,7 @@
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+713"/>
+ <location line="+716"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
@@ -2078,7 +2137,7 @@
<translation>N/A</translation>
</message>
<message>
- <location line="-1427"/>
+ <location line="-1430"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2178,7 +2237,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+238"/>
+ <location line="+241"/>
<source>&amp;Reset</source>
<translation type="unfinished"></translation>
</message>
@@ -2206,8 +2265,8 @@
</message>
<message>
<location line="+65"/>
- <location filename="../rpcconsole.cpp" line="+501"/>
- <location line="+754"/>
+ <location filename="../rpcconsole.cpp" line="+498"/>
+ <location line="+757"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
@@ -2242,13 +2301,13 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1161"/>
- <location line="+1069"/>
+ <location line="-1164"/>
+ <location line="+1072"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-734"/>
+ <location line="-737"/>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation type="unfinished"></translation>
</message>
@@ -2263,7 +2322,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+641"/>
+ <location line="+644"/>
<source>Services</source>
<translation type="unfinished"></translation>
</message>
@@ -2313,7 +2372,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1166"/>
+ <location line="-1169"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2328,7 +2387,7 @@
<translation>&amp;Console</translation>
</message>
<message>
- <location line="+214"/>
+ <location line="+217"/>
<source>&amp;Network Traffic</source>
<translation type="unfinished"></translation>
</message>
@@ -2348,7 +2407,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../forms/debugwindow.ui" line="-318"/>
+ <location filename="../forms/debugwindow.ui" line="-321"/>
<source>Debug log file</source>
<translation>Debug log file</translation>
</message>
@@ -2358,7 +2417,7 @@
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-249"/>
+ <location filename="../rpcconsole.cpp" line="-252"/>
<source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
@@ -2396,7 +2455,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+161"/>
+ <location line="+164"/>
<source>Welcome to the %1 RPC console.</source>
<translation type="unfinished"></translation>
</message>
@@ -2513,43 +2572,43 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-39"/>
- <location line="+153"/>
- <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <location line="+136"/>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don&apos;t support them. When checked, an address compatible with older wallets will be created instead.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-59"/>
- <source>Clear all fields of the form.</source>
+ <location line="+3"/>
+ <source>Generate legacy address</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Clear</source>
+ <location line="-178"/>
+ <location line="+153"/>
+ <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+78"/>
- <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don&apos;t support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <location line="-76"/>
+ <source>&amp;Create new receiving address</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Generate native segwit (Bech32) address</source>
+ <location line="+17"/>
+ <source>Clear all fields of the form.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+61"/>
- <source>Requested payments history</source>
+ <location line="+3"/>
+ <source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-162"/>
- <source>&amp;Request payment</source>
+ <location line="+142"/>
+ <source>Requested payments history</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+187"/>
+ <location line="+25"/>
<source>Show the selected request (does the same as double clicking an entry)</source>
<translation type="unfinished"></translation>
</message>
@@ -2569,7 +2628,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../receivecoinsdialog.cpp" line="+47"/>
+ <location filename="../receivecoinsdialog.cpp" line="+45"/>
<source>Copy URI</source>
<translation type="unfinished"></translation>
</message>
@@ -2612,7 +2671,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../receiverequestdialog.cpp" line="+65"/>
+ <location filename="../receiverequestdialog.cpp" line="+63"/>
<source>Request payment to %1</source>
<translation type="unfinished"></translation>
</message>
@@ -2651,16 +2710,6 @@
<source>Wallet</source>
<translation type="unfinished">Wallet</translation>
</message>
- <message>
- <location line="+11"/>
- <source>Resulting URI too long, try to reduce the text for label / message.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Error encoding URI into QR Code.</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>RecentRequestsTableModel</name>
@@ -2704,7 +2753,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+593"/>
+ <location filename="../sendcoinsdialog.cpp" line="+600"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2891,7 +2940,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-505"/>
+ <location filename="../sendcoinsdialog.cpp" line="-512"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -2931,10 +2980,19 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+133"/>
- <location line="+5"/>
- <location line="+6"/>
- <location line="+4"/>
+ <location line="+117"/>
+ <source> from wallet &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <location line="+11"/>
+ <source>%1 to &apos;%2&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-6"/>
+ <location line="+10"/>
<source>%1 to %2</source>
<translation type="unfinished"></translation>
</message>
@@ -2954,12 +3012,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-61"/>
- <source>from wallet %1</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+40"/>
+ <location line="-21"/>
<source>Please, review your transaction.</source>
<translation type="unfinished"></translation>
</message>
@@ -2979,12 +3032,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+9"/>
+ <source>To review recipient list click &quot;Show Details...&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
<source>Confirm send coins</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+191"/>
+ <location line="+190"/>
<source>The recipient address is not valid. Please recheck.</source>
<translation type="unfinished"></translation>
</message>
@@ -3179,7 +3237,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>SendConfirmationDialog</name>
<message>
- <location filename="../sendcoinsdialog.cpp" line="+83"/>
+ <location filename="../sendcoinsdialog.cpp" line="+88"/>
<location line="+5"/>
<source>Yes</source>
<translation type="unfinished"></translation>
@@ -3399,7 +3457,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionDesc</name>
<message numerus="yes">
- <location filename="../transactiondesc.cpp" line="+35"/>
+ <location filename="../transactiondesc.cpp" line="+34"/>
<source>Open for %n more block(s)</source>
<translation>
<numerusform>Open for %n more block</numerusform>
@@ -3640,7 +3698,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+227"/>
+ <location filename="../transactiontablemodel.cpp" line="+223"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3776,7 +3834,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionView</name>
<message>
- <location filename="../transactionview.cpp" line="+71"/>
+ <location filename="../transactionview.cpp" line="+70"/>
<location line="+16"/>
<source>All</source>
<translation type="unfinished"></translation>
@@ -3975,12 +4033,30 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
- <location filename="../bitcoingui.cpp" line="+154"/>
+ <location filename="../bitcoingui.cpp" line="+155"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+ <name>WalletController</name>
+ <message>
+ <location filename="../walletcontroller.cpp" line="+70"/>
+ <source>Close wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Are you sure you wish to close wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>WalletFrame</name>
<message>
<location filename="../walletframe.cpp" line="+29"/>
@@ -4043,7 +4119,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+35"/>
+ <location line="+30"/>
<source>default wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4099,7 +4175,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+29"/>
+ <location filename="../bitcoinstrings.cpp" line="+28"/>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
<translation type="unfinished"></translation>
</message>
@@ -4119,7 +4195,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+74"/>
+ <location line="+70"/>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
<translation type="unfinished"></translation>
</message>
@@ -4129,22 +4205,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+30"/>
+ <location line="+31"/>
<source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-172"/>
- <source>Bitcoin Core</source>
- <translation type="unfinished">Bitcoin Core</translation>
+ <location line="-168"/>
+ <source>The %s developers</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>The %s developers</source>
+ <location line="+4"/>
+ <source>Can&apos;t generate a change-address key. No keys in the internal keypool and can&apos;t generate any keys.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+3"/>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
@@ -4199,7 +4275,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+7"/>
<source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
<translation type="unfinished"></translation>
</message>
@@ -4290,11 +4366,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+6"/>
- <source>Error: Disk space is low!</source>
- <translation>Error: Disk space is low!</translation>
- </message>
- <message>
- <location line="+1"/>
<source>Failed to listen on any port. Use -listen=0 if you want this.</source>
<translation>Failed to listen on any port. Use -listen=0 if you want this.</translation>
</message>
@@ -4314,7 +4385,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+1"/>
<source>Initialization sanity check failed. %s is shutting down.</source>
<translation type="unfinished"></translation>
</message>
@@ -4334,22 +4405,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+23"/>
<source>Specified blocks directory &quot;%s&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+26"/>
<source>Upgrading txindex database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-42"/>
+ <location line="-45"/>
<source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="-15"/>
+ <source>Error: Disk space is too low!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
<source>Loading banlist...</source>
<translation type="unfinished"></translation>
</message>
@@ -4364,7 +4440,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Prune mode is incompatible with -txindex.</source>
<translation type="unfinished"></translation>
</message>
@@ -4394,12 +4470,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+3"/>
<source>Unable to generate keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+4"/>
<source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
@@ -4419,12 +4495,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Verifying blocks...</translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+2"/>
<source>Wallet needed to be rewritten: restart %s to complete</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-158"/>
+ <location line="-155"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
@@ -4439,7 +4515,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+35"/>
<source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
<translation type="unfinished"></translation>
</message>
@@ -4454,17 +4530,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+2"/>
<source>Error: Disk space is low for %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Information</source>
- <translation>Information</translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+8"/>
<source>Invalid -onion address or hostname: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -4489,7 +4560,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+3"/>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation type="unfinished"></translation>
</message>
@@ -4556,26 +4632,26 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+2"/>
- <source>Unable to generate initial keys</source>
+ <source>Unable to create the PID file &apos;%s&apos;: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Verifying wallet(s)...</source>
+ <location line="+1"/>
+ <source>Unable to generate initial keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Wallet %s resides outside wallet directory %s</source>
+ <location line="+3"/>
+ <source>Unknown -blockfilterindex value %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Warning</source>
- <translation>Warning</translation>
+ <location line="+7"/>
+ <source>Verifying wallet(s)...</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Warning: unknown new rules activated (versionbit %i)</source>
<translation type="unfinished"></translation>
</message>
@@ -4585,7 +4661,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-182"/>
+ <location line="-178"/>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
<translation type="unfinished"></translation>
</message>
@@ -4606,11 +4682,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+14"/>
- <source>Warning: Unknown block versions being mined! It&apos;s possible unknown rules are in effect</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
<source>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.</source>
<translation type="unfinished"></translation>
</message>
@@ -4625,12 +4696,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+20"/>
<source>Keypool ran out, please call keypoolrefill first</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+21"/>
<source>Starting network threads...</source>
<translation type="unfinished"></translation>
</message>
@@ -4665,22 +4736,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+10"/>
<source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="-48"/>
+ <location line="-51"/>
<source>Insufficient funds</source>
<translation>Insufficient funds</translation>
</message>
<message>
- <location line="-120"/>
- <source>Can&apos;t generate a change-address key. Private keys are disabled for this wallet.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+8"/>
+ <location line="-107"/>
<source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
<translation type="unfinished"></translation>
</message>
@@ -4695,12 +4761,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+21"/>
<source>Cannot write to data directory &apos;%s&apos;; check permissions.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+39"/>
+ <location line="+37"/>
<source>Loading block index...</source>
<translation>Loading block index...</translation>
</message>
@@ -4710,24 +4776,19 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Loading wallet...</translation>
</message>
<message>
- <location line="-42"/>
+ <location line="-40"/>
<source>Cannot downgrade wallet</source>
<translation>Cannot downgrade wallet</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+49"/>
<source>Rescanning...</source>
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-42"/>
+ <location line="-41"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
- <message>
- <location line="+12"/>
- <source>Error</source>
- <translation>Error</translation>
- </message>
</context>
</TS>
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/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 40dc7bf400..57cafaaac0 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -87,12 +87,12 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
/* Display elements init */
QDir translations(":translations");
- ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME)));
- ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME)));
+ ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(PACKAGE_NAME));
+ ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(PACKAGE_NAME));
- ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME)));
+ ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(PACKAGE_NAME));
- ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME)));
+ ui->lang->setToolTip(ui->lang->toolTip().arg(PACKAGE_NAME));
ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant(""));
for (const QString &langStr : translations.entryList())
{
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/res/movies/makespinner.sh b/src/qt/res/movies/makespinner.sh
index f47c66e02c..3507837da9 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/movies/makespinner.sh
@@ -9,6 +9,6 @@ FRAMEDIR=$(dirname $0)
for i in {0..35}
do
frame=$(printf "%03d" $i)
- angle=$(($i * 10))
+ angle=$((i * 10))
convert $FRAMEDIR/../src/spinner.png -background "rgba(0,0,0,0.0)" -distort SRT $angle $FRAMEDIR/spinner-$frame.png
done
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 071a197c3c..cdf84eae9a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -457,7 +457,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
QChar nonbreaking_hyphen(8209);
ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
- ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
+ ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME));
if (platformStyle->getImagesOnButtons()) {
ui->openDebugLogfileButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
@@ -804,7 +804,7 @@ void RPCConsole::clear(bool clearHistory)
QString clsKey = "Ctrl-L";
#endif
- message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) + "<br>" +
+ message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(PACKAGE_NAME) + "<br>" +
tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" +
tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" +
tr("For more information on using this console type %1.").arg("<b>help-console</b>") +
@@ -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 0126a2920e..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>
@@ -39,7 +40,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();
// define text to place
- QString titleText = tr(PACKAGE_NAME);
+ QString titleText = PACKAGE_NAME;
QString versionText = QString("Version %1").arg(QString::fromStdString(FormatFullVersion()));
QString copyrightText = QString::fromUtf8(CopyrightHolders(strprintf("\xc2\xA9 %u-%u ", 2009, COPYRIGHT_YEAR)).c_str());
QString titleAddText = networkStyle->getTitleAddText();
@@ -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/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index ea1019ad1d..11a518ebd2 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -12,7 +12,6 @@
#include <key.h>
#include <key_io.h>
-#include <pubkey.h>
#include <wallet/wallet.h>
#include <QApplication>
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index b9bf933ee5..49e9e072a8 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -5,11 +5,14 @@
#include <qt/test/apptests.h>
#include <chainparams.h>
+#include <key.h>
#include <qt/bitcoin.h>
#include <qt/bitcoingui.h>
#include <qt/networkstyle.h>
#include <qt/rpcconsole.h>
#include <shutdown.h>
+#include <test/setup_common.h>
+#include <univalue.h>
#include <validation.h>
#if defined(HAVE_CONFIG_H)
@@ -25,9 +28,6 @@
#include <QtGlobal>
#include <QtTest/QtTestWidgets>
#include <QtTest/QtTestGui>
-#include <new>
-#include <string>
-#include <univalue.h>
namespace {
//! Call getblockchaininfo RPC and check first field of JSON output.
@@ -62,6 +62,10 @@ void AppTests::appTests()
}
#endif
+ BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
+ ECC_Stop(); // Already started by the common test setup, so stop it to avoid interference
+ LogInstance().DisconnectTestLogger();
+
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
QScopedPointer<const NetworkStyle> style(
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index f0eca899fc..6cafe05461 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -13,7 +13,7 @@
#include <random.h>
#include <script/script.h>
#include <script/standard.h>
-#include <util/system.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <openssl/x509.h>
@@ -66,7 +66,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig
void PaymentServerTests::paymentServerTests()
{
- SelectParams(CBaseChainParams::MAIN);
+ BasicTestingSetup testing_setup(CBaseChainParams::MAIN);
auto node = interfaces::MakeNode();
OptionsModel optionsModel(*node);
PaymentServer* server = new PaymentServer(nullptr, false);
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 7a10ada438..3c2ffa6c00 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -4,7 +4,6 @@
#include <qt/test/rpcnestedtests.h>
-#include <fs.h>
#include <interfaces/node.h>
#include <rpc/server.h>
#include <qt/rpcconsole.h>
diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h
index e33f4e3da1..97143ff78a 100644
--- a/src/qt/test/rpcnestedtests.h
+++ b/src/qt/test/rpcnestedtests.h
@@ -8,9 +8,6 @@
#include <QObject>
#include <QTest>
-#include <txdb.h>
-#include <txmempool.h>
-
class RPCNestedTests : public QObject
{
Q_OBJECT
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index a2bf53973b..6bda8dc6eb 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -6,14 +6,13 @@
#include <config/bitcoin-config.h>
#endif
-#include <chainparams.h>
#include <interfaces/node.h>
#include <qt/bitcoin.h>
#include <qt/test/apptests.h>
#include <qt/test/rpcnestedtests.h>
-#include <util/system.h>
#include <qt/test/uritests.h>
#include <qt/test/compattests.h>
+#include <test/setup_common.h>
#ifdef ENABLE_WALLET
#include <qt/test/addressbooktests.h>
@@ -43,19 +42,19 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
#endif
#endif
-extern void noui_connect();
-
// This is all you need to run all the tests
int main(int argc, char *argv[])
{
- SetupEnvironment();
- SetupNetworking();
- SelectParams(CBaseChainParams::REGTEST);
- noui_connect();
- ClearDatadirCache();
- fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000));
- fs::create_directories(pathTemp);
- gArgs.ForceSetArg("-datadir", pathTemp.string());
+ // Initialize persistent globals with the testing setup state for sanity.
+ // E.g. -datadir in gArgs is set to a temp directory dummy value (instead
+ // of defaulting to the default datadir), or globalChainParams is set to
+ // regtest params.
+ //
+ // All tests must use their own testing setup (if needed).
+ {
+ BasicTestingSetup dummy{CBaseChainParams::REGTEST};
+ }
+
auto node = interfaces::MakeNode();
bool fInvalid = false;
@@ -109,7 +108,5 @@ int main(int argc, char *argv[])
}
#endif
- fs::remove_all(pathTemp);
-
return fInvalid;
}
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/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index 1588be8da3..006007be63 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.cpp
@@ -104,6 +104,7 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)
}
}
+ painter.setRenderHint(QPainter::Antialiasing);
if(!vSamplesIn.empty()) {
QPainterPath p;
paintPath(p, vSamplesIn);
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/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index aa810690c9..6509a701f3 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -36,7 +36,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
{
ui->setupUi(this);
- QString version = tr(PACKAGE_NAME) + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion());
+ QString version = QString{PACKAGE_NAME} + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion());
/* On x86 add a bit specifier to the version so that users can distinguish between
* 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambiguous.
*/
@@ -48,7 +48,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
if (about)
{
- setWindowTitle(tr("About %1").arg(tr(PACKAGE_NAME)));
+ setWindowTitle(tr("About %1").arg(PACKAGE_NAME));
std::string licenseInfo = LicenseInfo();
/// HTML-format the license message from the core
@@ -150,7 +150,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
{
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(new QLabel(
- tr("%1 is shutting down...").arg(tr(PACKAGE_NAME)) + "<br /><br />" +
+ tr("%1 is shutting down...").arg(PACKAGE_NAME) + "<br /><br />" +
tr("Do not shut down the computer until this window disappears.")));
setLayout(layout);
}
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 019bd65823..2aedb77798 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -40,19 +40,22 @@ WalletController::~WalletController()
m_activity_thread.wait();
}
-std::vector<WalletModel*> WalletController::getWallets() const
+std::vector<WalletModel*> WalletController::getOpenWallets() const
{
QMutexLocker locker(&m_mutex);
return m_wallets;
}
-std::vector<std::string> WalletController::getWalletsAvailableToOpen() const
+std::map<std::string, bool> WalletController::listWalletDir() const
{
QMutexLocker locker(&m_mutex);
- std::vector<std::string> wallets = m_node.listWalletDir();
+ std::map<std::string, bool> wallets;
+ for (const std::string& name : m_node.listWalletDir()) {
+ wallets[name] = false;
+ }
for (WalletModel* wallet_model : m_wallets) {
- auto it = std::remove(wallets.begin(), wallets.end(), wallet_model->wallet().getWalletName());
- if (it != wallets.end()) wallets.erase(it);
+ auto it = wallets.find(wallet_model->wallet().getWalletName());
+ if (it != wallets.end()) it->second = true;
}
return wallets;
}
@@ -121,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/walletcontroller.h b/src/qt/walletcontroller.h
index 19b3a82253..03039dd795 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -8,7 +8,7 @@
#include <qt/walletmodel.h>
#include <sync.h>
-#include <list>
+#include <map>
#include <memory>
#include <vector>
@@ -40,8 +40,12 @@ public:
WalletController(interfaces::Node& node, const PlatformStyle* platform_style, OptionsModel* options_model, QObject* parent);
~WalletController();
- std::vector<WalletModel*> getWallets() const;
- std::vector<std::string> getWalletsAvailableToOpen() const;
+ //! Returns wallet models currently open.
+ std::vector<WalletModel*> getOpenWallets() const;
+
+ //! Returns all wallet names in the wallet dir mapped to whether the wallet
+ //! is loaded.
+ std::map<std::string, bool> listWalletDir() const;
OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr);
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index a2b295df21..49a13330ec 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -221,11 +221,12 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return TransactionCreationFailed;
}
- // reject absurdly high fee. (This can never happen because the
- // wallet caps the fee at m_default_max_tx_fee. This merely serves as a
- // belt-and-suspenders check)
- 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);
@@ -260,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());
}
@@ -376,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,
@@ -394,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/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 50c4589d9f..b7dcd59c6d 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -14,7 +14,6 @@
#include <core_io.h>
#include <hash.h>
#include <index/blockfilterindex.h>
-#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -42,9 +41,9 @@
#include <boost/thread/thread.hpp> // boost::thread::interrupt
+#include <condition_variable>
#include <memory>
#include <mutex>
-#include <condition_variable>
struct CUpdatedBlock
{
@@ -166,10 +165,9 @@ 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 number of blocks in the longest blockchain.\n",
+ "\nReturns the height of the most-work fully-validated chain.\n"
+ "The genesis block has height 0.\n",
{},
RPCResult{
"n (numeric) The current block count\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,10 +184,8 @@ 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 longest blockchain.\n",
+ "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
RPCResult{
"\"hex\" (string) the block hash, hex-encoded\n"
@@ -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,15 +1256,13 @@ 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",
{},
RPCResult{
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
- " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
+ " \"blocks\": xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n"
" \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
" \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
@@ -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});
@@ -2247,41 +2183,12 @@ UniValue scantxoutset(const JSONRPCRequest& request)
// loop through the scan objects
for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
- std::string desc_str;
- std::pair<int64_t, int64_t> range = {0, 1000};
- if (scanobject.isStr()) {
- desc_str = scanobject.get_str();
- } else if (scanobject.isObject()) {
- UniValue desc_uni = find_value(scanobject, "desc");
- if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
- desc_str = desc_uni.get_str();
- UniValue range_uni = find_value(scanobject, "range");
- if (!range_uni.isNull()) {
- range = ParseDescriptorRange(range_uni);
- }
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
- }
-
FlatSigningProvider provider;
- auto desc = Parse(desc_str, provider);
- if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
- }
- if (!desc->IsRange()) {
- range.first = 0;
- range.second = 0;
- }
- for (int i = range.first; i <= range.second; ++i) {
- std::vector<CScript> scripts;
- if (!desc->Expand(i, provider, scripts, provider)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
- }
- for (const auto& script : scripts) {
- std::string inferred = InferDescriptor(script, provider)->ToString();
- needles.emplace(script);
- descriptors.emplace(std::move(script), std::move(inferred));
- }
+ auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
+ for (const auto& script : scripts) {
+ std::string inferred = InferDescriptor(script, provider)->ToString();
+ needles.emplace(script);
+ descriptors.emplace(std::move(script), std::move(inferred));
}
}
@@ -2330,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",
{
@@ -2347,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/client.cpp b/src/rpc/client.cpp
index 8f6bdc21aa..3cd661e067 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -28,6 +28,7 @@ public:
static const CRPCConvertParam vRPCConvertParams[] =
{
{ "setmocktime", 0, "timestamp" },
+ { "utxoupdatepsbt", 1, "descriptors" },
{ "generatetoaddress", 0, "nblocks" },
{ "generatetoaddress", 2, "maxtries" },
{ "getnetworkhashps", 0, "nblocks" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 477f05f46c..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);
@@ -103,7 +101,6 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
- static const int nInnerLoopCount = 0x10000;
int nHeightEnd = 0;
int nHeight = 0;
@@ -124,14 +121,14 @@ static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, ui
LOCK(cs_main);
IncrementExtraNonce(pblock, ::ChainActive().Tip(), nExtraNonce);
}
- while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
+ while (nMaxTries > 0 && pblock->nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()) && !ShutdownRequested()) {
++pblock->nNonce;
--nMaxTries;
}
- if (nMaxTries == 0) {
+ if (nMaxTries == 0 || ShutdownRequested()) {
break;
}
- if (pblock->nNonce == nInnerLoopCount) {
+ if (pblock->nNonce == std::numeric_limits<uint32_t>::max()) {
continue;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
@@ -145,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",
{
@@ -163,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;
@@ -183,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.",
{},
@@ -204,8 +197,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
- }.ToString());
- }
+ }.Check(request);
LOCK(cs_main);
@@ -225,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",
{
@@ -245,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);
@@ -291,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"
@@ -302,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",
@@ -367,7 +355,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
- }.ToString());
+ }.Check(request);
LOCK(cs_main);
@@ -469,7 +457,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
- // Release the wallet and main lock while waiting
+ // Release lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
@@ -480,6 +468,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
{
// Timeout: Check transactions for update
+ // without holding ::mempool.cs to avoid deadlocks
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
break;
checktxtime += std::chrono::seconds(10);
@@ -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 9da24afe79..0ab504de06 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,
@@ -426,7 +413,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
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 +470,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 +501,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 +532,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 +589,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 +606,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 +671,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 +727,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 +736,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 +759,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 +784,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,
@@ -848,7 +822,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
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 +855,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 +934,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 +1030,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("decodepsbt", "\"psbt\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -1239,8 +1207,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 +1223,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 +1255,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 +1276,7 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("finalizepsbt", "\"psbt\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
@@ -1348,8 +1312,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 +1354,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.ToString());
+ }.Check(request);
RPCTypeCheck(request.params, {
@@ -1424,7 +1386,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 +1410,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,22 +1453,26 @@ UniValue converttopsbt(const JSONRPCRequest& request)
UniValue utxoupdatepsbt(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
- throw std::runtime_error(
RPCHelpMan{"utxoupdatepsbt",
- "\nUpdates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool.\n",
+ "\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
- {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
+ {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
+ {"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of either strings or objects", {
+ {"", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with an output descriptor and extra information", {
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, "1000", "Up to what index HD chains should be explored (either end or [begin,end])"},
+ }},
+ }},
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
- RPCTypeCheck(request.params, {UniValue::VSTR}, true);
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
// Unserialize the transactions
PartiallySignedTransaction psbtx;
@@ -1519,6 +1481,17 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
+ // Parse descriptors, if any.
+ FlatSigningProvider provider;
+ if (!request.params[1].isNull()) {
+ auto descs = request.params[1].get_array();
+ for (size_t i = 0; i < descs.size(); ++i) {
+ EvalDescriptorStringOrObject(descs[i], provider);
+ }
+ }
+ // We don't actually need private keys further on; hide them as a precaution.
+ HidingSigningProvider public_provider(&provider, /* nosign */ true, /* nobip32derivs */ false);
+
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
@@ -1545,11 +1518,19 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
- std::vector<std::vector<unsigned char>> solutions_data;
- txnouttype which_type = Solver(coin.out.scriptPubKey, solutions_data);
- if (which_type == TX_WITNESS_V0_SCRIPTHASH || which_type == TX_WITNESS_V0_KEYHASH || which_type == TX_WITNESS_UNKNOWN) {
+ if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
input.witness_utxo = coin.out;
}
+
+ // Update script/keypath information using descriptor data.
+ // Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
+ // we don't actually care about those here, in fact.
+ SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
+ }
+
+ // Update script/keypath information using descriptor data.
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ UpdatePSBTOutput(public_provider, psbtx, i);
}
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -1559,8 +1540,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",
@@ -1575,8 +1554,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VARR}, true);
@@ -1633,8 +1611,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",
{
@@ -1668,8 +1644,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("analyzepsbt", "\"psbt\"")
- }}.ToString());
- }
+ }}.Check(request);
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -1750,7 +1725,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
- { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt"} },
+ { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt", "descriptors"} },
{ "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} },
{ "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} },
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 9c4cdc3a90..1c96d01232 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -8,11 +8,12 @@
#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>
@@ -148,7 +149,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()) {
@@ -221,6 +222,9 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
keystore->AddCScript(GetScriptForWitness(witnessScript));
}
+ if (rs.isNull() && ws.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");
+ }
}
}
}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index c115d33a77..d198887b93 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,7 +24,7 @@ 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);
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 ca17d379bc..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",
{},
@@ -210,15 +206,14 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
" \"method\" (string) The name of the RPC command \n"
" \"duration\" (numeric) The running time in microseconds\n"
" },...\n"
- " ]\n"
+ " ],\n"
+ " \"logpath\": \"xxx\" (string) The complete file path to the debug log\n"
"}\n"
},
RPCExamples{
HelpExampleCli("getrpcinfo", "")
+ HelpExampleRpc("getrpcinfo", "")},
- }.ToString()
- );
- }
+ }.Check(request);
LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
@@ -232,6 +227,10 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("active_commands", active_commands);
+ const std::string path = LogInstance().m_file_path.string();
+ UniValue log_path(UniValue::VSTR, path);
+ result.pushKV("logpath", log_path);
+
return result;
}
@@ -329,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 4642cf16b1..de90276677 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -3,9 +3,10 @@
// 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>
#include <util/strencodings.h>
@@ -130,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)) {
@@ -152,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) {
@@ -697,3 +698,40 @@ std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
}
return {low, high};
}
+
+std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
+{
+ std::string desc_str;
+ std::pair<int64_t, int64_t> range = {0, 1000};
+ if (scanobject.isStr()) {
+ desc_str = scanobject.get_str();
+ } else if (scanobject.isObject()) {
+ UniValue desc_uni = find_value(scanobject, "desc");
+ if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
+ desc_str = desc_uni.get_str();
+ UniValue range_uni = find_value(scanobject, "range");
+ if (!range_uni.isNull()) {
+ range = ParseDescriptorRange(range_uni);
+ }
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
+ }
+
+ auto desc = Parse(desc_str, provider);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
+ }
+ if (!desc->IsRange()) {
+ range.first = 0;
+ range.second = 0;
+ }
+ std::vector<CScript> ret;
+ for (int i = range.first; i <= range.second; ++i) {
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
+ }
+ std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
+ }
+ return ret;
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 0eb2fef5c3..4c3322b879 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -9,6 +9,9 @@
#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>
#include <univalue.h>
@@ -17,7 +20,7 @@
#include <boost/variant.hpp>
-class CKeyStore;
+class FillableSigningProvider;
class CPubKey;
class CScript;
struct InitInterfaces;
@@ -70,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);
@@ -84,6 +87,9 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
//! Parse a JSON range specified as int64, or [int64, int64]
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
+/** Evaluate a descriptor given as a string, or as a {"desc":...,"range":...} object, with default range of 1000. */
+std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider);
+
struct RPCArg {
enum class Type {
OBJ,
@@ -221,7 +227,7 @@ struct RPCResults {
struct RPCExamples {
const std::string m_examples;
- RPCExamples(
+ explicit RPCExamples(
std::string examples)
: m_examples(std::move(examples))
{
@@ -237,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 36dd68a3d8..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,49 +448,18 @@ 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
+bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
- 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;
+ std::vector<valtype> solutions;
+ auto whichtype = Solver(script, solutions);
+ if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ if (whichtype == TX_SCRIPTHASH) {
+ auto h160 = uint160(solutions[0]);
+ CScript subscript;
+ if (provider.GetCScript(h160, subscript)) {
+ whichtype = Solver(subscript, solutions);
+ if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ }
+ }
+ return false;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index f746325b90..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:
@@ -232,4 +166,7 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
* Solvability is unrelated to whether we consider this output to be ours. */
bool IsSolvable(const SigningProvider& provider, const CScript& script);
+/** Check whether a scriptPubKey is known to be segwit. */
+bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
+
#endif // BITCOIN_SCRIPT_SIGN_H
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/support/cleanse.cpp b/src/support/cleanse.cpp
index 17a4a4c2b2..ecb00510f7 100644
--- a/src/support/cleanse.cpp
+++ b/src/support/cleanse.cpp
@@ -11,33 +11,25 @@
#include <Windows.h> // For SecureZeroMemory.
#endif
-/* Compilers have a bad habit of removing "superfluous" memset calls that
- * are trying to zero memory. For example, when memset()ing a buffer and
- * then free()ing it, the compiler might decide that the memset is
- * unobservable and thus can be removed.
- *
- * Previously we used OpenSSL which tried to stop this by a) implementing
- * memset in assembly on x86 and b) putting the function in its own file
- * for other platforms.
- *
- * This change removes those tricks in favour of using asm directives to
- * scare the compiler away. As best as our compiler folks can tell, this is
- * sufficient and will continue to be so.
- *
- * Adam Langley <agl@google.com>
- * Commit: ad1907fe73334d6c696c8539646c21b11178f20f
- * BoringSSL (LICENSE: ISC)
- */
void memory_cleanse(void *ptr, size_t len)
{
- std::memset(ptr, 0, len);
-
- /* As best as we can tell, this is sufficient to break any optimisations that
- might try to eliminate "superfluous" memsets. If there's an easy way to
- detect memset_s, it would be better to use that. */
#if defined(_MSC_VER)
+ /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
SecureZeroMemory(ptr, len);
#else
+ std::memset(ptr, 0, len);
+
+ /* Memory barrier that scares the compiler away from optimizing out the memset.
+ *
+ * Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f
+ * in BoringSSL (ISC License):
+ * As best as we can tell, this is sufficient to break any optimisations that
+ * might try to eliminate "superfluous" memsets.
+ * This method is used in memzero_explicit() the Linux kernel, too. Its advantage is that it
+ * is pretty efficient because the compiler can still implement the memset() efficiently,
+ * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
+ * Yang et al. (USENIX Security 2017) for more background.
+ */
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
}
diff --git a/src/support/cleanse.h b/src/support/cleanse.h
index 5298214e44..b03520315d 100644
--- a/src/support/cleanse.h
+++ b/src/support/cleanse.h
@@ -8,7 +8,8 @@
#include <stdlib.h>
-// Attempt to overwrite data in the specified memory span.
+/** Secure overwrite a buffer (possibly containing secret data) with zero-bytes. The write
+ * operation will not be optimized out by the compiler. */
void memory_cleanse(void *ptr, size_t len);
#endif // BITCOIN_SUPPORT_CLEANSE_H
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/allocator_tests.cpp b/src/test/allocator_tests.cpp
index f255691704..e333763f27 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/memory.h>
#include <util/system.h>
-#include <support/allocators/secure.h>
#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 809c627d27..9ac87261b6 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -11,7 +11,6 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <string>
-#include <version.h>
#include <test/setup_common.h>
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 0c0423c0db..662878750e 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -4,9 +4,10 @@
#include <boost/test/unit_test.hpp>
+#include <clientversion.h>
#include <key.h>
#include <key_io.h>
-#include <uint256.h>
+#include <streams.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 13afcca375..ca75563ef0 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -2,6 +2,7 @@
#include <stdlib.h>
+#include <chain.h>
#include <rpc/blockchain.h>
#include <test/setup_common.h>
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index f57e1a0ebd..5ce8e6feb0 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -6,7 +6,7 @@
#include <consensus/merkle.h>
#include <chainparams.h>
#include <pow.h>
-#include <random.h>
+#include <streams.h>
#include <test/setup_common.h>
@@ -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/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 7ba483173c..cf87aa9303 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -10,6 +10,7 @@
#include <pow.h>
#include <test/setup_common.h>
#include <script/standard.h>
+#include <util/time.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -267,8 +268,6 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
{
- SetDataDir("tempdir");
-
BlockFilterIndex* filter_index;
filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 408a7fbda4..d796444419 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
@@ -17,8 +18,6 @@
#include <condition_variable>
#include <unordered_set>
-#include <memory>
-#include <random.h>
// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
// otherwise.
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 2c42596edc..948591196c 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -3,8 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <attributes.h>
+#include <clientversion.h>
#include <coins.h>
#include <script/standard.h>
+#include <streams.h>
#include <test/setup_common.h>
#include <uint256.h>
#include <undo.h>
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/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 0bde92c18d..efcadd51fc 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -4,8 +4,8 @@
#include <dbwrapper.h>
#include <uint256.h>
-#include <random.h>
#include <test/setup_common.h>
+#include <util/memory.h>
#include <memory>
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = InsecureRand256();
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper_batch").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = SetDataDir(std::string("dbwrapper_iterator").append(obfuscate ? "_true" : "_false"));
+ fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = SetDataDir("existing_data_no_obfuscate");
+ fs::path ph = GetDataDir() / "existing_data_no_obfuscate";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = SetDataDir("existing_data_reindex");
+ fs::path ph = GetDataDir() / "existing_data_reindex";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- fs::path ph = SetDataDir("iterator_ordering");
+ fs::path ph = GetDataDir() / "iterator_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- fs::path ph = SetDataDir("iterator_string_ordering");
+ fs::path ph = GetDataDir() / "iterator_string_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 3a2844861b..a50d6854f8 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -6,12 +6,15 @@
#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>
+#include <util/time.h>
#include <validation.h>
#include <test/setup_common.h>
@@ -367,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/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 1db2f8054c..740d805cce 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -2,8 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <clientversion.h>
#include <flatfile.h>
+#include <streams.h>
#include <test/setup_common.h>
+#include <util/system.h>
#include <boost/test/unit_test.hpp>
@@ -11,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(flatfile_filename)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFilePos pos(456, 789);
@@ -24,7 +27,7 @@ BOOST_AUTO_TEST_CASE(flatfile_filename)
BOOST_AUTO_TEST_CASE(flatfile_open)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 16 * 1024);
std::string line1("A purely peer-to-peer version of electronic cash would allow online "
@@ -85,7 +88,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
BOOST_AUTO_TEST_CASE(flatfile_allocate)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
@@ -105,7 +108,7 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate)
BOOST_AUTO_TEST_CASE(flatfile_flush)
{
- auto data_dir = SetDataDir("flatfile_test");
+ const auto data_dir = GetDataDir();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 6bd6bb1be3..6d5a6641f0 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -4,6 +4,7 @@
//
#include <fs.h>
#include <test/setup_common.h>
+#include <util/system.h>
#include <boost/test/unit_test.hpp>
@@ -11,7 +12,7 @@ BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(fsbridge_fstream)
{
- fs::path tmpfolder = SetDataDir("fsbridge_fstream");
+ fs::path tmpfolder = GetDataDir();
// tmpfile1 should be the same as tmpfile2
fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃";
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 97d7633715..9364ac4a32 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <addrdb.h>
#include <addrman.h>
#include <blockencodings.h>
#include <chain.h>
@@ -11,8 +12,6 @@
#include <net.h>
#include <primitives/block.h>
#include <protocol.h>
-#include <pubkey.h>
-#include <script/script.h>
#include <streams.h>
#include <undo.h>
#include <version.h>
@@ -20,8 +19,6 @@
#include <stdint.h>
#include <unistd.h>
-#include <algorithm>
-#include <memory>
#include <vector>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 8b03a7e46e..4e009d9b54 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_TEST_FUZZ_FUZZ_H
#define BITCOIN_TEST_FUZZ_FUZZ_H
-#include <functional>
#include <stdint.h>
#include <vector>
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index 2c0bfa360c..9b90d66755 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <script/interpreter.h>
-#include <script/script.h>
#include <streams.h>
#include <version.h>
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 325b7002f2..d91fcb0034 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -2,13 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <clientversion.h>
#include <crypto/siphash.h>
#include <hash.h>
#include <util/strencodings.h>
#include <test/setup_common.h>
-#include <vector>
-
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(hash_tests, BasicTestingSetup)
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index 8b508ed7f7..abcfc4547b 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -3,13 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
-#include <base58.h>
-#include <script/script.h>
#include <uint256.h>
#include <util/system.h>
-#include <util/strencodings.h>
#include <test/setup_common.h>
-#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 1b95105eab..3e99dcaa40 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -5,7 +5,6 @@
#include <key.h>
#include <key_io.h>
-#include <script/script.h>
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 0f74b379c0..fe5d31b7d3 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -5,15 +5,17 @@
#include <policy/policy.h>
#include <txmempool.h>
#include <util/system.h>
+#include <util/time.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
-#include <list>
#include <vector>
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 9a182d7bd3..05d7f76983 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -9,12 +9,12 @@
#include <consensus/tx_verify.h>
#include <miner.h>
#include <policy/policy.h>
-#include <pubkey.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/time.h>
#include <validation.h>
#include <test/setup_common.h>
@@ -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 10a732d64d..7c60abb93f 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -3,12 +3,13 @@
// 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>
@@ -173,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/net_tests.cpp b/src/test/net_tests.cpp
index 54d18c0a1c..fed65afdbf 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,16 +1,19 @@
// Copyright (c) 2012-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 <addrdb.h>
#include <addrman.h>
+#include <clientversion.h>
#include <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
-#include <hash.h>
#include <serialize.h>
#include <streams.h>
#include <net.h>
#include <netbase.h>
#include <chainparams.h>
+#include <util/memory.h>
#include <util/system.h>
#include <memory>
@@ -89,7 +92,6 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port)
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
- SetDataDir("caddrdb_read");
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
@@ -135,7 +137,6 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
- SetDataDir("caddrdb_read_corrupted");
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 149094fc00..016a4f471b 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -7,6 +7,7 @@
#include <txmempool.h>
#include <uint256.h>
#include <util/system.h>
+#include <util/time.h>
#include <test/setup_common.h>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 653433bfce..1123d4202c 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -5,7 +5,6 @@
#include <chain.h>
#include <chainparams.h>
#include <pow.h>
-#include <random.h>
#include <util/system.h>
#include <test/setup_common.h>
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 2b01acf7fa..41ca8029e5 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -14,8 +14,6 @@
#include <test/setup_common.h>
-#include <vector>
-
#include <boost/test/unit_test.hpp>
static std::map<void*, short> tags;
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 63bfe1d346..5ae0812243 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -9,8 +9,8 @@
#include <core_io.h>
#include <init.h>
#include <interfaces/chain.h>
-
#include <test/setup_common.h>
+#include <util/time.h>
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
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 195283f89f..412a57dd9d 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -3,9 +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/script_error.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 4798909e2f..84a70fe78b 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -6,20 +6,20 @@
#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>
#include <rpc/util.h>
+#include <streams.h>
#if defined(HAVE_CONSENSUS_LIB)
#include <script/bitcoinconsensus.h>
#endif
-#include <fstream>
#include <stdint.h>
#include <string>
#include <vector>
@@ -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/scriptnum10.h b/src/test/scriptnum10.h
index e763b64275..2c89a18331 100644
--- a/src/test/scriptnum10.h
+++ b/src/test/scriptnum10.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_TEST_SCRIPTNUM10_H
#define BITCOIN_TEST_SCRIPTNUM10_H
-#include <algorithm>
#include <limits>
#include <stdexcept>
#include <stdint.h>
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 2fab309aa4..8a8620938e 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -6,6 +6,7 @@
#include <streams.h>
#include <hash.h>
#include <test/setup_common.h>
+#include <util/strencodings.h>
#include <stdint.h>
diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp
index b11d090f67..de877fd167 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/setup_common.cpp
@@ -10,16 +10,25 @@
#include <consensus/params.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
+#include <init.h>
#include <miner.h>
-#include <net_processing.h>
+#include <net.h>
#include <noui.h>
#include <pow.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <script/sigcache.h>
#include <streams.h>
+#include <txdb.h>
+#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;
@@ -34,6 +43,13 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
: m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
+ fs::create_directories(m_path_root);
+ gArgs.ForceSetArg("-datadir", m_path_root.string());
+ ClearDatadirCache();
+ SelectParams(chainName);
+ gArgs.ForceSetArg("-printtoconsole", "0");
+ InitLogging();
+ LogInstance().StartLogging();
SHA256AutoDetect();
ECC_Start();
SetupEnvironment();
@@ -41,7 +57,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
- SelectParams(chainName);
static bool noui_connected = false;
if (!noui_connected) {
noui_connect();
@@ -51,27 +66,17 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::~BasicTestingSetup()
{
+ LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
ECC_Stop();
}
-fs::path BasicTestingSetup::SetDataDir(const std::string& name)
-{
- fs::path ret = m_path_root / name;
- fs::create_directories(ret);
- gArgs.ForceSetArg("-datadir", ret.string());
- return ret;
-}
-
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- SetDataDir("tempdir");
const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
-
RegisterAllCoreRPCCommands(tableRPC);
- ClearDatadirCache();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
diff --git a/src/test/setup_common.h b/src/test/setup_common.h
index 893eca216d..6c9494898c 100644
--- a/src/test/setup_common.h
+++ b/src/test/setup_common.h
@@ -11,10 +11,8 @@
#include <pubkey.h>
#include <random.h>
#include <scheduler.h>
-#include <txdb.h>
#include <txmempool.h>
-#include <memory>
#include <type_traits>
#include <boost/thread.hpp>
@@ -54,22 +52,19 @@ static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); }
static constexpr CAmount CENT{1000000};
/** Basic testing setup.
- * This just configures logging and chain parameters.
+ * This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
-
- fs::path SetDataDir(const std::string& name);
-
private:
const fs::path m_path_root;
};
/** Testing setup that configures a complete environment.
- * Included are data directory, coins database, script check threads setup.
+ * Included are coins database, script check threads setup.
*/
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 5c12ec13d2..a32f2cda92 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
-#include <consensus/validation.h>
#include <pubkey.h>
#include <key.h>
#include <script/script.h>
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 4e37199c63..b812cef801 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
-#include <support/allocators/zeroafterfree.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index f5ff18c055..34192c6b6a 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -12,14 +12,15 @@
#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>
#include <util/strencodings.h>
#include <map>
@@ -288,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);
@@ -321,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);
@@ -345,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;
@@ -422,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());
@@ -506,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);
@@ -681,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/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 26ae7be202..2356e0ccdc 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -3,8 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <validation.h>
-#include <txmempool.h>
-#include <amount.h>
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/script.h>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 352ce0295b..f99a3748c9 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -5,15 +5,11 @@
#include <consensus/validation.h>
#include <key.h>
#include <validation.h>
-#include <miner.h>
-#include <pubkey.h>
#include <txmempool.h>
-#include <random.h>
#include <script/standard.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <test/setup_common.h>
-#include <util/time.h>
-#include <keystore.h>
#include <boost/test/unit_test.hpp>
@@ -165,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/uint256_tests.cpp b/src/test/uint256_tests.cpp
index c1749fb856..33a118c2bb 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,19 +1,17 @@
// 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.
+
#include <arith_uint256.h>
+#include <streams.h>
#include <uint256.h>
#include <version.h>
#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
-#include <stdint.h>
#include <sstream>
#include <iomanip>
-#include <limits>
-#include <cmath>
#include <string>
-#include <stdio.h>
BOOST_FIXTURE_TEST_SUITE(uint256_tests, BasicTestingSetup)
diff --git a/src/test/util.cpp b/src/test/util.cpp
index bc09d00b7a..b7bb6deeaa 100644
--- a/src/test/util.cpp
+++ b/src/test/util.cpp
@@ -17,22 +17,15 @@
#include <wallet/wallet.h>
#endif
-#include <boost/thread.hpp>
-
const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
#ifdef ENABLE_WALLET
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 8fee66d6c3..9960573b33 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -5,14 +5,15 @@
#include <util/system.h>
#include <clientversion.h>
-#include <primitives/transaction.h>
#include <sync.h>
#include <test/util.h>
#include <util/strencodings.h>
#include <util/moneystr.h>
+#include <util/time.h>
#include <test/setup_common.h>
#include <stdint.h>
+#include <thread>
#include <vector>
#ifndef WIN32
#include <signal.h>
@@ -1398,7 +1399,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
- fs::path dirname = SetDataDir("test_LockDirectory") / fs::unique_path();
+ fs::path dirname = GetDataDir() / "lock_dir";
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
@@ -1487,7 +1488,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
{
// Should be able to write to the data dir.
- fs::path tmpdirname = SetDataDir("test_DirIsWritable");
+ fs::path tmpdirname = GetDataDir();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 5dee034b20..b3368d44b6 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -10,14 +10,20 @@
#include <miner.h>
#include <pow.h>
#include <random.h>
+#include <script/standard.h>
#include <test/setup_common.h>
+#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
+#include <thread>
+
struct RegtestingSetup : public TestingSetup {
RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};
+static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};
+
BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup)
struct TestSubscriber : public CValidationInterface {
@@ -59,8 +65,21 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash)
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
+ pubKey.clear();
+ {
+ WitnessV0ScriptHash witness_program;
+ CSHA256().Write(&V_OP_TRUE[0], V_OP_TRUE.size()).Finalize(witness_program.begin());
+ pubKey << OP_0 << ToByteVector(witness_program);
+ }
+
+ // Make the coinbase transaction with two outputs:
+ // One zero-value one that has a unique pubkey to make sure that blocks at the same height can have a different hash
+ // Another one that has the coinbase reward in a P2WSH with OP_TRUE as witness program to make it easy to spend
CMutableTransaction txCoinbase(*pblock->vtx[0]);
- txCoinbase.vout.resize(1);
+ txCoinbase.vout.resize(2);
+ txCoinbase.vout[1].scriptPubKey = pubKey;
+ txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue;
+ txCoinbase.vout[0].nValue = 0;
txCoinbase.vin[0].scriptWitness.SetNull();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
@@ -69,6 +88,9 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash)
std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
+ LOCK(cs_main); // For LookupBlockIndex
+ GenerateCoinbaseCommitment(*pblock, LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
@@ -79,13 +101,13 @@ std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
}
// construct a valid block
-const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
+std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
{
return FinalizeBlock(Block(prev_hash));
}
// construct an invalid block (but with a valid header)
-const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
+std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
{
auto pblock = Block(prev_hash);
@@ -185,4 +207,131 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}
+/**
+ * Test that mempool updates happen atomically with reorgs.
+ *
+ * This prevents RPC clients, among others, from retrieving immediately-out-of-date mempool data
+ * during large reorgs.
+ *
+ * The test verifies this by creating a chain of `num_txs` blocks, matures their coinbases, and then
+ * submits txns spending from their coinbase to the mempool. A fork chain is then processed,
+ * invalidating the txns and evicting them from the mempool.
+ *
+ * We verify that the mempool updates atomically by polling it continuously
+ * from another thread during the reorg and checking that its size only changes
+ * once. The size changing exactly once indicates that the polling thread's
+ * view of the mempool is either consistent with the chain state before reorg,
+ * or consistent with the chain state after the reorg, and not just consistent
+ * with some intermediate state during the reorg.
+ */
+BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
+{
+ bool ignored;
+ auto ProcessBlock = [&ignored](std::shared_ptr<const CBlock> block) -> bool {
+ return ProcessNewBlock(Params(), block, /* fForceProcessing */ true, /* fNewBlock */ &ignored);
+ };
+
+ // Process all mined blocks
+ BOOST_REQUIRE(ProcessBlock(std::make_shared<CBlock>(Params().GenesisBlock())));
+ auto last_mined = GoodBlock(Params().GenesisBlock().GetHash());
+ BOOST_REQUIRE(ProcessBlock(last_mined));
+
+ // Run the test multiple times
+ for (int test_runs = 3; test_runs > 0; --test_runs) {
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+
+ // Later on split from here
+ const uint256 split_hash{last_mined->hashPrevBlock};
+
+ // Create a bunch of transactions to spend the miner rewards of the
+ // most recent blocks
+ std::vector<CTransactionRef> txs;
+ for (int num_txs = 22; num_txs > 0; --num_txs) {
+ CMutableTransaction mtx;
+ mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}});
+ mtx.vin[0].scriptWitness.stack.push_back(V_OP_TRUE);
+ mtx.vout.push_back(last_mined->vtx[0]->vout[1]);
+ mtx.vout[0].nValue -= 1000;
+ txs.push_back(MakeTransactionRef(mtx));
+
+ last_mined = GoodBlock(last_mined->GetHash());
+ BOOST_REQUIRE(ProcessBlock(last_mined));
+ }
+
+ // Mature the inputs of the txs
+ for (int j = COINBASE_MATURITY; j > 0; --j) {
+ last_mined = GoodBlock(last_mined->GetHash());
+ BOOST_REQUIRE(ProcessBlock(last_mined));
+ }
+
+ // Mine a reorg (and hold it back) before adding the txs to the mempool
+ const uint256 tip_init{last_mined->GetHash()};
+
+ std::vector<std::shared_ptr<const CBlock>> reorg;
+ last_mined = GoodBlock(split_hash);
+ reorg.push_back(last_mined);
+ for (size_t j = COINBASE_MATURITY + txs.size() + 1; j > 0; --j) {
+ last_mined = GoodBlock(last_mined->GetHash());
+ reorg.push_back(last_mined);
+ }
+
+ // Add the txs to the tx pool
+ {
+ LOCK(cs_main);
+ CValidationState state;
+ std::list<CTransactionRef> plTxnReplaced;
+ for (const auto& tx : txs) {
+ BOOST_REQUIRE(AcceptToMemoryPool(
+ ::mempool,
+ state,
+ tx,
+ /* pfMissingInputs */ &ignored,
+ &plTxnReplaced,
+ /* bypass_limits */ false,
+ /* nAbsurdFee */ 0));
+ }
+ }
+
+ // Check that all txs are in the pool
+ {
+ LOCK(::mempool.cs);
+ BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size());
+ }
+
+ // Run a thread that simulates an RPC caller that is polling while
+ // validation is doing a reorg
+ std::thread rpc_thread{[&]() {
+ // This thread is checking that the mempool either contains all of
+ // the transactions invalidated by the reorg, or none of them, and
+ // not some intermediate amount.
+ while (true) {
+ LOCK(::mempool.cs);
+ if (::mempool.mapTx.size() == 0) {
+ // We are done with the reorg
+ break;
+ }
+ // Internally, we might be in the middle of the reorg, but
+ // externally the reorg to the most-proof-of-work chain should
+ // be atomic. So the caller assumes that the returned mempool
+ // is consistent. That is, it has all txs that were there
+ // before the reorg.
+ assert(::mempool.mapTx.size() == txs.size());
+ continue;
+ }
+ LOCK(cs_main);
+ // We are done with the reorg, so the tip must have changed
+ assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
+ }};
+
+ // Submit the reorg in this thread to invalidate and remove the txs from the tx pool
+ for (const auto& b : reorg) {
+ ProcessBlock(b);
+ }
+ // Check that the reorg was eventually successful
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+
+ // We can join the other thread, which returns when the reorg was successful
+ rpc_thread.join();
+ }
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/timedata.cpp b/src/timedata.cpp
index b43639d729..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.cpp b/src/txmempool.cpp
index cac7beb6a1..9257cff718 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -104,7 +104,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
// for each such descendant, also update the ancestor state to include the parent.
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
{
- LOCK(cs);
+ AssertLockHeld(cs);
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
// in-vHashesToUpdate transactions, so that we don't have to recalculate
// descendants when we come across a previously seen entry.
@@ -322,8 +322,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
assert(int(nSigOpCostWithAncestors) >= 0);
}
-CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) :
- nTransactionsUpdated(0), minerPolicyEstimator(estimator)
+CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator)
+ : nTransactionsUpdated(0), minerPolicyEstimator(estimator)
{
_clear(); //lock free clear
@@ -341,13 +341,11 @@ bool CTxMemPool::isSpent(const COutPoint& outpoint) const
unsigned int CTxMemPool::GetTransactionsUpdated() const
{
- LOCK(cs);
return nTransactionsUpdated;
}
void CTxMemPool::AddTransactionsUpdated(unsigned int n)
{
- LOCK(cs);
nTransactionsUpdated += n;
}
@@ -459,8 +457,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants
void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason)
{
// Remove transaction from memory pool
- {
- LOCK(cs);
+ AssertLockHeld(cs);
setEntries txToRemove;
txiter origit = mapTx.find(origTx.GetHash());
if (origit != mapTx.end()) {
@@ -485,13 +482,12 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
}
RemoveStaged(setAllRemoves, false, reason);
- }
}
void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags)
{
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
- LOCK(cs);
+ AssertLockHeld(cs);
setEntries txToRemove;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
@@ -547,7 +543,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
*/
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight)
{
- LOCK(cs);
+ AssertLockHeld(cs);
std::vector<const CTxMemPoolEntry*> entries;
for (const auto& tx : vtx)
{
@@ -922,7 +918,7 @@ void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPool
}
int CTxMemPool::Expire(int64_t time) {
- LOCK(cs);
+ AssertLockHeld(cs);
indexed_transaction_set::index<entry_time>::type::iterator it = mapTx.get<entry_time>().begin();
setEntries toremove;
while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) {
@@ -1015,7 +1011,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
}
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {
- LOCK(cs);
+ AssertLockHeld(cs);
unsigned nTxnRemoved = 0;
CFeeRate maxFeeRateRemoved(0);
diff --git a/src/txmempool.h b/src/txmempool.h
index ce0b762336..7169e80da2 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -6,12 +6,13 @@
#ifndef BITCOIN_TXMEMPOOL_H
#define BITCOIN_TXMEMPOOL_H
+#include <atomic>
+#include <map>
#include <memory>
#include <set>
-#include <map>
-#include <vector>
-#include <utility>
#include <string>
+#include <utility>
+#include <vector>
#include <amount.h>
#include <coins.h>
@@ -344,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
@@ -443,7 +443,7 @@ class CTxMemPool
{
private:
uint32_t nCheckFrequency GUARDED_BY(cs); //!< Value n means that n times in 2^32 we check.
- unsigned int nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
+ std::atomic<unsigned int> nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
CBlockPolicyEstimator* minerPolicyEstimator;
uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
@@ -513,21 +513,12 @@ public:
* `mempool.cs` whenever adding transactions to the mempool and whenever
* changing the chain tip. It's necessary to keep both mutexes locked until
* the mempool is consistent with the new chain tip and fully populated.
- *
- * @par Consistency bug
- *
- * The second guarantee above is not currently enforced, but
- * https://github.com/bitcoin/bitcoin/pull/14193 will fix it. No known code
- * in bitcoin currently depends on second guarantee, but it is important to
- * fix for third party code that needs be able to frequently poll the
- * mempool without locking `cs_main` and without encountering missing
- * transactions during reorgs.
*/
mutable RecursiveMutex cs;
indexed_transaction_set mapTx GUARDED_BY(cs);
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
- std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order
+ std::vector<std::pair<uint256, txiter>> vTxHashes GUARDED_BY(cs); //!< All tx witness hashes/entries in mapTx, in random order
struct CompareIteratorByHash {
bool operator()(const txiter &a, const txiter &b) const {
@@ -582,10 +573,10 @@ 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);
- void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
- void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
+ 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);
void clear();
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
@@ -598,7 +589,7 @@ public:
* Check that none of this transactions inputs are in the mempool, and thus
* the tx is not dependent on other mempool transactions to be included in a block.
*/
- bool HasNoInputsOf(const CTransaction& tx) const;
+ bool HasNoInputsOf(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Affect CreateNewBlock prioritisation of transactions */
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
@@ -621,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
@@ -632,7 +623,7 @@ public:
* for). Note: vHashesToUpdate should be the set of transactions from the
* disconnected block that have been accepted back into the mempool.
*/
- void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
/** Try to calculate all in-mempool ancestors of entry.
* (these are all calculated including the tx itself)
@@ -663,10 +654,10 @@ public:
* pvNoSpendsRemaining, if set, will be populated with the list of outpoints
* which are not in mempool which no longer have any spends in this mempool.
*/
- void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining=nullptr);
+ void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
- int Expire(int64_t time);
+ int Expire(int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
* Calculate the ancestor and descendant count for the given transaction.
@@ -743,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/ui_interface.h b/src/ui_interface.h
index d408f6f889..5e0380dc45 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -69,6 +69,9 @@ public:
/** Force blocking, modal message box dialog (not just OS notification) */
MODAL = 0x10000000U,
+ /** Do not prepend error/warning prefix */
+ MSG_NOPREFIX = 0x20000000U,
+
/** Do not print contents of message to debug log */
SECURE = 0x40000000U,
diff --git a/src/uint256.cpp b/src/uint256.cpp
index e3bc9712e8..ea7164c1f0 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -37,16 +37,15 @@ void base_blob<BITS>::SetHex(const char* psz)
psz += 2;
// hex string to uint
- const char* pbegin = psz;
- while (::HexDigit(*psz) != -1)
- psz++;
- psz--;
+ size_t digits = 0;
+ while (::HexDigit(psz[digits]) != -1)
+ digits++;
unsigned char* p1 = (unsigned char*)data;
unsigned char* pend = p1 + WIDTH;
- while (psz >= pbegin && p1 < pend) {
- *p1 = ::HexDigit(*psz--);
- if (psz >= pbegin) {
- *p1 |= ((unsigned char)::HexDigit(*psz--) << 4);
+ while (digits > 0 && p1 < pend) {
+ *p1 = ::HexDigit(psz[--digits]);
+ if (digits > 0) {
+ *p1 |= ((unsigned char)::HexDigit(psz[--digits]) << 4);
p1++;
}
}
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 68ffd8b046..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)
{
@@ -27,6 +29,8 @@ std::string TransactionErrorString(const TransactionError err)
return "PSBTs not compatible (different transactions)";
case TransactionError::SIGHASH_MISMATCH:
return "Specified sighash value does not match existing value";
+ case TransactionError::MAX_FEE_EXCEEDED:
+ return "Fee exceeds maximum configured by -maxtxfee";
// no default case, so the compiler can warn about missing cases
}
assert(false);
@@ -34,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/error.h b/src/util/error.h
index d93309551b..0fd474b962 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -27,6 +27,7 @@ enum class TransactionError {
INVALID_PSBT,
PSBT_MISMATCH,
SIGHASH_MISMATCH,
+ MAX_FEE_EXCEEDED,
};
std::string TransactionErrorString(const TransactionError error);
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
index 5fdaa1284c..cf16d5e44f 100644
--- a/src/util/fees.cpp
+++ b/src/util/fees.cpp
@@ -18,7 +18,6 @@ std::string StringForFeeReason(FeeReason reason) {
{FeeReason::PAYTXFEE, "PayTxFee set"},
{FeeReason::FALLBACK, "Fallback fee"},
{FeeReason::REQUIRED, "Minimum Required Fee"},
- {FeeReason::MAXTXFEE, "MaxTxFee limit"}
};
auto reason_string = fee_reason_strings.find(reason);
diff --git a/src/util/system.cpp b/src/util/system.cpp
index fca29a9f31..c27b0cc105 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -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.
@@ -7,6 +7,7 @@
#include <chainparamsbase.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <stdarg.h>
@@ -705,19 +706,16 @@ fs::path GetDefaultDataDir()
static fs::path g_blocks_path_cache_net_specific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
-static CCriticalSection csPathCached;
+static RecursiveMutex csPathCached;
const fs::path &GetBlocksDir()
{
-
LOCK(csPathCached);
-
fs::path &path = g_blocks_path_cache_net_specific;
- // This can be called during exceptions by LogPrintf(), so we cache the
- // value so we don't have to do memory allocations after that.
- if (!path.empty())
- return path;
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
if (gArgs.IsArgSet("-blocksdir")) {
path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
@@ -737,15 +735,12 @@ const fs::path &GetBlocksDir()
const fs::path &GetDataDir(bool fNetSpecific)
{
-
LOCK(csPathCached);
-
fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
- // This can be called during exceptions by LogPrintf(), so we cache the
- // value so we don't have to do memory allocations after that.
- if (!path.empty())
- return path;
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
if (gArgs.IsArgSet("-datadir")) {
path = fs::system_complete(gArgs.GetArg("-datadir", ""));
@@ -1120,6 +1115,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
}
#endif
+#if HAVE_SYSTEM
void runCommand(const std::string& strCommand)
{
if (strCommand.empty()) return;
@@ -1131,6 +1127,7 @@ void runCommand(const std::string& strCommand)
if (nErr)
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
}
+#endif
void SetupEnvironment()
{
@@ -1188,10 +1185,11 @@ int GetNumCores()
std::string CopyrightHolders(const std::string& strPrefix)
{
- std::string strCopyrightHolders = strPrefix + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION));
+ const auto copyright_devs = strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION);
+ std::string strCopyrightHolders = strPrefix + copyright_devs;
- // Check for untranslated substitution to make sure Bitcoin Core copyright is not removed by accident
- if (strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION).find("Bitcoin Core") == std::string::npos) {
+ // Make sure Bitcoin Core copyright is not removed by accident
+ if (copyright_devs.find("Bitcoin Core") == std::string::npos) {
strCopyrightHolders += "\n" + strPrefix + "The Bitcoin Core developers";
}
return strCopyrightHolders;
diff --git a/src/util/system.h b/src/util/system.h
index 1a83cb67b1..66a9eb4612 100644
--- a/src/util/system.h
+++ b/src/util/system.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.
@@ -20,18 +20,16 @@
#include <fs.h>
#include <logging.h>
#include <sync.h>
-#include <util/threadnames.h>
#include <tinyformat.h>
#include <util/memory.h>
+#include <util/threadnames.h>
#include <util/time.h>
-#include <atomic>
#include <exception>
#include <map>
#include <set>
#include <stdint.h>
#include <string>
-#include <unordered_set>
#include <utility>
#include <vector>
@@ -42,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();
@@ -85,12 +71,15 @@ fs::path GetDefaultDataDir();
// The blocks directory is always net specific.
const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
+/** Tests only */
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
+#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
+#endif
/**
* Most paths passed as configuration arguments are treated as relative to
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 2d2252c251..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);
@@ -304,7 +310,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
-static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
+static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
+{
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
@@ -341,7 +348,7 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
* and instead just erase from the mempool as needed.
*/
-static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
{
AssertLockHeld(cs_main);
std::vector<uint256> vHashUpdate;
@@ -610,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
@@ -1046,9 +1069,15 @@ 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();
+#if HAVE_SYSTEM
std::string strCmd = gArgs.GetArg("-alertnotify", "");
if (strCmd.empty()) return;
@@ -1062,6 +1091,7 @@ static void AlertNotify(const std::string& strMessage)
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
+#endif
}
static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1157,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);
@@ -1374,20 +1404,22 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
}
/** Abort with a message */
-static bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
{
SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
- uiInterface.ThreadSafeMessageBox(
- userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage,
- "", CClientUIInterface::MSG_ERROR);
+ if (!userMessage.empty()) {
+ uiInterface.ThreadSafeMessageBox(userMessage, "", CClientUIInterface::MSG_ERROR | prefix);
+ } else {
+ uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details").translated, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
+ }
StartShutdown();
return false;
}
-static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
{
- AbortNode(strMessage, userMessage);
+ AbortNode(strMessage, userMessage, prefix);
return state.Error(strMessage);
}
@@ -1690,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) {
@@ -1998,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 low!", _("Error: Disk space is low!"));
+ 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();
@@ -2033,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 low!", _("Error: Disk space is low!"));
+ 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())
@@ -2107,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 {
@@ -2124,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,
@@ -2155,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();
{
@@ -2361,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;
@@ -2411,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;
@@ -2545,7 +2583,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
LimitValidationInterfaceQueue();
{
- LOCK(cs_main);
+ LOCK2(cs_main, ::mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@@ -2665,6 +2703,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
LimitValidationInterfaceQueue();
LOCK(cs_main);
+ LOCK(::mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -2714,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);
}
@@ -2746,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);
@@ -2758,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++;
}
@@ -2768,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;
}
@@ -2778,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
@@ -2794,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;
@@ -2846,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));
}
}
}
@@ -2899,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 low!", _("Error: Disk space is low!"));
+ return AbortNode("Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -2923,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 low!", _("Error: Disk space is low!"));
+ 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;
@@ -3111,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");
@@ -3224,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)
@@ -3247,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)
@@ -3300,8 +3339,6 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (ppindex)
*ppindex = pindex;
- CheckBlockIndex(chainparams.GetConsensus());
-
return true;
}
@@ -3313,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;
}
@@ -3356,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
@@ -3507,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;
@@ -3517,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);
}
}
}
@@ -3675,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);
@@ -3683,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));
@@ -3723,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;
@@ -3733,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)
@@ -3746,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
@@ -3771,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) {
@@ -3824,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()
@@ -3858,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)) {
@@ -3916,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()))
@@ -3963,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);
}
@@ -4009,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;
}
@@ -4045,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;
}
@@ -4068,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);
}
@@ -4098,7 +4157,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Loop until the tip is below nHeight, or we reach a pruned block.
while (!ShutdownRequested()) {
{
- LOCK(cs_main);
+ LOCK2(cs_main, ::mempool.cs);
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
assert(tip == m_chain.Tip());
if (tip == nullptr || tip->nHeight < nHeight) break;
@@ -4174,7 +4233,6 @@ bool RewindBlockIndex(const CChainParams& params) {
void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1;
- m_failed_blocks.clear();
setBlockIndexCandidates.clear();
}
@@ -4185,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();
@@ -4197,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();
@@ -4214,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) {
@@ -4234,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 {
@@ -4245,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());
@@ -4390,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;
@@ -4457,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
@@ -4476,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);
@@ -4493,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);
@@ -4752,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 638229952d..d747fdbf27 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -18,6 +18,7 @@
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
#include <sync.h>
+#include <txmempool.h> // For CTxMemPool::cs
#include <versionbits.h>
#include <algorithm>
@@ -63,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 */
@@ -125,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;
@@ -143,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;
@@ -405,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);
@@ -438,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
@@ -472,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()
@@ -506,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.
@@ -540,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:
@@ -553,7 +598,7 @@ public:
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block disconnection on our pcoinsTip:
- bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
// Manual block validity manipulation:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
@@ -571,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);
- 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);
-
- 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.
*
@@ -585,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);
@@ -614,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/validationinterface.h b/src/validationinterface.h
index ea1b2e7e76..3ce617b827 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -16,7 +16,6 @@ extern CCriticalSection cs_main;
class CBlock;
class CBlockIndex;
struct CBlockLocator;
-class CBlockIndex;
class CConnman;
class CValidationInterface;
class CValidationState;
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/fees.cpp b/src/wallet/fees.cpp
index ad69e84358..2792058f2a 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -18,14 +18,7 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
- CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
- // Always obey the maximum
- const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
- if (fee_needed > max_tx_fee) {
- fee_needed = max_tx_fee;
- if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
- }
- return fee_needed;
+ return GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
}
CFeeRate GetRequiredFeeRate(const CWallet& wallet)
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 0265433863..cb94799d9e 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:
@@ -57,7 +58,9 @@ void WalletInit::AddWalletOptions() const
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);
+#if HAVE_SYSTEM
gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, 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("-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);
@@ -119,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/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
index ce4788dee1..721a244afb 100644
--- a/src/wallet/psbtwallet.cpp
+++ b/src/wallet/psbtwallet.cpp
@@ -44,16 +44,7 @@ TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- const CTxOut& out = psbtx.tx->vout.at(i);
- PSBTOutput& psbt_out = psbtx.outputs.at(i);
-
- // Fill a SignatureData with output info
- SignatureData sigdata;
- psbt_out.FillSignatureData(sigdata);
-
- MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
- ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
- psbt_out.FromSignatureData(sigdata);
+ UpdatePSBTOutput(HidingSigningProvider(pwallet, true, !bip32derivs), psbtx, i);
}
return TransactionError::OK;
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..a86e93481c 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",
@@ -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",
{
@@ -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::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, flags);
- if (!wallet) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
+ 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
}
- // 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();
- }
+ if (warning.empty()) {
+ warning = create_warning;
+ } else if (!warning.empty() && !create_warning.empty()){
+ warning += "; " + create_warning;
}
- 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"
@@ -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,18 +3270,16 @@ 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",
{
@@ -3423,7 +3287,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
{"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"
+ {"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",
@@ -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,
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index e4950af4e5..c961456572 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -16,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(getwalletenv_file)
{
std::string test_name = "test_name.dat";
- fs::path datadir = SetDataDir("tempdir");
+ const fs::path datadir = GetDataDir();
fs::path file_path = datadir / test_name;
std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR);
f.close();
@@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
{
std::string expected_name = "wallet.dat";
- fs::path datadir = SetDataDir("tempdir");
+ const fs::path datadir = GetDataDir();
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
@@ -40,8 +40,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
{
- fs::path datadir = SetDataDir("tempdir");
- fs::path datadir_2 = SetDataDir("tempdir_2");
+ fs::path datadir = GetDataDir() / "1";
+ fs::path datadir_2 = GetDataDir() / "2";
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
@@ -54,8 +54,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
{
- fs::path datadir = SetDataDir("tempdir");
- fs::path datadir_2 = SetDataDir("tempdir_2");
+ fs::path datadir = GetDataDir() / "1";
+ fs::path datadir_2 = GetDataDir() / "2";
std::string filename;
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 3b828d57f9..86ba0013fe 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
@@ -13,7 +14,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
std::string sep;
sep += fs::path::preferred_separator;
- m_datadir = SetDataDir("tempdir");
+ m_datadir = GetDataDir();
m_cwd = fs::current_path();
m_walletdir_path_cases["default"] = m_datadir / "wallets";
@@ -41,4 +42,4 @@ InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
{
gArgs.ForceSetArg("-walletdir", walletdir_path.string());
-} \ No newline at end of file
+}
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index eace53037a..279542ffad 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -6,6 +6,7 @@
#include <noui.h>
#include <test/setup_common.h>
+#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index 0cae055676..062fef7748 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -4,7 +4,6 @@
#include <key.h>
#include <script/script.h>
-#include <script/script_error.h>
#include <script/standard.h>
#include <test/setup_common.h>
#include <wallet/ismine.h>
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index cdf7113203..0400f1207c 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -3,12 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key_io.h>
-#include <script/sign.h>
#include <util/bip32.h>
#include <util/strencodings.h>
#include <wallet/psbtwallet.h>
#include <wallet/wallet.h>
-#include <univalue.h>
#include <boost/test/unit_test.hpp>
#include <test/setup_common.h>
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 7db0bc4249..ba0843f352 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -4,8 +4,6 @@
#include <wallet/test/wallet_test_fixture.h>
-#include <wallet/db.h>
-
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock())
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 922bb0fe65..8af05dea45 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -5,9 +5,7 @@
#include <wallet/wallet.h>
#include <memory>
-#include <set>
#include <stdint.h>
-#include <utility>
#include <vector>
#include <consensus/validation.h>
@@ -192,7 +190,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
auto locked_chain = chain->lock();
LockAssertion lock(::cs_main);
- std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
+ std::string backup_file = (GetDataDir() / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file.
{
@@ -274,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;
@@ -363,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);
@@ -466,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
@@ -490,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 0472334bc1..ef5f0a61e1 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);
@@ -1050,6 +1165,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
+#if HAVE_SYSTEM
// notify an external script when a wallet transaction comes in or is updated
std::string strCmd = gArgs.GetArg("-walletnotify", "");
@@ -1059,6 +1175,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
}
+#endif
return true;
}
@@ -1661,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;
}
@@ -1680,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);
@@ -1703,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;
@@ -1717,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) {
@@ -1728,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");
}
}
@@ -1898,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());
@@ -1917,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();
@@ -1973,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;
@@ -2266,7 +2406,7 @@ void MaybeResendWalletTxs()
CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const
{
Balance ret;
- isminefilter reuse_filter = avoid_reuse ? 0 : ISMINE_USED;
+ isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
@@ -2308,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);
@@ -2317,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)
{
@@ -2376,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)
@@ -2664,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
@@ -2785,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;
@@ -2805,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;
}
@@ -2822,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;
@@ -2843,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);
@@ -2912,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);
@@ -2945,7 +3082,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
continue;
}
else {
- strFailReason = _("Insufficient funds");
+ strFailReason = _("Insufficient funds").translated;
return false;
}
}
@@ -2976,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;
}
@@ -2995,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;
}
@@ -3050,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;
}
@@ -3079,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());
@@ -3109,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);
@@ -3125,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,
@@ -3152,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();
@@ -3166,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.
@@ -3418,8 +3552,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;
@@ -3480,8 +3613,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);
@@ -3568,6 +3700,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();
@@ -3747,7 +3915,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;
@@ -3763,25 +3931,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)
@@ -4081,17 +4253,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;
@@ -4102,26 +4274,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;
}
}
@@ -4140,7 +4312,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);
@@ -4153,7 +4325,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;
}
@@ -4181,7 +4353,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;
}
}
@@ -4201,7 +4373,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;
}
@@ -4209,12 +4381,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));
}
}
@@ -4236,21 +4408,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
@@ -4258,12 +4430,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);
}
@@ -4275,11 +4447,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;
}
@@ -4293,10 +4465,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;
}
@@ -4305,7 +4477,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);
@@ -4353,12 +4525,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
@@ -4372,7 +4544,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;
}
}
@@ -4458,13 +4630,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;
@@ -4473,7 +4639,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;
@@ -4481,7 +4647,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;
@@ -4490,7 +4656,7 @@ 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;
@@ -4583,3 +4749,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..8b0ae8bb85 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()
+ template<typename Stream>
+ void Unserialize(Stream& s)
{
- hashBlock = uint256();
- nIndex = -1;
- }
+ CTransactionRef tx;
+ uint256 hashBlock;
+ std::vector<uint256> vMerkleBranch;
+ int nIndex;
- void SetTx(CTransactionRef arg)
- {
- tx = std::move(arg);
+ s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
}
-
- ADD_SERIALIZE_METHODS;
-
- 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);
- }
-
- 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()
{
@@ -619,6 +592,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 +663,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 +680,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 +791,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 +833,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 +880,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 +896,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 +929,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 +982,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 +993,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 +1016,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 +1096,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 +1109,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 +1153,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 +1165,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 +1251,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 +1321,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/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_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 3ad83cd2b3..fdb608d457 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -78,7 +78,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
@@ -486,6 +486,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_cltv.py b/test/functional/feature_cltv.py
index b16eafccca..af34f9f0db 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -57,16 +57,34 @@ 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
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_cltv_info(self, *, is_active):
+ assert_equal(
+ next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'),
+ {
+ "id": "bip65",
+ "version": 4,
+ "reject": {
+ "status": is_active
+ }
+ },
+ )
+
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
+ self.test_cltv_info(is_active=False)
+
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2)]
self.nodeaddress = self.nodes[0].getnewaddress()
@@ -86,7 +104,9 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
+ self.test_cltv_info(is_active=False)
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -135,7 +155,9 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
+ self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index f0d6bc21e6..aae262344d 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,32 @@ 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.')
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..f227a06ebe 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -267,7 +267,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_dersig.py b/test/functional/feature_dersig.py
index 7480e5c5ba..067e3be1f4 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -51,9 +51,23 @@ class BIP66Test(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_dersig_info(self, *, is_active):
+ assert_equal(
+ next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'),
+ {
+ "id": "bip66",
+ "version": 3,
+ "reject": {
+ "status": is_active
+ }
+ },
+ )
+
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
+ self.test_dersig_info(is_active=False)
+
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)]
self.nodeaddress = self.nodes[0].getnewaddress()
@@ -74,7 +88,9 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
+ self.test_dersig_info(is_active=False)
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -128,8 +144,11 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
+ self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
+ self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
+
if __name__ == '__main__':
BIP66Test().main()
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_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_rpc.py b/test/functional/interface_rpc.py
index 49ae0fb1a9..e99fa22646 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests some generic aspects of the RPC interface."""
+import os
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than_or_equal
@@ -31,6 +32,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
command = info['active_commands'][0]
assert_equal(command['method'], 'getrpcinfo')
assert_greater_than_or_equal(command['duration'], 0)
+ assert_equal(info['logpath'], os.path.join(self.nodes[0].datadir, 'regtest', 'debug.log'))
def test_batch_request(self):
self.log.info("Testing basic JSON-RPC batch request...")
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/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 58010f7c2e..62f3843756 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -129,6 +129,11 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
outval = value - decimal.Decimal("0.00001000")
rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
+ prevtx_err = dict(prevtxs[0])
+ del prevtx_err["redeemScript"]
+
+ assert_raises_rpc_error(-8, "Missing redeemScript/witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index d89fd6461f..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,17 +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 = ""
@@ -681,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 #
######################################
@@ -692,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 430be6dd37..b3d8696208 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -9,6 +9,7 @@ from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
disconnect_nodes,
@@ -129,6 +130,15 @@ class PSBTTest(BitcoinTestFramework):
assert_equal(walletprocesspsbt_out['complete'], True)
self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])
+ # feeRate of 0.1 BTC / KB produces a total fee slightly below -maxtxfee (~0.05280000):
+ res = 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": 0.1})
+ assert_greater_than(res["fee"], 0.05)
+ assert_greater_than(0.06, res["fee"])
+
+ # feeRate of 10 BTC / KB produces a total fee well above -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
psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt']
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx)
@@ -315,18 +325,32 @@ class PSBTTest(BitcoinTestFramework):
vout3 = find_output(self.nodes[0], txid3, 11)
self.sync_all()
- # Update a PSBT with UTXOs from the node
- # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
+ def test_psbt_input_keys(psbt_input, keys):
+ """Check that the psbt input has only the expected keys."""
+ assert_equal(set(keys), set(psbt_input.keys()))
+
+ # Create a PSBT. None of the inputs are filled initially
psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
decoded = self.nodes[1].decodepsbt(psbt)
- assert "witness_utxo" not in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
- assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
- assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ test_psbt_input_keys(decoded['inputs'][0], [])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], [])
+
+ # Update a PSBT with UTXOs from the node
+ # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
updated = self.nodes[1].utxoupdatepsbt(psbt)
decoded = self.nodes[1].decodepsbt(updated)
- assert "witness_utxo" in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
- assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
- assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo'])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], [])
+
+ # Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in
+ descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
+ updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
+ decoded = self.nodes[1].decodepsbt(updated)
+ test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'bip32_derivs'])
+ test_psbt_input_keys(decoded['inputs'][1], [])
+ test_psbt_input_keys(decoded['inputs'][2], ['witness_utxo', 'bip32_derivs', 'redeem_script'])
# Two PSBTs with a common input should not be joinable
psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
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/util.py b/test/functional/test_framework/util.py
index 26215083fb..efd962ea93 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")
@@ -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..7ad2b78d4e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -107,6 +107,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 +158,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 +175,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 +237,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 +273,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 +309,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/tool_wallet.py b/test/functional/tool_wallet.py
index fbcf21e729..28a65f7823 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -1,14 +1,20 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-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 bitcoin-wallet."""
+
+import hashlib
+import os
+import stat
import subprocess
import textwrap
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+BUFFER_SIZE = 16 * 1024
+
class ToolWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -32,23 +38,54 @@ class ToolWalletTest(BitcoinTestFramework):
def assert_tool_output(self, output, *args):
p = self.bitcoin_wallet_process(*args)
stdout, stderr = p.communicate()
- assert_equal(p.poll(), 0)
assert_equal(stderr, '')
assert_equal(stdout, output)
+ assert_equal(p.poll(), 0)
- def run_test(self):
+ def wallet_shasum(self):
+ h = hashlib.sha1()
+ mv = memoryview(bytearray(BUFFER_SIZE))
+ with open(self.wallet_path, 'rb', buffering=0) as f:
+ for n in iter(lambda : f.readinto(mv), 0):
+ h.update(mv[:n])
+ return h.hexdigest()
+ def wallet_timestamp(self):
+ return os.path.getmtime(self.wallet_path)
+
+ def wallet_permissions(self):
+ return oct(os.lstat(self.wallet_path).st_mode)[-3:]
+
+ def log_wallet_timestamp_comparison(self, old, new):
+ result = 'unchanged' if new == old else 'increased!'
+ self.log.debug('Wallet file timestamp {}'.format(result))
+
+ def test_invalid_tool_commands_and_args(self):
+ self.log.info('Testing that various invalid commands raise with specific error messages')
self.assert_raises_tool_error('Invalid command: foo', 'foo')
- # `bitcoin-wallet help` is an error. Use `bitcoin-wallet -help`
+ # `bitcoin-wallet help` raises an error. Use `bitcoin-wallet -help`.
self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
self.assert_raises_tool_error('Error loading wallet.dat. Is wallet being used by other process?', '-wallet=wallet.dat', 'info')
self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
- # stop the node to close the wallet to call info command
+ def test_tool_wallet_info(self):
+ # Stop the node to close the wallet to call the info command.
self.stop_node(0)
-
+ self.log.info('Calling wallet tool info, testing output')
+ #
+ # TODO: Wallet tool info should work with wallet file permissions set to
+ # read-only without raising:
+ # "Error loading wallet.dat. Is wallet being used by another process?"
+ # The following lines should be uncommented and the tests still succeed:
+ #
+ # self.log.debug('Setting wallet file permissions to 400 (read-only)')
+ # os.chmod(self.wallet_path, stat.S_IRUSR)
+ # assert(self.wallet_permissions() in ['400', '666']) # Sanity check. 666 because Appveyor.
+ # shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Wallet info
===========
@@ -59,12 +96,35 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 3
''')
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
-
- # mutate the wallet to check the info command output changes accordingly
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ self.log.debug('Setting wallet file permissions back to 600 (read/write)')
+ os.chmod(self.wallet_path, stat.S_IRUSR | stat.S_IWUSR)
+ assert(self.wallet_permissions() in ['600', '666']) # Sanity check. 666 because Appveyor.
+ #
+ # TODO: Wallet tool info should not write to the wallet file.
+ # The following lines should be uncommented and the tests still succeed:
+ #
+ # assert_equal(timestamp_before, timestamp_after)
+ # shasum_after = self.wallet_shasum()
+ # assert_equal(shasum_before, shasum_after)
+ # self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_tool_wallet_info_after_transaction(self):
+ """
+ Mutate the wallet with a transaction to verify that the info command
+ output changes accordingly.
+ """
self.start_node(0)
+ self.log.info('Generating transaction to mutate wallet')
self.nodes[0].generate(1)
self.stop_node(0)
+ self.log.info('Calling wallet tool info after generating a transaction, testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Wallet info
===========
@@ -75,7 +135,22 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 3
''')
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
-
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ #
+ # TODO: Wallet tool info should not write to the wallet file.
+ # This assertion should be uncommented and succeed:
+ # assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_before, shasum_after)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_tool_wallet_create_on_existing_wallet(self):
+ self.log.info('Calling wallet tool create on an existing wallet, testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling create: {}'.format(timestamp_before))
out = textwrap.dedent('''\
Topping up keypool...
Wallet info
@@ -87,15 +162,48 @@ class ToolWalletTest(BitcoinTestFramework):
Address Book: 0
''')
self.assert_tool_output(out, '-wallet=foo', 'create')
-
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling create: {}'.format(timestamp_after))
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_before, shasum_after)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def test_getwalletinfo_on_different_wallet(self):
+ self.log.info('Starting node with arg -wallet=foo')
self.start_node(0, ['-wallet=foo'])
+
+ self.log.info('Calling getwalletinfo on a different wallet ("foo"), testing output')
+ shasum_before = self.wallet_shasum()
+ timestamp_before = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp before calling getwalletinfo: {}'.format(timestamp_before))
out = self.nodes[0].getwalletinfo()
self.stop_node(0)
+ shasum_after = self.wallet_shasum()
+ timestamp_after = self.wallet_timestamp()
+ self.log.debug('Wallet file timestamp after calling getwalletinfo: {}'.format(timestamp_after))
+
assert_equal(0, out['txcount'])
assert_equal(1000, out['keypoolsize'])
assert_equal(1000, out['keypoolsize_hd_internal'])
assert_equal(True, 'hdseedid' in out)
+ self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
+ assert_equal(timestamp_before, timestamp_after)
+ assert_equal(shasum_after, shasum_before)
+ self.log.debug('Wallet file shasum unchanged\n')
+
+ def run_test(self):
+ self.wallet_path = os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', 'wallet.dat')
+ self.test_invalid_tool_commands_and_args()
+ # Warning: The following tests are order-dependent.
+ self.test_tool_wallet_info()
+ self.test_tool_wallet_info_after_transaction()
+ self.test_tool_wallet_create_on_existing_wallet()
+ self.test_getwalletinfo_on_different_wallet()
+
+
if __name__ == '__main__':
ToolWalletTest().main()
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 15f2195e21..137c77a51e 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -203,6 +203,8 @@ class WalletTest(BitcoinTestFramework):
self.log.info('Put txs back into mempool of node 1 (not node 0)')
self.nodes[0].invalidateblock(block_reorg)
self.nodes[1].invalidateblock(block_reorg)
+ self.sync_blocks()
+ self.nodes[0].syncwithvalidationinterfacequeue()
assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY)
assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted
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 4d9bacf299..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)
@@ -304,7 +315,9 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
def test_bumpfee_metadata(rbf_node, dest_address):
- rbfid = rbf_node.sendtoaddress(dest_address, Decimal("0.00100000"), "comment value", "to value")
+ assert(rbf_node.getbalance() < 49)
+ rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value")
bumped_tx = rbf_node.bumpfee(rbfid)
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(bumped_wtx["comment"], "comment value")
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/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 5810e94938..91d26e9cb3 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -57,8 +57,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# after the last time we tried to broadcast. Use mocktime and give an extra minute to be sure.
block_time = int(time.time()) + 6 * 60
node.setmocktime(block_time)
- block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockchaininfo()['blocks']), block_time)
- block.nVersion = 3
+ block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockcount() + 1), block_time)
block.rehash()
block.solve()
node.submitblock(ToHex(block))
diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh
index 4267f9fa0d..5603456e62 100755
--- a/test/lint/commit-script-check.sh
+++ b/test/lint/commit-script-check.sh
@@ -18,12 +18,12 @@ if test "x$1" = "x"; then
fi
RET=0
-PREV_BRANCH=`git name-rev --name-only HEAD`
-PREV_HEAD=`git rev-parse HEAD`
-for commit in `git rev-list --reverse $1`; do
+PREV_BRANCH=$(git name-rev --name-only HEAD)
+PREV_HEAD=$(git rev-parse HEAD)
+for commit in $(git rev-list --reverse $1); do
if git rev-list -n 1 --pretty="%s" $commit | grep -q "^scripted-diff:"; then
git checkout --quiet $commit^ || exit
- SCRIPT="`git rev-list --format=%b -n1 $commit | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d'`"
+ SCRIPT="$(git rev-list --format=%b -n1 $commit | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d')"
if test "x$SCRIPT" = "x"; then
echo "Error: missing script for: $commit"
echo "Failed"
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-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index 70cc16337e..8607fc4371 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -39,7 +39,7 @@ CIRCULAR_DEPENDENCIES=()
IFS=$'\n'
for CIRC in $(cd src && ../contrib/devtools/circular-dependencies.py {*,*/*,*/*/*}.{h,cpp} | sed -e 's/^Circular dependency: //'); do
- CIRCULAR_DEPENDENCIES+=($CIRC)
+ CIRCULAR_DEPENDENCIES+=( "$CIRC" )
IS_EXPECTED_CIRC=0
for EXPECTED_CIRC in "${EXPECTED_CIRCULAR_DEPENDENCIES[@]}"; do
if [[ "${CIRC}" == "${EXPECTED_CIRC}" ]]; then
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 224e62f04a..47ad896589 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -16,8 +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, 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)"),
diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh
index 1afd4cfc1a..632ed7c812 100755
--- a/test/lint/lint-logs.sh
+++ b/test/lint/lint-logs.sh
@@ -19,6 +19,7 @@ UNTERMINATED_LOGS=$(git grep --extended-regexp "LogPrintf?\(" -- "*.cpp" | \
grep -v "LogPrint()" | \
grep -v "LogPrintf()")
if [[ ${UNTERMINATED_LOGS} != "" ]]; then
+ # shellcheck disable=SC2028
echo "All calls to LogPrintf() and LogPrint() should be terminated with \\n"
echo
echo "${UNTERMINATED_LOGS}"
diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh
index 6f5e6546c5..69fc3cf368 100755
--- a/test/lint/lint-shell.sh
+++ b/test/lint/lint-shell.sh
@@ -23,25 +23,9 @@ fi
# Disabled warnings:
disabled=(
- SC1087 # Use braces when expanding arrays, e.g. ${array[idx]} (or ${var}[.. to quiet).
- SC2001 # See if you can use ${variable//search/replace} instead.
- SC2004 # $/${} is unnecessary on arithmetic variables.
- SC2005 # Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
- SC2006 # Use $(..) instead of legacy `..`.
- SC2016 # Expressions don't expand in single quotes, use double quotes for that.
- SC2028 # echo won't expand escape sequences. Consider printf.
SC2046 # Quote this to prevent word splitting.
- SC2048 # Use "$@" (with quotes) to prevent whitespace problems.
- SC2066 # Since you double quoted this, it will not word split, and the loop will only run once.
SC2086 # Double quote to prevent globbing and word splitting.
- SC2116 # Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
SC2162 # read without -r will mangle backslashes.
- SC2166 # Prefer [ p ] {&&,||} [ q ] as [ p -{a,o} q ] is not well defined.
- SC2181 # Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
- SC2206 # Quote to prevent word splitting, or split robustly with mapfile or read -a.
- SC2207 # Prefer mapfile or read -a to split command output (or quote to avoid splitting).
- SC2230 # which is non-standard. Use builtin 'command -v' instead.
- SC2236 # Don't force -n instead of ! -z.
)
shellcheck -e "$(IFS=","; echo "${disabled[*]}")" \
$(git ls-files -- "*.sh" | grep -vE 'src/(secp256k1|univalue)/')