aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml4
-rw-r--r--.github/ISSUE_TEMPLATE/good_first_issue.md7
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml2
-rw-r--r--CONTRIBUTING.md58
-rwxr-xr-xci/lint/04_install.sh1
-rw-r--r--ci/test/00_setup_env_native_fuzz.sh2
-rw-r--r--ci/test/00_setup_env_native_fuzz_with_valgrind.sh2
-rw-r--r--ci/test/00_setup_env_native_tsan.sh8
-rwxr-xr-xci/test/03_before_install.sh4
-rwxr-xr-xci/test/04_install.sh14
-rwxr-xr-xci/test/05_before_script.sh4
-rw-r--r--configure.ac17
-rwxr-xr-xcontrib/devtools/previous_release.sh2
-rw-r--r--contrib/valgrind.supp12
-rw-r--r--depends/Makefile24
-rw-r--r--depends/hosts/default.mk9
-rw-r--r--depends/packages/boost.mk17
-rw-r--r--doc/build-windows.md2
-rw-r--r--doc/fuzzing.md8
-rw-r--r--doc/reduce-traffic.md2
-rw-r--r--doc/release-notes-16528.md120
-rw-r--r--doc/release-notes-18918.md0
-rw-r--r--doc/release-notes.md174
-rw-r--r--doc/release-notes/release-notes-0.20.0.md987
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include42
-rw-r--r--src/bench/hashpadding.cpp47
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/bitcoin-wallet.cpp1
-rw-r--r--src/blockencodings.cpp7
-rw-r--r--src/blockencodings.h2
-rw-r--r--src/blockfilter.h8
-rw-r--r--src/consensus/tx_verify.cpp10
-rw-r--r--src/consensus/tx_verify.h4
-rw-r--r--src/core_read.cpp6
-rw-r--r--src/core_write.cpp12
-rw-r--r--src/fs.cpp37
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/index/txindex.cpp3
-rw-r--r--src/init.cpp61
-rw-r--r--src/interfaces/chain.cpp4
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.cpp23
-rw-r--r--src/interfaces/node.h20
-rw-r--r--src/interfaces/wallet.cpp25
-rw-r--r--src/interfaces/wallet.h2
-rw-r--r--src/logging.cpp16
-rw-r--r--src/logging.h18
-rw-r--r--src/net.cpp99
-rw-r--r--src/net.h2
-rw-r--r--src/net_permissions.cpp30
-rw-r--r--src/net_permissions.h14
-rw-r--r--src/net_processing.cpp751
-rw-r--r--src/net_processing.h4
-rw-r--r--src/netbase.cpp18
-rw-r--r--src/protocol.cpp45
-rw-r--r--src/protocol.h20
-rw-r--r--src/psbt.h2
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/addresstablemodel.cpp9
-rw-r--r--src/qt/addresstablemodel.h2
-rw-r--r--src/qt/bitcoin.cpp53
-rw-r--r--src/qt/bitcoingui.cpp22
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/bitcoinunits.cpp16
-rw-r--r--src/qt/bitcoinunits.h4
-rw-r--r--src/qt/clientmodel.cpp42
-rw-r--r--src/qt/clientmodel.h8
-rw-r--r--src/qt/forms/overviewpage.ui28
-rw-r--r--src/qt/forms/receiverequestdialog.ui288
-rw-r--r--src/qt/guiutil.cpp47
-rw-r--r--src/qt/guiutil.h19
-rw-r--r--src/qt/modaloverlay.cpp26
-rw-r--r--src/qt/modaloverlay.h2
-rw-r--r--src/qt/overviewpage.cpp49
-rw-r--r--src/qt/overviewpage.h2
-rw-r--r--src/qt/receiverequestdialog.cpp88
-rw-r--r--src/qt/receiverequestdialog.h3
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/signverifymessagedialog.cpp1
-rw-r--r--src/qt/test/wallettests.cpp28
-rw-r--r--src/qt/trafficgraphwidget.cpp1
-rw-r--r--src/qt/transactionrecord.cpp8
-rw-r--r--src/qt/transactionrecord.h13
-rw-r--r--src/qt/transactiontablemodel.cpp10
-rw-r--r--src/qt/transactionview.cpp15
-rw-r--r--src/qt/transactionview.h14
-rw-r--r--src/qt/walletcontroller.cpp17
-rw-r--r--src/qt/walletcontroller.h1
-rw-r--r--src/qt/walletframe.cpp2
-rw-r--r--src/qt/walletmodel.cpp17
-rw-r--r--src/qt/walletmodel.h8
-rw-r--r--src/qt/walletview.cpp4
-rw-r--r--src/qt/walletview.h1
-rw-r--r--src/rpc/blockchain.cpp30
-rw-r--r--src/rpc/mining.cpp5
-rw-r--r--src/rpc/net.cpp3
-rw-r--r--src/rpc/request.h10
-rw-r--r--src/rpc/server.cpp25
-rw-r--r--src/rpc/util.cpp16
-rw-r--r--src/script/script.cpp4
-rw-r--r--src/script/script.h2
-rw-r--r--src/script/script_error.cpp4
-rw-r--r--src/script/script_error.h4
-rw-r--r--src/script/sigcache.cpp14
-rw-r--r--src/script/standard.cpp6
-rw-r--r--src/script/standard.h7
-rw-r--r--src/support/lockedpool.cpp1
-rw-r--r--src/support/lockedpool.h4
-rw-r--r--src/sync.cpp5
-rw-r--r--src/sync.h6
-rw-r--r--src/test/checkqueue_tests.cpp25
-rw-r--r--src/test/fuzz/crypto_common.cpp70
-rw-r--r--src/test/fuzz/kitchen_sink.cpp1
-rw-r--r--src/test/fuzz/net_permissions.cpp5
-rw-r--r--src/test/fuzz/process_message.cpp11
-rw-r--r--src/test/fuzz/process_messages.cpp3
-rw-r--r--src/test/fuzz/script.cpp39
-rw-r--r--src/test/fuzz/script_bitcoin_consensus.cpp31
-rw-r--r--src/test/fuzz/script_descriptor_cache.cpp42
-rw-r--r--src/test/fuzz/script_interpreter.cpp41
-rw-r--r--src/test/fuzz/script_sigcache.cpp45
-rw-r--r--src/test/fuzz/script_sign.cpp149
-rw-r--r--src/test/fuzz/string.cpp5
-rw-r--r--src/test/fuzz/util.h71
-rw-r--r--src/test/getarg_tests.cpp133
-rw-r--r--src/test/miner_tests.cpp4
-rw-r--r--src/test/netbase_tests.cpp11
-rw-r--r--src/test/raii_event_tests.cpp19
-rw-r--r--src/test/scheduler_tests.cpp2
-rw-r--r--src/test/script_tests.cpp4
-rw-r--r--src/test/timedata_tests.cpp3
-rw-r--r--src/test/util/setup_common.h2
-rw-r--r--src/threadsafety.h17
-rw-r--r--src/timedata.cpp38
-rw-r--r--src/txdb.cpp4
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/txmempool.h6
-rw-r--r--src/util/error.cpp26
-rw-r--r--src/util/error.h4
-rw-r--r--src/util/system.cpp18
-rw-r--r--src/util/translation.h5
-rw-r--r--src/validation.cpp149
-rw-r--r--src/validation.h3
-rw-r--r--src/validationinterface.cpp20
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/wallet/context.cpp8
-rw-r--r--src/wallet/context.h32
-rw-r--r--src/wallet/db.cpp166
-rw-r--r--src/wallet/db.h24
-rw-r--r--src/wallet/init.cpp11
-rw-r--r--src/wallet/load.cpp7
-rw-r--r--src/wallet/load.h2
-rw-r--r--src/wallet/rpcdump.cpp115
-rw-r--r--src/wallet/rpcwallet.cpp498
-rw-r--r--src/wallet/rpcwallet.h22
-rw-r--r--src/wallet/salvage.cpp150
-rw-r--r--src/wallet/salvage.h14
-rw-r--r--src/wallet/test/init_test_fixture.h1
-rw-r--r--src/wallet/test/wallet_test_fixture.h1
-rw-r--r--src/wallet/test/wallet_tests.cpp1
-rw-r--r--src/wallet/wallet.cpp72
-rw-r--r--src/wallet/wallet.h10
-rw-r--r--src/wallet/walletdb.cpp64
-rw-r--r--src/wallet/walletdb.h11
-rw-r--r--src/wallet/wallettool.cpp37
-rw-r--r--src/warnings.cpp57
-rw-r--r--src/warnings.h8
-rw-r--r--test/README.md1
-rw-r--r--test/config.ini.in1
-rw-r--r--test/functional/README.md6
-rw-r--r--test/functional/data/invalid_txs.py3
-rwxr-xr-xtest/functional/feature_config_args.py2
-rwxr-xr-xtest/functional/feature_dbcrash.py7
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py7
-rwxr-xr-xtest/functional/feature_notifications.py13
-rwxr-xr-xtest/functional/feature_segwit.py9
-rwxr-xr-xtest/functional/mempool_persist.py10
-rwxr-xr-xtest/functional/mempool_unbroadcast.py12
-rwxr-xr-xtest/functional/mining_basic.py38
-rwxr-xr-xtest/functional/p2p_blockfilters.py74
-rwxr-xr-xtest/functional/p2p_filter.py166
-rwxr-xr-xtest/functional/p2p_mempool.py34
-rwxr-xr-xtest/functional/p2p_nobloomfilter_messages.py43
-rwxr-xr-xtest/functional/p2p_node_network_limited.py3
-rwxr-xr-xtest/functional/p2p_segwit.py52
-rwxr-xr-xtest/functional/rpc_createmultisig.py14
-rwxr-xr-xtest/functional/rpc_getaddressinfo_label_deprecation.py2
-rwxr-xr-xtest/functional/rpc_help.py7
-rwxr-xr-xtest/functional/rpc_net.py9
-rwxr-xr-xtest/functional/rpc_psbt.py4
-rw-r--r--test/functional/test_framework/address.py49
-rw-r--r--test/functional/test_framework/blocktools.py8
-rw-r--r--test/functional/test_framework/key.py13
-rwxr-xr-xtest/functional/test_framework/messages.py51
-rwxr-xr-xtest/functional/test_framework/mininode.py11
-rw-r--r--test/functional/test_framework/script.py11
-rwxr-xr-xtest/functional/test_framework/test_framework.py11
-rw-r--r--test/functional/test_framework/util.py18
-rwxr-xr-xtest/functional/test_framework/wallet_util.py17
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/tool_wallet.py10
-rwxr-xr-xtest/functional/wallet_basic.py2
-rwxr-xr-xtest/functional/wallet_hd.py52
-rwxr-xr-xtest/functional/wallet_labels.py27
-rwxr-xr-xtest/functional/wallet_multiwallet.py4
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py15
-rwxr-xr-xtest/functional/wallet_txn_clone.py1
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py1
-rwxr-xr-xtest/lint/lint-includes.sh2
-rwxr-xr-xtest/lint/lint-python.sh15
-rw-r--r--test/sanitizer_suppressions/tsan29
214 files changed, 4822 insertions, 2207 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index fde5551531..44b49bb397 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -30,9 +30,5 @@ task:
folder: "/tmp/ccache_dir"
depends_built_cache:
folder: "/tmp/cirrus-ci-build/depends/built"
- install_script:
- - apt-get update
- - apt-get -y install git bash ccache
- - ccache --max-size=${CCACHE_SIZE}
ci_script:
- ./ci/test_run_all.sh
diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.md b/.github/ISSUE_TEMPLATE/good_first_issue.md
index c441f7a307..8be78a1f6e 100644
--- a/.github/ISSUE_TEMPLATE/good_first_issue.md
+++ b/.github/ISSUE_TEMPLATE/good_first_issue.md
@@ -7,6 +7,9 @@ assignees: ''
---
+<!-- A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution -->
+
+<!-- Motivate the issue and explain the solution briefly -->
#### Useful skills:
@@ -14,8 +17,4 @@ assignees: ''
#### Want to work on this issue?
-The purpose of the `good first issue` label is to highlight which issues are suitable for a new contributor without a deep understanding of the codebase.
-
-You do not need to request permission to start working on this. You are encouraged to comment on the issue if you are planning to work on it. This will help other contributors monitor which issues are actively being addressed and is also an effective way to request assistance if and when you need it.
-
For guidance on contributing, please read [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md) before opening your pull request.
diff --git a/.gitignore b/.gitignore
index 5b9ec1f355..23b6090265 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,6 +127,7 @@ linux-build
win32-build
test/config.ini
test/cache/*
+test/.mypy_cache/
!src/leveldb*/Makefile
diff --git a/.travis.yml b/.travis.yml
index 1598813ed2..03ab9d2613 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -112,7 +112,7 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_native_qt5.sh"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: thread (TSan), no wallet]'
+ name: 'x86_64 Linux [GOAL: install] [focal] [depends, sanitizers: thread (TSan), no gui]'
# Not enough memory on travis machines, so feature_block is excluded for now
env: >-
TEST_RUNNER_EXTRA="--exclude feature_block"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7216db0500..f34b1e1e15 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,15 +14,49 @@ purposes. As such there are repository "maintainers" who are responsible for
merging pull requests as well as a "lead maintainer" who is responsible for the
release cycle, overall merging, moderation and appointment of maintainers.
+Getting Started
+---------------
+
+New contributors are very welcome and needed.
+
+Reviewing and testing is the most effective way you can contribute as a new
+contributor, and it also will teach you much more about the code and process
+than opening PRs. Please refer to the section [peer review](#peer-review) later
+in this document.
+
+Before you start contributing, familiarize yourself with the Bitcoin Core build
+system and tests. Refer to the documentation in the repository on how to build
+Bitcoin Core and how to run the unit and functional tests.
+
+There are many open issues of varying difficulty waiting to be fixed.
If you're looking for somewhere to start contributing, check out the
[good first issue](https://github.com/bitcoin/bitcoin/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
-list or participate in a weekly [Bitcoin Core PR Review Club](https://bitcoincore.reviews/) meeting.
+list or changes that are
+[up for grabs](https://github.com/bitcoin/bitcoin/issues?utf8=%E2%9C%93&q=label%3A%22Up+for+grabs%22).
+Some of them might no longer be applicable. So if you are interested, but
+unsure, you might want to leave a comment on the issue first.
+
+You may also participate in the weekly
+[Bitcoin Core PR Review Club](https://bitcoincore.reviews/) meeting.
+
+### Good First Issue Label
+
+The purpose of the `good first issue` label is to highlight which issues are
+suitable for a new contributor without a deep understanding of the codebase.
+
+However, good first issues can be solved by anyone. If they remain unsolved
+for a longer time, a frequent contributor might address them.
+
+You do not need to request permission to start working on an issue. However,
+you are encouraged to leave a comment if you are planning to work on it. This
+will help other contributors monitor which issues are actively being addressed
+and is also an effective way to request assistance if and when you need it.
Communication Channels
----------------------
Most communication about Bitcoin Core development happens on IRC, in the
-#bitcoin-core-dev channel on Freenode. The easiest way to participate on IRC is
+`#bitcoin-core-dev` channel on Freenode. The easiest way to participate on IRC is
with the web client, [webchat.freenode.net](https://webchat.freenode.net/). Chat
history logs can be found
on [http://www.erisian.com.au/bitcoin-core-dev/](http://www.erisian.com.au/bitcoin-core-dev/)
@@ -33,7 +67,7 @@ requests.
The developer
[mailing list](https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev)
-should be used to discuss complicated or controversial changes before working on
+should be used to discuss complicated or controversial consensus or P2P protocol changes before working on
a patch set.
@@ -53,6 +87,8 @@ To contribute a patch, the workflow is as follows:
The project coding conventions in the [developer notes](doc/developer-notes.md)
must be followed.
+### Committing Patches
+
In general, [commits should be atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention)
and diffs should be easy to read. For this reason, do not mix any formatting
fixes or code moves with actual code changes.
@@ -76,6 +112,8 @@ about Git.
- Push changes to your fork
- Create pull request
+### Creating the Pull Request
+
The title of the pull request should be prefixed by the component or area that
the pull request affects. Valid areas as:
@@ -100,18 +138,24 @@ Examples:
qt: Add feed bump button
log: Fix typo in log message
+The body of the pull request should contain enough description about what the
+patch does together with any justification/reasoning. You should include
+references to any discussions (for example other tickets or mailing list
+discussions).
+
+### Translation changes
+
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)
for more information on helping with translations.
+### Work in Progress Changes and Requests for Comments
+
If a pull request is not to be considered for merging (yet), please
prefix the title with [WIP] or use [Tasks Lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#task-lists)
in the body of the pull request to indicate tasks are pending.
-The body of the pull request should contain enough description about what the
-patch does together with any justification/reasoning. You should include
-references to any discussions (for example other tickets or mailing list
-discussions).
+### Address Feedback
At this stage, one should expect comments and review from other contributors. You
can add more commits to your pull request by committing them locally and pushing
diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh
index 8b2d609504..26b576c1ae 100755
--- a/ci/lint/04_install.sh
+++ b/ci/lint/04_install.sh
@@ -9,6 +9,7 @@ export LC_ALL=C
travis_retry pip3 install codespell==1.15.0
travis_retry pip3 install flake8==3.7.8
travis_retry pip3 install yq
+travis_retry pip3 install mypy==0.700
SHELLCHECK_VERSION=v0.6.0
curl -s "https://storage.googleapis.com/shellcheck/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index f51cbf8f3c..43ee219ef9 100644
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -14,4 +14,4 @@ export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export GOAL="install"
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined --enable-c++17 CC=clang CXX=clang++"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
index fb4c27c36f..c27d525003 100644
--- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -15,4 +15,4 @@ export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export FUZZ_TESTS_CONFIG="--valgrind"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer --enable-c++17 CC=clang CXX=clang++"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 63d06dea64..87b9d9da95 100644
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -7,8 +7,8 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_tsan
-export DOCKER_NAME_TAG=ubuntu:18.04
-export PACKAGES="clang-9 llvm-9 python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
-export NO_DEPENDS=1
+export DOCKER_NAME_TAG=ubuntu:20.04
+export PACKAGES="clang llvm libc++abi-dev libc++-dev python3-zmq"
+export DEP_OPTS="CC=clang CXX='clang++ -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=thread --disable-hardening --disable-asm CC=clang-9 CXX=clang++-9"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=no CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=thread CC=clang CXX='clang++ -stdlib=libc++'"
diff --git a/ci/test/03_before_install.sh b/ci/test/03_before_install.sh
index e939b9eeeb..80806aab75 100755
--- a/ci/test/03_before_install.sh
+++ b/ci/test/03_before_install.sh
@@ -6,10 +6,6 @@
export LC_ALL=C.UTF-8
-# Add llvm-symbolizer directory to PATH. Needed to get symbolized stack traces from the sanitizers.
-PATH=$PATH:/usr/lib/llvm-6.0/bin/
-export PATH
-
BEGIN_FOLD () {
echo ""
CURRENT_FOLD_NAME=$1
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index 14ab943d68..165983d906 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -24,7 +24,7 @@ mkdir -p "${PREVIOUS_RELEASES_DIR}"
export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1"
export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan"
-export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan"
+export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan"
export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
env | grep -E '^(BITCOIN_CONFIG|BASE_|QEMU_|CCACHE_|LC_ALL|BOOST_TEST_RANDOM|DEBIAN_FRONTEND|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS|PREVIOUS_RELEASES_DIR)' | tee /tmp/env
if [[ $HOST = *-mingw32 ]]; then
@@ -48,16 +48,14 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
--env-file /tmp/env \
--name $CONTAINER_NAME \
$DOCKER_NAME_TAG)
-
- DOCKER_EXEC () {
- docker exec $DOCKER_ID bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $P_CI_DIR && $*"
- }
+ export DOCKER_CI_CMD_PREFIX="docker exec $DOCKER_ID"
else
echo "Running on host system without docker wrapper"
- DOCKER_EXEC () {
- bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $P_CI_DIR && $*"
- }
fi
+
+DOCKER_EXEC () {
+ $DOCKER_CI_CMD_PREFIX bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $P_CI_DIR && $*"
+}
export -f DOCKER_EXEC
if [ -n "$DPKG_ADD_ARCH" ]; then
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index 3685504524..efaaf154b1 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -33,7 +33,9 @@ if [ -z "$NO_DEPENDS" ]; then
else
SHELL_OPTS="CONFIG_SHELL="
fi
- DOCKER_EXEC $SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS
+ # Temporary workaround for https://github.com/bitcoin/bitcoin/issues/16368
+ python3 -c 'import time; [print(".") or time.sleep(500) for _ in range(4)]' &
+ ( DOCKER_EXEC $SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS ) &> /dev/null
fi
if [ -n "$PREVIOUS_RELEASES_TO_DOWNLOAD" ]; then
BEGIN_FOLD previous-versions
diff --git a/configure.ac b/configure.ac
index 7f0c5dbd7a..3bfbe2e7b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,7 +69,7 @@ AC_ARG_ENABLE([c++17],
[use_cxx17=no])
dnl Require C++11 or C++17 compiler (no GNU extensions)
-if test "x$use_cxx17" = xyes; then
+if test "x$use_cxx17" = xyes -o "x$enable_fuzz" = xyes ; then
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
else
AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory])
@@ -380,8 +380,9 @@ if test "x$enable_werror" = "xyes"; then
fi
AX_CHECK_COMPILE_FLAG([-Werror=gnu],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=gnu"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Werror=shadow-field],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=shadow-field"],,[[$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]])
+ AX_CHECK_COMPILE_FLAG([-Werror=thread-safety],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=unused-variable],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unused-variable"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=date-time],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=date-time"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=return-type],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=return-type"],,[[$CXXFLAG_WERROR]])
@@ -391,6 +392,7 @@ if test "x$enable_werror" = "xyes"; then
dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78010
AX_CHECK_COMPILE_FLAG([-Werror=suggest-override],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=suggest-override"],,[[$CXXFLAG_WERROR]],
[AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
+ AX_CHECK_COMPILE_FLAG([-Werror=unreachable-code-loop-increment],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
fi
if test "x$CXXFLAGS_overridden" = "xno"; then
@@ -399,9 +401,10 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wgnu],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wgnu"],,[[$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([-Wshadow-field],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wshadow-field"],,[[$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([-Wthread-safety],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wredundant-decls"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wunused-variable],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunused-variable"],,[[$CXXFLAG_WERROR]])
@@ -410,6 +413,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wsign-compare],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsign-compare"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wsuggest-override],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsuggest-override"],,[[$CXXFLAG_WERROR]],
[AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
+ AX_CHECK_COMPILE_FLAG([-Wunreachable-code-loop-increment],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
dnl Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
dnl unknown options if any other warning is produced. Test the -Wfoo case, and
@@ -422,6 +426,9 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]])
fi
+dnl Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
+AX_CHECK_COMPILE_FLAG([-fno-extended-identifiers],[[CXXFLAGS="$CXXFLAGS -fno-extended-identifiers"]],,[[$CXXFLAG_WERROR]])
+
enable_sse42=no
enable_sse41=no
enable_avx2=no
@@ -808,7 +815,7 @@ if test x$use_hardening != xno; then
fi
dnl These flags are specific to ld64, and may cause issues with other linkers.
-dnl For example: GNU ld will intepret -dead_strip as -de and then try and use
+dnl For example: GNU ld will interpret -dead_strip as -de and then try and use
dnl "ad_strip" as the symbol for the entry point.
if test x$TARGET_OS = xdarwin; then
AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"],, [[$LDFLAG_WERROR]])
@@ -1749,7 +1756,7 @@ echo " gprof enabled = $enable_gprof"
echo " werror = $enable_werror"
echo
echo " target os = $TARGET_OS"
-echo " build os = $BUILD_OS"
+echo " build os = $build_os"
echo
echo " CC = $CC"
echo " CFLAGS = $CFLAGS"
diff --git a/contrib/devtools/previous_release.sh b/contrib/devtools/previous_release.sh
index b2ecc274fb..d375291f47 100755
--- a/contrib/devtools/previous_release.sh
+++ b/contrib/devtools/previous_release.sh
@@ -137,7 +137,7 @@ pushd "$TARGET" || exit 1
else
BIN_PATH="bin/bitcoin-core-${tag:1}"
fi
- URL="https://bitcoin.org/$BIN_PATH/bitcoin-${tag:1}-$PLATFORM.tar.gz"
+ URL="https://bitcoincore.org/$BIN_PATH/bitcoin-${tag:1}-$PLATFORM.tar.gz"
echo "Fetching: $URL"
if ! curl -O -f $URL; then
echo "Download failed."
diff --git a/contrib/valgrind.supp b/contrib/valgrind.supp
index f232bb62c2..d2652119b4 100644
--- a/contrib/valgrind.supp
+++ b/contrib/valgrind.supp
@@ -1,7 +1,5 @@
-# Valgrind suppressions file for Bitcoin.
-#
-# Includes known Valgrind warnings in our dependencies that cannot be fixed
-# in-tree.
+# This valgrind suppressions file includes known Valgrind warnings in our
+# dependencies that cannot be fixed in-tree.
#
# Example use:
# $ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
@@ -14,6 +12,9 @@
# --error-limit=no src/test/test_bitcoin
#
# Note that suppressions may depend on OS and/or library versions.
+# Tested on:
+# * aarch64 (Ubuntu 20.04 system libs, without gui)
+# * x86_64 (Ubuntu 18.04 system libs, without gui)
{
Suppress libstdc++ warning - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65434
Memcheck:Leak
@@ -47,8 +48,7 @@
Suppress libdb warning
Memcheck:Param
pwrite64(buf)
- fun:pwrite
- fun:__os_io
+ ...
obj:*/libdb_cxx-*.so
}
{
diff --git a/depends/Makefile b/depends/Makefile
index 5ad82bb56a..3d0784cb6b 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -4,6 +4,30 @@
print-%:
@echo $* = $($*)
+# When invoking a sub-make, keep only the command line variable definitions
+# matching the pattern in the filter function.
+#
+# e.g. invoking:
+# $ make A=1 C=1 print-MAKEOVERRIDES print-MAKEFLAGS
+#
+# with the following in the Makefile:
+# MAKEOVERRIDES := $(filter A=% B=%,$(MAKEOVERRIDES))
+#
+# will print:
+# MAKEOVERRIDES = A=1
+# MAKEFLAGS = -- A=1
+#
+# this is because as the GNU make manual says:
+# The command line variable definitions really appear in the variable
+# MAKEOVERRIDES, and MAKEFLAGS contains a reference to this variable.
+#
+# and since the GNU make manual also says:
+# variables defined on the command line are passed to the sub-make through
+# MAKEFLAGS
+#
+# this means that sub-makes will be invoked as if:
+# $(MAKE) A=1 blah blah
+MAKEOVERRIDES := $(filter V=%,$(MAKEOVERRIDES))
SOURCES_PATH ?= $(BASEDIR)/sources
WORK_PATH = $(BASEDIR)/work
BASE_CACHE ?= $(BASEDIR)/built
diff --git a/depends/hosts/default.mk b/depends/hosts/default.mk
index 144e5f88b7..258619a9d0 100644
--- a/depends/hosts/default.mk
+++ b/depends/hosts/default.mk
@@ -13,9 +13,18 @@ default_host_OTOOL = $(host_toolchain)otool
default_host_NM = $(host_toolchain)nm
define add_host_tool_func
+ifneq ($(filter $(origin $1),undefined default),)
+# Do not consider the well-known var $1 if it is undefined or is taking a value
+# that is predefined by "make" (e.g. the make variable "CC" has a predefined
+# value of "cc")
$(host_os)_$1?=$$(default_host_$1)
$(host_arch)_$(host_os)_$1?=$$($(host_os)_$1)
$(host_arch)_$(host_os)_$(release_type)_$1?=$$($(host_os)_$1)
+else
+$(host_os)_$1=$(or $($1),$($(host_os)_$1),$(default_host_$1))
+$(host_arch)_$(host_os)_$1=$(or $($1),$($(host_arch)_$(host_os)_$1),$$($(host_os)_$1))
+$(host_arch)_$(host_os)_$(release_type)_$1=$(or $($1),$($(host_arch)_$(host_os)_$(release_type)_$1),$$($(host_os)_$1))
+endif
host_$1=$$($(host_arch)_$(host_os)_$1)
endef
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index 970c81041e..3a7e605b4f 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -9,9 +9,9 @@ $(package)_config_opts_release=variant=release
$(package)_config_opts_debug=variant=debug
$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
-$(package)_config_opts_linux=threadapi=pthread runtime-link=shared
-$(package)_config_opts_darwin=--toolset=clang-darwin runtime-link=shared
-$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
+$(package)_config_opts_linux=target-os=linux threadapi=pthread runtime-link=shared
+$(package)_config_opts_darwin=target-os=darwin runtime-link=shared
+$(package)_config_opts_mingw32=target-os=windows binary-format=pe threadapi=win32 runtime-link=static
$(package)_config_opts_x86_64_mingw32=address-model=64
$(package)_config_opts_i686_mingw32=address-model=32
$(package)_config_opts_i686_linux=address-model=32 architecture=x86
@@ -20,8 +20,11 @@ $(package)_config_opts_aarch64_android=address-model=64
$(package)_config_opts_x86_64_android=address-model=64
$(package)_config_opts_armv7a_android=address-model=32
$(package)_toolset_$(host_os)=gcc
+$(package)_toolset_darwin=clang
+ifneq (,$(findstring clang,$($(package)_cxx)))
+ $(package)_toolset_$(host_os)=clang
+endif
$(package)_archiver_$(host_os)=$($(package)_ar)
-$(package)_toolset_darwin=clang-darwin
$(package)_config_libraries=filesystem,system,thread,test
$(package)_cxxflags=-std=c++11 -fvisibility=hidden
$(package)_cxxflags_linux=-fPIC
@@ -33,13 +36,13 @@ define $(package)_preprocess_cmds
endef
define $(package)_config_cmds
- ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries)
+ ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries) --with-toolset=$($(package)_toolset_$(host_os))
endef
define $(package)_build_cmds
- ./b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) stage
+ ./b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) stage
endef
define $(package)_stage_cmds
- ./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install
+ ./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) install
endef
diff --git a/doc/build-windows.md b/doc/build-windows.md
index bbff638b90..d3dc467f19 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -9,7 +9,7 @@ The options known to work for building Bitcoin Core on Windows are:
and is the platform used to build the Bitcoin Core Windows release binaries.
* On Windows, using [Windows
Subsystem for Linux (WSL)](https://docs.microsoft.com/windows/wsl/about) and the Mingw-w64 cross compiler tool chain.
-* On Windows, using a native compiler tool chain such as [Visual Studio](https://www.visualstudio.com).
+* On Windows, using a native compiler tool chain such as [Visual Studio](https://www.visualstudio.com). See [README.md](/build_msvc/README.md).
Other options which may work, but which have not been extensively tested are (please contribute instructions):
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
index 16852bb970..419b1db44e 100644
--- a/doc/fuzzing.md
+++ b/doc/fuzzing.md
@@ -8,7 +8,7 @@ To quickly get started fuzzing Bitcoin Core using [libFuzzer](https://llvm.org/d
$ git clone https://github.com/bitcoin/bitcoin
$ cd bitcoin/
$ ./autogen.sh
-$ CC=clang CXX=clang++ ./configure --enable-fuzz --with-sanitizers=address,fuzzer,undefined --enable-c++17
+$ CC=clang CXX=clang++ ./configure --enable-fuzz --with-sanitizers=address,fuzzer,undefined
# macOS users: If you have problem with this step then make sure to read "macOS hints for
# libFuzzer" on https://github.com/bitcoin/bitcoin/blob/master/doc/fuzzing.md#macos-hints-for-libfuzzer
$ make
@@ -103,7 +103,7 @@ You may also need to take care of giving the correct path for `clang` and
Full configure that was tested on macOS Catalina with `brew` installed `llvm`:
```sh
-./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm --enable-c++17
+./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm
```
Read the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html) for more information. This [libFuzzer tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) might also be of interest.
@@ -121,7 +121,7 @@ $ git clone https://github.com/google/afl
$ make -C afl/
$ make -C afl/llvm_mode/
$ ./autogen.sh
-$ CC=$(pwd)/afl/afl-clang-fast CXX=$(pwd)/afl/afl-clang-fast++ ./configure --enable-fuzz --enable-c++17
+$ CC=$(pwd)/afl/afl-clang-fast CXX=$(pwd)/afl/afl-clang-fast++ ./configure --enable-fuzz
$ make
# For macOS you may need to ignore x86 compilation checks when running "make". If so,
# try compiling using: AFL_NO_X86=1 make
@@ -148,7 +148,7 @@ $ git clone https://github.com/google/honggfuzz
$ cd honggfuzz/
$ make
$ cd ..
-$ CC=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang CXX=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang++ ./configure --enable-fuzz --with-sanitizers=address,undefined --enable-c++17
+$ CC=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang CXX=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang++ ./configure --enable-fuzz --with-sanitizers=address,undefined
$ make
$ mkdir -p inputs/
$ honggfuzz/honggfuzz -i inputs/ -- src/test/fuzz/process_message
diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md
index e39e43df7a..ce77a00dd5 100644
--- a/doc/reduce-traffic.md
+++ b/doc/reduce-traffic.md
@@ -23,7 +23,7 @@ longer serving historic blocks (blocks older than one week).
Keep in mind that new nodes require other nodes that are willing to serve
historic blocks.
-Whitelisted peers will never be disconnected, although their traffic counts for
+Peers with the `noban` permission will never be disconnected, although their traffic counts for
calculating the target.
## 2. Disable "listening" (`-listen=0`)
diff --git a/doc/release-notes-16528.md b/doc/release-notes-16528.md
index 0a7a31cc9d..e69de29bb2 100644
--- a/doc/release-notes-16528.md
+++ b/doc/release-notes-16528.md
@@ -1,120 +0,0 @@
-Wallet
-------
-
-### Experimental Descriptor Wallets
-
-Please note that Descriptor Wallets are still experimental and not all expected functionality
-is available. Additionally there may be some bugs and current functions may change in the future.
-Bugs and missing functionality can be reported to the [issue tracker](https://github.com/bitcoin/bitcoin/issues).
-
-0.21 introduces a new type of wallet - Descriptor Wallets. Descriptor Wallets store
-scriptPubKey information using descriptors. This is in contrast to the Legacy Wallet
-structure where keys are used to generate scriptPubKeys and addresses. Because of this
-shift to being script based instead of key based, many of the confusing things that Legacy
-Wallets do are not possible with Descriptor Wallets. Descriptor Wallets use a definition
-of "mine" for scripts which is simpler and more intuitive than that used by Legacy Wallets.
-Descriptor Wallets also uses different semantics for watch-only things and imports.
-
-As Descriptor Wallets are a new type of wallet, their introduction does not affect existing wallets.
-Users who already have a Bitcoin Core wallet can continue to use it as they did before without
-any change in behavior. Newly created Legacy Wallets (which is the default type of wallet) will
-behave as they did in previous versions of Bitcoin Core.
-
-The differences between Descriptor Wallets and Legacy Wallets are largely limited to non user facing
-things. They are intended to behave similarly except for the import/export and watchonly functionality
-as described below.
-
-#### Creating Descriptor Wallets
-
-Descriptor Wallets are not created by default. They must be explicitly created using the
-`createwallet` RPC or via the GUI. A `descriptors` option has been added to `createwallet`.
-Setting `descriptors` to `true` will create a Descriptor Wallet instead of a Legacy Wallet.
-
-In the GUI, a checkbox has been added to the Create Wallet Dialog to indicate that a
-Descriptor Wallet should be created.
-
-Without those options being set, a Legacy Wallet will be created instead. Additionally the
-Default Wallet created upon first startup of Bitcoin Core will be a Legacy Wallet.
-
-#### `IsMine` Semantics
-
-`IsMine` refers to the function used to determine whether a script belongs to the wallet.
-This is used to determine whether an output belongs to the wallet. `IsMine` in Legacy Wallets
-returns true if the wallet would be able to sign an input that spends an output with that script.
-Since keys can be involved in a variety of different scripts, this definition for `IsMine` can
-lead to many unexpected scripts being considered part of the wallet.
-
-With Descriptor Wallets, descriptors explicitly specify the set of scripts that are owned by
-the wallet. Since descriptors are deterministic and easily enumerable, users will know exactly
-what scripts the wallet will consider to belong to it. Additionally the implementation of `IsMine`
-in Descriptor Wallets is far simpler than for Legacy Wallets. Notably, in Legacy Wallets, `IsMine`
-allowed for users to take one type of address (e.g. P2PKH), mutate it into another address type
-(e.g. P2WPKH), and the wallet would still detect outputs sending to the new address type
-even without that address being requested from the wallet. Descriptor Wallets does not
-allow for this and will only watch for the addresses that were explicitly requested from the wallet.
-
-These changes to `IsMine` will make it easier to reason about what scripts the wallet will
-actually be watching for in outputs. However for the vast majority of users, this change is
-largely transparent and will not have noticeable effect.
-
-#### Imports and Exports
-
-In Legacy Wallets, raw scripts and keys could be imported to the wallet. Those imported scripts
-and keys are treated separately from the keys generated by the wallet. This complicates the `IsMine`
-logic as it has to distinguish between spendable and watchonly.
-
-Descriptor Wallets handle importing scripts and keys differently. Only complete descriptors can be
-imported. These descriptors are then added to the wallet as if it were a descriptor generated by
-the wallet itself. This simplifies the `IsMine` logic so that it no longer has to distinguish
-between spendable and watchonly. As such, the watchonly model for Descriptor Wallets is also
-different and described in more detail in the next section.
-
-To import into a Descriptor Wallet, a new `importdescriptors` RPC has been added that uses a syntax
-similar to that of `importmulti`.
-
-As Legacy Wallets and Descriptor Wallets use different mechanisms for storing and importing scripts and keys
-the existing import RPCs have been disabled for descriptor wallets.
-New export RPCs for Descriptor Wallets have not yet been added.
-
-The following RPCs are disabled for Descriptor Wallets:
-
-* importprivkey
-* importpubkey
-* importaddress
-* importwallet
-* dumpprivkey
-* dumpwallet
-* importmulti
-* addmultisigaddress
-* sethdseed
-
-#### Watchonly Wallets
-
-A Legacy Wallet contains both private keys and scripts that were being watched.
-Those watched scripts would not contribute to your normal balance. In order to see the watchonly
-balance and to use watchonly things in transactions, an `include_watchonly` option was added
-to many RPCs that would allow users to do that. However it is easy to forget to include this option.
-
-Descriptor Wallets move to a per-wallet watchonly model. Instead an entire wallet is considered to be
-watchonly depending on whether it was created with private keys disabled. This eliminates the need
-to distinguish between things that are watchonly and things that are not within a wallet itself.
-
-This change does have a caveat. If a Descriptor Wallet with private keys *enabled* has
-a multiple key descriptor without all of the private keys (e.g. `multi(...)` with only one private key),
-then the wallet will fail to sign and broadcast transactions. Such wallets would need to use the PSBT
-workflow but the typical GUI Send, `sendtoaddress`, etc. workflows would still be available, just
-non-functional.
-
-This issue is worsened if the wallet contains both single key (e.g. `wpkh(...)`) descriptors and such
-multiple key descriptors as some transactions could be signed and broadast and others not. This is
-due to some transactions containing only single key inputs, while others would contain both single
-key and multiple key inputs, depending on which are available and how the coin selection algorithm
-selects inputs. However this is not considered to be a supported use case; multisigs
-should be in their own wallets which do not already have descriptors. Although users cannot export
-descriptors with private keys for now as explained earlier.
-
-#### BIP 44/49/84 Support
-
-The change to using descriptors changes the default derivation paths used by Bitcoin Core
-to adhere to BIP 44/49/84. Descriptors with different derivation paths can be imported without
-issue.
diff --git a/doc/release-notes-18918.md b/doc/release-notes-18918.md
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/doc/release-notes-18918.md
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 0d668a6302..d9d0ecd631 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -34,40 +34,55 @@ How to Upgrade
==============
If you are running an older version, shut it down. Wait until it has completely
-shut down (which might take a few minutes for older versions), then run the
+shut down (which might take a few minutes in some cases), then run the
installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
or `bitcoind`/`bitcoin-qt` (on Linux).
Upgrading directly from a version of Bitcoin Core that has reached its EOL is
-possible, but it might take some time if the datadir needs to be migrated. Old
+possible, but it might take some time if the data directory needs to be migrated. Old
wallet versions of Bitcoin Core are generally supported.
Compatibility
==============
-Bitcoin Core is supported and extensively tested on operating systems using
-the Linux kernel, macOS 10.12+, and Windows 7 and newer. It is not recommended
-to use Bitcoin Core on unsupported systems.
-
-Bitcoin Core should also work on most other Unix-like systems but is not
-as frequently tested on them.
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.12+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no
longer supported. Additionally, Bitcoin Core does not yet change appearance
when macOS "dark mode" is activated.
-In addition to previously supported CPU platforms, this release's pre-compiled
-distribution provides binaries for the RISC-V platform.
-
Notable changes
===============
P2P and network changes
-----------------------
+- The mempool now tracks whether transactions submitted via the wallet or RPCs
+ have been successfully broadcast. Every 10-15 minutes, the node will try to
+ announce unbroadcast transactions until a peer requests it via a `getdata`
+ message or the transaction is removed from the mempool for other reasons.
+ The node will not track the broadcast status of transactions submitted to the
+ node using P2P relay. This version reduces the initial broadcast guarantees
+ for wallet transactions submitted via P2P to a node running the wallet. (#18038)
+
Updated RPCs
------------
+- `getmempoolinfo` now returns an additional `unbroadcastcount` field. The
+ mempool tracks locally submitted transactions until their initial broadcast
+ is acknowledged by a peer. This field returns the count of transactions
+ waiting for acknowledgement.
+
+- Mempool RPCs such as `getmempoolentry` and `getrawmempool` with
+ `verbose=true` now return an additional `unbroadcast` field. This indicates
+ whether initial broadcast of the transaction has been acknowledged by a
+ peer. `getmempoolancestors` and `getmempooldescendants` are also updated.
+
+
Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section below.
New RPCs
@@ -79,6 +94,9 @@ Build System
Updated settings
----------------
+- The `-debug=db` logging category, which was deprecated in 0.20 and replaced by
+ `-debug=walletdb` to distinguish it from `coindb`, has been removed. (#19202)
+
Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below.
New settings
@@ -87,7 +105,139 @@ New settings
Wallet
------
-#### Wallet RPC changes
+- To improve wallet privacy, the frequency of wallet rebroadcast attempts is
+ reduced from approximately once every 15 minutes to once every 12-36 hours.
+ To maintain a similar level of guarantee for initial broadcast of wallet
+ transactions, the mempool tracks these transactions as a part of the newly
+ introduced unbroadcast set. See the "P2P and network changes" section for
+ more information on the unbroadcast set. (#18038)
+
+- The wallet can create a transaction without change even when the keypool is
+ empty. Previously it failed. (#17219)
+
+- The `-salvagewallet` startup option has been removed. A new `salvage` command
+ has been added to the `bitcoin-wallet` tool which performs the salvage
+ operations that `-salvagewallet` did. (#18918)
+
+### Experimental Descriptor Wallets
+
+Please note that Descriptor Wallets are still experimental and not all expected functionality
+is available. Additionally there may be some bugs and current functions may change in the future.
+Bugs and missing functionality can be reported to the [issue tracker](https://github.com/bitcoin/bitcoin/issues).
+
+0.21 introduces a new type of wallet - Descriptor Wallets. Descriptor Wallets store
+scriptPubKey information using descriptors. This is in contrast to the Legacy Wallet
+structure where keys are used to generate scriptPubKeys and addresses. Because of this
+shift to being script based instead of key based, many of the confusing things that Legacy
+Wallets do are not possible with Descriptor Wallets. Descriptor Wallets use a definition
+of "mine" for scripts which is simpler and more intuitive than that used by Legacy Wallets.
+Descriptor Wallets also uses different semantics for watch-only things and imports.
+
+As Descriptor Wallets are a new type of wallet, their introduction does not affect existing wallets.
+Users who already have a Bitcoin Core wallet can continue to use it as they did before without
+any change in behavior. Newly created Legacy Wallets (which is the default type of wallet) will
+behave as they did in previous versions of Bitcoin Core.
+
+The differences between Descriptor Wallets and Legacy Wallets are largely limited to non user facing
+things. They are intended to behave similarly except for the import/export and watchonly functionality
+as described below.
+
+#### Creating Descriptor Wallets
+
+Descriptor Wallets are not created by default. They must be explicitly created using the
+`createwallet` RPC or via the GUI. A `descriptors` option has been added to `createwallet`.
+Setting `descriptors` to `true` will create a Descriptor Wallet instead of a Legacy Wallet.
+
+In the GUI, a checkbox has been added to the Create Wallet Dialog to indicate that a
+Descriptor Wallet should be created.
+
+Without those options being set, a Legacy Wallet will be created instead. Additionally the
+Default Wallet created upon first startup of Bitcoin Core will be a Legacy Wallet.
+
+#### `IsMine` Semantics
+
+`IsMine` refers to the function used to determine whether a script belongs to the wallet.
+This is used to determine whether an output belongs to the wallet. `IsMine` in Legacy Wallets
+returns true if the wallet would be able to sign an input that spends an output with that script.
+Since keys can be involved in a variety of different scripts, this definition for `IsMine` can
+lead to many unexpected scripts being considered part of the wallet.
+
+With Descriptor Wallets, descriptors explicitly specify the set of scripts that are owned by
+the wallet. Since descriptors are deterministic and easily enumerable, users will know exactly
+what scripts the wallet will consider to belong to it. Additionally the implementation of `IsMine`
+in Descriptor Wallets is far simpler than for Legacy Wallets. Notably, in Legacy Wallets, `IsMine`
+allowed for users to take one type of address (e.g. P2PKH), mutate it into another address type
+(e.g. P2WPKH), and the wallet would still detect outputs sending to the new address type
+even without that address being requested from the wallet. Descriptor Wallets does not
+allow for this and will only watch for the addresses that were explicitly requested from the wallet.
+
+These changes to `IsMine` will make it easier to reason about what scripts the wallet will
+actually be watching for in outputs. However for the vast majority of users, this change is
+largely transparent and will not have noticeable effect.
+
+#### Imports and Exports
+
+In Legacy Wallets, raw scripts and keys could be imported to the wallet. Those imported scripts
+and keys are treated separately from the keys generated by the wallet. This complicates the `IsMine`
+logic as it has to distinguish between spendable and watchonly.
+
+Descriptor Wallets handle importing scripts and keys differently. Only complete descriptors can be
+imported. These descriptors are then added to the wallet as if it were a descriptor generated by
+the wallet itself. This simplifies the `IsMine` logic so that it no longer has to distinguish
+between spendable and watchonly. As such, the watchonly model for Descriptor Wallets is also
+different and described in more detail in the next section.
+
+To import into a Descriptor Wallet, a new `importdescriptors` RPC has been added that uses a syntax
+similar to that of `importmulti`.
+
+As Legacy Wallets and Descriptor Wallets use different mechanisms for storing and importing scripts and keys
+the existing import RPCs have been disabled for descriptor wallets.
+New export RPCs for Descriptor Wallets have not yet been added.
+
+The following RPCs are disabled for Descriptor Wallets:
+
+* importprivkey
+* importpubkey
+* importaddress
+* importwallet
+* dumpprivkey
+* dumpwallet
+* importmulti
+* addmultisigaddress
+* sethdseed
+
+#### Watchonly Wallets
+
+A Legacy Wallet contains both private keys and scripts that were being watched.
+Those watched scripts would not contribute to your normal balance. In order to see the watchonly
+balance and to use watchonly things in transactions, an `include_watchonly` option was added
+to many RPCs that would allow users to do that. However it is easy to forget to include this option.
+
+Descriptor Wallets move to a per-wallet watchonly model. Instead an entire wallet is considered to be
+watchonly depending on whether it was created with private keys disabled. This eliminates the need
+to distinguish between things that are watchonly and things that are not within a wallet itself.
+
+This change does have a caveat. If a Descriptor Wallet with private keys *enabled* has
+a multiple key descriptor without all of the private keys (e.g. `multi(...)` with only one private key),
+then the wallet will fail to sign and broadcast transactions. Such wallets would need to use the PSBT
+workflow but the typical GUI Send, `sendtoaddress`, etc. workflows would still be available, just
+non-functional.
+
+This issue is worsened if the wallet contains both single key (e.g. `wpkh(...)`) descriptors and such
+multiple key descriptors as some transactions could be signed and broadast and others not. This is
+due to some transactions containing only single key inputs, while others would contain both single
+key and multiple key inputs, depending on which are available and how the coin selection algorithm
+selects inputs. However this is not considered to be a supported use case; multisigs
+should be in their own wallets which do not already have descriptors. Although users cannot export
+descriptors with private keys for now as explained earlier.
+
+#### BIP 44/49/84 Support
+
+The change to using descriptors changes the default derivation paths used by Bitcoin Core
+to adhere to BIP 44/49/84. Descriptors with different derivation paths can be imported without
+issue.
+
+### Wallet RPC changes
- The `upgradewallet` RPC replaces the `-upgradewallet` command line option.
(#15761)
diff --git a/doc/release-notes/release-notes-0.20.0.md b/doc/release-notes/release-notes-0.20.0.md
new file mode 100644
index 0000000000..6fc1606b37
--- /dev/null
+++ b/doc/release-notes/release-notes-0.20.0.md
@@ -0,0 +1,987 @@
+0.20.0 Release Notes
+====================
+
+Bitcoin Core version 0.20.0 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.20.0/>
+
+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:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes in some cases), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the data directory needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.12+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
+
+From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no
+longer supported. Additionally, Bitcoin Core does not yet change appearance
+when macOS "dark mode" is activated.
+
+Known Bugs
+==========
+
+The process for generating the source code release ("tarball") has changed in an
+effort to make it more complete, however, there are a few regressions in
+this release:
+
+- The generated `configure` script is currently missing, and you will need to
+ install autotools and run `./autogen.sh` before you can run
+ `./configure`. This is the same as when checking out from git.
+
+- Instead of running `make` simply, you should instead run
+ `BITCOIN_GENBUILD_NO_GIT=1 make`.
+
+Notable changes
+===============
+
+P2P and network changes
+-----------------------
+
+#### Removal of BIP61 reject network messages from Bitcoin Core
+
+The `-enablebip61` command line option to enable BIP61 has been removed.
+(#17004)
+
+This feature has been disabled by default since Bitcoin Core version 0.18.0.
+Nodes on the network can not generally be trusted to send valid messages
+(including reject messages), so this should only ever be used when
+connected to a trusted node. Please use the alternatives recommended
+below if you rely on this removed feature:
+
+- Testing or debugging of implementations of the Bitcoin P2P network protocol
+ should be done by inspecting the log messages that are produced by a recent
+ version of Bitcoin Core. Bitcoin Core logs debug messages
+ (`-debug=<category>`) to a stream (`-printtoconsole`) or to a file
+ (`-debuglogfile=<debug.log>`).
+
+- Testing the validity of a block can be achieved by specific RPCs:
+
+ - `submitblock`
+
+ - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with
+ potentially invalid POW
+
+- Testing the validity of a transaction can be achieved by specific RPCs:
+
+ - `sendrawtransaction`
+
+ - `testmempoolaccept`
+
+- Wallets should not assume a transaction has propagated to the network
+ just because there are no reject messages. Instead, listen for the
+ transaction to be announced by other peers on the network. Wallets
+ should not assume a lack of reject messages means a transaction pays
+ an appropriate fee. Instead, set fees using fee estimation and use
+ replace-by-fee to increase a transaction's fee if it hasn't confirmed
+ within the desired amount of time.
+
+The removal of BIP61 reject message support also has the following minor RPC
+and logging implications:
+
+- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P reject
+ code when a transaction is not accepted to the mempool. They still return the
+ verbal reject reason.
+
+- Log messages that previously reported the reject code when a transaction was
+ not accepted to the mempool now no longer report the reject code. The reason
+ for rejection is still reported.
+
+Updated RPCs
+------------
+
+- The RPCs which accept descriptors now accept the new `sortedmulti(...)` descriptor
+ type which supports multisig scripts where the public keys are sorted
+ lexicographically in the resulting script. (#17056)
+
+- The `walletprocesspsbt` and `walletcreatefundedpsbt` RPCs now include
+ BIP32 derivation paths by default for public keys if we know them.
+ This can be disabled by setting the `bip32derivs` parameter to
+ `false`. (#17264)
+
+- The `bumpfee` RPC's parameter `totalFee`, which was deprecated in
+ 0.19, has been removed. (#18312)
+
+- The `bumpfee` RPC will return a PSBT when used with wallets that have
+ private keys disabled. (#16373)
+
+- The `getpeerinfo` RPC now includes a `mapped_as` field to indicate the
+ mapped Autonomous System used for diversifying peer selection. See the
+ `-asmap` configuration option described below in _New Settings_. (#16702)
+
+- The `createmultisig` and `addmultisigaddress` RPCs now return an
+ output script descriptor for the newly created address. (#18032)
+
+Build System
+------------
+
+- OpenSSL is no longer used by Bitcoin Core. (#17265)
+
+- BIP70 support has been fully removed from Bitcoin Core. The
+ `--enable-bip70` option remains, but it will throw an error during configure.
+ (#17165)
+
+- glibc 2.17 or greater is now required to run the release binaries. This
+ retains compatibility with RHEL 7, CentOS 7, Debian 8 and Ubuntu 14.04 LTS. (#17538)
+
+- The source code archives that are provided with gitian builds no longer contain
+ any autotools artifacts. Therefore, to build from such source, a user
+ should run the `./autogen.sh` script from the root of the unpacked archive.
+ This implies that `autotools` and other required packages are installed on the
+ user's system. (#18331)
+
+New settings
+------------
+
+- New `rpcwhitelist` and `rpcwhitelistdefault` configuration parameters
+ allow giving certain RPC users permissions to only some RPC calls.
+ (#12763)
+
+- A new `-asmap` configuration option has been added to diversify the
+ node's network connections by mapping IP addresses Autonomous System
+ Numbers (ASNs) and then limiting the number of connections made to any
+ single ASN. See [issue #16599](https://github.com/bitcoin/bitcoin/issues/16599),
+ [PR #16702](https://github.com/bitcoin/bitcoin/pull/16702), and the
+ `bitcoind help` for more information. This option is experimental and
+ subject to removal or breaking changes in future releases, so the
+ legacy /16 prefix mapping of IP addresses remains the default. (#16702)
+
+Updated settings
+----------------
+
+- All custom settings configured when Bitcoin Core starts are now
+ written to the `debug.log` file to assist troubleshooting. (#16115)
+
+- Importing blocks upon startup via the `bootstrap.dat` file no longer
+ occurs by default. The file must now be specified with
+ `-loadblock=<file>`. (#17044)
+
+- The `-debug=db` logging category has been renamed to
+ `-debug=walletdb` to distinguish it from `coindb`. The `-debug=db`
+ option has been deprecated and will be removed in the next major
+ release. (#17410)
+
+- The `-walletnotify` configuration parameter will now replace any `%w`
+ in its argument with the name of the wallet generating the
+ notification. This is not supported on Windows. (#13339)
+
+Removed settings
+----------------
+
+- The `-whitelistforcerelay` configuration parameter has been removed after
+ it was discovered that it was rendered ineffective in version 0.13 and
+ hasn't actually been supported for almost four years. (#17985)
+
+GUI changes
+-----------
+
+- The "Start Bitcoin Core on system login" option has been removed on macOS.
+ (#17567)
+
+- In the Peers window, the details for a peer now displays a `Mapped AS`
+ field to indicate the mapped Autonomous System used for diversifying
+ peer selection. See the `-asmap` configuration option in _New
+ Settings_, above. (#18402)
+
+- A "known bug" [announced](https://bitcoincore.org/en/releases/0.18.0/#wallet-gui)
+ in the release notes of version 0.18 has been fixed. The issue
+ affected anyone who simultaneously used multiple Bitcoin Core wallets
+ and the GUI coin control feature. (#18894)
+
+- For watch-only wallets, creating a new transaction in the Send screen
+ or fee bumping an existing transaction in the Transactions screen will
+ automatically copy a Partially-Signed Bitcoin Transaction (PSBT) to
+ the system clipboard. This can then be pasted into an external
+ program such as [HWI](https://github.com/bitcoin-core/HWI) for
+ signing. Future versions of Bitcoin Core should support a GUI option
+ for finalizing and broadcasting PSBTs, but for now the debug console
+ may be used with the `finalizepsbt` and `sendrawtransaction` RPCs.
+ (#16944, #17492)
+
+Wallet
+------
+
+- The wallet now by default uses bech32 addresses when using RPC, and
+ creates native segwit change outputs. (#16884)
+
+- The way that output trust was computed has been fixed, which affects
+ confirmed/unconfirmed balance status and coin selection. (#16766)
+
+- The `gettransaction`, `listtransactions` and `listsinceblock` RPC
+ responses now also include the height of the block that contains the
+ wallet transaction, if any. (#17437)
+
+- The `getaddressinfo` RPC has had its `label` field deprecated
+ (re-enable for this release using the configuration parameter
+ `-deprecatedrpc=label`). The `labels` field is altered from returning
+ JSON objects to returning a JSON array of label names (re-enable
+ previous behavior for this release using the configuration parameter
+ `-deprecatedrpc=labelspurpose`). Backwards compatibility using the
+ deprecated configuration parameters is expected to be dropped in the
+ 0.21 release. (#17585, #17578)
+
+Documentation changes
+---------------------
+
+- Bitcoin Core's automatically-generated source code documentation is
+ now available at https://doxygen.bitcoincore.org. (#17596)
+
+Low-level changes
+=================
+
+Utilities
+---------
+
+- The `bitcoin-cli` utility used with the `-getinfo` parameter now
+ returns a `headers` field with the number of downloaded block headers
+ on the best headers chain (similar to the `blocks` field that is also
+ returned) and a `verificationprogress` field that estimates how much
+ of the best block chain has been synced by the local node. The
+ information returned no longer includes the `protocolversion`,
+ `walletversion`, and `keypoololdest` fields. (#17302, #17650)
+
+- The `bitcoin-cli` utility now accepts a `-stdinwalletpassphrase`
+ parameter that can be used when calling the `walletpassphrase` and
+ `walletpassphrasechange` RPCs to read the passphrase from standard
+ input without echoing it to the terminal, improving security against
+ anyone who can look at your screen. The existing `-stdinrpcpass`
+ parameter is also updated to not echo the passphrase. (#13716)
+
+Command line
+------------
+
+- Command line options prefixed with main/test/regtest network names like
+ `-main.port=8333` `-test.server=1` previously were allowed but ignored. Now
+ they trigger "Invalid parameter" errors on startup. (#17482)
+
+New RPCs
+--------
+
+- The `dumptxoutset` RPC outputs a serialized snapshot of the current
+ UTXO set. A script is provided in the `contrib/devtools` directory
+ for generating a snapshot of the UTXO set at a particular block
+ height. (#16899)
+
+- The `generatetodescriptor` RPC allows testers using regtest mode to
+ generate blocks that pay an arbitrary output script descriptor.
+ (#16943)
+
+Updated RPCs
+------------
+
+- The `verifychain` RPC default values are now static instead of
+ depending on the command line options or configuration file
+ (`-checklevel`, and `-checkblocks`). Users can pass in the RPC
+ arguments explicitly when they don't want to rely on the default
+ values. (#18541)
+
+- The `getblockchaininfo` RPC's `verificationprogress` field will no
+ longer report values higher than 1. Previously it would occasionally
+ report the chain was more than 100% verified. (#17328)
+
+Tests
+-----
+
+- It is now an error to use an unqualified `walletdir=path` setting in
+ the config file if running on testnet or regtest networks. The setting
+ now needs to be qualified as `chain.walletdir=path` or placed in the
+ appropriate `[chain]` section. (#17447)
+
+- `-fallbackfee` was 0 (disabled) by default for the main chain, but
+ 0.0002 by default for the test chains. Now it is 0 by default for all
+ chains. Testnet and regtest users will have to add
+ `fallbackfee=0.0002` to their configuration if they weren't setting it
+ and they want it to keep working like before. (#16524)
+
+Build system
+------------
+
+- Support is provided for building with the Android Native Development
+ Kit (NDK). (#16110)
+
+0.20.0 change log
+=================
+
+### Mining
+- #18742 miner: Avoid stack-use-after-return in validationinterface (MarcoFalke)
+
+### Block and transaction handling
+- #15283 log: Fix UB with bench on genesis block (instagibbs)
+- #16507 feefilter: Compute the absolute fee rather than stored rate (instagibbs)
+- #16688 log: Add validation interface logging (jkczyz)
+- #16805 log: Add timing information to FlushStateToDisk() (jamesob)
+- #16902 O(1) `OP_IF/NOTIF/ELSE/ENDIF` script implementation (sipa)
+- #16945 introduce CChainState::GetCoinsCacheSizeState (jamesob)
+- #16974 Walk pindexBestHeader back to ChainActive().Tip() if it is invalid (TheBlueMatt)
+- #17004 Remove REJECT code from CValidationState (jnewbery)
+- #17080 Explain why `fCheckDuplicateInputs` can not be skipped and remove it (MarcoFalke)
+- #17328 GuessVerificationProgress: cap the ratio to 1 (darosior)
+- #17399 Templatize ValidationState instead of subclassing (jkczyz)
+- #17407 node: Add reference to mempool in NodeContext (MarcoFalke)
+- #17708 prevector: Avoid misaligned member accesses (ajtowns)
+- #17850,#17896,#17957,#18021,#18021,#18112 Serialization improvements (sipa)
+- #17925 Improve UpdateTransactionsFromBlock with Epochs (JeremyRubin)
+- #18002 Abstract out script execution out of `VerifyWitnessProgram()` (sipa)
+- #18388 Make VerifyWitnessProgram use a Span stack (sipa)
+- #18433 serialization: prevent int overflow for big Coin::nHeight (pierreN)
+- #18500 chainparams: Bump assumed valid hash (MarcoFalke)
+- #18551 Do not clear validationinterface entries being executed (sipa)
+
+### P2P protocol and network code
+- #15437 Remove BIP61 reject messages (MarcoFalke)
+- #16702 Supply and use asmap to improve IP bucketing in addrman (naumenkogs)
+- #16851 Continue relaying transactions after they expire from mapRelay (ajtowns)
+- #17164 Avoid allocating memory for addrKnown where we don't need it (naumenkogs)
+- #17243 tools: add PoissonNextSend method that returns mockable time (amitiuttarwar)
+- #17251 SocketHandler logs peer id for close and disconnect (Sjors)
+- #17573 Seed RNG with precision timestamps on receipt of net messages (TheBlueMatt)
+- #17624 Fix an uninitialized read in ProcessMessage(…, "tx", …) when receiving a transaction we already have (practicalswift)
+- #17754 Don't allow resolving of std::string with embedded NUL characters. Add tests (practicalswift)
+- #17758 Fix CNetAddr::IsRFC2544 comment + tests (tynes)
+- #17812 config, net, test: Asmap feature refinements and functional tests (jonatack)
+- #17951 Use rolling bloom filter of recent block txs for AlreadyHave() check (sdaftuar)
+- #17985 Remove forcerelay of rejected txs (MarcoFalke)
+- #18023 Fix some asmap issues (sipa)
+- #18054 Reference instead of copy in BlockConnected range loop (jonatack)
+- #18376 Fix use-after-free in tests (vasild)
+- #18454 Make addr relay mockable, add test (MarcoFalke)
+- #18458 Add missing `cs_vNodes` lock (MarcoFalke)
+- #18506 Hardcoded seeds update for 0.20 (laanwj)
+- #18808 Drop unknown types in getdata (jnewbery)
+- #18962 Only send a getheaders for one block in an INV (jnewbery)
+
+### Wallet
+- #13339 Replace %w by wallet name in -walletnotify script (promag)
+- #15931 Remove GetDepthInMainChain dependency on locked chain interface (ariard)
+- #16373 bumpfee: Return PSBT when wallet has privkeys disabled (instagibbs)
+- #16524 Disable -fallbackfee by default (jtimon)
+- #16766 Make IsTrusted scan parents recursively (JeremyRubin)
+- #16884 Change default address type to bech32 (instagibbs)
+- #16911 Only check the hash of transactions loaded from disk (achow101)
+- #16923 Handle duplicate fileid exception (promag)
+- #17056 descriptors: Introduce sortedmulti descriptor (achow101)
+- #17070 Avoid showing GUI popups on RPC errors (MarcoFalke)
+- #17138 Remove wallet access to some node arguments (jnewbery)
+- #17237 LearnRelatedScripts only if KeepDestination (promag)
+- #17260 Split some CWallet functions into new LegacyScriptPubKeyMan (achow101)
+- #17261 Make ScriptPubKeyMan an actual interface and the wallet to have multiple (achow101)
+- #17290 Enable BnB coin selection for preset inputs and subtract fee from outputs (achow101)
+- #17373 Various fixes and cleanup to keypool handling in LegacyScriptPubKeyMan and CWallet (achow101)
+- #17410 Rename `db` log category to `walletdb` (like `coindb`) (laanwj)
+- #17444 Avoid showing GUI popups on RPC errors (take 2) (MarcoFalke)
+- #17447 Make -walletdir network only (promag)
+- #17537 Cleanup and move opportunistic and superfluous TopUp()s (achow101)
+- #17553 Remove out of date comments for CalculateMaximumSignedTxSize (instagibbs)
+- #17568 Fix when sufficient preset inputs and subtractFeeFromOutputs (achow101)
+- #17677 Activate watchonly wallet behavior for LegacySPKM only (instagibbs)
+- #17719 Document better -keypool as a look-ahead safety mechanism (ariard)
+- #17843 Reset reused transactions cache (fjahr)
+- #17889 Improve CWallet:MarkDestinationsDirty (promag)
+- #18034 Get the OutputType for a descriptor (achow101)
+- #18067 Improve LegacyScriptPubKeyMan::CanProvide script recognition (ryanofsky)
+- #18115 Pass in transactions and messages for signing instead of exporting the private keys (achow101)
+- #18192,#18546 Bugfix: Wallet: Safely deal with change in the address book (luke-jr)
+- #18204 descriptors: Improve descriptor cache and cache xpubs (achow101)
+- #18274 rpc/wallet: Initialize nFeeRequired to avoid using garbage value on failure (kallewoof)
+- #18312 Remove deprecated fee bumping by totalFee (jonatack)
+- #18338 Fix wallet unload race condition (promag)
+
+### RPC and other APIs
+- #12763 Add RPC Whitelist Feature from #12248 (JeremyRubin)
+- #13716 cli: `-stdinwalletpassphrase` and non-echo stdin passwords (kallewoof)
+- #16689 Add missing fields to wallet rpc help output (ariard)
+- #16821 Fix bug where duplicate PSBT keys are accepted (erasmospunk)
+- #16899 UTXO snapshot creation (dumptxoutset)
+- #17156 psbt: Check that various indexes and amounts are within bounds (achow101)
+- #17264 Set default bip32derivs to true for psbt methods (Sjors)
+- #17283 improve getaddressinfo test coverage, help, code docs (jonatack)
+- #17302 cli: Add "headers" and "verificationprogress" to -getinfo (laanwj)
+- #17318 replace asserts in RPC code with `CHECK_NONFATAL` and add linter (adamjonas)
+- #17437 Expose block height of wallet transactions (promag)
+- #17519 Remove unused `COINBASE_FLAGS` (narula)
+- #17578 Simplify getaddressinfo labels, deprecate previous behavior (jonatack)
+- #17585 deprecate getaddressinfo label (jonatack)
+- #17746 Remove vector copy from listtransactions (promag)
+- #17809 Auto-format RPCResult (MarcoFalke)
+- #18032 Output a descriptor in createmultisig and addmultisigaddress (achow101)
+- #18122 Update validateaddress RPCExamples to bech32 (theStack)
+- #18208 Change RPCExamples to bech32 (yusufsahinhamza)
+- #18268 Remove redundant types from descriptions (docallag)
+- #18346 Document an RPCResult for all calls; Enforce at compile time (MarcoFalke)
+- #18396 Add missing HelpExampleRpc for getblockfilter (theStack)
+- #18398 Fix broken RPCExamples for waitforblock(height) (theStack)
+- #18444 Remove final comma for last entry of fixed-size arrays/objects in RPCResult (luke-jr)
+- #18459 Remove unused getbalances() code (jonatack)
+- #18484 Correctly compute redeemScript from witnessScript for signrawtransaction (achow101)
+- #18487 Fix rpcRunLater race in walletpassphrase (promag)
+- #18499 Make rpc documentation not depend on call-time rpc args (MarcoFalke)
+- #18532 Avoid initialization-order-fiasco on static CRPCCommand tables (MarcoFalke)
+- #18541 Make verifychain default values static, not depend on global args (MarcoFalke)
+- #18809 Do not advertise dumptxoutset as a way to flush the chainstate (MarcoFalke)
+- #18814 Relock wallet only if most recent callback (promag)
+
+### GUI
+- #15023 Restore RPC Console to non-wallet tray icon menu (luke-jr)
+- #15084 Don't disable the sync overlay when wallet is disabled (benthecarman)
+- #15098 Show addresses for "SendToSelf" transactions (hebasto)
+- #15756 Add shortcuts for tab tools (promag)
+- #16944 create PSBT with watch-only wallet (Sjors)
+- #16964 Change sendcoins dialogue Yes to Send (instagibbs)
+- #17068 Always generate `bitcoinstrings.cpp` on `make translate` (D4nte)
+- #17096 Rename debug window (Zero-1729)
+- #17105 Make RPCConsole::TabTypes an enum class (promag)
+- #17125 Add toolTip and placeholderText to sign message fields (dannmat)
+- #17165 Remove BIP70 support (fanquake)
+- #17180 Improved tooltip for send amount field (JeremyCrookshank)
+- #17186 Add placeholder text to the sign message field (Danny-Scott)
+- #17195 Send amount placeholder value (JeremyCrookshank)
+- #17226 Fix payAmount tooltip in SendCoinsEntry (promag)
+- #17360 Cleaning up hide button tool tip (Danny-Scott)
+- #17446 Changed tooltip for 'Label' & 'Message' text fields to be more clear (dannmat)
+- #17453 Fix intro dialog labels when the prune button is toggled (hebasto)
+- #17474 Bugfix: GUI: Recognise `NETWORK_LIMITED` in formatServicesStr (luke-jr)
+- #17492 Bump fee returns PSBT on clipboard for watchonly-only wallets (instagibbs)
+- #17567 Remove macOS start on login code (fanquake)
+- #17587 Show watch-only balance in send screen (Sjors)
+- #17694 Disable 3rd-party tx-urls when wallet disabled (brakmic)
+- #17696 Force set nPruneSize in QSettings after the intro dialog (hebasto)
+- #17702 Move static placeholder texts to forms (laanwj)
+- #17826 Log Qt related info (hebasto)
+- #17886 Restore English translation option (achow101)
+- #17906 Set CConnman byte counters earlier to avoid uninitialized reads (ryanofsky)
+- #17935 Hide HD & encryption icons when no wallet loaded (brakmic)
+- #17998 Shortcut to close ModalOverlay (emilengler)
+- #18007 Bugfix: GUI: Hide the HD/encrypt icons earlier so they get re-shown if another wallet is open (luke-jr)
+- #18060 Drop PeerTableModel dependency to ClientModel (promag)
+- #18062 Fix unintialized WalletView::progressDialog (promag)
+- #18091 Pass clientmodel changes from walletframe to walletviews (jonasschnelli)
+- #18101 Fix deprecated QCharRef usage (hebasto)
+- #18121 Throttle GUI update pace when -reindex (hebasto)
+- #18123 Fix race in WalletModel::pollBalanceChanged (ryanofsky)
+- #18160 Avoid Wallet::GetBalance in WalletModel::pollBalanceChanged (promag)
+- #18360 Bump transifex slug and update English translations for 0.20 (laanwj)
+- #18402 Display mapped AS in peers info window (jonatack)
+- #18492 Translations update pre-branch (laanwj)
+- #18549 Fix Window -> Minimize menu item (hebasto)
+- #18578 Fix leak in CoinControlDialog::updateView (promag)
+- #18894 Fix manual coin control with multiple wallets loaded (promag)
+
+### Build system
+- #16667 Remove mingw linker workaround from win gitian descriptor (fanquake)
+- #16669 Use new fork of osslsigncode for windows gitian signing (fanquake)
+- #16949 Only pass --disable-dependency-tracking to packages that understand it (fanquake)
+- #17008 Bump libevent to 2.1.11 in depends (stefanwouldgo)
+- #17029 gitian: Various improvements for windows descriptor (dongcarl)
+- #17033 Disable _FORTIFY_SOURCE when enable-debug (achow101)
+- #17057 Switch to upstream libdmg-hfsplus (fanquake)
+- #17066 Remove workaround for ancient libtool (hebasto)
+- #17074 Added double quotes (mztriz)
+- #17087 Add variable printing target to Makefiles (dongcarl)
+- #17118 depends macOS: point --sysroot to SDK (Sjors)
+- #17231 Fix boost mac cross build with clang 9+ (theuni)
+- #17265 Remove OpenSSL (fanquake)
+- #17284 Update retry to current version (RandyMcMillan)
+- #17308 nsis: Write to correct filename in first place (dongcarl)
+- #17324,#18099 Update univalue subtree (MarcoFalke)
+- #17398 Update leveldb to 1.22+ (laanwj)
+- #17409 Avoid hardcoded libfaketime dir in gitian (MarcoFalke)
+- #17466 Fix C{,XX} pickup (dongcarl)
+- #17483 Set gitian arch back to amd64 (MarcoFalke)
+- #17486 Make Travis catch unused variables (Sjors)
+- #17538 Bump minimum libc to 2.17 for release binaries (fanquake)
+- #17542 Create test utility library from src/test/util/ (brakmic)
+- #17545 Remove libanl.so.1 from ALLOWED_LIBRARIES (fanquake)
+- #17547 Fix configure report about qr (hebasto)
+- #17569 Allow export of environ symbols and work around rv64 toolchain issue (laanwj)
+- #17647 lcov: filter depends from coverage reports (nijynot)
+- #17658 Add ability to skip building qrencode (fanquake)
+- #17678 Support for S390X and POWER targets (MarcoFalke)
+- #17682 util: Update tinyformat to upstream (laanwj)
+- #17698 Don't configure `xcb_proto` (fanquake)
+- #17730 Remove Qt networking features (fanquake)
+- #17738 Remove linking librt for backwards compatibility (fanquake)
+- #17740 Remove configure checks for win libraries we don't link against (fanquake)
+- #17741 Included `test_bitcoin-qt` in msvc build (sipsorcery)
+- #17756 Remove `WINDOWS_BITS` from build system (fanquake)
+- #17769 Set `AC_PREREQ` to 2.69 (fanquake)
+- #17880 Add -Wdate-time to Werror flags (fanquake)
+- #17910 Remove double `LIBBITCOIN_SERVER` linking (fanquake)
+- #17928 Consistent use of package variable (Bushstar)
+- #17933 guix: Pin Guix using `guix time-machine` (dongcarl)
+- #17948 pass -fno-ident in Windows gitian descriptor (fanquake)
+- #18003 Remove --large-address-aware linker flag (fanquake)
+- #18004 Don't embed a build-id when building libdmg-hfsplus (fanquake)
+- #18051 Fix behavior when `ALLOW_HOST_PACKAGES` unset (hebasto)
+- #18059 Add missing attributes to Win installer (fanquake)
+- #18104 Skip i686 build by default in guix and gitian (MarcoFalke)
+- #18107 Add `cov_fuzz` target (MarcoFalke)
+- #18135 Add --enable-determinism configure flag (fanquake)
+- #18145 Add Wreturn-type to Werror flags, check on more Travis machines (Sjors)
+- #18264 Remove Boost Chrono (fanquake)
+- #18290 Set minimum Automake version to 1.13 (hebasto)
+- #18320 guix: Remove now-unnecessary gcc make flag (dongcarl)
+- #18331 Use git archive as source tarball (hebasto)
+- #18397 Fix libevent linking for `bench_bitcoin` binary (hebasto)
+- #18426 scripts: `Previous_release`: improve behaviour on failed download (theStack)
+- #18429 Remove double `LIBBITCOIN_SERVER` from bench-Makefile (brakmic)
+- #18528 Create `test_fuzz` library from src/test/fuzz/fuzz.cpp (brakmic)
+- #18558 Fix boost detection for arch armv7l (hebasto)
+- #18598 gitian: Add missing automake package to gitian-win-signer.yml (achow101)
+- #18676 Check libevent minimum version in configure script (hebasto)
+- #18945 Ensure source tarball has leading directory name (laanwj)
+
+### Platform support
+- #16110 Add Android NDK support (icota)
+- #16392 macOS toolchain update (fanquake)
+- #16569 Increase init file stop timeout (setpill)
+- #17151 Remove OpenSSL PRNG seeding (Windows, Qt only) (fanquake)
+- #17365 Update README.md with working Android targets and API levels (icota)
+- #17521 Only use D-Bus with Qt on linux (fanquake)
+- #17550 Set minimum supported macOS to 10.12 (fanquake)
+- #17592 Appveyor install libevent[thread] vcpkg (sipsorcery)
+- #17660 Remove deprecated key from macOS Info.plist (fanquake)
+- #17663 Pass `-dead_strip_dylibs` to ld on macOS (fanquake)
+- #17676 Don't use OpenGL in Qt on macOS (fanquake)
+- #17686 Add `-bind_at_load` to macOS hardened LDFLAGS (fanquake)
+- #17787 scripts: Add macho pie check to security-check.py (fanquake)
+- #17800 random: don't special case clock usage on macOS (fanquake)
+- #17863 scripts: Add macho dylib checks to symbol-check.py (fanquake)
+- #17899 msvc: Ignore msvc linker warning and update to msvc build instructions (sipsorcery)
+- #17916 windows: Enable heap terminate-on-corruption (fanquake)
+- #18082 logging: Enable `thread_local` usage on macos (fanquake)
+- #18108 Fix `.gitignore` policy in `build_msvc` directory (hebasto)
+- #18295 scripts: Add macho lazy bindings check to security-check.py (fanquake)
+- #18358 util: Fix compilation with mingw-w64 7.0.0 (fanquake)
+- #18359 Fix sysctl() detection on macOS (fanquake)
+- #18364 random: remove getentropy() fallback for macOS < 10.12 (fanquake)
+- #18395 scripts: Add pe dylib checking to symbol-check.py (fanquake)
+- #18415 scripts: Add macho tests to test-security-check.py (fanquake)
+- #18425 releases: Update with new Windows code signing certificate (achow101)
+- #18702 Fix ASLR for bitcoin-cli on Windows (fanquake)
+
+### Tests and QA
+- #12134 Build previous releases and run functional tests (Sjors)
+- #13693 Add coverage to estimaterawfee and estimatesmartfee (Empact)
+- #13728 lint: Run the ci lint stage on mac (Empact)
+- #15443 Add getdescriptorinfo functional test (promag)
+- #15888 Add `wallet_implicitsegwit` to test the ability to transform keys between address types (luke-jr)
+- #16540 Add `ASSERT_DEBUG_LOG` to unit test framework (MarcoFalke)
+- #16597 travis: Run full test suite on native macos (Sjors)
+- #16681 Use self.chain instead of 'regtest' in all current tests (jtimon)
+- #16786 add unit test for wallet watch-only methods involving PubKeys (theStack)
+- #16943 Add generatetodescriptor RPC (MarcoFalke)
+- #16973 Fix `combine_logs.py` for AppVeyor build (mzumsande)
+- #16975 Show debug log on unit test failure (MarcoFalke)
+- #16978 Seed test RNG context for each test case, print seed (MarcoFalke)
+- #17009, #17018, #17050, #17051, #17071, #17076, #17083, #17093, #17109, #17113, #17136, #17229, #17291, #17357, #17771, #17777, #17917, #17926, #17972, #17989, #17996, #18009, #18029, #18047, #18126, #18176, #18206, #18353, #18363, #18407, #18417, #18423, #18445, #18455, #18565 Add fuzzing harnesses (practicalswift)
+- #17011 ci: Use busybox utils for one build (MarcoFalke)
+- #17030 Fix Python Docstring to include all Args (jbampton)
+- #17041 ci: Run tests on arm (MarcoFalke)
+- #17069 Pass fuzzing inputs as constant references (practicalswift)
+- #17091 Add test for loadblock option and linearize scripts (fjahr)
+- #17108 fix "tx-size-small" errors after default address change (theStack)
+- #17121 Speed up `wallet_backup` by whitelisting peers (immediate tx relay) (theStack)
+- #17124 Speed up `wallet_address_types` by whitelisting peers (immediate tx relay) (theStack)
+- #17140 Fix bug in `blockfilter_index_tests` (jimpo)
+- #17199 use default address type (bech32) for `wallet_bumpfee` tests (theStack)
+- #17205 ci: Enable address sanitizer (asan) stack-use-after-return checking (practicalswift)
+- #17206 Add testcase to simulate bitcoin schema in leveldb (adamjonas)
+- #17209 Remove no longer needed UBSan suppressions (issues fixed). Add documentation (practicalswift)
+- #17220 Add unit testing for the CompressScript function (adamjonas)
+- #17225 Test serialisation as part of deserialisation fuzzing. Test round-trip equality where possible (practicalswift)
+- #17228 Add RegTestingSetup to `setup_common` (MarcoFalke)
+- #17233 travis: Run unit and functional tests on native arm (MarcoFalke)
+- #17235 Skip unnecessary fuzzer initialisation. Hold ECCVerifyHandle only when needed (practicalswift)
+- #17240 ci: Disable functional tests on mac host (MarcoFalke)
+- #17254 Fix `script_p2sh_tests` `OP_PUSHBACK2/4` missing (adamjonas)
+- #17267 bench: Fix negative values and zero for -evals flag (nijynot)
+- #17275 pubkey: Assert CPubKey's ECCVerifyHandle precondition (practicalswift)
+- #17288 Added TestWrapper class for interactive Python environments (jachiang)
+- #17292 Add new mempool benchmarks for a complex pool (JeremyRubin)
+- #17299 add reason checks for non-standard txs in `test_IsStandard` (theStack)
+- #17322 Fix input size assertion in `wallet_bumpfee.py` (instagibbs)
+- #17327 Add `rpc_fundrawtransaction` logging (jonatack)
+- #17330 Add `shrinkdebugfile=0` to regtest bitcoin.conf (sdaftuar)
+- #17340 Speed up fundrawtransaction test (jnewbery)
+- #17345 Do not instantiate CAddrDB for static call CAddrDB::Read() (hebasto)
+- #17362 Speed up `wallet_avoidreuse`, add logging (jonatack)
+- #17363 add "diamond" unit test to MempoolAncestryTests (theStack)
+- #17366 Reset global args between test suites (MarcoFalke)
+- #17367 ci: Run non-cross-compile builds natively (MarcoFalke)
+- #17378 TestShell: Fix typos & implement cleanups (jachiang)
+- #17384 Create new test library (MarcoFalke)
+- #17387 `wallet_importmulti`: use addresses of the same type as being imported (achow101)
+- #17388 Add missing newline in `util_ChainMerge` test (ryanofsky)
+- #17390 Add `util_ArgParsing` test (ryanofsky)
+- #17420 travis: Rework `cache_err_msg` (MarcoFalke)
+- #17423 ci: Make ci system read-only on the git work tree (MarcoFalke)
+- #17435 check custom ancestor limit in `mempool_packages.py` (theStack)
+- #17455 Update valgrind suppressions (practicalswift)
+- #17461 Check custom descendant limit in `mempool_packages.py` (theStack)
+- #17469 Remove fragile `assert_memory_usage_stable` (MarcoFalke)
+- #17470 ci: Use clang-8 for fuzzing to run on aarch64 ci systems (MarcoFalke)
+- #17480 Add unit test for non-standard txs with too large scriptSig (theStack)
+- #17497 Skip tests when utils haven't been compiled (fanquake)
+- #17502 Add unit test for non-standard bare multisig txs (theStack)
+- #17511 Add bounds checks before base58 decoding (sipa)
+- #17517 ci: Bump to clang-8 for asan build to avoid segfaults on ppc64le (MarcoFalke)
+- #17522 Wait until mempool is loaded in `wallet_abandonconflict` (MarcoFalke)
+- #17532 Add functional test for non-standard txs with too large scriptSig (theStack)
+- #17541 Add functional test for non-standard bare multisig txs (theStack)
+- #17555 Add unit test for non-standard txs with wrong nVersion (dspicher)
+- #17571 Add `libtest_util` library to msvc build configuration (sipsorcery)
+- #17591 ci: Add big endian platform - s390x (elichai)
+- #17593 Move more utility functions into test utility library (mzumsande)
+- #17633 Add option --valgrind to run the functional tests under Valgrind (practicalswift)
+- #17635 ci: Add centos 7 build (hebasto)
+- #17641 Add unit test for leveldb creation with unicode path (sipsorcery)
+- #17674 Add initialization order fiasco detection in Travis (practicalswift)
+- #17675 Enable tests which are incorrectly skipped when running `test_runner.py --usecli` (practicalswift)
+- #17685 Fix bug in the descriptor parsing fuzzing harness (`descriptor_parse`) (practicalswift)
+- #17705 re-enable CLI test support by using EncodeDecimal in json.dumps() (fanquake)
+- #17720 add unit test for non-standard "scriptsig-not-pushonly" txs (theStack)
+- #17767 ci: Fix qemu issues (MarcoFalke)
+- #17793 ci: Update github actions ci vcpkg cache on msbuild update (hebasto)
+- #17806 Change filemode of `rpc_whitelist.py` (emilengler)
+- #17849 ci: Fix brew python link (hebasto)
+- #17851 Add `std::to_string` to list of locale dependent functions (practicalswift)
+- #17893 Fix double-negative arg test (hebasto)
+- #17900 ci: Combine 32-bit build with centos 7 build (theStack)
+- #17921 Test `OP_CSV` empty stack fail in `feature_csv_activation.py` (theStack)
+- #17931 Fix `p2p_invalid_messages` failing in Python 3.8 because of warning (elichai)
+- #17947 add unit test for non-standard txs with too large tx size (theStack)
+- #17959 Check specific reject reasons in `feature_csv_activation.py` (theStack)
+- #17984 Add p2p test for forcerelay permission (MarcoFalke)
+- #18001 Updated appveyor job to checkout a specific vcpkg commit ID (sipsorcery)
+- #18008 fix fuzzing using libFuzzer on macOS (fanquake)
+- #18013 bench: Fix benchmarks filters (elichai)
+- #18018 reset fIsBareMultisigStd after bare-multisig tests (fanquake)
+- #18022 Fix appveyor `test_bitcoin` build of `*.raw` (MarcoFalke)
+- #18037 util: Allow scheduler to be mocked (amitiuttarwar)
+- #18056 ci: Check for submodules (emilengler)
+- #18069 Replace 'regtest' leftovers by self.chain (theStack)
+- #18081 Set a name for CI Docker containers (fanquake)
+- #18109 Avoid hitting some known minor tinyformat issues when fuzzing strprintf(…) (practicalswift)
+- #18155 Add harness which fuzzes EvalScript and VerifyScript using a fuzzed signature checker (practicalswift)
+- #18159 Add --valgrind option to `test/fuzz/test_runner.py` for running fuzzing test cases under valgrind (practicalswift)
+- #18166 ci: Run fuzz testing test cases (bitcoin-core/qa-assets) under valgrind to catch memory errors (practicalswift)
+- #18172 Transaction expiry from mempool (0xB10C)
+- #18181 Remove incorrect assumptions in `validation_flush_tests` (MarcoFalke)
+- #18183 Set `catch_system_errors=no` on boost unit tests (MarcoFalke)
+- #18195 Add `cost_of_change` parameter assertions to `bnb_search_test` (yancyribbens)
+- #18209 Reduce unneeded whitelist permissions in tests (MarcoFalke)
+- #18211 Disable mockforward scheduler unit test for now (MarcoFalke)
+- #18213 Fix race in `p2p_segwit` (MarcoFalke)
+- #18224 Make AnalyzePSBT next role calculation simple, correct (instagibbs)
+- #18228 Add missing syncwithvalidationinterfacequeue (MarcoFalke)
+- #18247 Wait for both veracks in `add_p2p_connection` (MarcoFalke)
+- #18249 Bump timeouts to accomodate really slow disks (MarcoFalke)
+- #18255 Add `bad-txns-*-toolarge` test cases to `invalid_txs` (MarcoFalke)
+- #18263 rpc: change setmocktime check to use IsMockableChain (gzhao408)
+- #18285 Check that `wait_until` returns if time point is in the past (MarcoFalke)
+- #18286 Add locale fuzzer to `FUZZERS_MISSING_CORPORA` (practicalswift)
+- #18292 fuzz: Add `assert(script == decompressed_script)` (MarcoFalke)
+- #18299 Update `FUZZERS_MISSING_CORPORA` to enable regression fuzzing for all harnesses in master (practicalswift)
+- #18300 fuzz: Add option to merge input dir to test runner (MarcoFalke)
+- #18305 Explain why test logging should be used (MarcoFalke)
+- #18306 Add logging to `wallet_listsinceblock.py` (jonatack)
+- #18311 Bumpfee test fix (instagibbs)
+- #18314 Add deserialization fuzzing of SnapshotMetadata (`utxo_snapshot`) (practicalswift)
+- #18319 fuzz: Add missing `ECC_Start` to `key_io` test (MarcoFalke)
+- #18334 Add basic test for BIP 37 (MarcoFalke)
+- #18350 Fix mining to an invalid target + ensure that a new block has the correct hash internally (TheQuantumPhysicist)
+- #18378 Bugfix & simplify bn2vch using `int.to_bytes` (sipa)
+- #18393 Don't assume presence of `__builtin_mul_overflow(…)` in `MultiplicationOverflow(…)` fuzzing harness (practicalswift)
+- #18406 add executable flag for `rpc_estimatefee.py` (theStack)
+- #18420 listsinceblock block height checks (jonatack)
+- #18430 ci: Only clone bitcoin-core/qa-assets when fuzzing (MarcoFalke)
+- #18438 ci: Use homebrew addon on native macos (hebasto)
+- #18447 Add coverage for script parse error in ParseScript (pierreN)
+- #18472 Remove unsafe `BOOST_TEST_MESSAGE` (MarcoFalke)
+- #18474 check that peer is connected when calling sync_* (MarcoFalke)
+- #18477 ci: Use focal for fuzzers (MarcoFalke)
+- #18481 add BIP37 'filterclear' test to p2p_filter.py (theStack)
+- #18496 Remove redundant `sync_with_ping` after `add_p2p_connection` (jonatack)
+- #18509 fuzz: Avoid running over all inputs after merging them (MarcoFalke)
+- #18510 fuzz: Add CScriptNum::getint coverage (MarcoFalke)
+- #18514 remove rapidcheck integration and tests (fanquake)
+- #18515 Add BIP37 remote crash bug [CVE-2013-5700] test to `p2p_filter.py` (theStack)
+- #18516 relax bumpfee `dust_to_fee` txsize an extra vbyte (jonatack)
+- #18518 fuzz: Extend descriptor fuzz test (MarcoFalke)
+- #18519 fuzz: Extend script fuzz test (MarcoFalke)
+- #18521 fuzz: Add `process_messages` harness (MarcoFalke)
+- #18529 Add fuzzer version of randomized prevector test (sipa)
+- #18534 skip backwards compat tests if not compiled with wallet (fanquake)
+- #18540 `wallet_bumpfee` assertion fixup (jonatack)
+- #18543 Use one node to avoid a race due to missing sync in `rpc_signrawtransaction` (MarcoFalke)
+- #18561 Properly raise FailedToStartError when rpc shutdown before warmup finished (MarcoFalke)
+- #18562 ci: Run unit tests sequential once (MarcoFalke)
+- #18563 Fix `unregister_all_during_call` cleanup (ryanofsky)
+- #18566 Set `-use_value_profile=1` when merging fuzz inputs (MarcoFalke)
+- #18757 Remove enumeration of expected deserialization exceptions in ProcessMessage(…) fuzzer (practicalswift)
+- #18878 Add test for conflicted wallet tx notifications (ryanofsky)
+- #18975 Remove const to work around compiler error on xenial (laanwj)
+
+### Documentation
+- #16947 Doxygen-friendly script/descriptor.h comments (ch4ot1c)
+- #16983 Add detailed info about Bitcoin Core files (hebasto)
+- #16986 Doxygen-friendly CuckooCache comments (ch4ot1c)
+- #17022 move-only: Steps for "before major release branch-off" (MarcoFalke)
+- #17026 Update bips.md for default bech32 addresses in 0.20.0 (MarcoFalke)
+- #17081 Fix Makefile target in benchmarking.md (theStack)
+- #17102 Add missing indexes/blockfilter/basic to doc/files.md (MarcoFalke)
+- #17119 Fix broken bitcoin-cli examples (andrewtoth)
+- #17134 Add switch on enum example to developer notes (hebasto)
+- #17142 Update macdeploy README to include all files produced by `make deploy` (za-kk)
+- #17146 github: Add warning for bug reports (laanwj)
+- #17157 Added instructions for how to add an upsteam to forked repo (dannmat)
+- #17159 Add a note about backporting (carnhofdaki)
+- #17169 Correct function name in ReportHardwareRand() (fanquake)
+- #17177 Describe log files + consistent paths in test READMEs (fjahr)
+- #17239 Changed miniupnp links to https (sandakersmann)
+- #17281 Add developer note on `c_str()` (laanwj)
+- #17285 Bip70 removal follow-up (fjahr)
+- #17286 Fix help-debug -checkpoints (ariard)
+- #17309 update MSVC instructions to remove Qt OpenSSL linking (fanquake)
+- #17339 Add template for good first issues (michaelfolkson)
+- #17351 Fix some misspellings (RandyMcMillan)
+- #17353 Add ShellCheck to lint tests dependencies (hebasto)
+- #17370 Update doc/bips.md with recent changes in master (MarcoFalke)
+- #17393 Added regtest config for linearize script (gr0kchain)
+- #17411 Add some better examples for scripted diff (laanwj)
+- #17503 Remove bitness from bitcoin-qt help message and manpage (laanwj)
+- #17539 Update and improve Developer Notes (hebasto)
+- #17561 Changed MiniUPnPc link to https in dependencies.md (sandakersmann)
+- #17596 Change doxygen URL to doxygen.bitcoincore.org (laanwj)
+- #17598 Update release process with latest changes (MarcoFalke)
+- #17617 Unify unix epoch time descriptions (jonatack)
+- #17637 script: Add keyserver to verify-commits readme (emilengler)
+- #17648 Rename wallet-tool references to bitcoin-wallet (hel-o)
+- #17688 Add "ci" prefix to CONTRIBUTING.md (hebasto)
+- #17751 Use recommended shebang approach in documentation code block (hackerrdave)
+- #17752 Fix directory path for secp256k1 subtree in developer-notes (hackerrdave)
+- #17772 Mention PR Club in CONTRIBUTING.md (emilengler)
+- #17804 Misc RPC help fixes (MarcoFalke)
+- #17819 Developer notes guideline on RPCExamples addresses (jonatack)
+- #17825 Update dependencies.md (hebasto)
+- #17873 Add to Doxygen documentation guidelines (jonatack)
+- #17907 Fix improper Doxygen inline comments (Empact)
+- #17942 Improve fuzzing docs for macOS users (fjahr)
+- #17945 Fix doxygen errors (Empact)
+- #18025 Add missing supported rpcs to doc/descriptors.md (andrewtoth)
+- #18070 Add note about `brew doctor` (givanse)
+- #18125 Remove PPA note from release-process.md (fanquake)
+- #18170 Minor grammatical changes and flow improvements (travinkeith)
+- #18212 Add missing step in win deployment instructions (dangershony)
+- #18219 Add warning against wallet.dat re-use (corollari)
+- #18253 Correct spelling errors in comments (Empact)
+- #18278 interfaces: Describe and follow some code conventions (ryanofsky)
+- #18283 Explain rebase policy in CONTRIBUTING.md (MarcoFalke)
+- #18340 Mention MAKE=gmake workaround when building on a BSD (fanquake)
+- #18341 Replace remaining literal BTC with `CURRENCY_UNIT` (domob1812)
+- #18342 Add fuzzing quickstart guides for libFuzzer and afl-fuzz (practicalswift)
+- #18344 Fix nit in getblockchaininfo (stevenroose)
+- #18379 Comment fix merkle.cpp (4d55397500)
+- #18382 note the costs of fetching all pull requests (vasild)
+- #18391 Update init and reduce-traffic docs for -blocksonly (glowang)
+- #18464 Block-relay-only vs blocksonly (MarcoFalke)
+- #18486 Explain new test logging (MarcoFalke)
+- #18505 Update webchat URLs in README.md (SuriyaaKudoIsc)
+- #18513 Fix git add argument (HashUnlimited)
+- #18577 Correct scripted-diff example link (yahiheb)
+- #18589 Fix naming of macOS SDK and clarify version (achow101)
+
+### Miscellaneous
+- #15600 lockedpool: When possible, use madvise to avoid including sensitive information in core dumps (luke-jr)
+- #15934 Merge settings one place instead of five places (ryanofsky)
+- #16115 On bitcoind startup, write config args to debug.log (LarryRuane)
+- #16117 util: Replace boost sleep with std sleep (MarcoFalke)
+- #16161 util: Fix compilation errors in support/lockedpool.cpp (jkczyz)
+- #16802 scripts: In linearize, search for next position of magic bytes rather than fail (takinbo)
+- #16889 Add some general std::vector utility functions (sipa)
+- #17049 contrib: Bump gitian descriptors for 0.20 (MarcoFalke)
+- #17052 scripts: Update `copyright_header` script to include additional files (GChuf)
+- #17059 util: Simplify path argument for cblocktreedb ctor (hebasto)
+- #17191 random: Remove call to `RAND_screen()` (Windows only) (fanquake)
+- #17192 util: Add `check_nonfatal` and use it in src/rpc (MarcoFalke)
+- #17218 Replace the LogPrint function with a macro (jkczyz)
+- #17266 util: Rename decodedumptime to parseiso8601datetime (elichai)
+- #17270 Feed environment data into RNG initializers (sipa)
+- #17282 contrib: Remove accounts from bash completion (fanquake)
+- #17293 Add assertion to randrange that input is not 0 (JeremyRubin)
+- #17325 log: Fix log message for -par=1 (hebasto)
+- #17329 linter: Strip trailing / in path for git-subtree-check (jnewbery)
+- #17336 scripts: Search for first block file for linearize-data with some block files pruned (Rjected)
+- #17361 scripts: Lint gitian descriptors with shellcheck (hebasto)
+- #17482 util: Disallow network-qualified command line options (ryanofsky)
+- #17507 random: mark RandAddPeriodic and SeedPeriodic as noexcept (fanquake)
+- #17527 Fix CPUID subleaf iteration (sipa)
+- #17604 util: Make schedulebatchpriority advisory only (fanquake)
+- #17650 util: Remove unwanted fields from bitcoin-cli -getinfo (malevolent)
+- #17671 script: Fixed wget call in gitian-build.py (willyko)
+- #17699 Make env data logging optional (sipa)
+- #17721 util: Don't allow base58 decoding of non-base58 strings. add base58 tests (practicalswift)
+- #17750 util: Change getwarnings parameter to bool (jnewbery)
+- #17753 util: Don't allow base32/64-decoding or parsemoney(…) on strings with embedded nul characters. add tests (practicalswift)
+- #17823 scripts: Read suspicious hosts from a file instead of hardcoding (sanjaykdragon)
+- #18162 util: Avoid potential uninitialized read in `formatiso8601datetime(int64_t)` by checking `gmtime_s`/`gmtime_r` return value (practicalswift)
+- #18167 Fix a violation of C++ standard rules where unions are used for type-punning (TheQuantumPhysicist)
+- #18225 util: Fail to parse empty string in parsemoney (MarcoFalke)
+- #18270 util: Fail to parse whitespace-only strings in parsemoney(…) (instead of parsing as zero) (practicalswift)
+- #18316 util: Helpexamplerpc formatting (jonatack)
+- #18357 Fix missing header in sync.h (promag)
+- #18412 script: Fix `script_err_sig_pushonly` error string (theStack)
+- #18416 util: Limit decimal range of numbers parsescript accepts (pierreN)
+- #18503 init: Replace `URL_WEBSITE` with `PACKAGE_URL` (MarcoFalke)
+- #18526 Remove PID file at the very end (hebasto)
+- #18553 Avoid non-trivial global constants in SHA-NI code (sipa)
+- #18665 Do not expose and consider `-logthreadnames` when it does not work (hebasto)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 0xb10c
+- 251
+- 4d55397500
+- Aaron Clauson
+- Adam Jonas
+- Albert
+- Amiti Uttarwar
+- Andrew Chow
+- Andrew Toth
+- Anthony Towns
+- Antoine Riard
+- Ava Barron
+- Ben Carman
+- Ben Woosley
+- Block Mechanic
+- Brian Solon
+- Bushstar
+- Carl Dong
+- Carnhof Daki
+- Cory Fields
+- Daki Carnhof
+- Dan Gershony
+- Daniel Kraft
+- dannmat
+- Danny-Scott
+- darosior
+- David O'Callaghan
+- Dominik Spicher
+- Elichai Turkel
+- Emil Engler
+- emu
+- Fabian Jahr
+- fanquake
+- Filip Gospodinov
+- Franck Royer
+- Gastón I. Silva
+- gchuf
+- Gleb Naumenko
+- Gloria Zhao
+- glowang
+- Gr0kchain
+- Gregory Sanders
+- hackerrdave
+- Harris
+- hel0
+- Hennadii Stepanov
+- ianliu
+- Igor Cota
+- James Chiang
+- James O'Beirne
+- Jan Beich
+- Jan Sarenik
+- Jeffrey Czyz
+- Jeremy Rubin
+- JeremyCrookshank
+- Jim Posen
+- John Bampton
+- John L. Jegutanis
+- John Newbery
+- Jon Atack
+- Jon Layton
+- Jonas Schnelli
+- João Barbosa
+- Jorge Timón
+- Karl-Johan Alm
+- kodslav
+- Larry Ruane
+- Luke Dashjr
+- malevolent
+- MapleLaker
+- marcaiaf
+- MarcoFalke
+- Marius Kjærstad
+- Mark Erhardt
+- Mark Tyneway
+- Martin Erlandsson
+- Martin Zumsande
+- Matt Corallo
+- Matt Ward
+- Michael Folkson
+- Michael Polzer
+- Micky Yun Chan
+- Neha Narula
+- nijynot
+- naumenkogs
+- NullFunctor
+- Peter Bushnell
+- pierrenn
+- Pieter Wuille
+- practicalswift
+- randymcmillan
+- Rjected
+- Russell Yanofsky
+- Samer Afach
+- Samuel Dobson
+- Sanjay K
+- Sebastian Falbesoner
+- setpill
+- Sjors Provoost
+- Stefan Richter
+- stefanwouldgo
+- Steven Roose
+- Suhas Daftuar
+- Suriyaa Sundararuban
+- TheCharlatan
+- Tim Akinbo
+- Travin Keith
+- tryphe
+- Vasil Dimov
+- Willy Ko
+- Wilson Ccasihue S
+- Wladimir J. van der Laan
+- Yahia Chiheb
+- Yancy Ribbens
+- Yusuf Sahin HAMZA
+- Zakk
+- Zero
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/src/Makefile.am b/src/Makefile.am
index 882d83e0b8..7a280a67a7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -241,6 +241,7 @@ BITCOIN_CORE_H = \
versionbitsinfo.h \
walletinitinterface.h \
wallet/coincontrol.h \
+ wallet/context.h \
wallet/crypter.h \
wallet/db.h \
wallet/feebumper.h \
@@ -248,6 +249,7 @@ BITCOIN_CORE_H = \
wallet/ismine.h \
wallet/load.h \
wallet/rpcwallet.h \
+ wallet/salvage.h \
wallet/scriptpubkeyman.h \
wallet/wallet.h \
wallet/walletdb.h \
@@ -349,6 +351,7 @@ libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
wallet/coincontrol.cpp \
+ wallet/context.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/feebumper.cpp \
@@ -356,6 +359,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/load.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
+ wallet/salvage.cpp \
wallet/scriptpubkeyman.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 766c0fca54..93b5156af3 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -29,6 +29,7 @@ bench_bench_bitcoin_SOURCES = \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
+ bench/hashpadding.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
bench/mempool_stress.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 2480cdadbb..03cd9133c8 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -32,6 +32,7 @@ FUZZ_TARGETS = \
test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
test/fuzz/coins_view \
+ test/fuzz/crypto_common \
test/fuzz/cuckoocache \
test/fuzz/decode_tx \
test/fuzz/descriptor_parse \
@@ -110,9 +111,14 @@ FUZZ_TARGETS = \
test/fuzz/rbf \
test/fuzz/rolling_bloom_filter \
test/fuzz/script \
+ test/fuzz/script_bitcoin_consensus \
+ test/fuzz/script_descriptor_cache \
test/fuzz/script_deserialize \
test/fuzz/script_flags \
+ test/fuzz/script_interpreter \
test/fuzz/script_ops \
+ test/fuzz/script_sigcache \
+ test/fuzz/script_sign \
test/fuzz/scriptnum_ops \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
@@ -473,6 +479,12 @@ test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
+test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
+
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -941,6 +953,18 @@ test_fuzz_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_SOURCES = test/fuzz/script.cpp
+test_fuzz_script_bitcoin_consensus_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_bitcoin_consensus_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_bitcoin_consensus_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_bitcoin_consensus_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_bitcoin_consensus_SOURCES = test/fuzz/script_bitcoin_consensus.cpp
+
+test_fuzz_script_descriptor_cache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_descriptor_cache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_descriptor_cache_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_descriptor_cache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_descriptor_cache_SOURCES = test/fuzz/script_descriptor_cache.cpp
+
test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1
test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -953,12 +977,30 @@ test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_flags_SOURCES = test/fuzz/script_flags.cpp
+test_fuzz_script_interpreter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_interpreter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_interpreter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
+
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_script_ops_SOURCES = test/fuzz/script_ops.cpp
+test_fuzz_script_sigcache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_sigcache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_sigcache_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_sigcache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sigcache_SOURCES = test/fuzz/script_sigcache.cpp
+
+test_fuzz_script_sign_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_sign_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_sign_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_sign_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sign_SOURCES = test/fuzz/script_sign.cpp
+
test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/bench/hashpadding.cpp b/src/bench/hashpadding.cpp
new file mode 100644
index 0000000000..985be8bdba
--- /dev/null
+++ b/src/bench/hashpadding.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2015-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.
+
+#include <bench/bench.h>
+#include <hash.h>
+#include <random.h>
+#include <uint256.h>
+
+
+static void PrePadded(benchmark::State& state)
+{
+
+ CSHA256 hasher;
+
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ hasher.Write(nonce.begin(), 32);
+ hasher.Write(nonce.begin(), 32);
+ uint256 data = GetRandHash();
+ while (state.KeepRunning()) {
+ unsigned char out[32];
+ CSHA256 h = hasher;
+ h.Write(data.begin(), 32);
+ h.Finalize(out);
+ }
+}
+
+BENCHMARK(PrePadded, 10000);
+
+static void RegularPadded(benchmark::State& state)
+{
+ CSHA256 hasher;
+
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ uint256 data = GetRandHash();
+ while (state.KeepRunning()) {
+ unsigned char out[32];
+ CSHA256 h = hasher;
+ h.Write(nonce.begin(), 32);
+ h.Write(data.begin(), 32);
+ h.Finalize(out);
+ }
+}
+
+BENCHMARK(RegularPadded, 10000);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 45a586cd12..8d85789b4e 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -21,6 +21,7 @@
#include <functional>
#include <memory>
#include <stdio.h>
+#include <string>
#include <tuple>
#include <event2/buffer.h>
@@ -158,7 +159,7 @@ struct HTTPReply
std::string body;
};
-static const char *http_errorstring(int code)
+static std::string http_errorstring(int code)
{
switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 7f9439788a..b420463c00 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -31,6 +31,7 @@ static void SetupWalletToolArgs()
gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("salvage", "Attempt to recover private keys from a corrupt wallet", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
}
static bool WalletAppInit(int argc, char* argv[])
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 263d863cfa..a47709cd82 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -105,13 +105,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
std::vector<bool> have_txn(txn_available.size());
{
LOCK(pool->cs);
- const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes;
- for (size_t i = 0; i < vTxHashes.size(); i++) {
- uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
+ for (size_t i = 0; i < pool->vTxHashes.size(); i++) {
+ uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first);
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
if (idit != shorttxids.end()) {
if (!have_txn[idit->second]) {
- txn_available[idit->second] = vTxHashes[i].second->GetSharedTx();
+ txn_available[idit->second] = pool->vTxHashes[i].second->GetSharedTx();
have_txn[idit->second] = true;
mempool_count++;
} else {
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 9ec1beeaf7..326db1b4a7 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -126,7 +126,7 @@ class PartiallyDownloadedBlock {
protected:
std::vector<CTransactionRef> txn_available;
size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
- CTxMemPool* pool;
+ const CTxMemPool* pool;
public:
CBlockHeader header;
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
diff --git a/src/blockfilter.h b/src/blockfilter.h
index ff8744b217..96cefbf3b2 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -144,8 +144,8 @@ public:
template <typename Stream>
void Serialize(Stream& s) const {
- s << m_block_hash
- << static_cast<uint8_t>(m_filter_type)
+ s << static_cast<uint8_t>(m_filter_type)
+ << m_block_hash
<< m_filter.GetEncoded();
}
@@ -154,8 +154,8 @@ public:
std::vector<unsigned char> encoded_filter;
uint8_t filter_type;
- s >> m_block_hash
- >> filter_type
+ s >> filter_type
+ >> m_block_hash
>> encoded_filter;
m_filter_type = static_cast<BlockFilterType>(filter_type);
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 81245e3e11..9e8e6530f1 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -27,9 +27,9 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}
-std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block)
{
- assert(prevHeights->size() == tx.vin.size());
+ assert(prevHeights.size() == tx.vin.size());
// Will be set to the equivalent height- and time-based nLockTime
// values that would be necessary to satisfy all relative lock-
@@ -59,11 +59,11 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
// consensus-enforced meaning at this point.
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) {
// The height of this input is not relevant for sequence locks
- (*prevHeights)[txinIndex] = 0;
+ prevHeights[txinIndex] = 0;
continue;
}
- int nCoinHeight = (*prevHeights)[txinIndex];
+ int nCoinHeight = prevHeights[txinIndex];
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
@@ -99,7 +99,7 @@ bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> loc
return true;
}
-bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
+bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block)
{
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
}
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index ffcaf3cab1..e2a9328df8 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -66,13 +66,13 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
* Also removes from the vector of input heights any entries which did not
* correspond to sequence locked inputs as they do not affect the calculation.
*/
-std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
+std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair);
/**
* Check if transaction is final per BIP 68 sequence numbers and can be included in a block.
* Consensus critical. Takes as input a list of heights at which tx's inputs (in order) confirmed.
*/
-bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
+bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
#endif // BITCOIN_CONSENSUS_TX_VERIFY_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index df78c319ee..1c0a8a096d 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -19,6 +19,7 @@
#include <boost/algorithm/string/split.hpp>
#include <algorithm>
+#include <string>
CScript ParseScript(const std::string& s)
{
@@ -34,10 +35,9 @@ CScript ParseScript(const std::string& s)
if (op < OP_NOP && op != OP_RESERVED)
continue;
- const char* name = GetOpName(static_cast<opcodetype>(op));
- if (strcmp(name, "OP_UNKNOWN") == 0)
+ std::string strName = GetOpName(static_cast<opcodetype>(op));
+ if (strName == "OP_UNKNOWN")
continue;
- std::string strName(name);
mapOpNames[strName] = static_cast<opcodetype>(op);
// Convenience: OP_ADD and just ADD are both recognized:
boost::algorithm::replace_first(strName, "OP_", "");
diff --git a/src/core_write.cpp b/src/core_write.cpp
index cb1fc214eb..eb0cc35f06 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -198,13 +198,13 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o);
- if (!tx.vin[i].scriptWitness.IsNull()) {
- UniValue txinwitness(UniValue::VARR);
- for (const auto& item : tx.vin[i].scriptWitness.stack) {
- txinwitness.push_back(HexStr(item.begin(), item.end()));
- }
- in.pushKV("txinwitness", txinwitness);
+ }
+ if (!tx.vin[i].scriptWitness.IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : tx.vin[i].scriptWitness.stack) {
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
}
+ in.pushKV("txinwitness", txinwitness);
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
diff --git a/src/fs.cpp b/src/fs.cpp
index 066c6c10d3..e68c97b3ca 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -6,6 +6,9 @@
#ifndef WIN32
#include <fcntl.h>
+#include <string>
+#include <sys/file.h>
+#include <sys/utsname.h>
#else
#ifndef NOMINMAX
#define NOMINMAX
@@ -47,20 +50,38 @@ FileLock::~FileLock()
}
}
+static bool IsWSL()
+{
+ struct utsname uname_data;
+ return uname(&uname_data) == 0 && std::string(uname_data.version).find("Microsoft") != std::string::npos;
+}
+
bool FileLock::TryLock()
{
if (fd == -1) {
return false;
}
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- if (fcntl(fd, F_SETLK, &lock) == -1) {
- reason = GetErrorReason();
- return false;
+
+ // Exclusive file locking is broken on WSL using fcntl (issue #18622)
+ // This workaround can be removed once the bug on WSL is fixed
+ static const bool is_wsl = IsWSL();
+ if (is_wsl) {
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+ reason = GetErrorReason();
+ return false;
+ }
+ } else {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd, F_SETLK, &lock) == -1) {
+ reason = GetErrorReason();
+ return false;
+ }
}
+
return true;
}
#else
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 74ea421e13..1d09f2e577 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -21,7 +21,7 @@ template<typename... Args>
static void FatalError(const char* fmt, const Args&... args)
{
std::string strMessage = tfm::format(fmt, args...);
- SetMiscWarning(strMessage);
+ SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
uiInterface.ThreadSafeMessageBox(
Untranslated("Error: A fatal internal error occurred, see debug.log for details"),
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 4626395ef0..59d1888fff 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -9,8 +9,6 @@
#include <util/translation.h>
#include <validation.h>
-#include <boost/thread.hpp>
-
constexpr char DB_BEST_BLOCK = 'B';
constexpr char DB_TXINDEX = 't';
constexpr char DB_TXINDEX_BLOCK = 'T';
@@ -150,7 +148,6 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
bool interrupted = false;
std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
- boost::this_thread::interruption_point();
if (ShutdownRequested()) {
interrupted = true;
break;
diff --git a/src/init.cpp b/src/init.cpp
index 37e6251295..fd7c8d0f80 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -18,6 +18,7 @@
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
@@ -42,6 +43,7 @@
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
+#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
@@ -49,12 +51,11 @@
#include <ui_interface.h>
#include <util/asmap.h>
#include <util/moneystr.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
-#include <hash.h>
-
#include <validationinterface.h>
#include <walletinitinterface.h>
@@ -71,11 +72,9 @@
#include <sys/stat.h>
#endif
-#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/algorithm/string/split.hpp>
#include <boost/signals2/signal.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
@@ -173,11 +172,10 @@ void Interrupt(NodeContext& node)
void Shutdown(NodeContext& node)
{
+ static Mutex g_shutdown_mutex;
+ TRY_LOCK(g_shutdown_mutex, lock_shutdown);
+ if (!lock_shutdown) return;
LogPrintf("%s: In progress...\n", __func__);
- static RecursiveMutex cs_Shutdown;
- TRY_LOCK(cs_Shutdown, lockShutdown);
- if (!lockShutdown)
- return;
/// Note: Shutdown() must be able to handle cases in which initialization failed part of the way,
/// for example if the data directory was found to be locked.
@@ -444,7 +442,7 @@ void SetupServerArgs(NodeContext& node)
gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'noban' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -468,11 +466,7 @@ void SetupServerArgs(NodeContext& node)
hidden_args.emplace_back("-upnp");
#endif
gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. "
- "Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), "
- "noban (do not ban for misbehavior), "
- "forcerelay (relay transactions that are already in the mempool; implies relay), "
- "relay (relay even in -blocksonly mode), "
- "and mempool (allow requesting BIP35 mempool contents). "
+ "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". "
"Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-whitelist=<[permissions@]IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or "
@@ -502,14 +496,7 @@ void SetupServerArgs(NodeContext& node)
#endif
gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
- "level 0 reads the blocks from disk, "
- "level 1 verifies block validity, "
- "level 2 verifies undo data, "
- "level 3 checks disconnection of tip blocks, "
- "and level 4 tries to reconnect the blocks, "
- "each level includes the checks of the previous levels "
- "(0-4, default: %u)", DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block 295000 (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -964,17 +951,27 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
- // Warn if network-specific options (-addnode, -connect, etc) are
+ // Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
std::string network = gArgs.GetChainName();
+ bilingual_str errors;
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));
+ errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
+ }
+
+ if (!errors.empty()) {
+ return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
+ bilingual_str warnings;
for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
+ warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
+ }
+
+ if (!warnings.empty()) {
+ InitWarning(warnings);
}
if (!fs::is_directory(GetBlocksDir())) {
@@ -1468,7 +1465,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
else
- return InitError(Untranslated(ResolveErrMsg("externalip", strAddr)));
+ return InitError(ResolveErrMsg("externalip", strAddr));
}
// Read asmap file if configured
@@ -1907,21 +1904,21 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
for (const std::string& strBind : gArgs.GetArgs("-bind")) {
CService addrBind;
if (!Lookup(strBind, addrBind, GetListenPort(), false)) {
- return InitError(Untranslated(ResolveErrMsg("bind", strBind)));
+ return InitError(ResolveErrMsg("bind", strBind));
}
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
- std::string error;
- if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(Untranslated(error));
+ bilingual_str error;
+ if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
- std::string error;
- if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(Untranslated(error));
+ bilingual_str error;
+ if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index d8e459a8e8..d1e04b114d 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -63,9 +63,9 @@ public:
{
m_notifications->transactionAddedToMempool(tx);
}
- void TransactionRemovedFromMempool(const CTransactionRef& tx) override
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override
{
- m_notifications->transactionRemovedFromMempool(tx);
+ m_notifications->transactionRemovedFromMempool(tx, reason);
}
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 7dfc77db7b..61d7ddb934 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -20,6 +20,7 @@ class CRPCCommand;
class CScheduler;
class Coin;
class uint256;
+enum class MemPoolRemovalReason;
enum class RBFTransactionState;
struct bilingual_str;
struct CBlockLocator;
@@ -239,7 +240,7 @@ public:
public:
virtual ~Notifications() {}
virtual void transactionAddedToMempool(const CTransactionRef& tx) {}
- virtual void transactionRemovedFromMempool(const CTransactionRef& ptx) {}
+ virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
virtual void blockConnected(const CBlock& block, int height) {}
virtual void blockDisconnected(const CBlock& block, int height) {}
virtual void updatedBlockTip() {}
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 3c94e44b53..d420788dbe 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -56,7 +56,7 @@ namespace {
class NodeImpl : public Node
{
public:
- void initError(const std::string& message) override { InitError(Untranslated(message)); }
+ void initError(const bilingual_str& message) override { InitError(message); }
bool parseParameters(int argc, const char* const argv[], std::string& error) override
{
return gArgs.ParseParameters(argc, argv, error);
@@ -71,7 +71,7 @@ public:
std::string getNetwork() override { return Params().NetworkIDString(); }
void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); }
- std::string getWarnings() override { return GetWarnings(true); }
+ bilingual_str getWarnings() override { return GetWarnings(true); }
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
@@ -88,7 +88,15 @@ public:
Interrupt(m_context);
Shutdown(m_context);
}
- void startShutdown() override { StartShutdown(); }
+ void startShutdown() override
+ {
+ StartShutdown();
+ // Stop RPC for clean shutdown if any of waitfor* commands is executed.
+ if (gArgs.GetBoolArg("-server", false)) {
+ InterruptRPC();
+ StopRPC();
+ }
+ }
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp) override
{
@@ -187,6 +195,11 @@ public:
LOCK(::cs_main);
return ::ChainActive().Height();
}
+ uint256 getBestBlockHash() override
+ {
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
+ return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
+ }
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
@@ -310,7 +323,7 @@ public:
std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override
{
return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) {
- fn(sync_state, block->nHeight, block->GetBlockTime(),
+ fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
GuessVerificationProgress(Params().TxData(), block));
}));
}
@@ -318,7 +331,7 @@ public:
{
return MakeHandler(
::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) {
- fn(sync_state, block->nHeight, block->GetBlockTime(),
+ fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
/* verification progress is unused when a header was received */ 0);
}));
}
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 45b0e18fae..877a40568f 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -10,6 +10,7 @@
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
#include <support/allocators/secure.h> // For SecureString
+#include <util/translation.h>
#include <functional>
#include <memory>
@@ -36,6 +37,7 @@ struct bilingual_str;
namespace interfaces {
class Handler;
class Wallet;
+struct BlockTip;
//! Top-level interface for a bitcoin node (bitcoind process).
class Node
@@ -44,7 +46,7 @@ public:
virtual ~Node() {}
//! Send init error.
- virtual void initError(const std::string& message) = 0;
+ virtual void initError(const bilingual_str& message) = 0;
//! Set command line arguments.
virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
@@ -80,7 +82,7 @@ public:
virtual void initParameterInteraction() = 0;
//! Get warnings.
- virtual std::string getWarnings() = 0;
+ virtual bilingual_str getWarnings() = 0;
// Get log flags.
virtual uint32_t getLogCategories() = 0;
@@ -149,6 +151,9 @@ public:
//! Get num blocks.
virtual int getNumBlocks() = 0;
+ //! Get best block hash.
+ virtual uint256 getBestBlockHash() = 0;
+
//! Get last block time.
virtual int64_t getLastBlockTime() = 0;
@@ -250,12 +255,12 @@ public:
//! Register handler for block tip messages.
using NotifyBlockTipFn =
- std::function<void(SynchronizationState, int height, int64_t block_time, double verification_progress)>;
+ std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
//! Register handler for header tip messages.
using NotifyHeaderTipFn =
- std::function<void(SynchronizationState, int height, int64_t block_time, double verification_progress)>;
+ std::function<void(SynchronizationState, interfaces::BlockTip tip, double verification_progress)>;
virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
//! Return pointer to internal chain interface, useful for testing.
@@ -265,6 +270,13 @@ public:
//! Return implementation of Node interface.
std::unique_ptr<Node> MakeNode();
+//! Block tip (could be a header or not, depends on the subscribed signal).
+struct BlockTip {
+ int block_height;
+ int64_t block_time;
+ uint256 block_hash;
+};
+
} // namespace interfaces
#endif // BITCOIN_INTERFACES_NODE_H
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 349dce0247..397403d308 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -9,13 +9,16 @@
#include <interfaces/handler.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
+#include <rpc/server.h>
#include <script/standard.h>
#include <support/allocators/secure.h>
#include <sync.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/check.h>
+#include <util/ref.h>
#include <util/system.h>
+#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/ismine.h>
@@ -351,13 +354,13 @@ public:
}
return result;
}
- bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
+ bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override
{
TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
- num_blocks = m_wallet->GetLastBlockHeight();
+ block_hash = m_wallet->GetLastBlockHash();
balances = getBalances();
return true;
}
@@ -481,16 +484,21 @@ class WalletClientImpl : public ChainClient
{
public:
WalletClientImpl(Chain& chain, std::vector<std::string> wallet_filenames)
- : m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
+ : m_wallet_filenames(std::move(wallet_filenames))
{
+ m_context.chain = &chain;
}
void registerRpcs() override
{
- g_rpc_chain = &m_chain;
- return RegisterWalletRPCCommands(m_chain, m_rpc_handlers);
+ for (const CRPCCommand& command : GetWalletRPCCommands()) {
+ m_rpc_commands.emplace_back(command.category, command.name, [this, &command](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
+ return command.actor({request, m_context}, result, last_handler);
+ }, command.argNames, command.unique_id);
+ m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
+ }
}
- bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
- bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
+ bool verify() override { return VerifyWallets(*m_context.chain, m_wallet_filenames); }
+ bool load() override { return LoadWallets(*m_context.chain, m_wallet_filenames); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
@@ -505,9 +513,10 @@ public:
}
~WalletClientImpl() override { UnloadWallets(); }
- Chain& m_chain;
+ WalletContext m_context;
std::vector<std::string> m_wallet_filenames;
std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
+ std::list<CRPCCommand> m_rpc_commands;
};
} // namespace
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 421d35af15..67569a3e55 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -203,7 +203,7 @@ public:
virtual WalletBalances getBalances() = 0;
//! Get balances if possible without blocking.
- virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0;
+ virtual bool tryGetBalances(WalletBalances& balances, uint256& block_hash) = 0;
//! Get balance.
virtual CAmount getBalance() = 0;
diff --git a/src/logging.cpp b/src/logging.cpp
index 56c44ae1ea..35e0754f20 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -41,7 +41,7 @@ static int FileWriteStr(const std::string &str, FILE *fp)
bool BCLog::Logger::StartLogging()
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
assert(m_buffering);
assert(m_fileout == nullptr);
@@ -80,7 +80,7 @@ bool BCLog::Logger::StartLogging()
void BCLog::Logger::DisconnectTestLogger()
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_buffering = true;
if (m_fileout != nullptr) fclose(m_fileout);
m_fileout = nullptr;
@@ -95,15 +95,7 @@ void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
bool BCLog::Logger::EnableCategory(const std::string& str)
{
BCLog::LogFlags flag;
- if (!GetLogCategory(flag, str)) {
- if (str == "db") {
- // DEPRECATION: Added in 0.20, should start returning an error in 0.21
- LogPrintf("Warning: logging category 'db' is deprecated, use 'walletdb' instead\n");
- EnableCategory(BCLog::WALLETDB);
- return true;
- }
- return false;
- }
+ if (!GetLogCategory(flag, str)) return false;
EnableCategory(flag);
return true;
}
@@ -246,7 +238,7 @@ namespace BCLog {
void BCLog::Logger::LogPrintStr(const std::string& str)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str);
if (m_log_threadnames && m_started_new_line) {
diff --git a/src/logging.h b/src/logging.h
index ab07010316..7e646ef67a 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -8,6 +8,7 @@
#include <fs.h>
#include <tinyformat.h>
+#include <threadsafety.h>
#include <util/string.h>
#include <atomic>
@@ -61,10 +62,11 @@ namespace BCLog {
class Logger
{
private:
- mutable std::mutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
- FILE* m_fileout = nullptr; // GUARDED_BY(m_cs)
- std::list<std::string> m_msgs_before_open; // GUARDED_BY(m_cs)
- bool m_buffering{true}; //!< Buffer messages before logging can be started. GUARDED_BY(m_cs)
+ mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
+
+ FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
+ std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
+ bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
/**
* m_started_new_line is a state variable that will suppress printing of
@@ -79,7 +81,7 @@ namespace BCLog {
std::string LogTimestampStr(const std::string& str);
/** Slots that connect to the print signal */
- std::list<std::function<void(const std::string&)>> m_print_callbacks /* GUARDED_BY(m_cs) */ {};
+ std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {};
public:
bool m_print_to_console = false;
@@ -98,14 +100,14 @@ namespace BCLog {
/** Returns whether logs will be written to any output */
bool Enabled() const
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
}
/** Connect a slot to the print signal and return the connection */
std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_print_callbacks.push_back(std::move(fun));
return --m_print_callbacks.end();
}
@@ -113,7 +115,7 @@ namespace BCLog {
/** Delete a connection */
void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it)
{
- std::lock_guard<std::mutex> scoped_lock(m_cs);
+ StdLockGuard scoped_lock(m_cs);
m_print_callbacks.erase(it);
}
diff --git a/src/net.cpp b/src/net.cpp
index 9950b9aea4..f3b7e8dab1 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -52,6 +52,19 @@ static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15};
/** Number of DNS seeds to query when the number of connections is low. */
static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3;
+/** How long to delay before querying DNS seeds
+ *
+ * If we have more than THRESHOLD entries in addrman, then it's likely
+ * that we got those addresses from having previously connected to the P2P
+ * network, and that we'll be able to successfully reconnect to the P2P
+ * network via contacting one of them. So if that's the case, spend a
+ * little longer trying to connect to known peers before querying the
+ * DNS seeds.
+ */
+static constexpr std::chrono::seconds DNSSEEDS_DELAY_FEW_PEERS{11};
+static constexpr std::chrono::minutes DNSSEEDS_DELAY_MANY_PEERS{5};
+static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // "many" vs "few" peers
+
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
@@ -1455,7 +1468,7 @@ void CConnman::ThreadSocketHandler()
void CConnman::WakeMessageHandler()
{
{
- std::lock_guard<std::mutex> lock(mutexMsgProc);
+ LOCK(mutexMsgProc);
fMsgProcWake = true;
}
condMsgProc.notify_one();
@@ -1587,31 +1600,72 @@ void CConnman::ThreadDNSAddressSeed()
if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
// When -forcednsseed is provided, query all.
seeds_right_now = seeds.size();
+ } else if (addrman.size() == 0) {
+ // If we have no known peers, query all.
+ // This will occur on the first run, or if peers.dat has been
+ // deleted.
+ seeds_right_now = seeds.size();
}
+ // goal: only query DNS seed if address need is acute
+ // * If we have a reasonable number of peers in addrman, spend
+ // some time trying them first. This improves user privacy by
+ // creating fewer identifying DNS requests, reduces trust by
+ // giving seeds less influence on the network topology, and
+ // reduces traffic to the seeds.
+ // * When querying DNS seeds query a few at once, this ensures
+ // that we don't give DNS seeds the ability to eclipse nodes
+ // that query them.
+ // * If we continue having problems, eventually query all the
+ // DNS seeds, and if that fails too, also try the fixed seeds.
+ // (done in ThreadOpenConnections)
+ const std::chrono::seconds seeds_wait_time = (addrman.size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS);
+
for (const std::string& seed : seeds) {
- // goal: only query DNS seed if address need is acute
- // Avoiding DNS seeds when we don't need them improves user privacy by
- // creating fewer identifying DNS requests, reduces trust by giving seeds
- // less influence on the network topology, and reduces traffic to the seeds.
- if (addrman.size() > 0 && seeds_right_now == 0) {
- if (!interruptNet.sleep_for(std::chrono::seconds(11))) return;
+ if (seeds_right_now == 0) {
+ seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;
- LOCK(cs_vNodes);
- int nRelevant = 0;
- for (const CNode* pnode : vNodes) {
- nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
- }
- if (nRelevant >= 2) {
- LogPrintf("P2P peers available. Skipped DNS seeding.\n");
- return;
+ if (addrman.size() > 0) {
+ LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count());
+ std::chrono::seconds to_wait = seeds_wait_time;
+ while (to_wait.count() > 0) {
+ // if sleeping for the MANY_PEERS interval, wake up
+ // early to see if we have enough peers and can stop
+ // this thread entirely freeing up its resources
+ std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
+ if (!interruptNet.sleep_for(w)) return;
+ to_wait -= w;
+
+ int nRelevant = 0;
+ {
+ LOCK(cs_vNodes);
+ for (const CNode* pnode : vNodes) {
+ nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
+ }
+ }
+ if (nRelevant >= 2) {
+ if (found > 0) {
+ LogPrintf("%d addresses found from DNS seeds\n", found);
+ LogPrintf("P2P peers available. Finished DNS seeding.\n");
+ } else {
+ LogPrintf("P2P peers available. Skipped DNS seeding.\n");
+ }
+ return;
+ }
+ }
}
- seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE;
}
- if (interruptNet) {
- return;
+ if (interruptNet) return;
+
+ // hold off on querying seeds if P2P network deactivated
+ if (!fNetworkActive) {
+ LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
+ do {
+ if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
+ } while (!fNetworkActive);
}
+
LogPrintf("Loading addresses from DNS seed %s\n", seed);
if (HaveNameProxy()) {
AddOneShot(seed);
@@ -1755,6 +1809,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
return;
// Add seed nodes if DNS seeds are all down (an infrastructure attack?).
+ // Note that we only do this if we started with an empty peers.dat,
+ // (in which case we will query DNS seeds immediately) *and* the DNS
+ // seeds have not returned any results.
if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
static bool done = false;
if (!done) {
@@ -2058,7 +2115,7 @@ void CConnman::ThreadMessageHandler()
WAIT_LOCK(mutexMsgProc, lock);
if (!fMoreWork) {
- condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });
+ condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this]() EXCLUSIVE_LOCKS_REQUIRED(mutexMsgProc) { return fMsgProcWake; });
}
fMsgProcWake = false;
}
@@ -2366,7 +2423,7 @@ static CNetCleanup instance_of_cnetcleanup;
void CConnman::Interrupt()
{
{
- std::lock_guard<std::mutex> lock(mutexMsgProc);
+ LOCK(mutexMsgProc);
flagInterruptMsgProc = true;
}
condMsgProc.notify_all();
@@ -2582,7 +2639,7 @@ void CConnman::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle = 0;
}
- // TODO, exclude whitebind peers
+ // TODO, exclude peers with noban permission
nMaxOutboundTotalBytesSentInCycle += bytes;
}
diff --git a/src/net.h b/src/net.h
index 66aac6f084..517445e8f4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -454,7 +454,7 @@ private:
const uint64_t nSeed0, nSeed1;
/** flag for waking the message processor. */
- bool fMsgProcWake;
+ bool fMsgProcWake GUARDED_BY(mutexMsgProc);
std::condition_variable condMsgProc;
Mutex mutexMsgProc;
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
index 22fa5ee73b..da09149856 100644
--- a/src/net_permissions.cpp
+++ b/src/net_permissions.cpp
@@ -8,8 +8,18 @@
#include <util/system.h>
#include <util/translation.h>
+const std::vector<std::string> NET_PERMISSIONS_DOC{
+ "bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
+ "noban (do not ban for misbehavior)",
+ "forcerelay (relay transactions that are already in the mempool; implies relay)",
+ "relay (relay even in -blocksonly mode)",
+ "mempool (allow requesting BIP35 mempool contents)",
+};
+
+namespace {
+
// The parse the following format "perm1,perm2@xxxxxx"
-bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, std::string& error)
+bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, bilingual_str& error)
{
NetPermissionFlags flags = PF_NONE;
const auto atSeparator = str.find('@');
@@ -40,7 +50,7 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
else if (permission.length() == 0); // Allow empty entries
else {
- error = strprintf(_("Invalid P2P permission: '%s'").translated, permission);
+ error = strprintf(_("Invalid P2P permission: '%s'"), permission);
return false;
}
}
@@ -48,10 +58,12 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
}
output = flags;
- error = "";
+ error = Untranslated("");
return true;
}
+}
+
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
{
std::vector<std::string> strings;
@@ -63,7 +75,7 @@ std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
return strings;
}
-bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error)
+bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
@@ -76,17 +88,17 @@ bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermis
return false;
}
if (addrBind.GetPort() == 0) {
- error = strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind);
+ error = strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind);
return false;
}
output.m_flags = flags;
output.m_service = addrBind;
- error = "";
+ error = Untranslated("");
return true;
}
-bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error)
+bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
@@ -96,12 +108,12 @@ bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermis
CSubNet subnet;
LookupSubNet(net, subnet);
if (!subnet.IsValid()) {
- error = strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net);
+ error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
return false;
}
output.m_flags = flags;
output.m_subnet = subnet;
- error = "";
+ error = Untranslated("");
return true;
}
diff --git a/src/net_permissions.h b/src/net_permissions.h
index 962a2159fc..e004067e75 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -2,12 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
+
#include <string>
#include <vector>
-#include <netaddress.h>
#ifndef BITCOIN_NET_PERMISSIONS_H
#define BITCOIN_NET_PERMISSIONS_H
+
+struct bilingual_str;
+
+extern const std::vector<std::string> NET_PERMISSIONS_DOC;
+
enum NetPermissionFlags
{
PF_NONE = 0,
@@ -27,6 +33,7 @@ enum NetPermissionFlags
PF_ISIMPLICIT = (1U << 31),
PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL,
};
+
class NetPermissions
{
public:
@@ -45,17 +52,18 @@ public:
flags = static_cast<NetPermissionFlags>(flags & ~f);
}
};
+
class NetWhitebindPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error);
+ static bool TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error);
CService m_service;
};
class NetWhitelistPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error);
+ static bool TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error);
CSubNet m_subnet;
};
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 159036a237..d48745aef2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -129,6 +129,8 @@ static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
/** Maximum feefilter broadcast delay after significant change. */
static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
+/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
+static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
@@ -439,32 +441,32 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
-static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && !node->fClient;
+ state->fPreferredDownload = (!node.fInbound || node.HasPermission(PF_NOBAN)) && !node.fOneShot && !node.fClient;
nPreferredDownload += state->fPreferredDownload;
}
-static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
+static void PushNodeVersion(CNode& pnode, CConnman* connman, int64_t nTime)
{
// Note that pnode->GetLocalServices() is a reflection of the local
// services we were offering when the CNode object was created for this
// peer.
- ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
- uint64_t nonce = pnode->GetLocalNonce();
- int nNodeStartingHeight = pnode->GetMyStartingHeight();
- NodeId nodeid = pnode->GetId();
- CAddress addr = pnode->addr;
+ ServiceFlags nLocalNodeServices = pnode.GetLocalServices();
+ uint64_t nonce = pnode.GetLocalNonce();
+ int nNodeStartingHeight = pnode.GetMyStartingHeight();
+ NodeId nodeid = pnode.GetId();
+ CAddress addr = pnode.addr;
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
- connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr));
+ connman->PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode.m_tx_relay != nullptr));
if (fLogIPs) {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
@@ -797,9 +799,9 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
// Returns true for outbound peers, excluding manual connections, feelers, and
// one-shots.
-static bool IsOutboundDisconnectionCandidate(const CNode *node)
+static bool IsOutboundDisconnectionCandidate(const CNode& node)
{
- return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
+ return !(node.fInbound || node.m_manual_connection || node.fFeeler || node.fOneShot);
}
void PeerLogicValidation::InitializeNode(CNode *pnode) {
@@ -811,7 +813,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
}
if(!pnode->fInbound)
- PushNodeVersion(pnode, connman, GetTime());
+ PushNodeVersion(*pnode, connman, GetTime());
}
void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
@@ -827,7 +829,8 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
}
}
- // schedule next run for 10-15 minutes in the future
+ // Schedule next run for 10-15 minutes in the future.
+ // We add randomness on every cycle to avoid the possibility of P2P fingerprinting.
const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1459,7 +1462,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman)
+void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman)
{
bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
@@ -1501,28 +1504,30 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (pindex) {
send = BlockRequestAllowed(pindex, consensusParams);
if (!send) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
}
}
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
- // never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
- {
- LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
+ if (send &&
+ connman->OutboundTargetReached(true) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ !pfrom.HasPermission(PF_NOBAN) // never disconnect nodes with the noban permission
+ ) {
+ LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
//disconnect node
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->HasPermission(PF_NOBAN) && (
- (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
+ if (send && !pfrom.HasPermission(PF_NOBAN) && (
+ (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
- LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId());
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
send = false;
}
// Pruned nodes may have deleted the block, so check whether
@@ -1539,7 +1544,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) {
assert(!"cannot load block from disk");
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
// Don't set pblock as we've sent the block
} else {
// Send block from disk
@@ -1550,22 +1555,22 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
if (pblock) {
if (inv.type == MSG_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter);
}
}
if (sendMerkleBlock) {
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didn't send here -
@@ -1574,7 +1579,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
for (PairType& pair : merkleBlock.vMatchedTxn)
- connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ connman->PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1585,44 +1590,44 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
+ bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else {
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
}
}
}
// Trigger the peer node to send a getblocks request for the next batch of inventory
- if (inv.hash == pfrom->hashContinue)
+ if (inv.hash == pfrom.hashContinue)
{
// Bypass PushInventory, this must send even if redundant,
// and we want it right after the last block so they don't
// wait for other stuff first.
std::vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv));
- pfrom->hashContinue.SetNull();
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ pfrom.hashContinue.SetNull();
}
}
}
//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-CTransactionRef static FindTxForGetData(CNode* peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds longlived_mempool_time) LOCKS_EXCLUDED(cs_main)
+CTransactionRef static FindTxForGetData(CNode& peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds longlived_mempool_time) LOCKS_EXCLUDED(cs_main)
{
// Check if the requested transaction is so recent that we're just
// about to announce it to the peer; if so, they certainly shouldn't
// know we already have it.
{
- LOCK(peer->m_tx_relay->cs_tx_inventory);
- if (peer->m_tx_relay->setInventoryTxToSend.count(txid)) return {};
+ LOCK(peer.m_tx_relay->cs_tx_inventory);
+ if (peer.m_tx_relay->setInventoryTxToSend.count(txid)) return {};
}
{
@@ -1645,32 +1650,32 @@ CTransactionRef static FindTxForGetData(CNode* peer, const uint256& txid, const
return {};
}
-void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
+void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnman* connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
{
AssertLockNotHeld(cs_main);
- std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin();
std::vector<CInv> vNotFound;
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
// mempool entries added before this time have likely expired from mapRelay
const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
// Get last mempool request time
- const std::chrono::seconds mempool_req = pfrom->m_tx_relay != nullptr ? pfrom->m_tx_relay->m_last_mempool_req.load()
+ const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
: std::chrono::seconds::min();
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
// them.
- while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
+ while (it != pfrom.vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
if (interruptMsgProc) return;
// The send buffer provides backpressure. If there's no space in
// the buffer, pause processing until the next call.
- if (pfrom->fPauseSend) break;
+ if (pfrom.fPauseSend) break;
const CInv &inv = *it++;
- if (pfrom->m_tx_relay == nullptr) {
+ if (pfrom.m_tx_relay == nullptr) {
// Ignore GETDATA requests for transactions from blocks-only peers.
continue;
}
@@ -1678,7 +1683,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
CTransactionRef tx = FindTxForGetData(pfrom, inv.hash, mempool_req, longlived_mempool_time);
if (tx) {
int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
+ connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
mempool.RemoveUnbroadcastTx(inv.hash);
} else {
vNotFound.push_back(inv);
@@ -1687,7 +1692,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// Only process one BLOCK item per call, since they're uncommon and can be
// expensive to process.
- if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
+ if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
@@ -1696,7 +1701,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// and continue processing the queue on the next call.
}
- pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
+ pfrom.vRecvGetData.erase(pfrom.vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
@@ -1713,37 +1718,37 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// In normal operation, we often send NOTFOUND messages for parents of
// transactions that we relay; if a peer is missing a parent, they may
// assume we have them and request the parents from us.
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
-static uint32_t GetFetchFlags(CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
uint32_t nFetchFlags = 0;
- if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
+ if ((pfrom.GetLocalServices() & NODE_WITNESS) && State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman* connman) {
+inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman* connman) {
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId()));
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom.GetId()));
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
}
LOCK(cs_main);
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ connman->PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
-bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
+bool static ProcessHeadersMessage(CNode& pfrom, CConnman* connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
{
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
size_t nCount = headers.size();
if (nCount == 0) {
@@ -1755,7 +1760,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
const CBlockIndex *pindexLast = nullptr;
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this looks like it could be a block announcement (nCount <
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
@@ -1767,19 +1772,19 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
pindexBestHeader->nHeight,
- pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ pfrom.GetId(), nodestate->nUnconnectingHeaders);
// Set hashLastUnknownBlock for this peer, so that if we
// eventually get the headers - even from a different peer -
// we can use this peer to download.
- UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
+ UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash());
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
- Misbehaving(pfrom->GetId(), 20);
+ Misbehaving(pfrom.GetId(), 20);
}
return true;
}
@@ -1787,7 +1792,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
uint256 hashLastBlock;
for (const CBlockHeader& header : headers) {
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
- Misbehaving(pfrom->GetId(), 20, "non-continuous headers sequence");
+ Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
return false;
}
hashLastBlock = header.GetHash();
@@ -1803,21 +1808,21 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
BlockValidationState state;
if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
if (state.IsInvalid()) {
- MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, "invalid header received");
+ MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
return false;
}
}
{
LOCK(cs_main);
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
if (nodestate->nUnconnectingHeaders > 0) {
- LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
+ LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom.GetId(), nodestate->nUnconnectingHeaders);
}
nodestate->nUnconnectingHeaders = 0;
assert(pindexLast);
- UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
// because it is set in UpdateBlockAvailability. Some nullptr checks
@@ -1831,8 +1836,8 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
- LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight);
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
@@ -1845,7 +1850,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
+ (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -1869,9 +1874,9 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- pindex->GetBlockHash().ToString(), pfrom->GetId());
+ pindex->GetBlockHash().ToString(), pfrom.GetId());
}
if (vGetData.size() > 1) {
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
@@ -1882,7 +1887,7 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
@@ -1901,19 +1906,19 @@ bool static ProcessHeadersMessage(CNode* pfrom, CConnman* connman, ChainstateMan
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
if (IsOutboundDisconnectionCandidate(pfrom)) {
- LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
}
}
- if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) {
+ if (!pfrom.fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
// If this is an outbound full-relay peer, check to see if we should protect
// it from the bad/lagging chain logic.
// Note that block-relay-only peers are already implicitly protected, so we
// only consider setting m_protect for the full-relay peers.
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
- LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
}
@@ -1998,7 +2003,7 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_params,
+static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_params,
BlockFilterType filter_type, uint32_t start_height,
const uint256& stop_hash, uint32_t max_height_diff,
const CBlockIndex*& stop_index,
@@ -2009,8 +2014,8 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS));
if (!supported_filter_type) {
LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n",
- pfrom->GetId(), static_cast<uint8_t>(filter_type));
- pfrom->fDisconnect = true;
+ pfrom.GetId(), static_cast<uint8_t>(filter_type));
+ pfrom.fDisconnect = true;
return false;
}
@@ -2021,8 +2026,8 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
// Check that the stop block exists and the peer would be allowed to fetch it.
if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n",
- pfrom->GetId(), stop_hash.ToString());
- pfrom->fDisconnect = true;
+ pfrom.GetId(), stop_hash.ToString());
+ pfrom.fDisconnect = true;
return false;
}
}
@@ -2031,14 +2036,14 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
if (start_height > stop_height) {
LogPrint(BCLog::NET, "peer %d sent invalid getcfilters/getcfheaders with " /* Continued */
"start height %d and stop height %d\n",
- pfrom->GetId(), start_height, stop_height);
- pfrom->fDisconnect = true;
+ pfrom.GetId(), start_height, stop_height);
+ pfrom.fDisconnect = true;
return false;
}
if (stop_height - start_height >= max_height_diff) {
LogPrint(BCLog::NET, "peer %d requested too many cfilters/cfheaders: %d / %d\n",
- pfrom->GetId(), stop_height - start_height + 1, max_height_diff);
- pfrom->fDisconnect = true;
+ pfrom.GetId(), stop_height - start_height + 1, max_height_diff);
+ pfrom.fDisconnect = true;
return false;
}
@@ -2052,6 +2057,49 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
}
/**
+ * Handle a cfilters request.
+ *
+ * May disconnect from the peer in the case of a bad request.
+ *
+ * @param[in] pfrom The peer that we received the request from
+ * @param[in] vRecv The raw message received
+ * @param[in] chain_params Chain parameters
+ * @param[in] connman Pointer to the connection manager
+ */
+static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
+{
+ uint8_t filter_type_ser;
+ uint32_t start_height;
+ uint256 stop_hash;
+
+ vRecv >> filter_type_ser >> start_height >> stop_hash;
+
+ const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
+
+ const CBlockIndex* stop_index;
+ BlockFilterIndex* filter_index;
+ if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ MAX_GETCFILTERS_SIZE, stop_index, filter_index)) {
+ return;
+ }
+
+ std::vector<BlockFilter> filters;
+
+ if (!filter_index->LookupFilterRange(start_height, stop_index, filters)) {
+ LogPrint(BCLog::NET, "Failed to find block filter in index: filter_type=%s, start_height=%d, stop_hash=%s\n",
+ BlockFilterTypeName(filter_type), start_height, stop_hash.ToString());
+ return;
+ }
+
+ for (const auto& filter : filters) {
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ .Make(NetMsgType::CFILTER, filter);
+ connman.PushMessage(&pfrom, std::move(msg));
+ }
+}
+
+/**
* Handle a cfheaders request.
*
* May disconnect from the peer in the case of a bad request.
@@ -2061,8 +2109,8 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFHeaders(CNode* pfrom, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman* connman)
+static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
{
uint8_t filter_type_ser;
uint32_t start_height;
@@ -2097,13 +2145,13 @@ static void ProcessGetCFHeaders(CNode* pfrom, CDataStream& vRecv, const CChainPa
return;
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom->GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
.Make(NetMsgType::CFHEADERS,
filter_type_ser,
stop_index->GetBlockHash(),
prev_header,
filter_hashes);
- connman->PushMessage(pfrom, std::move(msg));
+ connman.PushMessage(&pfrom, std::move(msg));
}
/**
@@ -2116,8 +2164,8 @@ static void ProcessGetCFHeaders(CNode* pfrom, CDataStream& vRecv, const CChainPa
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman* connman)
+static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+ CConnman& connman)
{
uint8_t filter_type_ser;
uint256 stop_hash;
@@ -2149,17 +2197,17 @@ static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainPa
}
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom->GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
.Make(NetMsgType::CFCHECKPT,
filter_type_ser,
stop_index->GetBlockHash(),
headers);
- connman->PushMessage(pfrom, std::move(msg));
+ connman.PushMessage(&pfrom, std::move(msg));
}
-bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, ChainstateManager& chainman, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
+bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, ChainstateManager& chainman, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
{
- LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
@@ -2167,26 +2215,26 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
}
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM) &&
(msg_type == NetMsgType::FILTERLOAD ||
msg_type == NetMsgType::FILTERADD))
{
- if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+ if (pfrom.nVersion >= NO_BLOOM_VERSION) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100);
return false;
} else {
- pfrom->fDisconnect = true;
+ pfrom.fDisconnect = true;
return false;
}
}
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
- if (pfrom->nVersion != 0)
+ if (pfrom.nVersion != 0)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1);
return false;
}
@@ -2205,21 +2253,21 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
nSendVersion = std::min(nVersion, PROTOCOL_VERSION);
nServices = ServiceFlags(nServiceInt);
- if (!pfrom->fInbound)
+ if (!pfrom.fInbound)
{
- connman->SetServices(pfrom->addr, nServices);
+ connman->SetServices(pfrom.addr, nServices);
}
- if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
+ if (!pfrom.fInbound && !pfrom.fFeeler && !pfrom.m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
- LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom.GetId(), nServices, GetDesirableServiceFlags(nServices));
+ pfrom.fDisconnect = true;
return false;
}
if (nVersion < MIN_PEER_PROTO_VERSION) {
// disconnect from peers older than this proto version
- LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom.GetId(), nVersion);
+ pfrom.fDisconnect = true;
return false;
}
@@ -2236,145 +2284,145 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce))
+ if (pfrom.fInbound && !connman->CheckIncomingNonce(nNonce))
{
- LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
- pfrom->fDisconnect = true;
+ LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
+ pfrom.fDisconnect = true;
return true;
}
- if (pfrom->fInbound && addrMe.IsRoutable())
+ if (pfrom.fInbound && addrMe.IsRoutable())
{
SeenLocal(addrMe);
}
// Be shy and don't send version until we hear
- if (pfrom->fInbound)
+ if (pfrom.fInbound)
PushNodeVersion(pfrom, connman, GetAdjustedTime());
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ connman->PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
- pfrom->nServices = nServices;
- pfrom->SetAddrLocal(addrMe);
+ pfrom.nServices = nServices;
+ pfrom.SetAddrLocal(addrMe);
{
- LOCK(pfrom->cs_SubVer);
- pfrom->cleanSubVer = cleanSubVer;
+ LOCK(pfrom.cs_SubVer);
+ pfrom.cleanSubVer = cleanSubVer;
}
- pfrom->nStartingHeight = nStartingHeight;
+ pfrom.nStartingHeight = nStartingHeight;
// set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients"
- pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
+ pfrom.fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED));
// set nodes not capable of serving the complete blockchain history as "limited nodes"
- pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
+ pfrom.m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
}
// Change version
- pfrom->SetSendVersion(nSendVersion);
- pfrom->nVersion = nVersion;
+ pfrom.SetSendVersion(nSendVersion);
+ pfrom.nVersion = nVersion;
if((nServices & NODE_WITNESS))
{
LOCK(cs_main);
- State(pfrom->GetId())->fHaveWitness = true;
+ State(pfrom.GetId())->fHaveWitness = true;
}
// Potentially mark this peer as a preferred download peer.
{
LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
+ UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
+ if (!pfrom.fInbound && pfrom.IsAddrRelayPeer())
{
// Advertise our address
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
- CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
+ CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
FastRandomContext insecure_rand;
if (addr.IsRoutable())
{
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
- } else if (IsPeerAddrLocalGood(pfrom)) {
+ pfrom.PushAddress(addr, insecure_rand);
+ } else if (IsPeerAddrLocalGood(&pfrom)) {
addr.SetIP(addrMe);
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom->PushAddress(addr, insecure_rand);
+ pfrom.PushAddress(addr, insecure_rand);
}
}
// Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000)
+ if (pfrom.fOneShot || pfrom.nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000)
{
- connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
- pfrom->fGetAddr = true;
+ connman->PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ pfrom.fGetAddr = true;
}
- connman->MarkAddressGood(pfrom->addr);
+ connman->MarkAddressGood(pfrom.addr);
}
std::string remoteAddr;
if (fLogIPs)
- remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
+ remoteAddr = ", peeraddr=" + pfrom.addr.ToString();
LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
- cleanSubVer, pfrom->nVersion,
- pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(),
+ cleanSubVer, pfrom.nVersion,
+ pfrom.nStartingHeight, addrMe.ToString(), pfrom.GetId(),
remoteAddr);
int64_t nTimeOffset = nTime - GetTime();
- pfrom->nTimeOffset = nTimeOffset;
- AddTimeData(pfrom->addr, nTimeOffset);
+ pfrom.nTimeOffset = nTimeOffset;
+ AddTimeData(pfrom.addr, nTimeOffset);
// If the peer is old enough to have the old alert system, send it the final alert.
- if (pfrom->nVersion <= 70012) {
+ if (pfrom.nVersion <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ connman->PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
}
// Feeler connections exist only to verify if address is online.
- if (pfrom->fFeeler) {
- assert(pfrom->fInbound == false);
- pfrom->fDisconnect = true;
+ if (pfrom.fFeeler) {
+ assert(pfrom.fInbound == false);
+ pfrom.fDisconnect = true;
}
return true;
}
- if (pfrom->nVersion == 0) {
+ if (pfrom.nVersion == 0) {
// Must have a version message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1);
return false;
}
// At this point, the outgoing message serialization version can't change.
- const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
if (msg_type == NetMsgType::VERACK)
{
- pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
+ pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION));
- if (!pfrom->fInbound) {
+ if (!pfrom.fInbound) {
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
- State(pfrom->GetId())->fCurrentlyConnected = true;
+ State(pfrom.GetId())->fCurrentlyConnected = true;
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
- pfrom->nVersion.load(), pfrom->nStartingHeight,
- pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
- pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
+ pfrom.nVersion.load(), pfrom.nStartingHeight,
+ pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""),
+ pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay");
}
- if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ if (pfrom.nVersion >= SENDHEADERS_VERSION) {
// Tell our peer we prefer to receive headers rather than inv's
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
// nodes)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
- if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 1 or 2 cmpctblocks
// However, we do not request new block announcements using
// cmpctblock messages.
@@ -2382,19 +2430,19 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
- pfrom->fSuccessfullyConnected = true;
+ pfrom.fSuccessfullyConnected = true;
return true;
}
- if (!pfrom->fSuccessfullyConnected) {
+ if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 1);
+ Misbehaving(pfrom.GetId(), 1);
return false;
}
@@ -2403,15 +2451,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
+ if (pfrom.nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
return true;
- if (!pfrom->IsAddrRelayPeer()) {
+ if (!pfrom.IsAddrRelayPeer()) {
return true;
}
if (vAddr.size() > 1000)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
return false;
}
@@ -2432,10 +2480,10 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
- pfrom->AddAddressKnown(addr);
+ pfrom.AddAddressKnown(addr);
if (banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
bool fReachable = IsReachable(addr);
- if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
+ if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
RelayAddress(addr, fReachable, *connman);
@@ -2444,17 +2492,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (fReachable)
vAddrOk.push_back(addr);
}
- connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ connman->AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
- pfrom->fGetAddr = false;
- if (pfrom->fOneShot)
- pfrom->fDisconnect = true;
+ pfrom.fGetAddr = false;
+ if (pfrom.fOneShot)
+ pfrom.fDisconnect = true;
return true;
}
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
- State(pfrom->GetId())->fPreferHeaders = true;
+ State(pfrom.GetId())->fPreferHeaders = true;
return true;
}
@@ -2462,20 +2510,20 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom->GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
- if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) {
- State(pfrom->GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom->GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
+ if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
+ State(pfrom.GetId())->fProvidesHeaderAndIDs = true;
+ State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
}
- if (State(pfrom->GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
- State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) {
- if (pfrom->GetLocalServices() & NODE_WITNESS)
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
+ if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces
+ State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
+ if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
+ if (pfrom.GetLocalServices() & NODE_WITNESS)
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
else
- State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
+ State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1);
}
}
return true;
@@ -2487,16 +2535,16 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message inv size() = %u", vInv.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("message inv size() = %u", vInv.size()));
return false;
}
// We won't accept tx inv's if we're in blocks-only mode, or this is a
// block-relay-only peer
- bool fBlocksOnly = !g_relay_txes || (pfrom->m_tx_relay == nullptr);
+ bool fBlocksOnly = !g_relay_txes || (pfrom.m_tx_relay == nullptr);
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->HasPermission(PF_RELAY))
+ if (pfrom.HasPermission(PF_RELAY))
fBlocksOnly = false;
LOCK(cs_main);
@@ -2511,14 +2559,14 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
return true;
bool fAlreadyHave = AlreadyHave(inv, mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->GetId());
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
if (inv.type == MSG_TX) {
inv.type |= nFetchFlags;
}
if (inv.type == MSG_BLOCK) {
- UpdateBlockAvailability(pfrom->GetId(), inv.hash);
+ UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
@@ -2528,20 +2576,20 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
best_block = &inv.hash;
}
} else {
- pfrom->AddInventoryKnown(inv);
+ pfrom.AddInventoryKnown(inv);
if (fBlocksOnly) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
+ pfrom.fDisconnect = true;
return true;
} else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
- RequestTx(State(pfrom->GetId()), inv.hash, current_time);
+ RequestTx(State(pfrom.GetId()), inv.hash, current_time);
}
}
}
if (best_block != nullptr) {
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
- LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom->GetId());
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
return true;
@@ -2553,17 +2601,17 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("message getdata size() = %u", vInv.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("message getdata size() = %u", vInv.size()));
return false;
}
- LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom.GetId());
if (vInv.size() > 0) {
- LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom.GetId());
}
- pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
+ pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
return true;
}
@@ -2574,8 +2622,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
- LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "getblocks locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
return true;
}
@@ -2607,7 +2655,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (pindex)
pindex = ::ChainActive().Next(pindex);
int nLimit = 500;
- LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId());
+ LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
for (; pindex; pindex = ::ChainActive().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -2623,13 +2671,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
}
- pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ pfrom.PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll
// trigger the peer to getblocks the next batch of inventory.
LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
- pfrom->hashContinue = pindex->GetBlockHash();
+ pfrom.hashContinue = pindex->GetBlockHash();
break;
}
}
@@ -2656,7 +2704,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
return true;
}
@@ -2668,11 +2716,11 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// might maliciously send lots of getblocktxn requests to trigger
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
- inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
inv.hash = req.blockhash;
- pfrom->vRecvGetData.push_back(inv);
+ pfrom.vRecvGetData.push_back(inv);
// The message processing loop will go around again (without pausing) and we'll respond then (without cs_main)
return true;
}
@@ -2691,18 +2739,18 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> locator >> hashStop;
if (locator.vHave.size() > MAX_LOCATOR_SZ) {
- LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "getheaders locator size %lld > %d, disconnect peer=%d\n", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId());
+ pfrom.fDisconnect = true;
return true;
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_NOBAN)) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
return true;
}
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
const CBlockIndex* pindex = nullptr;
if (locator.IsNull())
{
@@ -2713,7 +2761,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
}
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
return true;
}
}
@@ -2728,7 +2776,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
- LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
for (; pindex; pindex = ::ChainActive().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
@@ -2748,7 +2796,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return true;
}
@@ -2756,10 +2804,10 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// Stop processing the transaction early if
// 1) We are in blocks only mode and peer has no relay permission
// 2) This peer is a block-relay-only peer
- if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || (pfrom->m_tx_relay == nullptr))
+ if ((!g_relay_txes && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr))
{
- LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
return true;
}
@@ -2768,13 +2816,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
const CTransaction& tx = *ptx;
CInv inv(MSG_TX, tx.GetHash());
- pfrom->AddInventoryKnown(inv);
+ pfrom.AddInventoryKnown(inv);
LOCK2(cs_main, g_cs_orphans);
TxValidationState state;
- CNodeState* nodestate = State(pfrom->GetId());
+ CNodeState* nodestate = State(pfrom.GetId());
nodestate->m_tx_download.m_tx_announced.erase(inv.hash);
nodestate->m_tx_download.m_tx_in_flight.erase(inv.hash);
EraseTxRequest(inv.hash);
@@ -2789,20 +2837,20 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
- pfrom->orphan_work_set.insert(elem->first);
+ pfrom.orphan_work_set.insert(elem->first);
}
}
}
- pfrom->nLastTXTime = GetTime();
+ pfrom.nLastTXTime = GetTime();
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
- pfrom->GetId(),
+ pfrom.GetId(),
tx.GetHash().ToString(),
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(connman, mempool, pfrom->orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(connman, mempool, pfrom.orphan_work_set, lRemovedTxn);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -2819,10 +2867,10 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
for (const CTxIn& txin : tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
- pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom->GetId()), _inv.hash, current_time);
+ pfrom.AddInventoryKnown(_inv);
+ if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom.GetId()), _inv.hash, current_time);
}
- AddOrphanTx(ptx, pfrom->GetId());
+ AddOrphanTx(ptx, pfrom.GetId());
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
@@ -2850,15 +2898,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->HasPermission(PF_FORCERELAY)) {
+ if (pfrom.HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
// if they were already in the mempool,
// allowing the node to function as a gateway for
// nodes hidden behind it.
if (!mempool.exists(tx.GetHash())) {
- LogPrintf("Not relaying non-mempool transaction %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
+ LogPrintf("Not relaying non-mempool transaction %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
- LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
RelayTransaction(tx.GetHash(), *connman);
}
}
@@ -2887,9 +2935,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (state.IsInvalid())
{
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
- pfrom->GetId(),
+ pfrom.GetId(),
state.ToString());
- MaybePunishNodeForTx(pfrom->GetId(), state);
+ MaybePunishNodeForTx(pfrom.GetId(), state);
}
return true;
}
@@ -2898,7 +2946,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// Ignore cmpctblock received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom.GetId());
return true;
}
@@ -2913,7 +2961,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!::ChainstateActive().IsInitialBlockDownload())
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return true;
}
@@ -2926,7 +2974,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
BlockValidationState state;
if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (state.IsInvalid()) {
- MaybePunishNodeForBlock(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
+ MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return true;
}
}
@@ -2951,9 +2999,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
LOCK2(cs_main, g_cs_orphans);
// If AcceptBlockHeader returned true, it set pindex
assert(pindex);
- UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), pindex->GetBlockHash());
- CNodeState *nodestate = State(pfrom->GetId());
+ CNodeState *nodestate = State(pfrom.GetId());
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
@@ -2974,7 +3022,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
return true;
}
@@ -2993,9 +3041,9 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// possibilities in compact block processing...
if (pindex->nHeight <= ::ChainActive().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
- (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) {
+ (fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(mempool, pfrom->GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
else {
@@ -3009,13 +3057,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom->GetId()));
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom.GetId()));
return true;
} else if (status == READ_STATUS_FAILED) {
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return true;
}
@@ -3032,7 +3080,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -3058,7 +3106,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return true;
} else {
// If this was an announce-cmpctblock, we want the same treatment as a header message
@@ -3084,7 +3132,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// block that is in flight from some other peer.
{
LOCK(cs_main);
- mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
}
bool fNewBlock = false;
// Setting fForceProcessing to true means that we bypass some of
@@ -3098,7 +3146,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// reconstructed compact blocks as having been requested.
chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
@@ -3119,7 +3167,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// Ignore blocktxn received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom.GetId());
return true;
}
@@ -3133,8 +3181,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
- it->second.first != pfrom->GetId()) {
- LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->GetId());
+ it->second.first != pfrom.GetId()) {
+ LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
return true;
}
@@ -3142,13 +3190,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId()));
+ Misbehaving(pfrom.GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom.GetId()));
return true;
} else if (status == READ_STATUS_FAILED) {
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -3175,7 +3223,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// BIP 152 permits peers to relay compact blocks after validating
// the header only; we should not punish peers if the block turns
// out to be invalid.
- mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false));
+ mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom.GetId(), false));
}
} // Don't hold cs_main when we call into ProcessNewBlock
if (fBlockRead) {
@@ -3188,7 +3236,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// in compact block optimistic reconstruction handling.
chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
@@ -3201,7 +3249,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// Ignore headers received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom.GetId());
return true;
}
@@ -3211,7 +3259,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 20, strprintf("headers message size = %u", nCount));
+ Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
return false;
}
headers.resize(nCount);
@@ -3227,14 +3275,14 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// Ignore block received while importing
if (fImporting || fReindex) {
- LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom->GetId());
+ LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom.GetId());
return true;
}
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
- LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom.GetId());
bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
@@ -3246,12 +3294,12 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
- mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
+ mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
- pfrom->nLastBlockTime = GetTime();
+ pfrom.nLastBlockTime = GetTime();
} else {
LOCK(cs_main);
mapBlockSource.erase(pblock->GetHash());
@@ -3265,64 +3313,64 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
- if (!pfrom->fInbound) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
+ if (!pfrom.fInbound) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
return true;
}
- if (!pfrom->IsAddrRelayPeer()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom->GetId());
+ if (!pfrom.IsAddrRelayPeer()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
return true;
}
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
- if (pfrom->fSentAddr) {
- LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->GetId());
+ if (pfrom.fSentAddr) {
+ LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom.GetId());
return true;
}
- pfrom->fSentAddr = true;
+ pfrom.fSentAddr = true;
- pfrom->vAddrToSend.clear();
+ pfrom.vAddrToSend.clear();
std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
if (!banman->IsBanned(addr)) {
- pfrom->PushAddress(addr, insecure_rand);
+ pfrom.PushAddress(addr, insecure_rand);
}
}
return true;
}
if (msg_type == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
return true;
}
- if (connman->OutboundTargetReached(false) && !pfrom->HasPermission(PF_MEMPOOL))
+ if (connman->OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
{
- if (!pfrom->HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(PF_NOBAN))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
}
return true;
}
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_tx_inventory);
- pfrom->m_tx_relay->fSendMempool = true;
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_tx_inventory);
+ pfrom.m_tx_relay->fSendMempool = true;
}
return true;
}
if (msg_type == NetMsgType::PING) {
- if (pfrom->nVersion > BIP0031_VERSION)
+ if (pfrom.nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
vRecv >> nonce;
@@ -3337,7 +3385,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
return true;
}
@@ -3353,15 +3401,15 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom->nPingNonceSent != 0) {
- if (nonce == pfrom->nPingNonceSent) {
+ if (pfrom.nPingNonceSent != 0) {
+ if (nonce == pfrom.nPingNonceSent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
- int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
+ int64_t pingUsecTime = pingUsecEnd - pfrom.nPingUsecStart;
if (pingUsecTime > 0) {
// Successful ping time measurement, replace previous
- pfrom->nPingUsecTime = pingUsecTime;
- pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime);
+ pfrom.nPingUsecTime = pingUsecTime;
+ pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), pingUsecTime);
} else {
// This should never happen
sProblem = "Timing mishap";
@@ -3386,14 +3434,14 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
if (!(sProblem.empty())) {
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
- pfrom->GetId(),
+ pfrom.GetId(),
sProblem,
- pfrom->nPingNonceSent,
+ pfrom.nPingNonceSent,
nonce,
nAvail);
}
if (bPingFinished) {
- pfrom->nPingNonceSent = 0;
+ pfrom.nPingNonceSent = 0;
}
return true;
}
@@ -3406,13 +3454,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
{
// There is no excuse for sending a too-large filter
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100);
}
- else if (pfrom->m_tx_relay != nullptr)
+ else if (pfrom.m_tx_relay != nullptr)
{
- LOCK(pfrom->m_tx_relay->cs_filter);
- pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
- pfrom->m_tx_relay->fRelayTxes = true;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ pfrom.m_tx_relay->pfilter.reset(new CBloomFilter(filter));
+ pfrom.m_tx_relay->fRelayTxes = true;
}
return true;
}
@@ -3426,30 +3474,30 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
- } else if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->m_tx_relay->pfilter) {
- pfrom->m_tx_relay->pfilter->insert(vData);
+ } else if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.m_tx_relay->pfilter) {
+ pfrom.m_tx_relay->pfilter->insert(vData);
} else {
bad = true;
}
}
if (bad) {
LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ Misbehaving(pfrom.GetId(), 100);
}
return true;
}
if (msg_type == NetMsgType::FILTERCLEAR) {
- if (pfrom->m_tx_relay == nullptr) {
+ if (pfrom.m_tx_relay == nullptr) {
return true;
}
- LOCK(pfrom->m_tx_relay->cs_filter);
- if (pfrom->GetLocalServices() & NODE_BLOOM) {
- pfrom->m_tx_relay->pfilter = nullptr;
+ LOCK(pfrom.m_tx_relay->cs_filter);
+ if (pfrom.GetLocalServices() & NODE_BLOOM) {
+ pfrom.m_tx_relay->pfilter = nullptr;
}
- pfrom->m_tx_relay->fRelayTxes = true;
+ pfrom.m_tx_relay->fRelayTxes = true;
return true;
}
@@ -3457,29 +3505,34 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
- if (pfrom->m_tx_relay != nullptr) {
- LOCK(pfrom->m_tx_relay->cs_feeFilter);
- pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
+ if (pfrom.m_tx_relay != nullptr) {
+ LOCK(pfrom.m_tx_relay->cs_feeFilter);
+ pfrom.m_tx_relay->minFeeFilter = newFeeFilter;
}
- LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId());
}
return true;
}
+ if (msg_type == NetMsgType::GETCFILTERS) {
+ ProcessGetCFilters(pfrom, vRecv, chainparams, *connman);
+ return true;
+ }
+
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFHeaders(pfrom, vRecv, chainparams, *connman);
return true;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFCheckPt(pfrom, vRecv, chainparams, *connman);
return true;
}
if (msg_type == NetMsgType::NOTFOUND) {
// Remove the NOTFOUND transactions from the peer
LOCK(cs_main);
- CNodeState *state = State(pfrom->GetId());
+ CNodeState *state = State(pfrom.GetId());
std::vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
@@ -3502,31 +3555,31 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
}
// Ignore unknown commands for extensibility
- LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom->GetId());
+ LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
return true;
}
-bool PeerLogicValidation::CheckIfBanned(CNode* pnode)
+bool PeerLogicValidation::CheckIfBanned(CNode& pnode)
{
AssertLockHeld(cs_main);
- CNodeState &state = *State(pnode->GetId());
+ CNodeState &state = *State(pnode.GetId());
if (state.fShouldBan) {
state.fShouldBan = false;
- if (pnode->HasPermission(PF_NOBAN))
- LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
- else if (pnode->m_manual_connection)
- LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
- else if (pnode->addr.IsLocal()) {
+ if (pnode.HasPermission(PF_NOBAN))
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode.addr.ToString());
+ else if (pnode.m_manual_connection)
+ LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode.addr.ToString());
+ else if (pnode.addr.IsLocal()) {
// Disconnect but don't ban _this_ local node
- LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode->addr.ToString());
- pnode->fDisconnect = true;
+ LogPrintf("Warning: disconnecting but not banning local peer %s!\n", pnode.addr.ToString());
+ pnode.fDisconnect = true;
} else {
// Disconnect and ban all nodes sharing the address
if (m_banman) {
- m_banman->Ban(pnode->addr, BanReasonNodeMisbehaving);
+ m_banman->Ban(pnode.addr, BanReasonNodeMisbehaving);
}
- connman->DisconnectNode(pnode->addr);
+ connman->DisconnectNode(pnode.addr);
}
return true;
}
@@ -3547,7 +3600,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fMoreWork = false;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(pfrom, chainparams, connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, chainparams, connman, m_mempool, interruptMsgProc);
if (!pfrom->orphan_work_set.empty()) {
std::list<CTransactionRef> removed_txn;
@@ -3615,7 +3668,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, connman, m_banman, interruptMsgProc);
+ fRet = ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, connman, m_banman, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3631,17 +3684,17 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
- CheckIfBanned(pfrom);
+ CheckIfBanned(*pfrom);
return fMoreWork;
}
-void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
+void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
{
AssertLockHeld(cs_main);
- CNodeState &state = *State(pto->GetId());
- const CNetMsgMaker msgMaker(pto->GetSendVersion());
+ CNodeState &state = *State(pto.GetId());
+ const CNetMsgMaker msgMaker(pto.GetSendVersion());
if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) {
// This is an outbound peer subject to disconnection if they don't
@@ -3670,12 +3723,12 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
// message to give the peer a chance to update us.
if (state.m_chain_sync.m_sent_getheaders) {
// They've run out of time to catch up!
- LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
- pto->fDisconnect = true;
+ LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>");
+ pto.fDisconnect = true;
} else {
assert(state.m_chain_sync.m_work_header);
- LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
+ connman->PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -3705,7 +3758,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
AssertLockHeld(cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
- if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
+ if (!IsOutboundDisconnectionCandidate(*pnode) || pnode->fDisconnect) return;
CNodeState *state = State(pnode->GetId());
if (state == nullptr) return; // shouldn't be possible, but just in case
// Don't evict our protected peers
@@ -3834,7 +3887,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
- if (CheckIfBanned(pto)) return true;
+ if (CheckIfBanned(*pto)) return true;
CNodeState &state = *State(pto->GetId());
@@ -4245,7 +4298,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Check that outbound peers have reasonable chains
// GetTime() is used by this anti-DoS logic so we can test this using mocktime
- ConsiderEviction(pto, GetTime());
+ ConsiderEviction(*pto, GetTime());
//
// Message: getdata (blocks)
@@ -4256,7 +4309,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
for (const CBlockIndex *pindex : vToDownload) {
- uint32_t nFetchFlags = GetFetchFlags(pto);
+ uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
MarkBlockAsInFlight(m_mempool, pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
@@ -4300,7 +4353,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Erase this entry from tx_process_time (it may be added back for
// processing at a later time, see below)
tx_process_time.erase(tx_process_time.begin());
- CInv inv(MSG_TX | GetFetchFlags(pto), txid);
+ CInv inv(MSG_TX | GetFetchFlags(*pto), txid);
if (!AlreadyHave(inv, m_mempool)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
diff --git a/src/net_processing.h b/src/net_processing.h
index ec758c7537..19beca0cc4 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -31,7 +31,7 @@ private:
ChainstateManager& m_chainman;
CTxMemPool& m_mempool;
- bool CheckIfBanned(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool CheckIfBanned(CNode& pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
public:
PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
@@ -74,7 +74,7 @@ public:
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode *pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
diff --git a/src/netbase.cpp b/src/netbase.cpp
index a70179cb16..9fe03c6a24 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -28,9 +28,9 @@
#endif
// Settings
-static RecursiveMutex cs_proxyInfos;
-static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos);
-static proxyType nameProxy GUARDED_BY(cs_proxyInfos);
+static Mutex g_proxyinfo_mutex;
+static proxyType proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
+static proxyType nameProxy GUARDED_BY(g_proxyinfo_mutex);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
@@ -711,14 +711,14 @@ bool SetProxy(enum Network net, const proxyType &addrProxy) {
assert(net >= 0 && net < NET_MAX);
if (!addrProxy.IsValid())
return false;
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
proxyInfo[net] = addrProxy;
return true;
}
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
assert(net >= 0 && net < NET_MAX);
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
if (!proxyInfo[net].IsValid())
return false;
proxyInfoOut = proxyInfo[net];
@@ -744,13 +744,13 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
bool SetNameProxy(const proxyType &addrProxy) {
if (!addrProxy.IsValid())
return false;
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
nameProxy = addrProxy;
return true;
}
bool GetNameProxy(proxyType &nameProxyOut) {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
if(!nameProxy.IsValid())
return false;
nameProxyOut = nameProxy;
@@ -758,12 +758,12 @@ bool GetNameProxy(proxyType &nameProxyOut) {
}
bool HaveNameProxy() {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
return nameProxy.IsValid();
}
bool IsProxy(const CNetAddr &addr) {
- LOCK(cs_proxyInfos);
+ LOCK(g_proxyinfo_mutex);
for (int i = 0; i < NET_MAX; i++) {
if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy))
return true;
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 243111c449..2dfe4bee74 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -5,8 +5,8 @@
#include <protocol.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#ifndef WIN32
# include <arpa/inet.h>
@@ -40,6 +40,8 @@ const char *SENDCMPCT="sendcmpct";
const char *CMPCTBLOCK="cmpctblock";
const char *GETBLOCKTXN="getblocktxn";
const char *BLOCKTXN="blocktxn";
+const char *GETCFILTERS="getcfilters";
+const char *CFILTER="cfilter";
const char *GETCFHEADERS="getcfheaders";
const char *CFHEADERS="cfheaders";
const char *GETCFCHECKPT="getcfcheckpt";
@@ -75,6 +77,8 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::CMPCTBLOCK,
NetMsgType::GETBLOCKTXN,
NetMsgType::BLOCKTXN,
+ NetMsgType::GETCFILTERS,
+ NetMsgType::CFILTER,
NetMsgType::GETCFHEADERS,
NetMsgType::CFHEADERS,
NetMsgType::GETCFCHECKPT,
@@ -194,3 +198,42 @@ const std::vector<std::string> &getAllNetMessageTypes()
{
return allNetMessageTypesVec;
}
+
+/**
+ * Convert a service flag (NODE_*) to a human readable string.
+ * It supports unknown service flags which will be returned as "UNKNOWN[...]".
+ * @param[in] bit the service flag is calculated as (1 << bit)
+ */
+static std::string serviceFlagToStr(size_t bit)
+{
+ const uint64_t service_flag = 1ULL << bit;
+ switch ((ServiceFlags)service_flag) {
+ case NODE_NONE: abort(); // impossible
+ case NODE_NETWORK: return "NETWORK";
+ case NODE_GETUTXO: return "GETUTXO";
+ case NODE_BLOOM: return "BLOOM";
+ case NODE_WITNESS: return "WITNESS";
+ case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
+ // Not using default, so we get warned when a case is missing
+ }
+
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
+ stream << "UNKNOWN[";
+ stream << "2^" << bit;
+ stream << "]";
+ return stream.str();
+}
+
+std::vector<std::string> serviceFlagsToStr(uint64_t flags)
+{
+ std::vector<std::string> str_flags;
+
+ for (size_t i = 0; i < sizeof(flags) * 8; ++i) {
+ if (flags & (1ULL << i)) {
+ str_flags.emplace_back(serviceFlagToStr(i));
+ }
+ }
+
+ return str_flags;
+}
diff --git a/src/protocol.h b/src/protocol.h
index 9527dce960..985f44640b 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -226,6 +226,17 @@ extern const char* GETBLOCKTXN;
*/
extern const char* BLOCKTXN;
/**
+ * getcfilters requests compact filters for a range of blocks.
+ * Only available with service bit NODE_COMPACT_FILTERS as described by
+ * BIP 157 & 158.
+ */
+extern const char* GETCFILTERS;
+/**
+ * cfilter is a response to a getcfilters request containing a single compact
+ * filter.
+ */
+extern const char* CFILTER;
+/**
* getcfheaders requests a compact filter header and the filter hashes for a
* range of blocks, which can then be used to reconstruct the filter headers
* for those blocks.
@@ -257,7 +268,7 @@ const std::vector<std::string>& getAllNetMessageTypes();
/** nServices flags */
enum ServiceFlags : uint64_t {
- // NOTE: When adding here, be sure to update qt/guiutil.cpp's formatServicesStr too
+ // NOTE: When adding here, be sure to update serviceFlagToStr too
// Nothing
NODE_NONE = 0,
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
@@ -289,6 +300,13 @@ enum ServiceFlags : uint64_t {
};
/**
+ * Convert service flags (a bitmask of NODE_*) to human readable strings.
+ * It supports unknown service flags which will be returned as "UNKNOWN[...]".
+ * @param[in] flags multiple NODE_* bitwise-OR-ed together
+ */
+std::vector<std::string> serviceFlagsToStr(uint64_t flags);
+
+/**
* Gets the set of service flags which are "desirable" for a given peer.
*
* These are the flags which are required for a peer to support for them
diff --git a/src/psbt.h b/src/psbt.h
index af57994f3a..888e0fd119 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -41,7 +41,7 @@ static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
static constexpr uint8_t PSBT_SEPARATOR = 0x00;
// BIP 174 does not specify a maximum file size, but we set a limit anyway
-// to prevent reading a stream indefinately and running out of memory.
+// to prevent reading a stream indefinitely and running out of memory.
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MiB
/** A structure for PSBTs which contain per-input information */
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 0d3b08fe7d..aa4ec04497 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -106,7 +106,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
ui->newAddress->setVisible(true);
break;
case ReceivingTab:
- ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses."));
+ ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.\nSigning is only possible with addresses of the type 'legacy'."));
ui->deleteAddress->setVisible(false);
ui->newAddress->setVisible(false);
break;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 8110f4e895..665c8e6053 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -11,6 +11,7 @@
#include <wallet/wallet.h>
#include <algorithm>
+#include <typeinfo>
#include <QFont>
#include <QDebug>
@@ -75,12 +76,14 @@ public:
explicit AddressTablePriv(AddressTableModel *_parent):
parent(_parent) {}
- void refreshAddressTable(interfaces::Wallet& wallet)
+ void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)
{
cachedAddressTable.clear();
{
for (const auto& address : wallet.getAddresses())
{
+ if (pk_hash_only && address.dest.type() != typeid(PKHash))
+ continue;
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(address.purpose), address.is_mine);
cachedAddressTable.append(AddressTableEntry(addressType,
@@ -159,12 +162,12 @@ public:
}
};
-AddressTableModel::AddressTableModel(WalletModel *parent) :
+AddressTableModel::AddressTableModel(WalletModel *parent, bool pk_hash_only) :
QAbstractTableModel(parent), walletModel(parent)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(this);
- priv->refreshAddressTable(parent->wallet());
+ priv->refreshAddressTable(parent->wallet(), pk_hash_only);
}
AddressTableModel::~AddressTableModel()
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index 97f673caf1..73316cadc4 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -25,7 +25,7 @@ class AddressTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit AddressTableModel(WalletModel *parent = nullptr);
+ explicit AddressTableModel(WalletModel *parent = nullptr, bool pk_hash_only = false);
~AddressTableModel();
enum ColumnIndex {
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 6fdf6322ff..e0b9345a32 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -33,6 +33,7 @@
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
+#include <util/translation.h>
#include <util/threadnames.h>
#include <validation.h>
@@ -65,6 +66,23 @@ Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(SynchronizationState)
Q_DECLARE_METATYPE(uint256)
+static void RegisterMetaTypes()
+{
+ // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
+ qRegisterMetaType<bool*>();
+ qRegisterMetaType<SynchronizationState>();
+ #ifdef ENABLE_WALLET
+ qRegisterMetaType<WalletModel*>();
+ #endif
+ // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
+ // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
+ qRegisterMetaType<CAmount>("CAmount");
+ qRegisterMetaType<size_t>("size_t");
+
+ qRegisterMetaType<std::function<void()>>("std::function<void()>");
+ qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
+}
+
static QString GetLangTerritory()
{
QSettings settings;
@@ -137,7 +155,7 @@ BitcoinCore::BitcoinCore(interfaces::Node& node) :
void BitcoinCore::handleRunawayException(const std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings()));
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
}
void BitcoinCore::initialize()
@@ -184,6 +202,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node):
returnValue(0),
platformStyle(nullptr)
{
+ RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
@@ -212,8 +231,6 @@ BitcoinApplication::~BitcoinApplication()
delete window;
window = nullptr;
- delete optionsModel;
- optionsModel = nullptr;
delete platformStyle;
platformStyle = nullptr;
}
@@ -227,7 +244,7 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(m_node, nullptr, resetSettings);
+ optionsModel = new OptionsModel(m_node, this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -383,7 +400,7 @@ void BitcoinApplication::shutdownResult()
void BitcoinApplication::handleRunawayException(const QString &message)
{
- QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("\n\n") + message);
+ QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("<br><br>") + message);
::exit(EXIT_FAILURE);
}
@@ -435,27 +452,13 @@ int GuiMain(int argc, char* argv[])
BitcoinApplication app(*node);
- // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
- qRegisterMetaType<bool*>();
- qRegisterMetaType<SynchronizationState>();
-#ifdef ENABLE_WALLET
- qRegisterMetaType<WalletModel*>();
-#endif
- // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
- // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
- qRegisterMetaType<CAmount>("CAmount");
- qRegisterMetaType<size_t>("size_t");
-
- qRegisterMetaType<std::function<void()>>("std::function<void()>");
- qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
-
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
node->setupServerArgs();
SetupUIArgs();
std::string error;
if (!node->parseParameters(argc, argv, error)) {
- node->initError(strprintf("Error parsing command line arguments: %s\n", error));
+ node->initError(strprintf(Untranslated("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
@@ -496,13 +499,13 @@ int GuiMain(int argc, char* argv[])
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!CheckDataDirOption()) {
- node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
+ node->initError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
if (!node->readConfigFiles(error)) {
- node->initError(strprintf("Error reading configuration file: %s\n", error));
+ node->initError(strprintf(Untranslated("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;
@@ -518,7 +521,7 @@ int GuiMain(int argc, char* argv[])
try {
node->selectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- node->initError(strprintf("%s\n", e.what()));
+ node->initError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
@@ -596,10 +599,10 @@ int GuiMain(int argc, char* argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
}
return rv;
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 2090c233ac..195d80d47c 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -350,10 +350,18 @@ void BitcoinGUI::createActions()
m_create_wallet_action->setEnabled(false);
m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
+ m_close_all_wallets_action = new QAction(tr("Close All Wallets..."), this);
+ m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
+
showHelpMessageAction = new QAction(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(PACKAGE_NAME));
+ m_mask_values_action = new QAction(tr("&Mask values"), this);
+ m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
+ m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
+ m_mask_values_action->setCheckable(true);
+
connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
@@ -416,6 +424,10 @@ void BitcoinGUI::createActions()
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
+ connect(m_close_all_wallets_action, &QAction::triggered, [this] {
+ m_wallet_controller->closeAllWallets(this);
+ });
+ connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
}
#endif // ENABLE_WALLET
@@ -440,6 +452,7 @@ void BitcoinGUI::createMenuBar()
file->addAction(m_create_wallet_action);
file->addAction(m_open_wallet_action);
file->addAction(m_close_wallet_action);
+ file->addAction(m_close_all_wallets_action);
file->addSeparator();
file->addAction(openAction);
file->addAction(backupWalletAction);
@@ -456,6 +469,8 @@ void BitcoinGUI::createMenuBar()
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
+ settings->addAction(m_mask_values_action);
+ settings->addSeparator();
}
settings->addAction(optionsAction);
@@ -718,6 +733,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
m_close_wallet_action->setEnabled(enabled);
+ m_close_all_wallets_action->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon()
@@ -1414,6 +1430,12 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
m_handler_question->disconnect();
}
+bool BitcoinGUI::isPrivacyModeActivated() const
+{
+ assert(m_mask_values_action);
+ return m_mask_values_action->isChecked();
+}
+
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
optionsModel(nullptr),
menu(nullptr)
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 82a2db9ba2..b009e279b6 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -99,6 +99,8 @@ public:
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
+ bool isPrivacyModeActivated() const;
+
protected:
void changeEvent(QEvent *e) override;
void closeEvent(QCloseEvent *event) override;
@@ -153,8 +155,10 @@ private:
QAction* m_open_wallet_action{nullptr};
QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
+ QAction* m_close_all_wallets_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
+ QAction* m_mask_values_action{nullptr};
QLabel *m_wallet_selector_label = nullptr;
QComboBox* m_wallet_selector = nullptr;
@@ -207,6 +211,7 @@ Q_SIGNALS:
void receivedURI(const QString &uri);
/** Signal raised when RPC console shown */
void consoleShown(RPCConsole* console);
+ void setPrivacy(bool privacy);
public Q_SLOTS:
/** Set number of connections shown in the UI */
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index d9711af123..318a6dcbfd 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -6,6 +6,8 @@
#include <QStringList>
+#include <cassert>
+
BitcoinUnits::BitcoinUnits(QObject *parent):
QAbstractListModel(parent),
unitlist(availableUnits())
@@ -94,7 +96,7 @@ int BitcoinUnits::decimals(int unit)
}
}
-QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators)
+QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
@@ -106,6 +108,7 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator
qint64 n_abs = (n > 0 ? n : -n);
qint64 quotient = n_abs / coin;
QString quotient_str = QString::number(quotient);
+ if (justify) quotient_str = quotient_str.rightJustified(16 - num_decimals, ' ');
// Use SI-style thin space separators as these are locale independent and can't be
// confused with the decimal marker.
@@ -150,6 +153,17 @@ QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool p
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
+QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
+{
+ assert(amount >= 0);
+ QString value;
+ if (privacy) {
+ value = format(unit, 0, false, separators, true).replace('0', '#');
+ } else {
+ value = format(unit, amount, false, separators, true);
+ }
+ return value + QString(" ") + shortName(unit);
+}
bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
{
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 1ff4702117..dac5484393 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -72,11 +72,13 @@ public:
//! Number of decimals left
static int decimals(int unit);
//! Format as string
- static QString format(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = separatorStandard, bool justify = false);
//! Format as string (with unit)
static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Format as HTML string (with unit)
static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ //! Format as string (with unit) of fixed length to preserve privacy, if it is set.
+ static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, CAmount *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 159b0d3df3..7822d4c5f3 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -114,6 +114,29 @@ int ClientModel::getNumBlocks() const
return m_cached_num_blocks;
}
+uint256 ClientModel::getBestBlockHash()
+{
+ uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
+
+ if (!tip.IsNull()) {
+ return tip;
+ }
+
+ // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
+ // The following will lock `cs_main` (and release it), so we must not
+ // own `m_cached_tip_mutex` here.
+ tip = m_node.getBestBlockHash();
+
+ LOCK(m_cached_tip_mutex);
+ // We checked that `m_cached_tip_blocks` is not null above, but then we
+ // released the mutex `m_cached_tip_mutex`, so it could have changed in the
+ // meantime. Thus, check again.
+ if (m_cached_tip_blocks.IsNull()) {
+ m_cached_tip_blocks = tip;
+ }
+ return m_cached_tip_blocks;
+}
+
void ClientModel::updateNumConnections(int numConnections)
{
Q_EMIT numConnectionsChanged(numConnections);
@@ -143,7 +166,7 @@ enum BlockSource ClientModel::getBlockSource() const
QString ClientModel::getStatusBarWarnings() const
{
- return QString::fromStdString(m_node.getWarnings());
+ return QString::fromStdString(m_node.getWarnings().translated);
}
OptionsModel *ClientModel::getOptionsModel()
@@ -235,14 +258,15 @@ static void BannedListChanged(ClientModel *clientmodel)
assert(invoked);
}
-static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, int height, int64_t blockTime, double verificationProgress, bool fHeader)
+static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{
if (fHeader) {
// cache best headers time and height to reduce future cs_main locks
- clientmodel->cachedBestHeaderHeight = height;
- clientmodel->cachedBestHeaderTime = blockTime;
+ clientmodel->cachedBestHeaderHeight = tip.block_height;
+ clientmodel->cachedBestHeaderTime = tip.block_time;
} else {
- clientmodel->m_cached_num_blocks = height;
+ clientmodel->m_cached_num_blocks = tip.block_height;
+ WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
}
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
@@ -254,8 +278,8 @@ static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_
}
bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
- Q_ARG(int, height),
- Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)),
+ Q_ARG(int, tip.block_height),
+ Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
Q_ARG(double, verificationProgress),
Q_ARG(bool, fHeader),
Q_ARG(SynchronizationState, sync_state));
@@ -271,8 +295,8 @@ void ClientModel::subscribeToCoreSignals()
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
- m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, false));
- m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, true));
+ m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
+ m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
}
void ClientModel::unsubscribeFromCoreSignals()
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index ace77f5972..7f12cce1d9 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -10,6 +10,8 @@
#include <atomic>
#include <memory>
+#include <sync.h>
+#include <uint256.h>
class BanTableModel;
class CBlockIndex;
@@ -57,6 +59,7 @@ public:
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
+ uint256 getBestBlockHash();
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
@@ -74,11 +77,14 @@ public:
bool getProxyInfo(std::string& ip_port) const;
- // caches for the best header, number of blocks
+ // caches for the best header: hash, number of blocks and block time
mutable std::atomic<int> cachedBestHeaderHeight;
mutable std::atomic<int64_t> cachedBestHeaderTime;
mutable std::atomic<int> m_cached_num_blocks{-1};
+ Mutex m_cached_tip_mutex;
+ uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
+
private:
interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index 710801ee96..4d3f90c484 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>596</width>
- <height>342</height>
+ <width>798</width>
+ <height>318</height>
</rect>
</property>
<property name="windowTitle">
@@ -118,6 +118,7 @@
<widget class="QLabel" name="labelWatchPending">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -129,7 +130,7 @@
<string>Unconfirmed transactions to watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -143,6 +144,7 @@
<widget class="QLabel" name="labelUnconfirmed">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -154,7 +156,7 @@
<string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -168,6 +170,7 @@
<widget class="QLabel" name="labelWatchImmature">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -179,7 +182,7 @@
<string>Mined balance in watch-only addresses that has not yet matured</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -226,6 +229,7 @@
<widget class="QLabel" name="labelImmature">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -237,7 +241,7 @@
<string>Mined balance that has not yet matured</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">0.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -271,6 +275,7 @@
<widget class="QLabel" name="labelTotal">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -282,7 +287,7 @@
<string>Your current total balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -296,6 +301,7 @@
<widget class="QLabel" name="labelWatchTotal">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -307,7 +313,7 @@
<string>Current total balance in watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -338,6 +344,7 @@
<widget class="QLabel" name="labelBalance">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -349,7 +356,7 @@
<string>Your current spendable balance</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@@ -363,6 +370,7 @@
<widget class="QLabel" name="labelWatchAvailable">
<property name="font">
<font>
+ <family>Monospace</family>
<weight>75</weight>
<bold>true</bold>
</font>
@@ -374,7 +382,7 @@
<string>Your current balance in watch-only addresses</string>
</property>
<property name="text">
- <string notr="true">0.000 000 00 BTC</string>
+ <string notr="true">21 000 000.00000000 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index 9f896ee3b1..f6d4723465 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -6,68 +6,233 @@
<rect>
<x>0</x>
<y>0</y>
- <width>487</width>
- <height>597</height>
+ <width>413</width>
+ <height>229</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QRImageWidget" name="lblQRCode">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>320</height>
- </size>
- </property>
- <property name="toolTip">
- <string>QR Code</string>
+ <property name="windowTitle">
+ <string>Request payment to ...</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item row="0" column="0" colspan="2" alignment="Qt::AlignHCenter">
+ <widget class="QRImageWidget" name="qr_code">
+ <property name="text">
+ <string notr="true">QR image</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="payment_header">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Payment information</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="uri_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true">URI:</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="uri_content">
+ <property name="text">
+ <string notr="true">bitcoin:BC1...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="address_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Address:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
</widget>
</item>
- <item>
- <widget class="QTextEdit" name="outUri">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>50</height>
- </size>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Plain</enum>
- </property>
- <property name="tabChangesFocus">
+ <item row="3" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="address_content">
+ <property name="text">
+ <string notr="true">bc1...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="amount_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Amount:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="amount_content">
+ <property name="text">
+ <string notr="true">0.00000000 BTC</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="label_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Label:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="label_content">
+ <property name="text">
+ <string notr="true">label content</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="message_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="message_content">
+ <property name="text">
+ <string notr="true">message content</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
+ <widget class="QLabel" name="wallet_tag">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Wallet:</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1" alignment="Qt::AlignTop">
+ <widget class="QLabel" name="wallet_content">
+ <property name="text">
+ <string notr="true">wallet name</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
- <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ <set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
- <item>
+ <item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnCopyURI">
@@ -114,8 +279,11 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
<property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
+ <set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@@ -130,37 +298,27 @@
<header>qt/qrimagewidget.h</header>
</customwidget>
</customwidgets>
+ <tabstops>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>btnCopyURI</tabstop>
+ <tabstop>btnCopyAddress</tabstop>
+ <tabstop>btnSaveAs</tabstop>
+ </tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ReceiveRequestDialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>452</x>
- <y>573</y>
- </hint>
- <hint type="destinationlabel">
- <x>243</x>
- <y>298</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ReceiveRequestDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
- <x>452</x>
- <y>573</y>
+ <x>135</x>
+ <y>230</y>
</hint>
<hint type="destinationlabel">
- <x>243</x>
- <y>298</y>
+ <x>135</x>
+ <y>126</y>
</hint>
</hints>
</connection>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index af86fe5d27..ce44d4f3a5 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -48,13 +48,14 @@
#include <QKeyEvent>
#include <QLineEdit>
#include <QList>
+#include <QMenu>
#include <QMouseEvent>
#include <QProgressDialog>
#include <QScreen>
#include <QSettings>
+#include <QShortcut>
#include <QSize>
#include <QString>
-#include <QShortcut>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
#include <QUrlQuery>
@@ -231,7 +232,7 @@ QString HtmlEscape(const std::string& str, bool fMultiLine)
return HtmlEscape(QString::fromStdString(str), fMultiLine);
}
-void copyEntryData(QAbstractItemView *view, int column, int role)
+void copyEntryData(const QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
return;
@@ -244,13 +245,20 @@ void copyEntryData(QAbstractItemView *view, int column, int role)
}
}
-QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
+QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column)
{
if(!view || !view->selectionModel())
return QList<QModelIndex>();
return view->selectionModel()->selectedRows(column);
}
+bool hasEntryData(const QAbstractItemView *view, int column, int role)
+{
+ QModelIndexList selection = getEntryData(view, column);
+ if (selection.isEmpty()) return false;
+ return !selection.at(0).data(role).toString().isEmpty();
+}
+
QString getDefaultDataDirectory()
{
return boostPathToQString(GetDefaultDataDir());
@@ -743,34 +751,12 @@ QString formatDurationStr(int secs)
return strList.join(" ");
}
-QString serviceFlagToStr(const quint64 mask, const int bit)
-{
- switch (ServiceFlags(mask)) {
- case NODE_NONE: abort(); // impossible
- case NODE_NETWORK: return "NETWORK";
- case NODE_GETUTXO: return "GETUTXO";
- case NODE_BLOOM: return "BLOOM";
- case NODE_WITNESS: return "WITNESS";
- case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
- // Not using default, so we get warned when a case is missing
- }
- if (bit < 8) {
- return QString("%1[%2]").arg("UNKNOWN").arg(mask);
- } else {
- return QString("%1[2^%2]").arg("UNKNOWN").arg(bit);
- }
-}
-
QString formatServicesStr(quint64 mask)
{
QStringList strList;
- for (int i = 0; i < 64; i++) {
- uint64_t check = 1LL << i;
- if (mask & check)
- {
- strList.append(serviceFlagToStr(check, i));
- }
+ for (const auto& flag : serviceFlagsToStr(mask)) {
+ strList.append(QString::fromStdString(flag));
}
if (strList.size())
@@ -910,4 +896,11 @@ void LogQtInfo()
}
}
+void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action)
+{
+ // The qminimal plugin does not provide window system integration.
+ if (QApplication::platformName() == "minimal") return;
+ menu->popup(point, at_action);
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 8b9fca4fb1..8741d90102 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -28,9 +28,12 @@ namespace interfaces
QT_BEGIN_NAMESPACE
class QAbstractItemView;
+class QAction;
class QDateTime;
class QFont;
class QLineEdit;
+class QMenu;
+class QPoint;
class QProgressDialog;
class QUrl;
class QWidget;
@@ -68,14 +71,21 @@ namespace GUIUtil
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
- void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole);
+ void copyEntryData(const QAbstractItemView *view, int column, int role=Qt::EditRole);
/** Return a field of the currently selected entry as a QString. Does nothing if nothing
is selected.
@param[in] column Data column to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
- QList<QModelIndex> getEntryData(QAbstractItemView *view, int column);
+ QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column);
+
+ /** Returns true if the specified field of the currently selected view entry is not empty.
+ @param[in] column Data column to extract from the model
+ @param[in] role Data role to extract from the model
+ @see TransactionView::contextualMenu
+ */
+ bool hasEntryData(const QAbstractItemView *view, int column, int role);
void setClipboard(const QString& str);
@@ -273,6 +283,11 @@ namespace GUIUtil
* Writes to debug.log short info about the used Qt and the host system.
*/
void LogQtInfo();
+
+ /**
+ * Call QMenu::popup() only on supported QT_QPA_PLATFORM.
+ */
+ void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action = nullptr);
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 49a1992803..0ba1beaf3e 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -5,12 +5,12 @@
#include <qt/modaloverlay.h>
#include <qt/forms/ui_modaloverlay.h>
-#include <qt/guiutil.h>
-
#include <chainparams.h>
+#include <qt/guiutil.h>
-#include <QResizeEvent>
+#include <QEasingCurve>
#include <QPropertyAnimation>
+#include <QResizeEvent>
ModalOverlay::ModalOverlay(bool enable_wallet, QWidget *parent) :
QWidget(parent),
@@ -33,6 +33,11 @@ userClosed(false)
ui->infoText->setVisible(false);
ui->infoTextStrong->setText(tr("%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.").arg(PACKAGE_NAME));
}
+
+ m_animation.setTargetObject(this);
+ m_animation.setPropertyName("pos");
+ m_animation.setDuration(300 /* ms */);
+ m_animation.setEasingCurve(QEasingCurve::OutQuad);
}
ModalOverlay::~ModalOverlay()
@@ -48,6 +53,9 @@ bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) {
if (!layerIsVisible)
setGeometry(0, height(), width(), height());
+ if (m_animation.endValue().toPoint().y() > 0) {
+ m_animation.setEndValue(QPoint(0, height()));
+ }
}
else if (ev->type() == QEvent::ChildAdded) {
raise();
@@ -166,14 +174,10 @@ void ModalOverlay::showHide(bool hide, bool userRequested)
if (!isVisible() && !hide)
setVisible(true);
- setGeometry(0, hide ? 0 : height(), width(), height());
-
- QPropertyAnimation* animation = new QPropertyAnimation(this, "pos");
- animation->setDuration(300);
- animation->setStartValue(QPoint(0, hide ? 0 : this->height()));
- animation->setEndValue(QPoint(0, hide ? this->height() : 0));
- animation->setEasingCurve(QEasingCurve::OutQuad);
- animation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_animation.setStartValue(QPoint(0, hide ? 0 : height()));
+ // The eventFilter() updates the endValue if it is required for QEvent::Resize.
+ m_animation.setEndValue(QPoint(0, hide ? height() : 0));
+ m_animation.start(QAbstractAnimation::KeepWhenStopped);
layerIsVisible = !hide;
}
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
index a565d7d8f3..1d84046d3d 100644
--- a/src/qt/modaloverlay.h
+++ b/src/qt/modaloverlay.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_MODALOVERLAY_H
#include <QDateTime>
+#include <QPropertyAnimation>
#include <QWidget>
//! The required delta of headers to the estimated number of available headers until we show the IBD progress
@@ -45,6 +46,7 @@ private:
QVector<QPair<qint64, double> > blockProcessTime;
bool layerIsVisible;
bool userClosed;
+ QPropertyAnimation m_animation;
void UpdateHeaderSyncLabel();
};
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index e20ec229fc..0af70f2735 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -16,7 +16,9 @@
#include <qt/walletmodel.h>
#include <QAbstractItemDelegate>
+#include <QApplication>
#include <QPainter>
+#include <QStatusTipEvent>
#define DECORATION_SIZE 54
#define NUM_ITEMS 5
@@ -152,6 +154,21 @@ void OverviewPage::handleOutOfSyncWarningClicks()
Q_EMIT outOfSyncWarningClicked();
}
+void OverviewPage::setPrivacy(bool privacy)
+{
+ m_privacy = privacy;
+ if (m_balances.balance != -1) {
+ setBalance(m_balances);
+ }
+
+ ui->listTransactions->setVisible(!m_privacy);
+
+ const QString status_tip = m_privacy ? tr("Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings->Mask values.") : "";
+ setStatusTip(status_tip);
+ QStatusTipEvent event(status_tip);
+ QApplication::sendEvent(this, &event);
+}
+
OverviewPage::~OverviewPage()
{
delete ui;
@@ -163,25 +180,25 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
m_balances = balances;
if (walletModel->wallet().isLegacy()) {
if (walletModel->wallet().privateKeysDisabled()) {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
}
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
}
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 00ba7ad4ce..4cf673b6a6 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -39,6 +39,7 @@ public:
public Q_SLOTS:
void setBalance(const interfaces::WalletBalances& balances);
+ void setPrivacy(bool privacy);
Q_SIGNALS:
void transactionClicked(const QModelIndex &index);
@@ -49,6 +50,7 @@ private:
ClientModel *clientModel;
WalletModel *walletModel;
interfaces::WalletBalances m_balances;
+ bool m_privacy{false};
TxViewDelegate *txdelegate;
std::unique_ptr<TransactionFilterProxy> filter;
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 30bd5c6a5a..d385c42821 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -8,10 +8,11 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/qrimagewidget.h>
#include <qt/walletmodel.h>
-#include <QClipboard>
-#include <QPixmap>
+#include <QDialog>
+#include <QString>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h> /* for USE_QRCODE */
@@ -23,14 +24,6 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
model(nullptr)
{
ui->setupUi(this);
-
-#ifndef USE_QRCODE
- ui->btnSaveAs->setVisible(false);
- ui->lblQRCode->setVisible(false);
-#endif
-
- connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, &QRImageWidget::saveImage);
-
GUIUtil::handleCloseWindowShortcut(this);
}
@@ -44,7 +37,7 @@ void ReceiveRequestDialog::setModel(WalletModel *_model)
this->model = _model;
if (_model)
- connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::update);
+ connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::updateDisplayUnit);
// update the display unit if necessary
update();
@@ -53,40 +46,55 @@ void ReceiveRequestDialog::setModel(WalletModel *_model)
void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
{
this->info = _info;
- update();
-}
+ setWindowTitle(tr("Request payment to %1").arg(info.label.isEmpty() ? info.address : info.label));
+ QString uri = GUIUtil::formatBitcoinURI(info);
-void ReceiveRequestDialog::update()
-{
- if(!model)
- return;
- QString target = info.label;
- if(target.isEmpty())
- target = info.address;
- setWindowTitle(tr("Request payment to %1").arg(target));
+#ifdef USE_QRCODE
+ if (ui->qr_code->setQR(uri, info.address)) {
+ connect(ui->btnSaveAs, &QPushButton::clicked, ui->qr_code, &QRImageWidget::saveImage);
+ } else {
+ ui->btnSaveAs->setEnabled(false);
+ }
+#else
+ ui->btnSaveAs->hide();
+ ui->qr_code->hide();
+#endif
- QString uri = GUIUtil::formatBitcoinURI(info);
- ui->btnSaveAs->setEnabled(false);
- QString html;
- html += "<html><font face='verdana, arial, helvetica, sans-serif'>";
- html += "<b>"+tr("Payment information")+"</b><br>";
- html += "<b>"+tr("URI")+"</b>: ";
- html += "<a href=\""+uri+"\">" + GUIUtil::HtmlEscape(uri) + "</a><br>";
- html += "<b>"+tr("Address")+"</b>: " + GUIUtil::HtmlEscape(info.address) + "<br>";
- if(info.amount)
- html += "<b>"+tr("Amount")+"</b>: " + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), info.amount) + "<br>";
- if(!info.label.isEmpty())
- html += "<b>"+tr("Label")+"</b>: " + GUIUtil::HtmlEscape(info.label) + "<br>";
- if(!info.message.isEmpty())
- html += "<b>"+tr("Message")+"</b>: " + GUIUtil::HtmlEscape(info.message) + "<br>";
- if(model->isMultiwallet()) {
- html += "<b>"+tr("Wallet")+"</b>: " + GUIUtil::HtmlEscape(model->getWalletName()) + "<br>";
+ ui->uri_content->setText("<a href=\"" + uri + "\">" + GUIUtil::HtmlEscape(uri) + "</a>");
+ ui->address_content->setText(info.address);
+
+ if (!info.amount) {
+ ui->amount_tag->hide();
+ ui->amount_content->hide();
+ } // Amount is set in updateDisplayUnit() slot.
+ updateDisplayUnit();
+
+ if (!info.label.isEmpty()) {
+ ui->label_content->setText(info.label);
+ } else {
+ ui->label_tag->hide();
+ ui->label_content->hide();
}
- ui->outUri->setText(html);
- if (ui->lblQRCode->setQR(uri, info.address)) {
- ui->btnSaveAs->setEnabled(true);
+ if (!info.message.isEmpty()) {
+ ui->message_content->setText(info.message);
+ } else {
+ ui->message_tag->hide();
+ ui->message_content->hide();
}
+
+ if (!model->getWalletName().isEmpty()) {
+ ui->wallet_content->setText(model->getWalletName());
+ } else {
+ ui->wallet_tag->hide();
+ ui->wallet_content->hide();
+ }
+}
+
+void ReceiveRequestDialog::updateDisplayUnit()
+{
+ if (!model) return;
+ ui->amount_content->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), info.amount));
}
void ReceiveRequestDialog::on_btnCopyURI_clicked()
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index 40e3d5ffa8..846478643d 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -29,8 +29,7 @@ public:
private Q_SLOTS:
void on_btnCopyURI_clicked();
void on_btnCopyAddress_clicked();
-
- void update();
+ void updateDisplayUnit();
private:
Ui::ReceiveRequestDialog *ui;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 66f1c8fd9c..0f89d4e6fe 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -28,6 +28,7 @@
#include <wallet/wallet.h>
#endif
+#include <QFont>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
@@ -494,7 +495,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
- consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
+ consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt();
clear();
GUIUtil::handleCloseWindowShortcut(this);
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 5cfbb67449..4835dd7954 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -91,6 +91,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked()
{
if (model && model->getAddressTableModel())
{
+ model->refresh(/* pk_hash_only */ true);
AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this);
dlg.setModel(model->getAddressTableModel());
if (dlg.exec())
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 2ee9ae0d86..8da0250e57 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -201,7 +201,7 @@ void TestGUI(interfaces::Node& node)
OverviewPage overviewPage(platformStyle.get());
overviewPage.setWalletModel(&walletModel);
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
- QString balanceText = balanceLabel->text();
+ QString balanceText = balanceLabel->text().trimmed();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
@@ -229,15 +229,23 @@ void TestGUI(interfaces::Node& node)
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("ReceiveRequestDialog")) {
ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
- QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri");
- QString paymentText = rlist->toPlainText();
- QStringList paymentTextList = paymentText.split('\n');
- QCOMPARE(paymentTextList.at(0), QString("Payment information"));
- QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1);
- QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
- QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
- QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
- QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("payment_header")->text(), QString("Payment information"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("uri_tag")->text(), QString("URI:"));
+ QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();
+ QCOMPARE(uri.count("bitcoin:"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));
+
+ QCOMPARE(uri.count("amount=0.00000001"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
+
+ QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_content")->text(), QString("TEST_LABEL_1"));
+
+ QCOMPARE(uri.count("message=TEST_MESSAGE_1"), 2);
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_tag")->text(), QString("Message:"));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_content")->text(), QString("TEST_MESSAGE_1"));
}
}
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index 757648f485..6428fc4daf 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.cpp
@@ -7,6 +7,7 @@
#include <qt/clientmodel.h>
#include <QPainter>
+#include <QPainterPath>
#include <QColor>
#include <QTimer>
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index a32d218fc9..01dff8069c 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -162,7 +162,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
return parts;
}
-void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t block_time)
+void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time)
{
// Determine transaction status
@@ -174,7 +174,7 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
idx);
status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
status.depth = wtx.depth_in_main_chain;
- status.cur_num_blocks = numBlocks;
+ status.m_cur_block_hash = block_hash;
const bool up_to_date = ((int64_t)QDateTime::currentMSecsSinceEpoch() / 1000 - block_time < MAX_BLOCK_TIME_GAP);
if (up_to_date && !wtx.is_final) {
@@ -233,9 +233,9 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
status.needsUpdate = false;
}
-bool TransactionRecord::statusUpdateNeeded(int numBlocks) const
+bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
{
- return status.cur_num_blocks != numBlocks || status.needsUpdate;
+ return status.m_cur_block_hash != block_hash || status.needsUpdate;
}
QString TransactionRecord::getTxHash() const
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 3f64cefd09..c983c527c0 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -23,9 +23,8 @@ struct WalletTxStatus;
class TransactionStatus
{
public:
- TransactionStatus():
- countsForBalance(false), sortKey(""),
- matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1)
+ TransactionStatus() : countsForBalance(false), sortKey(""),
+ matures_in(0), status(Unconfirmed), depth(0), open_for(0)
{ }
enum Status {
@@ -61,8 +60,8 @@ public:
finalization */
/**@}*/
- /** Current number of blocks (to know whether cached status is still valid) */
- int cur_num_blocks;
+ /** Current block hash (to know whether cached status is still valid) */
+ uint256 m_cur_block_hash{};
bool needsUpdate;
};
@@ -138,11 +137,11 @@ public:
/** Update status from core wallet tx.
*/
- void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t block_time);
+ void updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time);
/** Return whether a status update is needed.
*/
- bool statusUpdateNeeded(int numBlocks) const;
+ bool statusUpdateNeeded(const uint256& block_hash) const;
};
#endif // BITCOIN_QT_TRANSACTIONRECORD_H
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 7a15503228..04eb1ae706 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -176,7 +176,7 @@ public:
return cachedWallet.size();
}
- TransactionRecord *index(interfaces::Wallet& wallet, const int cur_num_blocks, const int idx)
+ TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int idx)
{
if(idx >= 0 && idx < cachedWallet.size())
{
@@ -192,8 +192,8 @@ public:
interfaces::WalletTxStatus wtx;
int numBlocks;
int64_t block_time;
- if (rec->statusUpdateNeeded(cur_num_blocks) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
- rec->updateStatus(wtx, numBlocks, block_time);
+ if (rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
+ rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);
}
return rec;
}
@@ -618,7 +618,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return details;
}
case ConfirmedRole:
- return rec->status.countsForBalance;
+ return rec->status.status == TransactionStatus::Status::Confirming || rec->status.status == TransactionStatus::Status::Confirmed;
case FormattedAmountRole:
// Used for copy/export, so don't include separators
return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
@@ -664,7 +664,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
- TransactionRecord *data = priv->index(walletModel->wallet(), walletModel->getNumBlocks(), row);
+ TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->clientModel().getBestBlockHash(), row);
if(data)
{
return createIndex(row, column, data);
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 3c638fb358..3df81807f0 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -8,6 +8,7 @@
#include <qt/bitcoinunits.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
+#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/transactiondescdialog.h>
@@ -36,8 +37,7 @@
#include <QVBoxLayout>
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
- QWidget(parent), model(nullptr), transactionProxyModel(nullptr),
- transactionView(nullptr), abandonAction(nullptr), bumpFeeAction(nullptr), columnResizingFixer(nullptr)
+ QWidget(parent)
{
// Build filter row
setContentsMargins(0,0,0,0);
@@ -152,8 +152,8 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
abandonAction = new QAction(tr("Abandon transaction"), this);
bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
bumpFeeAction->setObjectName("bumpFeeAction");
- QAction *copyAddressAction = new QAction(tr("Copy address"), this);
- QAction *copyLabelAction = new QAction(tr("Copy label"), this);
+ copyAddressAction = new QAction(tr("Copy address"), this);
+ copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
@@ -395,10 +395,11 @@ void TransactionView::contextualMenu(const QPoint &point)
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash));
bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash));
+ copyAddressAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::AddressRole));
+ copyLabelAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::LabelRole));
- if(index.isValid())
- {
- contextMenu->popup(transactionView->viewport()->mapToGlobal(point));
+ if (index.isValid()) {
+ GUIUtil::PopupMenu(contextMenu, transactionView->viewport()->mapToGlobal(point));
}
}
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 268e3751b3..9ce7f4ad97 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -60,9 +60,9 @@ public:
};
private:
- WalletModel *model;
- TransactionFilterProxy *transactionProxyModel;
- QTableView *transactionView;
+ WalletModel *model{nullptr};
+ TransactionFilterProxy *transactionProxyModel{nullptr};
+ QTableView *transactionView{nullptr};
QComboBox *dateWidget;
QComboBox *typeWidget;
@@ -75,12 +75,14 @@ private:
QFrame *dateRangeWidget;
QDateTimeEdit *dateFrom;
QDateTimeEdit *dateTo;
- QAction *abandonAction;
- QAction *bumpFeeAction;
+ QAction *abandonAction{nullptr};
+ QAction *bumpFeeAction{nullptr};
+ QAction *copyAddressAction{nullptr};
+ QAction *copyLabelAction{nullptr};
QWidget *createDateRangeWidget();
- GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
+ GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer{nullptr};
virtual void resizeEvent(QResizeEvent* event) override;
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 20f2ef5b5f..f1438e3439 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -92,6 +92,23 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
removeAndDeleteWallet(wallet_model);
}
+void WalletController::closeAllWallets(QWidget* parent)
+{
+ QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),
+ tr("Are you sure you wish to close all wallets?"),
+ QMessageBox::Yes|QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (button != QMessageBox::Yes) return;
+
+ QMutexLocker locker(&m_mutex);
+ for (WalletModel* wallet_model : m_wallets) {
+ wallet_model->wallet().remove();
+ Q_EMIT walletRemoved(wallet_model);
+ delete wallet_model;
+ }
+ m_wallets.clear();
+}
+
WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
QMutexLocker locker(&m_mutex);
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 24dd83adf7..f7e366878d 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -62,6 +62,7 @@ public:
std::map<std::string, bool> listWalletDir() const;
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
+ void closeAllWallets(QWidget* parent = nullptr);
Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 02a9583ae9..5e68ee4f93 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -53,6 +53,7 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
+ walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@@ -73,6 +74,7 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
+ connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index b1e61e03b3..671b5e1ce6 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -46,7 +46,6 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
transactionTableModel(nullptr),
recentRequestsTableModel(nullptr),
cachedEncryptionStatus(Unencrypted),
- cachedNumBlocks(0),
timer(new QTimer(this))
{
fHaveWatchOnly = m_wallet->haveWatchOnly();
@@ -88,24 +87,23 @@ void WalletModel::pollBalanceChanged()
{
// Avoid recomputing wallet balances unless a TransactionChanged or
// BlockTip notification was received.
- if (!fForceCheckBalanceChanged && cachedNumBlocks == m_client_model->getNumBlocks()) return;
+ if (!fForceCheckBalanceChanged && m_cached_last_update_tip == m_client_model->getBestBlockHash()) return;
// Try to get balances and return early if locks can't be acquired. This
// avoids the GUI from getting stuck on periodical polls if the core is
// holding the locks for a longer time - for example, during a wallet
// rescan.
interfaces::WalletBalances new_balances;
- int numBlocks = -1;
- if (!m_wallet->tryGetBalances(new_balances, numBlocks)) {
+ uint256 block_hash;
+ if (!m_wallet->tryGetBalances(new_balances, block_hash)) {
return;
}
- if(fForceCheckBalanceChanged || numBlocks != cachedNumBlocks)
- {
+ if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed
- cachedNumBlocks = numBlocks;
+ m_cached_last_update_tip = block_hash;
checkBalanceChanged(new_balances);
if(transactionTableModel)
@@ -585,3 +583,8 @@ bool WalletModel::isMultiwallet()
{
return m_node.getWallets().size() > 1;
}
+
+void WalletModel::refresh(bool pk_hash_only)
+{
+ addressTableModel = new AddressTableModel(this, pk_hash_only);
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 23232ec66b..38e8a14556 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -144,8 +144,8 @@ public:
interfaces::Node& node() const { return m_node; }
interfaces::Wallet& wallet() const { return *m_wallet; }
+ ClientModel& clientModel() const { return *m_client_model; }
void setClientModel(ClientModel* client_model);
- int getNumBlocks() const { return cachedNumBlocks; }
QString getWalletName() const;
QString getDisplayName() const;
@@ -153,6 +153,8 @@ public:
bool isMultiwallet();
AddressTableModel* getAddressTableModel() const { return addressTableModel; }
+
+ void refresh(bool pk_hash_only = false);
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
std::unique_ptr<interfaces::Handler> m_handler_unload;
@@ -179,9 +181,11 @@ private:
// Cache some values to be able to detect changes
interfaces::WalletBalances m_cached_balances;
EncryptionStatus cachedEncryptionStatus;
- int cachedNumBlocks;
QTimer* timer;
+ // Block hash denoting when the last balance update was done.
+ uint256 m_cached_last_update_tip{};
+
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
void checkBalanceChanged(const interfaces::WalletBalances& new_balances);
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index c4e607990a..861d1c5f4a 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -85,6 +85,8 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
// Pass through messages from transactionView
connect(transactionView, &TransactionView::message, this, &WalletView::message);
+
+ connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy);
}
WalletView::~WalletView()
@@ -256,7 +258,7 @@ void WalletView::gotoLoadPSBT()
TransactionError result = BroadcastTransaction(*clientModel->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false);
if (result == TransactionError::OK) {
- Q_EMIT message(tr("Success"), tr("Broadcasted transaction sucessfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
+ Q_EMIT message(tr("Success"), tr("Broadcasted transaction successfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
} else {
Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR);
}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 11f894e7f8..fd09456baa 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -115,6 +115,7 @@ public Q_SLOTS:
void requestedSyncWarningInfo();
Q_SIGNALS:
+ void setPrivacy(bool privacy);
void transactionClicked();
void coinsSent();
/** Fired when a message should be reported to the user */
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 7d43de6646..8252af3ee6 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -32,6 +32,7 @@
#include <util/ref.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -52,7 +53,7 @@ struct CUpdatedBlock
static Mutex cs_blockchange;
static std::condition_variable cond_blockchange;
-static CUpdatedBlock latestblock;
+static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
NodeContext& EnsureNodeContext(const util::Ref& context)
{
@@ -223,7 +224,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
void RPCNotifyBlockChange(const CBlockIndex* pindex)
{
if(pindex) {
- std::lock_guard<std::mutex> lock(cs_blockchange);
+ LOCK(cs_blockchange);
latestblock.hash = pindex->GetBlockHash();
latestblock.height = pindex->nHeight;
}
@@ -258,9 +259,9 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
WAIT_LOCK(cs_blockchange, lock);
block = latestblock;
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
else
- cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
@@ -300,9 +301,9 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{
WAIT_LOCK(cs_blockchange, lock);
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning();});
else
- cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning(); });
block = latestblock;
}
@@ -344,9 +345,9 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{
WAIT_LOCK(cs_blockchange, lock);
if(timeout)
- cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
else
- cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
+ cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
block = latestblock;
}
UniValue ret(UniValue::VOBJ);
@@ -414,7 +415,7 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
- RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet confirmed)"},
+ RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
};}
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
@@ -1095,7 +1096,8 @@ static UniValue verifychain(const JSONRPCRequest& request)
RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
- {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL), "How thorough the block verification is."},
+ {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL),
+ strprintf("How thorough the block verification is:\n - %s", Join(CHECKLEVEL_DOC, "\n- "))},
{"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS), "The number of blocks to check."},
},
RPCResult{
@@ -1277,7 +1279,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
obj.pushKV("softforks", softforks);
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
@@ -1995,7 +1997,6 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
}
/** RAII object to prevent concurrency issue when scanning the txout set */
-static std::mutex g_utxosetscan;
static std::atomic<int> g_scan_progress;
static std::atomic<bool> g_scan_in_progress;
static std::atomic<bool> g_should_abort_scan;
@@ -2008,18 +2009,15 @@ public:
bool reserve() {
CHECK_NONFATAL(!m_could_reserve);
- std::lock_guard<std::mutex> lock(g_utxosetscan);
- if (g_scan_in_progress) {
+ if (g_scan_in_progress.exchange(true)) {
return false;
}
- g_scan_in_progress = true;
m_could_reserve = true;
return true;
}
~CoinsViewScanReserver() {
if (m_could_reserve) {
- std::lock_guard<std::mutex> lock(g_utxosetscan);
g_scan_in_progress = false;
}
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 3612f14bbf..a2de175c85 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -29,6 +29,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -179,7 +180,7 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
}
- // Combo desriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
+ // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4);
if (scripts.size() == 1) {
@@ -416,7 +417,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
obj.pushKV("networkhashps", getnetworkhashps(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e29aa03695..df1e0fe623 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -22,6 +22,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/translation.h>
#include <validation.h>
#include <version.h>
#include <warnings.h>
@@ -548,7 +549,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
}
}
obj.pushKV("localaddresses", localAddresses);
- obj.pushKV("warnings", GetWarnings(false));
+ obj.pushKV("warnings", GetWarnings(false).original);
return obj;
}
diff --git a/src/rpc/request.h b/src/rpc/request.h
index 02ec5393a7..4761e9e371 100644
--- a/src/rpc/request.h
+++ b/src/rpc/request.h
@@ -41,6 +41,16 @@ public:
const util::Ref& context;
JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {}
+
+ //! Initializes request information from another request object and the
+ //! given context. The implementation should be updated if any members are
+ //! added or removed above.
+ JSONRPCRequest(const JSONRPCRequest& other, const util::Ref& context)
+ : id(other.id), strMethod(other.strMethod), params(other.params), fHelp(other.fHelp), URI(other.URI),
+ authUser(other.authUser), peerAddr(other.peerAddr), context(context)
+ {
+ }
+
void parse(const UniValue& valRequest);
};
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 99c649d15a..844f62cbc6 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -15,7 +15,9 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/signals2/signal.hpp>
+#include <cassert>
#include <memory> // for unique_ptr
+#include <mutex>
#include <unordered_map>
static RecursiveMutex cs_rpcWarmup;
@@ -291,17 +293,26 @@ void StartRPC()
void InterruptRPC()
{
- LogPrint(BCLog::RPC, "Interrupting RPC\n");
- // Interrupt e.g. running longpolls
- g_rpc_running = false;
+ static std::once_flag g_rpc_interrupt_flag;
+ // This function could be called twice if the GUI has been started with -server=1.
+ std::call_once(g_rpc_interrupt_flag, []() {
+ LogPrint(BCLog::RPC, "Interrupting RPC\n");
+ // Interrupt e.g. running longpolls
+ g_rpc_running = false;
+ });
}
void StopRPC()
{
- LogPrint(BCLog::RPC, "Stopping RPC\n");
- WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
- DeleteAuthCookie();
- g_rpcSignals.Stopped();
+ static std::once_flag g_rpc_stop_flag;
+ // This function could be called twice if the GUI has been started with -server=1.
+ assert(!g_rpc_running);
+ std::call_once(g_rpc_stop_flag, []() {
+ LogPrint(BCLog::RPC, "Stopping RPC\n");
+ WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
+ DeleteAuthCookie();
+ g_rpcSignals.Stopped();
+ });
}
bool IsRPCRunning()
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 860fa198d5..e7afef4cac 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -10,6 +10,7 @@
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/translation.h>
#include <tuple>
@@ -285,7 +286,7 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
if (err_string.length() > 0) {
return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
} else {
- return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr));
+ return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
}
}
@@ -843,16 +844,9 @@ UniValue GetServicesNames(ServiceFlags services)
{
UniValue servicesNames(UniValue::VARR);
- if (services & NODE_NETWORK)
- servicesNames.push_back("NETWORK");
- if (services & NODE_GETUTXO)
- servicesNames.push_back("GETUTXO");
- if (services & NODE_BLOOM)
- servicesNames.push_back("BLOOM");
- if (services & NODE_WITNESS)
- servicesNames.push_back("WITNESS");
- if (services & NODE_NETWORK_LIMITED)
- servicesNames.push_back("NETWORK_LIMITED");
+ for (const auto& flag : serviceFlagsToStr(services)) {
+ servicesNames.push_back(flag);
+ }
return servicesNames;
}
diff --git a/src/script/script.cpp b/src/script/script.cpp
index ae0de1d24e..92c6fe7785 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -7,7 +7,9 @@
#include <util/strencodings.h>
-const char* GetOpName(opcodetype opcode)
+#include <string>
+
+std::string GetOpName(opcodetype opcode)
{
switch (opcode)
{
diff --git a/src/script/script.h b/src/script/script.h
index b61581767f..c1f2b66921 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -193,7 +193,7 @@ enum opcodetype
// Maximum value that an opcode can be
static const unsigned int MAX_OPCODE = OP_NOP10;
-const char* GetOpName(opcodetype opcode);
+std::string GetOpName(opcodetype opcode);
class scriptnum_error : public std::runtime_error
{
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 57e8fee539..69e14803f1 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -5,7 +5,9 @@
#include <script/script_error.h>
-const char* ScriptErrorString(const ScriptError serror)
+#include <string>
+
+std::string ScriptErrorString(const ScriptError serror)
{
switch (serror)
{
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 400f63ff0f..2978c147e1 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_SCRIPT_SCRIPT_ERROR_H
#define BITCOIN_SCRIPT_SCRIPT_ERROR_H
+#include <string>
+
typedef enum ScriptError_t
{
SCRIPT_ERR_OK = 0,
@@ -73,6 +75,6 @@ typedef enum ScriptError_t
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
-const char* ScriptErrorString(const ScriptError error);
+std::string ScriptErrorString(const ScriptError error);
#endif // BITCOIN_SCRIPT_SCRIPT_ERROR_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index e7b6df3ce8..aaecab1ef2 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -11,7 +11,7 @@
#include <util/system.h>
#include <cuckoocache.h>
-#include <boost/thread.hpp>
+#include <boost/thread/shared_mutex.hpp>
namespace {
/**
@@ -23,7 +23,7 @@ class CSignatureCache
{
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
- uint256 nonce;
+ CSHA256 m_salted_hasher;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
@@ -31,13 +31,19 @@ private:
public:
CSignatureCache()
{
- GetRandBytes(nonce.begin(), 32);
+ uint256 nonce = GetRandHash();
+ // We want the nonce to be 64 bytes long to force the hasher to process
+ // this chunk, which makes later hash computations more efficient. We
+ // just write our 32-byte entropy twice to fill the 64 bytes.
+ m_salted_hasher.Write(nonce.begin(), 32);
+ m_salted_hasher.Write(nonce.begin(), 32);
}
void
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
{
- CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
+ CSHA256 hasher = m_salted_hasher;
+ hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
}
bool
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 7d89a336fb..c90c2c24a0 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -9,6 +9,8 @@
#include <pubkey.h>
#include <script/script.h>
+#include <string>
+
typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
@@ -25,7 +27,7 @@ WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
CSHA256().Write(in.data(), in.size()).Finalize(begin());
}
-const char* GetTxnOutputType(txnouttype t)
+std::string GetTxnOutputType(txnouttype t)
{
switch (t)
{
@@ -39,7 +41,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_UNKNOWN: return "witness_unknown";
}
- return nullptr;
+ assert(false);
}
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
diff --git a/src/script/standard.h b/src/script/standard.h
index 49a45f3eba..2929425670 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -11,6 +11,8 @@
#include <boost/variant.hpp>
+#include <string>
+
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
@@ -44,8 +46,7 @@ extern unsigned nMaxDatacarrierBytes;
/**
* Mandatory script verification flags that all new blocks must comply with for
* them to be valid. (but old blocks may not comply with) Currently just P2SH,
- * but in the future other flags may be added, such as a soft-fork to enforce
- * strict DER encoding.
+ * but in the future other flags may be added.
*
* Failing one of these tests may trigger a DoS ban - see CheckInputScripts() for
* details.
@@ -146,7 +147,7 @@ typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash,
bool IsValidDestination(const CTxDestination& dest);
/** Get the name of a txnouttype as a C string, or nullptr if unknown. */
-const char* GetTxnOutputType(txnouttype t);
+std::string GetTxnOutputType(txnouttype t);
/**
* Parse a scriptPubKey and identify script type for standard scripts. If
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index f17b539e09..b4f392116c 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -29,7 +29,6 @@
#endif
LockedPoolManager* LockedPoolManager::_instance = nullptr;
-std::once_flag LockedPoolManager::init_flag;
/*******************************************************************************/
// Utilities
diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h
index de668f0773..b9e2e99d1a 100644
--- a/src/support/lockedpool.h
+++ b/src/support/lockedpool.h
@@ -221,7 +221,8 @@ public:
/** Return the current instance, or create it once */
static LockedPoolManager& Instance()
{
- std::call_once(LockedPoolManager::init_flag, LockedPoolManager::CreateInstance);
+ static std::once_flag init_flag;
+ std::call_once(init_flag, LockedPoolManager::CreateInstance);
return *LockedPoolManager::_instance;
}
@@ -234,7 +235,6 @@ private:
static bool LockingFailed();
static LockedPoolManager* _instance;
- static std::once_flag init_flag;
};
#endif // BITCOIN_SUPPORT_LOCKEDPOOL_H
diff --git a/src/sync.cpp b/src/sync.cpp
index c3312b5a00..9abdedbed4 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -219,12 +219,15 @@ static bool LockHeld(void* mutex)
return false;
}
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+template <typename MutexType>
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
if (LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
+template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
+template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
{
diff --git a/src/sync.h b/src/sync.h
index 0c6f0ef0a7..60e5a87aec 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -52,7 +52,8 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
+template <typename MutexType>
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void DeleteLock(void* cs);
@@ -66,7 +67,8 @@ extern bool g_debug_lockorder_abort;
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
-void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
+template <typename MutexType>
+void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
#endif
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 0565982215..8348810ac1 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <checkqueue.h>
+#include <sync.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
#include <boost/test/unit_test.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#include <atomic>
#include <condition_variable>
@@ -57,14 +58,14 @@ struct FailingCheck {
};
struct UniqueCheck {
- static std::mutex m;
- static std::unordered_multiset<size_t> results;
+ static Mutex m;
+ static std::unordered_multiset<size_t> results GUARDED_BY(m);
size_t check_id;
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
UniqueCheck() : check_id(0){};
bool operator()()
{
- std::lock_guard<std::mutex> l(m);
+ LOCK(m);
results.insert(check_id);
return true;
}
@@ -127,7 +128,7 @@ struct FrozenCleanupCheck {
std::mutex FrozenCleanupCheck::m{};
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
std::condition_variable FrozenCleanupCheck::cv{};
-std::mutex UniqueCheck::m;
+Mutex UniqueCheck::m;
std::unordered_multiset<size_t> UniqueCheck::results;
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
@@ -290,11 +291,15 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
control.Add(vChecks);
}
}
- bool r = true;
- BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
- for (size_t i = 0; i < COUNT; ++i)
- r = r && UniqueCheck::results.count(i) == 1;
- BOOST_REQUIRE(r);
+ {
+ LOCK(UniqueCheck::m);
+ bool r = true;
+ BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
+ for (size_t i = 0; i < COUNT; ++i) {
+ r = r && UniqueCheck::results.count(i) == 1;
+ }
+ BOOST_REQUIRE(r);
+ }
tg.interrupt_all();
tg.join_all();
}
diff --git a/src/test/fuzz/crypto_common.cpp b/src/test/fuzz/crypto_common.cpp
new file mode 100644
index 0000000000..7ccb125216
--- /dev/null
+++ b/src/test/fuzz/crypto_common.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/common.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const uint16_t random_u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
+ const uint32_t random_u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ const uint64_t random_u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ const std::vector<uint8_t> random_bytes_2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 2);
+ const std::vector<uint8_t> random_bytes_4 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 4);
+ const std::vector<uint8_t> random_bytes_8 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 8);
+
+ std::array<uint8_t, 2> writele16_arr;
+ WriteLE16(writele16_arr.data(), random_u16);
+ assert(ReadLE16(writele16_arr.data()) == random_u16);
+
+ std::array<uint8_t, 4> writele32_arr;
+ WriteLE32(writele32_arr.data(), random_u32);
+ assert(ReadLE32(writele32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writele64_arr;
+ WriteLE64(writele64_arr.data(), random_u64);
+ assert(ReadLE64(writele64_arr.data()) == random_u64);
+
+ std::array<uint8_t, 4> writebe32_arr;
+ WriteBE32(writebe32_arr.data(), random_u32);
+ assert(ReadBE32(writebe32_arr.data()) == random_u32);
+
+ std::array<uint8_t, 8> writebe64_arr;
+ WriteBE64(writebe64_arr.data(), random_u64);
+ assert(ReadBE64(writebe64_arr.data()) == random_u64);
+
+ const uint16_t readle16_result = ReadLE16(random_bytes_2.data());
+ std::array<uint8_t, 2> readle16_arr;
+ WriteLE16(readle16_arr.data(), readle16_result);
+ assert(std::memcmp(random_bytes_2.data(), readle16_arr.data(), 2) == 0);
+
+ const uint32_t readle32_result = ReadLE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readle32_arr;
+ WriteLE32(readle32_arr.data(), readle32_result);
+ assert(std::memcmp(random_bytes_4.data(), readle32_arr.data(), 4) == 0);
+
+ const uint64_t readle64_result = ReadLE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readle64_arr;
+ WriteLE64(readle64_arr.data(), readle64_result);
+ assert(std::memcmp(random_bytes_8.data(), readle64_arr.data(), 8) == 0);
+
+ const uint32_t readbe32_result = ReadBE32(random_bytes_4.data());
+ std::array<uint8_t, 4> readbe32_arr;
+ WriteBE32(readbe32_arr.data(), readbe32_result);
+ assert(std::memcmp(random_bytes_4.data(), readbe32_arr.data(), 4) == 0);
+
+ const uint64_t readbe64_result = ReadBE64(random_bytes_8.data());
+ std::array<uint8_t, 8> readbe64_arr;
+ WriteBE64(readbe64_arr.data(), readbe64_result);
+ assert(std::memcmp(random_bytes_8.data(), readbe64_arr.data(), 8) == 0);
+}
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index af6dc71322..82cbc00a3a 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -7,6 +7,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <util/error.h>
+#include <util/translation.h>
#include <cstdint>
#include <vector>
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index c071283467..ae531f4462 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -6,6 +6,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/translation.h>
#include <cassert>
#include <cstdint>
@@ -29,7 +30,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
NetWhitebindPermissions net_whitebind_permissions;
- std::string error_net_whitebind_permissions;
+ bilingual_str error_net_whitebind_permissions;
if (NetWhitebindPermissions::TryParse(s, net_whitebind_permissions, error_net_whitebind_permissions)) {
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags);
@@ -39,7 +40,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
NetWhitelistPermissions net_whitelist_permissions;
- std::string error_net_whitelist_permissions;
+ bilingual_str error_net_whitelist_permissions;
if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, error_net_whitelist_permissions)) {
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 665a6224b4..211a84b5f2 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -14,6 +14,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/mining.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
#include <validationinterface.h>
@@ -29,7 +30,7 @@
#include <string>
#include <vector>
-bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, ChainstateManager& chainman, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
+bool ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, ChainstateManager& chainman, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
namespace {
@@ -63,19 +64,23 @@ void initialize()
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get();
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
return;
}
CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
- CNode p2p_node{0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false};
+ CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false).release();
p2p_node.fSuccessfullyConnected = true;
p2p_node.nVersion = PROTOCOL_VERSION;
p2p_node.SetSendVersion(PROTOCOL_VERSION);
+ connman.AddTestNode(p2p_node);
g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
try {
- (void)ProcessMessage(&p2p_node, random_message_type, random_bytes_data_stream, GetTimeMillis(), Params(), *g_setup->m_node.chainman, *g_setup->m_node.mempool, g_setup->m_node.connman.get(), g_setup->m_node.banman.get(), std::atomic<bool>{false});
+ (void)ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream, GetTimeMillis(), Params(), *g_setup->m_node.chainman, *g_setup->m_node.mempool, g_setup->m_node.connman.get(), g_setup->m_node.banman.get(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
SyncWithValidationInterfaceQueue();
+ LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
+ g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index bcbf65bdca..ad6c115a49 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -75,6 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
} catch (const std::ios_base::failure&) {
}
}
- connman.ClearTestNodes();
SyncWithValidationInterfaceQueue();
+ LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
+ g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index e0c4ad7eb7..933cf9049d 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -11,6 +11,7 @@
#include <script/descriptor.h>
#include <script/interpreter.h>
#include <script/script.h>
+#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
@@ -21,6 +22,8 @@
#include <univalue.h>
#include <util/memory.h>
+#include <algorithm>
+#include <cassert>
#include <cstdint>
#include <optional>
#include <string>
@@ -124,4 +127,40 @@ void test_one_input(const std::vector<uint8_t>& buffer)
wit.SetNull();
}
}
+
+ (void)GetOpName(ConsumeOpcodeType(fuzzed_data_provider));
+ (void)ScriptErrorString(static_cast<ScriptError>(fuzzed_data_provider.ConsumeIntegralInRange<int>(0, SCRIPT_ERR_ERROR_COUNT)));
+
+ {
+ const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ CScript append_script{bytes.begin(), bytes.end()};
+ append_script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ append_script << ConsumeOpcodeType(fuzzed_data_provider);
+ append_script << CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
+ append_script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ }
+
+ {
+ WitnessUnknown witness_unknown_1{};
+ witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown_1.length = witness_unknown_program_1.size();
+ std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown_1.program);
+
+ WitnessUnknown witness_unknown_2{};
+ witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_2 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown_2.length = witness_unknown_program_2.size();
+ std::copy(witness_unknown_program_2.begin(), witness_unknown_program_2.end(), witness_unknown_2.program);
+
+ (void)(witness_unknown_1 == witness_unknown_2);
+ (void)(witness_unknown_1 < witness_unknown_2);
+ }
+
+ {
+ const CTxDestination tx_destination_1 = ConsumeTxDestination(fuzzed_data_provider);
+ const CTxDestination tx_destination_2 = ConsumeTxDestination(fuzzed_data_provider);
+ (void)(tx_destination_1 == tx_destination_2);
+ (void)(tx_destination_1 < tx_destination_2);
+ }
}
diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp
new file mode 100644
index 0000000000..22f4b4f44a
--- /dev/null
+++ b/src/test/fuzz/script_bitcoin_consensus.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <script/bitcoinconsensus.h>
+#include <script/interpreter.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::vector<uint8_t> random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const std::vector<uint8_t> random_bytes_2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const CAmount money = ConsumeMoney(fuzzed_data_provider);
+ bitcoinconsensus_error err;
+ bitcoinconsensus_error* err_p = fuzzed_data_provider.ConsumeBool() ? &err : nullptr;
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ assert(bitcoinconsensus_version() == BITCOINCONSENSUS_API_VER);
+ if ((flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
+ return;
+ }
+ (void)bitcoinconsensus_verify_script(random_bytes_1.data(), random_bytes_1.size(), random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
+ (void)bitcoinconsensus_verify_script_with_amount(random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
+}
diff --git a/src/test/fuzz/script_descriptor_cache.cpp b/src/test/fuzz/script_descriptor_cache.cpp
new file mode 100644
index 0000000000..4bfe61cec7
--- /dev/null
+++ b/src/test/fuzz/script_descriptor_cache.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <optional.h>
+#include <pubkey.h>
+#include <script/descriptor.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ DescriptorCache descriptor_cache;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> code = fuzzed_data_provider.ConsumeBytes<uint8_t>(BIP32_EXTKEY_SIZE);
+ if (code.size() == BIP32_EXTKEY_SIZE) {
+ CExtPubKey xpub;
+ xpub.Decode(code.data());
+ const uint32_t key_exp_pos = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ CExtPubKey xpub_fetched;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ (void)descriptor_cache.GetCachedParentExtPubKey(key_exp_pos, xpub_fetched);
+ descriptor_cache.CacheParentExtPubKey(key_exp_pos, xpub);
+ assert(descriptor_cache.GetCachedParentExtPubKey(key_exp_pos, xpub_fetched));
+ } else {
+ const uint32_t der_index = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ (void)descriptor_cache.GetCachedDerivedExtPubKey(key_exp_pos, der_index, xpub_fetched);
+ descriptor_cache.CacheDerivedExtPubKey(key_exp_pos, der_index, xpub);
+ assert(descriptor_cache.GetCachedDerivedExtPubKey(key_exp_pos, der_index, xpub_fetched));
+ }
+ assert(xpub == xpub_fetched);
+ }
+ (void)descriptor_cache.GetCachedParentExtPubKeys();
+ (void)descriptor_cache.GetCachedDerivedExtPubKeys();
+ }
+}
diff --git a/src/test/fuzz/script_interpreter.cpp b/src/test/fuzz/script_interpreter.cpp
new file mode 100644
index 0000000000..26d5732f24
--- /dev/null
+++ b/src/test/fuzz/script_interpreter.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <primitives/transaction.h>
+#include <script/interpreter.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+bool CastToBool(const std::vector<unsigned char>& vch);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ {
+ const CScript script_code = ConsumeScript(fuzzed_data_provider);
+ const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (mtx) {
+ const CTransaction tx_to{*mtx};
+ const unsigned int in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ if (in < tx_to.vin.size()) {
+ (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), nullptr);
+ const std::optional<CMutableTransaction> mtx_precomputed = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (mtx_precomputed) {
+ const CTransaction tx_precomputed{*mtx_precomputed};
+ const PrecomputedTransactionData precomputed_transaction_data{tx_precomputed};
+ (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), &precomputed_transaction_data);
+ }
+ }
+ }
+ }
+ {
+ (void)CastToBool(ConsumeRandomLengthByteVector(fuzzed_data_provider));
+ }
+}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
new file mode 100644
index 0000000000..434a47b702
--- /dev/null
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/sigcache.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+ InitSignatureCache();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const CTransaction tx = mutable_transaction ? CTransaction{*mutable_transaction} : CTransaction{};
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const CAmount amount = ConsumeMoney(fuzzed_data_provider);
+ const bool store = fuzzed_data_provider.ConsumeBool();
+ PrecomputedTransactionData tx_data;
+ CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
+ const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (pub_key) {
+ const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (!random_bytes.empty()) {
+ (void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
+ }
+}
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
new file mode 100644
index 0000000000..c626f950e7
--- /dev/null
+++ b/src/test/fuzz/script_sign.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <key.h>
+#include <pubkey.h>
+#include <script/keyorigin.h>
+#include <script/sign.h>
+#include <script/signingprovider.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <map>
+#include <optional>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const ECCVerifyHandle ecc_verify_handle;
+ ECC_Start();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::vector<uint8_t> key = ConsumeRandomLengthByteVector(fuzzed_data_provider, 128);
+
+ {
+ CDataStream random_data_stream = ConsumeDataStream(fuzzed_data_provider);
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ try {
+ DeserializeHDKeypaths(random_data_stream, key, hd_keypaths);
+ } catch (const std::ios_base::failure&) {
+ }
+ CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
+ SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ {
+ std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (!pub_key) {
+ break;
+ }
+ const std::optional<KeyOriginInfo> key_origin_info = ConsumeDeserializable<KeyOriginInfo>(fuzzed_data_provider);
+ if (!key_origin_info) {
+ break;
+ }
+ hd_keypaths[*pub_key] = *key_origin_info;
+ }
+ CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
+ try {
+ SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ } catch (const std::ios_base::failure&) {
+ }
+ std::map<CPubKey, KeyOriginInfo> deserialized_hd_keypaths;
+ try {
+ DeserializeHDKeypaths(serialized, key, hd_keypaths);
+ } catch (const std::ios_base::failure&) {
+ }
+ assert(hd_keypaths.size() >= deserialized_hd_keypaths.size());
+ }
+
+ {
+ SignatureData signature_data_1{ConsumeScript(fuzzed_data_provider)};
+ SignatureData signature_data_2{ConsumeScript(fuzzed_data_provider)};
+ signature_data_1.MergeSignatureData(signature_data_2);
+ }
+
+ FillableSigningProvider provider;
+ CKey k;
+ const std::vector<uint8_t> key_data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ k.Set(key_data.begin(), key_data.end(), fuzzed_data_provider.ConsumeBool());
+ if (k.IsValid()) {
+ provider.AddKey(k);
+ }
+
+ {
+ const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ const std::optional<CTxOut> tx_out = ConsumeDeserializable<CTxOut>(fuzzed_data_provider);
+ const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ if (mutable_transaction && tx_out && mutable_transaction->vin.size() > n_in) {
+ SignatureData signature_data_1 = DataFromTransaction(*mutable_transaction, n_in, *tx_out);
+ CTxIn input;
+ UpdateInput(input, signature_data_1);
+ const CScript script = ConsumeScript(fuzzed_data_provider);
+ SignatureData signature_data_2{script};
+ signature_data_1.MergeSignatureData(signature_data_2);
+ }
+ if (mutable_transaction) {
+ CTransaction tx_from{*mutable_transaction};
+ CMutableTransaction tx_to;
+ const std::optional<CMutableTransaction> opt_tx_to = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (opt_tx_to) {
+ tx_to = *opt_tx_to;
+ }
+ CMutableTransaction script_tx_to = tx_to;
+ CMutableTransaction sign_transaction_tx_to = tx_to;
+ if (n_in < tx_to.vin.size() && tx_to.vin[n_in].prevout.n < tx_from.vout.size()) {
+ (void)SignSignature(provider, tx_from, tx_to, n_in, fuzzed_data_provider.ConsumeIntegral<int>());
+ }
+ if (n_in < script_tx_to.vin.size()) {
+ (void)SignSignature(provider, ConsumeScript(fuzzed_data_provider), script_tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>());
+ MutableTransactionSignatureCreator signature_creator{&tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>()};
+ std::vector<unsigned char> vch_sig;
+ CKeyID address;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ if (k.IsValid()) {
+ address = k.GetPubKey().GetID();
+ }
+ } else {
+ address = CKeyID{ConsumeUInt160(fuzzed_data_provider)};
+ }
+ (void)signature_creator.CreateSig(provider, vch_sig, address, ConsumeScript(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}));
+ }
+ std::map<COutPoint, Coin> coins;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::optional<COutPoint> outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+ if (!outpoint) {
+ break;
+ }
+ const std::optional<Coin> coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+ if (!coin) {
+ break;
+ }
+ coins[*outpoint] = *coin;
+ }
+ std::map<int, std::string> input_errors;
+ (void)SignTransaction(sign_transaction_tx_to, &provider, coins, fuzzed_data_provider.ConsumeIntegral<int>(), input_errors);
+ }
+ }
+
+ {
+ SignatureData signature_data_1;
+ (void)ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, ConsumeScript(fuzzed_data_provider), signature_data_1);
+ SignatureData signature_data_2;
+ (void)ProduceSignature(provider, DUMMY_MAXIMUM_SIGNATURE_CREATOR, ConsumeScript(fuzzed_data_provider), signature_data_2);
+ }
+}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 50984b1aef..271062dc95 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -12,6 +12,7 @@
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
+#include <script/script.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -89,6 +90,10 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)urlDecode(random_string_1);
(void)ValidAsCString(random_string_1);
(void)_(random_string_1.c_str());
+ try {
+ throw scriptnum_error{random_string_1};
+ } catch (const std::runtime_error&) {
+ }
{
CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 9d0fb02128..1c1b2cd254 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -12,6 +12,7 @@
#include <consensus/consensus.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -20,6 +21,7 @@
#include <uint256.h>
#include <version.h>
+#include <algorithm>
#include <cstdint>
#include <optional>
#include <string>
@@ -31,6 +33,11 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr
return {s.begin(), s.end()};
}
+NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+{
+ return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
+}
+
NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept
{
const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
@@ -87,10 +94,19 @@ NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_pro
return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
}
+NODISCARD inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const std::vector<uint8_t> v160 = fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8);
+ if (v160.size() != 160 / 8) {
+ return {};
+ }
+ return uint160{v160};
+}
+
NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
- const std::vector<unsigned char> v256 = fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256));
- if (v256.size() != sizeof(uint256)) {
+ const std::vector<uint8_t> v256 = fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8);
+ if (v256.size() != 256 / 8) {
return {};
}
return uint256{v256};
@@ -116,6 +132,43 @@ NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzze
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
}
+NODISCARD inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ CTxDestination tx_destination;
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
+ case 0: {
+ tx_destination = CNoDestination{};
+ break;
+ }
+ case 1: {
+ tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 2: {
+ tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 3: {
+ tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
+ break;
+ }
+ case 4: {
+ tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
+ break;
+ }
+ case 5: {
+ WitnessUnknown witness_unknown{};
+ witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<int>();
+ const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
+ witness_unknown.length = witness_unknown_program_1.size();
+ std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
+ tx_destination = witness_unknown;
+ break;
+ }
+ }
+ return tx_destination;
+}
+
template <typename T>
NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept
{
@@ -161,4 +214,18 @@ NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsVie
return false;
}
+/**
+ * Returns a byte vector of specified size regardless of the number of remaining bytes available
+ * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
+ */
+NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept
+{
+ std::vector<uint8_t> result(length);
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length);
+ if (!random_bytes.empty()) {
+ std::memcpy(result.data(), random_bytes.data(), random_bytes.size());
+ }
+ return result;
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 512e48f8e5..45c9b90ee9 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -13,9 +13,18 @@
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)
+namespace getarg_tests{
+ class LocalTestingSetup : BasicTestingSetup {
+ protected:
+ void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args);
+ void ResetArgs(const std::string& strArg);
+ ArgsManager m_args;
+ };
+}
+
+BOOST_FIXTURE_TEST_SUITE(getarg_tests, LocalTestingSetup)
-static void ResetArgs(const std::string& strArg)
+void LocalTestingSetup :: ResetArgs(const std::string& strArg)
{
std::vector<std::string> vecArg;
if (strArg.size())
@@ -30,14 +39,14 @@ static void ResetArgs(const std::string& strArg)
vecChar.push_back(s.c_str());
std::string error;
- BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
+ BOOST_CHECK(m_args.ParseParameters(vecChar.size(), vecChar.data(), error));
}
-static void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
+void LocalTestingSetup :: SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
- gArgs.ClearArgs();
+ m_args.ClearArgs();
for (const auto& arg : args) {
- gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
+ m_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
@@ -46,52 +55,52 @@ BOOST_AUTO_TEST_CASE(boolarg)
const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
SetupArgs({foo});
ResetArgs("-foo");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-fo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-fo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-fo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-fo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-fooo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-fooo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-fooo", true));
ResetArgs("-foo=0");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
// New 0.6 feature: auto-map -nosomething to !-something:
ResetArgs("-nofoo");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo -nofoo"); // -nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
// New 0.6 feature: treat -- same as -:
ResetArgs("--foo=1");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
ResetArgs("--nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
}
@@ -101,24 +110,24 @@ BOOST_AUTO_TEST_CASE(stringarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=11");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "11");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "11");
ResetArgs("-foo=eleven");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "eleven");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
}
@@ -128,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 11);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 0);
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
ResetArgs("-foo=11 -bar=12");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 11);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 12);
ResetArgs("-foo=NaN -bar=NotANumber");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0);
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 1), 0);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
}
BOOST_AUTO_TEST_CASE(doubledash)
@@ -150,11 +159,11 @@ BOOST_AUTO_TEST_CASE(doubledash)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("--foo");
- BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);
+ BOOST_CHECK_EQUAL(m_args.GetBoolArg("-foo", false), true);
ResetArgs("--foo=verbose --bar=1");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose");
- BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1);
+ BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "verbose");
+ BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 0), 1);
}
BOOST_AUTO_TEST_CASE(boolargno)
@@ -163,24 +172,24 @@ BOOST_AUTO_TEST_CASE(boolargno)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("-nofoo");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=0");
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
ResetArgs("-foo --nofoo"); // --nofoo should win
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo -foo"); // foo always wins:
- BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
- BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_args.GetBoolArg("-foo", false));
}
BOOST_AUTO_TEST_CASE(logargs)
@@ -200,7 +209,7 @@ BOOST_AUTO_TEST_CASE(logargs)
});
// Log the arguments
- gArgs.LogArgs();
+ m_args.LogArgs();
LogInstance().DeleteCallback(print_connection);
// Check that what should appear does, and what shouldn't doesn't.
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 57eee94330..11ff7b833b 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -448,7 +448,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
@@ -461,7 +461,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 2e1972cc3f..d0ec401f9d 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -6,6 +6,7 @@
#include <netbase.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
+#include <util/translation.h>
#include <string>
@@ -325,15 +326,15 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_AUTO_TEST_CASE(netpermissions_test)
{
- std::string error;
+ bilingual_str error;
NetWhitebindPermissions whitebindPermissions;
NetWhitelistPermissions whitelistPermissions;
// Detect invalid white bind
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
- BOOST_CHECK(error.find("Cannot resolve -whitebind address") != std::string::npos);
+ BOOST_CHECK(error.original.find("Cannot resolve -whitebind address") != std::string::npos);
BOOST_CHECK(!NetWhitebindPermissions::TryParse("127.0.0.1", whitebindPermissions, error));
- BOOST_CHECK(error.find("Need to specify a port with -whitebind") != std::string::npos);
+ BOOST_CHECK(error.original.find("Need to specify a port with -whitebind") != std::string::npos);
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
// If no permission flags, assume backward compatibility
@@ -377,11 +378,11 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
// Detect invalid flag
BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK(error.find("Invalid P2P permission") != std::string::npos);
+ BOOST_CHECK(error.original.find("Invalid P2P permission") != std::string::npos);
// Check whitelist error
BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
- BOOST_CHECK(error.find("Invalid netmask specified in -whitelist") != std::string::npos);
+ BOOST_CHECK(error.original.find("Invalid netmask specified in -whitelist") != std::string::npos);
// Happy path for whitelist parsing
BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 04bf7c20c1..8c2712f764 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -4,9 +4,6 @@
#include <event2/event.h>
-#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
-// It would probably be ideal to define dummy test(s) that report skipped, but boost::test doesn't seem to make that practical (at least not in versions available with common distros)
-
#include <map>
#include <stdlib.h>
@@ -16,6 +13,10 @@
#include <boost/test/unit_test.hpp>
+BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
+
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+
static std::map<void*, short> tags;
static std::map<void*, uint16_t> orders;
static uint16_t tagSequence = 0;
@@ -34,8 +35,6 @@ static void tag_free(void* mem) {
free(mem);
}
-BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
-
BOOST_AUTO_TEST_CASE(raii_event_creation)
{
event_set_mem_functions(tag_malloc, realloc, tag_free);
@@ -87,6 +86,14 @@ BOOST_AUTO_TEST_CASE(raii_event_order)
event_set_mem_functions(malloc, realloc, free);
}
-BOOST_AUTO_TEST_SUITE_END()
+#else
+
+BOOST_AUTO_TEST_CASE(raii_event_tests_SKIPPED)
+{
+ // It would probably be ideal to report skipped, but boost::test doesn't seem to make that practical (at least not in versions available with common distros)
+ BOOST_TEST_MESSAGE("Skipping raii_event_tess: libevent doesn't support event_set_mem_functions");
+}
#endif // EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 1395a7f38c..fcee6a9b9d 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -7,7 +7,7 @@
#include <util/time.h>
#include <boost/test/unit_test.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
#include <mutex>
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 56454f61f3..cb3ae290d1 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -102,7 +102,7 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
};
-static const char *FormatScriptError(ScriptError_t err)
+static std::string FormatScriptError(ScriptError_t err)
{
for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
if (script_errors[i].err == err)
@@ -134,7 +134,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, scriptWitness, txCredit);
CMutableTransaction tx2 = tx;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message);
- BOOST_CHECK_MESSAGE(err == scriptError, std::string(FormatScriptError(err)) + " where " + std::string(FormatScriptError((ScriptError_t)scriptError)) + " expected: " + message);
+ BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
for (int i = 0; i < 16; ++i) {
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 8c880babd1..1dcee23bbb 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -9,6 +9,7 @@
#include <test/util/setup_common.h>
#include <timedata.h>
#include <util/string.h>
+#include <util/translation.h>
#include <warnings.h>
#include <string>
@@ -66,7 +67,7 @@ BOOST_AUTO_TEST_CASE(addtimedata)
MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
}
- BOOST_CHECK(GetWarnings(true).find("clock is wrong") != std::string::npos);
+ BOOST_CHECK(GetWarnings(true).original.find("clock is wrong") != std::string::npos);
// nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT
BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 2477f9ad06..d5cda8a95b 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -17,7 +17,7 @@
#include <type_traits>
-#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
/** This is connected to the logger. Can be used to redirect logs to any other log */
extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
diff --git a/src/threadsafety.h b/src/threadsafety.h
index bb988dfdfd..942aa3fdcd 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_THREADSAFETY_H
#define BITCOIN_THREADSAFETY_H
+#include <mutex>
+
#ifdef __clang__
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
@@ -54,4 +56,19 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
+// StdMutex provides an annotated version of std::mutex for us,
+// and should only be used when sync.h Mutex/LOCK/etc are not usable.
+class LOCKABLE StdMutex : public std::mutex
+{
+};
+
+// StdLockGuard provides an annotated version of std::lock_guard for us,
+// and should only be used when sync.h Mutex/LOCK/etc are not usable.
+class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard<StdMutex>
+{
+public:
+ explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
+ ~StdLockGuard() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.cpp b/src/timedata.cpp
index dd56acf44f..16dac24a48 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -15,9 +15,8 @@
#include <util/translation.h>
#include <warnings.h>
-
-static RecursiveMutex cs_nTimeOffset;
-static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0;
+static Mutex g_timeoffset_mutex;
+static int64_t nTimeOffset GUARDED_BY(g_timeoffset_mutex) = 0;
/**
* "Never go to sea with two chronometers; take one or three."
@@ -28,7 +27,7 @@ static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0;
*/
int64_t GetTimeOffset()
{
- LOCK(cs_nTimeOffset);
+ LOCK(g_timeoffset_mutex);
return nTimeOffset;
}
@@ -46,7 +45,7 @@ static int64_t abs64(int64_t n)
void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
{
- LOCK(cs_nTimeOffset);
+ LOCK(g_timeoffset_mutex);
// Ignore duplicates
static std::set<CNetAddr> setKnown;
if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
@@ -57,7 +56,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
// Add data
static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
vTimeOffsets.input(nOffsetSample);
- LogPrint(BCLog::NET,"added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+ LogPrint(BCLog::NET, "added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample / 60);
// There is a known issue here (see issue #4521):
//
@@ -76,33 +75,27 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
// So we should hold off on fixing this and clean it up as part of
// a timing cleanup that strengthens it in a number of other ways.
//
- if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
- {
+ if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) {
int64_t nMedian = vTimeOffsets.median();
std::vector<int64_t> vSorted = vTimeOffsets.sorted();
// Only let other nodes change our time by so much
- if (abs64(nMedian) <= std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT)))
- {
+ if (abs64(nMedian) <= std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT))) {
nTimeOffset = nMedian;
- }
- else
- {
+ } else {
nTimeOffset = 0;
static bool fDone;
- if (!fDone)
- {
+ if (!fDone) {
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
bool fMatch = false;
- for (const int64_t nOffset : vSorted)
- if (nOffset != 0 && abs64(nOffset) < 5 * 60)
- fMatch = true;
+ for (const int64_t nOffset : vSorted) {
+ if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true;
+ }
- if (!fMatch)
- {
+ if (!fMatch) {
fDone = true;
bilingual_str 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);
- SetMiscWarning(strMessage.translated);
+ SetMiscWarning(strMessage);
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
}
}
@@ -113,8 +106,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
LogPrint(BCLog::NET, "%+d ", n); /* Continued */
}
LogPrint(BCLog::NET, "| "); /* Continued */
-
- LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
+ LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset / 60);
}
}
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 129697f0e7..6f652c1375 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -16,8 +16,6 @@
#include <stdint.h>
-#include <boost/thread.hpp>
-
static const char DB_COIN = 'C';
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
@@ -242,7 +240,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load m_block_index
while (pcursor->Valid()) {
- boost::this_thread::interruption_point();
if (ShutdownRequested()) return false;
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
@@ -354,7 +351,6 @@ bool CCoinsViewDB::Upgrade() {
std::pair<unsigned char, uint256> key;
std::pair<unsigned char, uint256> prev_key = {DB_COINS, uint256()};
while (pcursor->Valid()) {
- boost::this_thread::interruption_point();
if (ShutdownRequested()) {
break;
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c5c0208d8f..7d8eb8a323 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -410,7 +410,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
// for any reason except being included in a block. Clients interested
// in transactions included in blocks can subscribe to the BlockConnected
// notification.
- GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx());
+ GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason);
}
const uint256 hash = it->GetTx().GetHash();
diff --git a/src/txmempool.h b/src/txmempool.h
index 4568eb928d..583f7614b7 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -704,7 +704,7 @@ public:
/** Adds a transaction to the unbroadcast set */
void AddUnbroadcastTx(const uint256& txid) {
LOCK(cs);
- /** Sanity Check: the transaction should also be in the mempool */
+ // Sanity Check: the transaction should also be in the mempool
if (exists(txid)) {
m_unbroadcast_txids.insert(txid);
}
@@ -714,12 +714,12 @@ public:
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);
/** Returns transactions in unbroadcast set */
- const std::set<uint256> GetUnbroadcastTxs() const {
+ std::set<uint256> GetUnbroadcastTxs() const {
LOCK(cs);
return m_unbroadcast_txids;
}
- // Returns if a txid is in the unbroadcast set
+ /** Returns whether a txid is in the unbroadcast set */
bool IsUnbroadcastTx(const uint256& txid) const {
LOCK(cs);
return (m_unbroadcast_txids.count(txid) != 0);
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 72a6e87cde..c4d9ffd037 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -8,37 +8,37 @@
#include <util/system.h>
#include <util/translation.h>
-std::string TransactionErrorString(const TransactionError err)
+bilingual_str TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
- return "No error";
+ return Untranslated("No error");
case TransactionError::MISSING_INPUTS:
- return "Missing inputs";
+ return Untranslated("Missing inputs");
case TransactionError::ALREADY_IN_CHAIN:
- return "Transaction already in block chain";
+ return Untranslated("Transaction already in block chain");
case TransactionError::P2P_DISABLED:
- return "Peer-to-peer functionality missing or disabled";
+ return Untranslated("Peer-to-peer functionality missing or disabled");
case TransactionError::MEMPOOL_REJECTED:
- return "Transaction rejected by AcceptToMemoryPool";
+ return Untranslated("Transaction rejected by AcceptToMemoryPool");
case TransactionError::MEMPOOL_ERROR:
- return "AcceptToMemoryPool failed";
+ return Untranslated("AcceptToMemoryPool failed");
case TransactionError::INVALID_PSBT:
- return "PSBT is not sane";
+ return Untranslated("PSBT is not sane");
case TransactionError::PSBT_MISMATCH:
- return "PSBTs not compatible (different transactions)";
+ return Untranslated("PSBTs not compatible (different transactions)");
case TransactionError::SIGHASH_MISMATCH:
- return "Specified sighash value does not match existing value";
+ return Untranslated("Specified sighash value does not match existing value");
case TransactionError::MAX_FEE_EXCEEDED:
- return "Fee exceeds maximum configured by -maxtxfee";
+ return Untranslated("Fee exceeds maximum configured by -maxtxfee");
// no default case, so the compiler can warn about missing cases
}
assert(false);
}
-std::string ResolveErrMsg(const std::string& optname, const std::string& strBind)
+bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind)
{
- return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
+ return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
bilingual_str AmountHighWarn(const std::string& optname)
diff --git a/src/util/error.h b/src/util/error.h
index 61af88ddea..b9830c9eea 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -32,9 +32,9 @@ enum class TransactionError {
MAX_FEE_EXCEEDED,
};
-std::string TransactionErrorString(const TransactionError error);
+bilingual_str TransactionErrorString(const TransactionError error);
-std::string ResolveErrMsg(const std::string& optname, const std::string& strBind);
+bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind);
bilingual_str AmountHighWarn(const std::string& optname);
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 2013b416db..7e7ba840cd 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <sync.h>
#include <util/system.h>
#include <chainparamsbase.h>
@@ -75,18 +76,18 @@ const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
ArgsManager gArgs;
+/** Mutex to protect dir_locks. */
+static Mutex cs_dir_locks;
/** A map that contains all the currently held directory locks. After
* successful locking, these will be held here until the global destructor
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
* is called.
*/
-static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
-/** Mutex to protect dir_locks. */
-static std::mutex cs_dir_locks;
+static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
{
- std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ LOCK(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
// If a lock for this directory already exists in the map, don't try to re-lock it
@@ -110,13 +111,13 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
{
- std::lock_guard<std::mutex> lock(cs_dir_locks);
+ LOCK(cs_dir_locks);
dir_locks.erase((directory / lockfile_name).string());
}
void ReleaseDirectoryLocks()
{
- std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ LOCK(cs_dir_locks);
dir_locks.clear();
}
@@ -1162,8 +1163,9 @@ void ScheduleBatchPriority()
{
#ifdef SCHED_BATCH
const static sched_param param{};
- if (pthread_setschedparam(pthread_self(), SCHED_BATCH, &param) != 0) {
- LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
+ const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param);
+ if (rc != 0) {
+ LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(rc));
}
#endif
}
diff --git a/src/util/translation.h b/src/util/translation.h
index 268bcf30a7..695d6dac96 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -23,6 +23,11 @@ struct bilingual_str {
translated += rhs.translated;
return *this;
}
+
+ bool empty() const
+ {
+ return original.empty();
+ }
};
inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs)
diff --git a/src/validation.cpp b/src/validation.cpp
index dbdf5028fd..cbe89443b8 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -50,7 +50,6 @@
#include <string>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/thread.hpp>
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
@@ -71,12 +70,20 @@ static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
-/** Time to wait (in seconds) between writing blocks/block index to disk. */
-static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
-/** Time to wait (in seconds) between flushing chainstate to disk. */
-static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
-/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
-static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
+/** Time to wait between writing blocks/block index to disk. */
+static constexpr std::chrono::hours DATABASE_WRITE_INTERVAL{1};
+/** Time to wait between flushing chainstate to disk. */
+static constexpr std::chrono::hours DATABASE_FLUSH_INTERVAL{24};
+/** Maximum age of our tip for us to be considered current for fee estimation */
+static constexpr std::chrono::hours MAX_FEE_ESTIMATION_TIP_AGE{3};
+const std::vector<std::string> CHECKLEVEL_DOC {
+ "level 0 reads the blocks from disk",
+ "level 1 verifies block validity",
+ "level 2 verifies undo data",
+ "level 3 checks disconnection of tip blocks",
+ "level 4 tries to reconnect the blocks",
+ "each level includes the checks of the previous levels",
+};
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
// First sort by most total work, ...
@@ -295,7 +302,7 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
prevheights[txinIndex] = coin.nHeight;
}
}
- lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
+ lockPair = CalculateSequenceLocks(tx, flags, prevheights, index);
if (lp) {
lp->height = lockPair.first;
lp->time = lockPair.second;
@@ -347,7 +354,7 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
if (::ChainstateActive().IsInitialBlockDownload())
return false;
- if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
return false;
@@ -1481,14 +1488,21 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
}
-static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache;
-static uint256 scriptExecutionCacheNonce(GetRandHash());
+static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
+static CSHA256 g_scriptExecutionCacheHasher;
void InitScriptExecutionCache() {
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ // We want the nonce to be 64 bytes long to force the hasher to process
+ // this chunk, which makes later hash computations more efficient. We
+ // just write our 32-byte entropy twice to fill the 64 bytes.
+ g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
+ g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize);
+ size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}
@@ -1526,12 +1540,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
- // We only use the first 19 bytes of nonce to avoid a second SHA
- // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
- static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
- CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
+ CSHA256 hasher = g_scriptExecutionCacheHasher;
+ hasher.Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
- if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}
@@ -1586,7 +1598,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
- scriptExecutionCache.insert(hashCacheEntry);
+ g_scriptExecutionCache.insert(hashCacheEntry);
}
return true;
@@ -1654,7 +1666,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
// TODO: AbortNode() should take bilingual_str userMessage parameter.
static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
{
- SetMiscWarning(strMessage);
+ SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
if (!userMessage.empty()) {
uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix);
@@ -1765,19 +1777,24 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
-void static FlushBlockFile(bool fFinalize = false)
+static void FlushUndoFile(int block_file, bool finalize = false)
{
- LOCK(cs_LastBlockFile);
+ FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
+ if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
+ AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
+ }
+}
+static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
+{
+ LOCK(cs_LastBlockFile);
FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
- FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize);
-
- bool status = true;
- status &= BlockFileSeq().Flush(block_pos_old, fFinalize);
- status &= UndoFileSeq().Flush(undo_pos_old, fFinalize);
- if (!status) {
+ if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
+ // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
+ // e.g. during IBD or a sync after a node going offline
+ if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
}
static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
@@ -1791,6 +1808,14 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationSt
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
+ // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
+ // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
+ // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
+ // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
+ // the FindBlockPos function
+ if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
+ FlushUndoFile(_pos.nFile, true);
+ }
// update nUndoPos in block index
pindex->nUndoPos = _pos.nPos;
@@ -2147,7 +2172,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
}
- if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
+ if (!SequenceLocks(tx, nLockTimeFlags, prevheights, *pindex)) {
LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-nonfinal");
}
@@ -2264,8 +2289,8 @@ bool CChainState::FlushStateToDisk(
{
LOCK(cs_main);
assert(this->CanFlushToDisk());
- static int64_t nLastWrite = 0;
- static int64_t nLastFlush = 0;
+ static std::chrono::microseconds nLastWrite{0};
+ static std::chrono::microseconds nLastFlush{0};
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
@@ -2297,12 +2322,12 @@ bool CChainState::FlushStateToDisk(
}
}
}
- int64_t nNow = GetTimeMicros();
+ const auto nNow = GetTime<std::chrono::microseconds>();
// Avoid writing/flushing immediately after startup.
- if (nLastWrite == 0) {
+ if (nLastWrite.count() == 0) {
nLastWrite = nNow;
}
- if (nLastFlush == 0) {
+ if (nLastFlush.count() == 0) {
nLastFlush = nNow;
}
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
@@ -2310,9 +2335,9 @@ bool CChainState::FlushStateToDisk(
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cache_state >= CoinsCacheSizeState::CRITICAL;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
- bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + DATABASE_WRITE_INTERVAL;
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
- bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
@@ -2404,20 +2429,20 @@ void CChainState::PruneAndFlush() {
}
}
-static void DoWarning(const std::string& strWarning)
+static void DoWarning(const bilingual_str& warning)
{
static bool fWarned = false;
- SetMiscWarning(strWarning);
+ SetMiscWarning(warning);
if (!fWarned) {
- AlertNotify(strWarning);
+ AlertNotify(warning.original);
fWarned = true;
}
}
/** Private helper function that concatenates warning messages. */
-static void AppendWarning(std::string& res, const std::string& warn)
+static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
{
- if (!res.empty()) res += ", ";
+ if (!res.empty()) res += Untranslated(", ");
res += warn;
}
@@ -2434,7 +2459,7 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
g_best_block_cv.notify_all();
}
- std::string warningMessages;
+ bilingual_str warning_messages;
if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
@@ -2443,11 +2468,11 @@ 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)").translated, bit);
+ const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
- DoWarning(strWarning);
+ DoWarning(warning);
} else {
- AppendWarning(warningMessages, strWarning);
+ AppendWarning(warning_messages, warning);
}
}
}
@@ -2460,14 +2485,14 @@ 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").translated, nUpgraded));
+ AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
- !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
+ !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
@@ -2854,8 +2879,6 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
- boost::this_thread::interruption_point();
-
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead.
@@ -2924,8 +2947,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
// never shutdown before connecting the genesis block during LoadChainTip(). Previously this
// caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks
// that the best block hash is non-null.
- if (ShutdownRequested())
- break;
+ if (ShutdownRequested()) break;
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
@@ -3243,8 +3265,13 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
vinfoBlockFile.resize(nFile + 1);
}
+ bool finalize_undo = false;
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
+ // when the undo file is keeping up with the block file, we want to flush it explicitly
+ // when it is lagging behind (more blocks arrive than are being connected), we let the
+ // undo block write case handle it
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -3258,7 +3285,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
if (!fKnown) {
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
- FlushBlockFile(!fKnown);
+ FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
}
@@ -4267,7 +4294,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
- boost::this_thread::interruption_point();
const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4313,8 +4339,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
nGoodTransactions += block.vtx.size();
}
}
- if (ShutdownRequested())
- return true;
+ if (ShutdownRequested()) return true;
}
if (pindexFailure)
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
@@ -4325,7 +4350,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
while (pindex != ::ChainActive().Tip()) {
- boost::this_thread::interruption_point();
const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4339,6 +4363,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ if (ShutdownRequested()) return true;
}
}
@@ -5067,12 +5092,18 @@ bool LoadMempool(CTxMemPool& pool)
pool.PrioritiseTransaction(i.first, i.second);
}
- std::set<uint256> unbroadcast_txids;
- file >> unbroadcast_txids;
- unbroadcast = unbroadcast_txids.size();
+ // TODO: remove this try except in v0.22
+ try {
+ std::set<uint256> unbroadcast_txids;
+ file >> unbroadcast_txids;
+ unbroadcast = unbroadcast_txids.size();
- for (const auto& txid : unbroadcast_txids) {
+ for (const auto& txid : unbroadcast_txids) {
pool.AddUnbroadcastTx(txid);
+ }
+ } catch (const std::exception&) {
+ // mempool.dat files created prior to v0.21 will not have an
+ // unbroadcast set. No need to log a failure if parsing fails here.
}
} catch (const std::exception& e) {
diff --git a/src/validation.h b/src/validation.h
index 8112e38704..e403bcb51a 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -29,6 +29,7 @@
#include <memory>
#include <set>
#include <stdint.h>
+#include <string>
#include <utility>
#include <vector>
@@ -149,6 +150,8 @@ extern bool fHavePruned;
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
+/** Documentation for argument 'checklevel'. */
+extern const std::vector<std::string> CHECKLEVEL_DOC;
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 9437f9c817..3dfbcc581c 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -199,22 +199,22 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
fInitialDownload);
}
-void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
- auto event = [ptx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(ptx); });
+void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx) {
+ auto event = [tx, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
- ptx->GetHash().ToString(),
- ptx->GetWitnessHash().ToString());
+ tx->GetHash().ToString(),
+ tx->GetWitnessHash().ToString());
}
-void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
- auto event = [ptx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(ptx); });
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
+ auto event = [tx, reason, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
- ptx->GetHash().ToString(),
- ptx->GetWitnessHash().ToString());
+ tx->GetHash().ToString(),
+ tx->GetWitnessHash().ToString());
}
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 9c23965bc1..e96f2883fc 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -21,6 +21,7 @@ class CConnman;
class CValidationInterface;
class uint256;
class CScheduler;
+enum class MemPoolRemovalReason;
/** Register subscriber */
void RegisterValidationInterface(CValidationInterface* callbacks);
@@ -96,7 +97,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
/**
* Notifies listeners of a transaction leaving mempool.
*
@@ -129,7 +130,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) {}
+ virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
/**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
@@ -196,8 +197,8 @@ public:
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
- void TransactionAddedToMempool(const CTransactionRef &);
- void TransactionRemovedFromMempool(const CTransactionRef &);
+ void TransactionAddedToMempool(const CTransactionRef&);
+ void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
diff --git a/src/wallet/context.cpp b/src/wallet/context.cpp
new file mode 100644
index 0000000000..09b2f30467
--- /dev/null
+++ b/src/wallet/context.cpp
@@ -0,0 +1,8 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <wallet/context.h>
+
+WalletContext::WalletContext() {}
+WalletContext::~WalletContext() {}
diff --git a/src/wallet/context.h b/src/wallet/context.h
new file mode 100644
index 0000000000..3c8fdd1c59
--- /dev/null
+++ b/src/wallet/context.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_CONTEXT_H
+#define BITCOIN_WALLET_CONTEXT_H
+
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+//! WalletContext struct containing references to state shared between CWallet
+//! instances, like the reference to the chain interface, and the list of opened
+//! wallets.
+//!
+//! Future shared state can be added here as an alternative to adding global
+//! variables.
+//!
+//! The struct isn't intended to have any member functions. It should just be a
+//! collection of state pointers that doesn't pull in dependencies or implement
+//! behavior.
+struct WalletContext {
+ interfaces::Chain* chain{nullptr};
+
+ //! Declare default constructor and destructor that are not inline, so code
+ //! instantiating the WalletContext struct doesn't need to #include class
+ //! definitions for smart pointer and container members.
+ WalletContext();
+ ~WalletContext();
+};
+
+#endif // BITCOIN_WALLET_CONTEXT_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 1b2bd83a4c..d90e8e6433 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -14,8 +14,6 @@
#include <sys/stat.h>
#endif
-#include <boost/thread.hpp>
-
namespace {
//! Make sure database has a unique fileid within the environment. If it
@@ -268,21 +266,14 @@ BerkeleyEnvironment::BerkeleyEnvironment()
fMockDb = true;
}
-BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
+bool BerkeleyEnvironment::Verify(const std::string& strFile)
{
LOCK(cs_db);
assert(mapFileUseCount.count(strFile) == 0);
Db db(dbenv.get(), 0);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
- if (result == 0)
- return VerifyResult::VERIFY_OK;
- else if (recoverFunc == nullptr)
- return VerifyResult::RECOVER_FAIL;
-
- // Try to recover:
- bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
- return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
+ return result == 0;
}
BerkeleyBatch::SafeDbt::SafeDbt()
@@ -324,75 +315,6 @@ BerkeleyBatch::SafeDbt::operator Dbt*()
return &m_dbt;
}
-bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
-{
- std::string filename;
- std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
-
- // Recovery procedure:
- // move wallet file to walletfilename.timestamp.bak
- // Call Salvage with fAggressive=true to
- // get as much data as possible.
- // Rewrite salvaged data to fresh wallet file
- // Set -rescan so any missing transactions will be
- // found.
- int64_t now = GetTime();
- newFilename = strprintf("%s.%d.bak", filename, now);
-
- int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
- newFilename.c_str(), DB_AUTO_COMMIT);
- if (result == 0)
- LogPrintf("Renamed %s to %s\n", filename, newFilename);
- else
- {
- LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
- return false;
- }
-
- std::vector<BerkeleyEnvironment::KeyValPair> salvagedData;
- bool fSuccess = env->Salvage(newFilename, true, salvagedData);
- if (salvagedData.empty())
- {
- LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
- return false;
- }
- LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
-
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
- int ret = pdbCopy->open(nullptr, // Txn pointer
- filename.c_str(), // Filename
- "main", // Logical db name
- DB_BTREE, // Database type
- DB_CREATE, // Flags
- 0);
- if (ret > 0) {
- LogPrintf("Cannot create database file %s\n", filename);
- pdbCopy->close(0);
- return false;
- }
-
- DbTxn* ptxn = env->TxnBegin();
- for (BerkeleyEnvironment::KeyValPair& row : salvagedData)
- {
- if (recoverKVcallback)
- {
- CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
- CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
- if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
- continue;
- }
- Dbt datKey(&row.first[0], row.first.size());
- Dbt datValue(&row.second[0], row.second.size());
- int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
- if (ret2 > 0)
- fSuccess = false;
- }
- ptxn->commit(0);
- pdbCopy->close(0);
-
- return fSuccess;
-}
-
bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
{
std::string walletFile;
@@ -410,7 +332,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str&
return true;
}
-bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
+bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr)
{
std::string walletFile;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
@@ -418,19 +340,8 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<bi
if (fs::exists(walletDir / walletFile))
{
- std::string backup_filename;
- BerkeleyEnvironment::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
- if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK)
- {
- warnings.push_back(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."),
- walletFile, backup_filename, walletDir));
- }
- if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL)
- {
- errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
+ if (!env->Verify(walletFile)) {
+ errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), walletFile);
return false;
}
}
@@ -438,72 +349,6 @@ bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::vector<bi
return true;
}
-/* End of headers, beginning of key/value data */
-static const char *HEADER_END = "HEADER=END";
-/* End of key/value data */
-static const char *DATA_END = "DATA=END";
-
-bool BerkeleyEnvironment::Salvage(const std::string& strFile, bool fAggressive, std::vector<BerkeleyEnvironment::KeyValPair>& vResult)
-{
- LOCK(cs_db);
- assert(mapFileUseCount.count(strFile) == 0);
-
- u_int32_t flags = DB_SALVAGE;
- if (fAggressive)
- flags |= DB_AGGRESSIVE;
-
- std::stringstream strDump;
-
- Db db(dbenv.get(), 0);
- int result = db.verify(strFile.c_str(), nullptr, &strDump, flags);
- if (result == DB_VERIFY_BAD) {
- LogPrintf("BerkeleyEnvironment::Salvage: Database salvage found errors, all data may not be recoverable.\n");
- if (!fAggressive) {
- LogPrintf("BerkeleyEnvironment::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
- return false;
- }
- }
- if (result != 0 && result != DB_VERIFY_BAD) {
- LogPrintf("BerkeleyEnvironment::Salvage: Database salvage failed with result %d.\n", result);
- return false;
- }
-
- // Format of bdb dump is ascii lines:
- // header lines...
- // HEADER=END
- // hexadecimal key
- // hexadecimal value
- // ... repeated
- // DATA=END
-
- std::string strLine;
- while (!strDump.eof() && strLine != HEADER_END)
- getline(strDump, strLine); // Skip past header
-
- std::string keyHex, valueHex;
- while (!strDump.eof() && keyHex != DATA_END) {
- getline(strDump, keyHex);
- if (keyHex != DATA_END) {
- if (strDump.eof())
- break;
- getline(strDump, valueHex);
- if (valueHex == DATA_END) {
- LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Number of keys in data does not match number of values.\n");
- break;
- }
- vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
- }
- }
-
- if (keyHex != DATA_END) {
- LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
- return false;
- }
-
- return (result == 0);
-}
-
-
void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
{
dbenv->txn_checkpoint(0, 0, 0);
@@ -824,7 +669,6 @@ bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
if (nRefCount == 0)
{
- boost::this_thread::interruption_point();
std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
if (mi != env->mapFileUseCount.end())
{
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 37f96a1a96..54ce144ffc 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -66,26 +66,7 @@ public:
bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; }
- /**
- * Verify that database file strFile is OK. If it is not,
- * call the callback to try to recover.
- * This must be called BEFORE strFile is opened.
- * Returns true if strFile is OK.
- */
- enum class VerifyResult { VERIFY_OK,
- RECOVER_OK,
- RECOVER_FAIL };
- typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
- VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
- /**
- * Salvage data from a file that Verify says is bad.
- * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
- * Appends binary key/value pairs to vResult, returns true if successful.
- * NOTE: reads the entire database into memory, so cannot be used
- * for huge databases.
- */
- typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
- bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
+ bool Verify(const std::string& strFile);
bool Open(bool retry);
void Close();
@@ -245,7 +226,6 @@ public:
void Flush();
void Close();
- static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
/* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */
@@ -253,7 +233,7 @@ public:
/* verifies the database environment */
static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& file_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
+ static bool VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr);
template <typename K, typename T>
bool Read(const K& key, T& value)
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 6f973aab1c..3885eb6185 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -54,7 +54,6 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
@@ -89,16 +88,6 @@ bool WalletInit::ParameterInteraction() const
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- if (gArgs.GetBoolArg("-salvagewallet", false)) {
- if (is_multiwallet) {
- return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-salvagewallet"));
- }
- // Rewrite just private keys: rescan to find transactions
- if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
- }
- }
-
bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
// -zapwallettxes implies dropping the mempool on startup
if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 16f3699d37..8df3e78215 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -37,11 +37,6 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
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
- // here should have no effect.
- bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
-
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
@@ -55,7 +50,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
bilingual_str error_string;
std::vector<bilingual_str> warnings;
- bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warnings);
+ bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!verify_success) {
chain.initError(error_string);
diff --git a/src/wallet/load.h b/src/wallet/load.h
index 5a62e29303..e24b1f2e69 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -16,8 +16,6 @@ class Chain;
} // namespace interfaces
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
-//! This function will perform salvage on the wallet if requested, as long as only one wallet is
-//! being loaded (WalletInit::ParameterInteraction() forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
//! Load wallet databases.
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7bf3d169c3..d9fe741a6e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -92,12 +92,6 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
UniValue importprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -124,6 +118,10 @@ UniValue importprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@@ -195,12 +193,6 @@ UniValue importprivkey(const JSONRPCRequest& request)
UniValue abortrescan(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
@@ -216,6 +208,10 @@ UniValue abortrescan(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
@@ -223,12 +219,6 @@ UniValue abortrescan(const JSONRPCRequest& request)
UniValue importaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -255,6 +245,10 @@ UniValue importaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel;
@@ -325,12 +319,6 @@ UniValue importaddress(const JSONRPCRequest& request)
UniValue importprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
{
@@ -341,6 +329,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
@@ -383,12 +375,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
UniValue removeprunedfunds(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
{
@@ -402,6 +388,10 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -422,12 +412,6 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
UniValue importpubkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -450,6 +434,10 @@ UniValue importpubkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
std::string strLabel;
@@ -509,12 +497,6 @@ UniValue importpubkey(const JSONRPCRequest& request)
UniValue importwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
@@ -532,6 +514,10 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
EnsureLegacyScriptPubKeyMan(*wallet, true);
if (pwallet->chain().havePruned()) {
@@ -667,12 +653,6 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue dumpprivkey(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
@@ -689,6 +669,10 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -714,11 +698,6 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
UniValue dumpwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -739,6 +718,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
CWallet& wallet = *pwallet;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(wallet);
@@ -746,7 +728,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
- LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
+ LOCK2(wallet.cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(&wallet);
@@ -769,7 +751,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
std::map<CKeyID, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = spk_man.GetAllReserveKeys();
- pwallet->GetKeyBirthTimes(mapKeyBirth);
+ wallet.GetKeyBirthTimes(mapKeyBirth);
std::set<CScriptID> scripts = spk_man.GetCScripts();
@@ -1259,12 +1241,6 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
UniValue importmulti(const JSONRPCRequest& mainRequest)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -1340,6 +1316,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
},
}.Check(mainRequest);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
@@ -1585,14 +1564,8 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request) {
- // Acquire the wallet
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
- CWallet* const pwallet = wallet.get();
- if (!EnsureWalletIsAvailable(pwallet, main_request.fHelp)) {
- return NullUniValue;
- }
-
+UniValue importdescriptors(const JSONRPCRequest& main_request)
+{
RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
@@ -1644,6 +1617,10 @@ UniValue importdescriptors(const JSONRPCRequest& main_request) {
},
}.Check(main_request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2a9ac189ea..0907f2aabd 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -21,12 +21,14 @@
#include <util/fees.h>
#include <util/message.h> // For MessageSign()
#include <util/moneystr.h>
+#include <util/ref.h>
#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
#include <util/url.h>
#include <util/vector.h>
#include <wallet/coincontrol.h>
+#include <wallet/context.h>
#include <wallet/feebumper.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
@@ -91,6 +93,7 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string&
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
{
+ CHECK_NONFATAL(!request.fHelp);
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
@@ -99,13 +102,10 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
}
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
- return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
-}
+ if (wallets.size() == 1) {
+ return wallets[0];
+ }
-bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
-{
- if (pwallet) return true;
- if (avoidException) return false;
if (!HasWallets()) {
throw JSONRPCError(
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
@@ -121,6 +121,14 @@ void EnsureWalletIsUnlocked(const CWallet* pwallet)
}
}
+WalletContext& EnsureWalletContext(const util::Ref& context)
+{
+ if (!context.Has<WalletContext>()) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
+ }
+ return context.Get<WalletContext>();
+}
+
// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
{
@@ -185,13 +193,6 @@ static std::string LabelFromValue(const UniValue& value)
static UniValue getnewaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
@@ -209,6 +210,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses()) {
@@ -238,13 +243,6 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
@@ -260,6 +258,10 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses(true)) {
@@ -284,13 +286,6 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
static UniValue setlabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
@@ -304,6 +299,10 @@ static UniValue setlabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -356,13 +355,6 @@ static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& a
static UniValue sendtoaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HELP_REQUIRING_PASSPHRASE,
@@ -396,6 +388,10 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -451,13 +447,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
static UniValue listaddressgroupings(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -483,6 +472,10 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -513,13 +506,6 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
static UniValue signmessage(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HELP_REQUIRING_PASSPHRASE,
@@ -542,6 +528,10 @@ static UniValue signmessage(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -618,13 +608,6 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
@@ -646,6 +629,10 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -658,13 +645,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
@@ -686,6 +666,10 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -698,13 +682,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
static UniValue getbalance(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -728,6 +705,10 @@ static UniValue getbalance(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -755,13 +736,6 @@ static UniValue getbalance(const JSONRPCRequest& request)
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
@@ -769,6 +743,10 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
RPCExamples{""},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -781,13 +759,6 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
static UniValue sendmany(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
@@ -831,6 +802,10 @@ static UniValue sendmany(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -915,13 +890,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
static UniValue addmultisigaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"addmultisigaddress",
"\nAdd an 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"
@@ -954,6 +922,10 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -1157,13 +1129,6 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
@@ -1197,6 +1162,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1208,13 +1177,6 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
@@ -1241,6 +1203,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1369,13 +1335,6 @@ static const std::vector<RPCResult> TransactionDescriptionString()
UniValue listtransactions(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
@@ -1423,6 +1382,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1482,12 +1445,6 @@ UniValue listtransactions(const JSONRPCRequest& request)
static UniValue listsinceblock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
-
- if (!EnsureWalletIsAvailable(pwallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -1541,6 +1498,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
const CWallet& wallet = *pwallet;
// 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
@@ -1623,13 +1583,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
static UniValue gettransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
@@ -1684,6 +1637,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1736,13 +1693,6 @@ static UniValue gettransaction(const JSONRPCRequest& request)
static UniValue abandontransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -1759,6 +1709,10 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1780,13 +1734,6 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
static UniValue backupwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
@@ -1799,6 +1746,10 @@ static UniValue backupwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1816,13 +1767,6 @@ static UniValue backupwallet(const JSONRPCRequest& request)
static UniValue keypoolrefill(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
@@ -1836,6 +1780,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
@@ -1863,13 +1811,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -1891,6 +1832,10 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
int64_t nSleepTime;
int64_t relock_time;
// Prevent concurrent calls to walletpassphrase with the same wallet.
@@ -1960,13 +1905,6 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
static UniValue walletpassphrasechange(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
@@ -1980,6 +1918,10 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2010,13 +1952,6 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
static UniValue walletlock(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -2035,6 +1970,10 @@ static UniValue walletlock(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
@@ -2050,13 +1989,6 @@ static UniValue walletlock(const JSONRPCRequest& request)
static UniValue encryptwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -2081,6 +2013,10 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -2110,13 +2046,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
static UniValue lockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"lockunspent",
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
@@ -2155,6 +2084,10 @@ static UniValue lockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2236,13 +2169,6 @@ static UniValue lockunspent(const JSONRPCRequest& request)
static UniValue listlockunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
@@ -2271,6 +2197,10 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
@@ -2291,13 +2221,6 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
static UniValue settxfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
@@ -2313,6 +2236,10 @@ static UniValue settxfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
@@ -2334,12 +2261,6 @@ static UniValue settxfee(const JSONRPCRequest& request)
static UniValue getbalances(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
- if (!EnsureWalletIsAvailable(rpc_wallet.get(), request.fHelp)) {
- return NullUniValue;
- }
- CWallet& wallet = *rpc_wallet;
-
RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
@@ -2367,6 +2288,10 @@ static UniValue getbalances(const JSONRPCRequest& request)
HelpExampleRpc("getbalances", "")},
}.Check(request);
+ std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ if (!rpc_wallet) return NullUniValue;
+ CWallet& wallet = *rpc_wallet;
+
// 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
wallet.BlockUntilSyncedToCurrentChain();
@@ -2401,13 +2326,6 @@ static UniValue getbalances(const JSONRPCRequest& request)
static UniValue getwalletinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
@@ -2443,6 +2361,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
// 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
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2550,12 +2472,7 @@ static UniValue listwallets(const JSONRPCRequest& request)
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
- if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
- return NullUniValue;
- }
-
LOCK(wallet->cs_wallet);
-
obj.push_back(wallet->GetName());
}
@@ -2584,6 +2501,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ WalletContext& context = EnsureWalletContext(request.context);
WalletLocation location(request.params[0].get_str());
if (!location.Exists()) {
@@ -2598,7 +2516,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warnings);
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, error, warnings);
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
UniValue obj(UniValue::VOBJ);
@@ -2610,13 +2528,6 @@ static UniValue loadwallet(const JSONRPCRequest& request)
static UniValue setwalletflag(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
std::string flags = "";
for (auto& it : WALLET_FLAG_MAP)
if (it.second & MUTABLE_WALLET_FLAGS)
@@ -2641,6 +2552,10 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2702,6 +2617,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ WalletContext& context = EnsureWalletContext(request.context);
uint64_t flags = 0;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
@@ -2731,7 +2647,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
bilingual_str error;
std::shared_ptr<CWallet> wallet;
- WalletCreationStatus status = CreateWallet(*g_rpc_chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
+ WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
switch (status) {
case WalletCreationStatus::CREATION_FAILED:
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
@@ -2792,13 +2708,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
static UniValue listunspent(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
@@ -2856,6 +2765,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
@@ -3125,13 +3038,6 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
static UniValue fundrawtransaction(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -3201,6 +3107,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
// parse hex string from parameter
@@ -3225,13 +3135,6 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -3285,6 +3188,10 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
@@ -3319,13 +3226,6 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
static UniValue bumpfee(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
- return NullUniValue;
-
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"
@@ -3377,6 +3277,10 @@ static UniValue bumpfee(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -3491,13 +3395,6 @@ static UniValue bumpfee(const JSONRPCRequest& request)
UniValue rescanblockchain(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
@@ -3518,6 +3415,10 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@@ -3690,13 +3591,6 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
UniValue getaddressinfo(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
@@ -3757,6 +3651,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
UniValue ret(UniValue::VOBJ);
@@ -3833,13 +3731,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
@@ -3860,6 +3751,10 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string label = LabelFromValue(request.params[0]);
@@ -3893,13 +3788,6 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
static UniValue listlabels(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
@@ -3923,6 +3811,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
LOCK(pwallet->cs_wallet);
std::string purpose;
@@ -3949,13 +3841,6 @@ static UniValue listlabels(const JSONRPCRequest& request)
UniValue sethdseed(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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"
@@ -3978,6 +3863,10 @@ UniValue sethdseed(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -4022,13 +3911,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- const CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
@@ -4057,6 +3939,10 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ const CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
// Unserialize the transaction
@@ -4089,13 +3975,6 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
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",
@@ -4170,6 +4049,10 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
},
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -4214,13 +4097,6 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
static UniValue upgradewallet(const JSONRPCRequest& request)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"New keys may be generated and a new wallet backup will need to be made.",
@@ -4234,6 +4110,10 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
}
}.Check(request);
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
EnsureWalletIsUnlocked(pwallet);
@@ -4263,7 +4143,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
UniValue importdescriptors(const JSONRPCRequest& request);
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
+Span<const CRPCCommand> GetWalletRPCCommands()
{
// clang-format off
static const CRPCCommand commands[] =
@@ -4329,9 +4209,5 @@ static const CRPCCommand commands[] =
{ "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- handlers.emplace_back(chain.handleRpc(commands[vcidx]));
+ return MakeSpan(commands);
}
-
-interfaces::Chain* g_rpc_chain = nullptr;
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 8c149d455b..fb1e91282b 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -5,30 +5,22 @@
#ifndef BITCOIN_WALLET_RPCWALLET_H
#define BITCOIN_WALLET_RPCWALLET_H
+#include <span.h>
+
#include <memory>
#include <string>
#include <vector>
-class CRPCTable;
+class CRPCCommand;
class CWallet;
class JSONRPCRequest;
class LegacyScriptPubKeyMan;
class UniValue;
-struct PartiallySignedTransaction;
class CTransaction;
+struct PartiallySignedTransaction;
+struct WalletContext;
-namespace interfaces {
-class Chain;
-class Handler;
-}
-
-//! Pointer to chain interface that needs to be declared as a global to be
-//! accessible loadwallet and createwallet methods. Due to limitations of the
-//! RPC framework, there's currently no direct way to pass in state to RPC
-//! methods without globals.
-extern interfaces::Chain* g_rpc_chain;
-
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers);
+Span<const CRPCCommand> GetWalletRPCCommands();
/**
* Figures out what wallet, if any, to use for a JSONRPCRequest.
@@ -39,7 +31,7 @@ void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
void EnsureWalletIsUnlocked(const CWallet*);
-bool EnsureWalletIsAvailable(const CWallet*, bool avoidException);
+WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
UniValue getaddressinfo(const JSONRPCRequest& request);
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
new file mode 100644
index 0000000000..70067ebef0
--- /dev/null
+++ b/src/wallet/salvage.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <fs.h>
+#include <streams.h>
+#include <wallet/salvage.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
+
+/* End of headers, beginning of key/value data */
+static const char *HEADER_END = "HEADER=END";
+/* End of key/value data */
+static const char *DATA_END = "DATA=END";
+typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
+
+bool RecoverDatabaseFile(const fs::path& file_path)
+{
+ std::string filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
+
+ // Recovery procedure:
+ // move wallet file to walletfilename.timestamp.bak
+ // Call Salvage with fAggressive=true to
+ // get as much data as possible.
+ // Rewrite salvaged data to fresh wallet file
+ // Set -rescan so any missing transactions will be
+ // found.
+ int64_t now = GetTime();
+ std::string newFilename = strprintf("%s.%d.bak", filename, now);
+
+ int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
+ newFilename.c_str(), DB_AUTO_COMMIT);
+ if (result == 0)
+ LogPrintf("Renamed %s to %s\n", filename, newFilename);
+ else
+ {
+ LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
+ return false;
+ }
+
+ /**
+ * Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
+ * key/value pairs are appended to salvagedData which are then written out to a new wallet file.
+ * NOTE: reads the entire database into memory, so cannot be used
+ * for huge databases.
+ */
+ std::vector<KeyValPair> salvagedData;
+
+ std::stringstream strDump;
+
+ Db db(env->dbenv.get(), 0);
+ result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
+ if (result == DB_VERIFY_BAD) {
+ LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
+ }
+ if (result != 0 && result != DB_VERIFY_BAD) {
+ LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
+ return false;
+ }
+
+ // Format of bdb dump is ascii lines:
+ // header lines...
+ // HEADER=END
+ // hexadecimal key
+ // hexadecimal value
+ // ... repeated
+ // DATA=END
+
+ std::string strLine;
+ while (!strDump.eof() && strLine != HEADER_END)
+ getline(strDump, strLine); // Skip past header
+
+ std::string keyHex, valueHex;
+ while (!strDump.eof() && keyHex != DATA_END) {
+ getline(strDump, keyHex);
+ if (keyHex != DATA_END) {
+ if (strDump.eof())
+ break;
+ getline(strDump, valueHex);
+ if (valueHex == DATA_END) {
+ LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
+ break;
+ }
+ salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
+ }
+ }
+
+ bool fSuccess;
+ if (keyHex != DATA_END) {
+ LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
+ fSuccess = false;
+ } else {
+ fSuccess = (result == 0);
+ }
+
+ if (salvagedData.empty())
+ {
+ LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
+ return false;
+ }
+ LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
+
+ std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+ int ret = pdbCopy->open(nullptr, // Txn pointer
+ filename.c_str(), // Filename
+ "main", // Logical db name
+ DB_BTREE, // Database type
+ DB_CREATE, // Flags
+ 0);
+ if (ret > 0) {
+ LogPrintf("Cannot create database file %s\n", filename);
+ pdbCopy->close(0);
+ return false;
+ }
+
+ DbTxn* ptxn = env->TxnBegin();
+ CWallet dummyWallet(nullptr, WalletLocation(), WalletDatabase::CreateDummy());
+ for (KeyValPair& row : salvagedData)
+ {
+ /* Filter for only private key type KV pairs to be added to the salvaged wallet */
+ CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
+ std::string strType, strErr;
+ bool fReadOK;
+ {
+ // Required in LoadKeyMetadata():
+ LOCK(dummyWallet.cs_wallet);
+ fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr);
+ }
+ if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
+ continue;
+ }
+ if (!fReadOK)
+ {
+ LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
+ continue;
+ }
+ Dbt datKey(&row.first[0], row.first.size());
+ Dbt datValue(&row.second[0], row.second.size());
+ int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
+ if (ret2 > 0)
+ fSuccess = false;
+ }
+ ptxn->commit(0);
+ pdbCopy->close(0);
+
+ return fSuccess;
+}
diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h
new file mode 100644
index 0000000000..e361930f5e
--- /dev/null
+++ b/src/wallet/salvage.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_SALVAGE_H
+#define BITCOIN_WALLET_SALVAGE_H
+
+#include <fs.h>
+#include <streams.h>
+
+bool RecoverDatabaseFile(const fs::path& file_path);
+
+#endif // BITCOIN_WALLET_SALVAGE_H
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index 6ba7d66b7c..c95b4f1f6e 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -18,7 +18,6 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_datadir;
fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases;
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<interfaces::ChainClient> m_chain_client;
};
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index a294935b64..6c32868b1e 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -19,7 +19,6 @@
struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, {});
CWallet m_wallet;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 3654420eb2..6f8c973f25 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -535,7 +535,6 @@ public:
return it->second;
}
- NodeContext m_node;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
std::unique_ptr<CWallet> wallet;
};
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 255770552f..a59aa8b980 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -21,6 +21,7 @@
#include <script/descriptor.h>
#include <script/script.h>
#include <script/signingprovider.h>
+#include <txmempool.h>
#include <util/bip32.h>
#include <util/check.h>
#include <util/error.h>
@@ -153,7 +154,7 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
- if (!CWallet::Verify(chain, location, false, error, warnings)) {
+ if (!CWallet::Verify(chain, location, error, warnings)) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
@@ -195,7 +196,7 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
- if (!CWallet::Verify(chain, location, false, error, warnings)) {
+ if (!CWallet::Verify(chain, location, error, warnings)) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED;
}
@@ -1100,23 +1101,52 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
MarkInputsDirty(ptx);
}
-void CWallet::transactionAddedToMempool(const CTransactionRef& ptx) {
+void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
LOCK(cs_wallet);
- CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
- SyncTransaction(ptx, confirm);
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
- auto it = mapWallet.find(ptx->GetHash());
+ auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
it->second.fInMempool = true;
}
}
-void CWallet::transactionRemovedFromMempool(const CTransactionRef &ptx) {
+void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
LOCK(cs_wallet);
- auto it = mapWallet.find(ptx->GetHash());
+ auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
it->second.fInMempool = false;
}
+ // Handle transactions that were removed from the mempool because they
+ // conflict with transactions in a newly connected block.
+ if (reason == MemPoolRemovalReason::CONFLICT) {
+ // Call SyncNotifications, so external -walletnotify notifications will
+ // be triggered for these transactions. Set Status::UNCONFIRMED instead
+ // of Status::CONFLICTED for a few reasons:
+ //
+ // 1. The transactionRemovedFromMempool callback does not currently
+ // provide the conflicting block's hash and height, and for backwards
+ // compatibility reasons it may not be not safe to store conflicted
+ // wallet transactions with a null block hash. See
+ // https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993.
+ // 2. For most of these transactions, the wallet's internal conflict
+ // detection in the blockConnected handler will subsequently call
+ // MarkConflicted and update them with CONFLICTED status anyway. This
+ // applies to any wallet transaction that has inputs spent in the
+ // block, or that has ancestors in the wallet with inputs spent by
+ // the block.
+ // 3. Longstanding behavior since the sync implementation in
+ // https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync
+ // implementation before that was to mark these transactions
+ // unconfirmed rather than conflicted.
+ //
+ // Nothing described above should be seen as an unchangeable requirement
+ // when improving this code in the future. The wallet's heuristics for
+ // distinguishing between conflicted and unconfirmed transactions are
+ // imperfect, and could be improved in general, see
+ // https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ }
}
void CWallet::blockConnected(const CBlock& block, int height)
@@ -1127,9 +1157,8 @@ void CWallet::blockConnected(const CBlock& block, int height)
m_last_block_processed_height = height;
m_last_block_processed = block_hash;
for (size_t index = 0; index < block.vtx.size(); index++) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index);
- SyncTransaction(block.vtx[index], confirm);
- transactionRemovedFromMempool(block.vtx[index]);
+ SyncTransaction(block.vtx[index], {CWalletTx::Status::CONFIRMED, height, block_hash, (int)index});
+ transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
}
}
@@ -1144,8 +1173,7 @@ void CWallet::blockDisconnected(const CBlock& block, int height)
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
- SyncTransaction(ptx, confirm);
+ SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
}
}
@@ -1685,8 +1713,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block_height, block_hash, posInBlock);
- SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -2970,7 +2997,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
}
if (nFeeRet > m_default_max_tx_fee) {
- error = Untranslated(TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED));
+ error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false;
}
@@ -3650,7 +3677,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
+bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
{
// Do some checking on wallet path. It should be either a:
//
@@ -3690,16 +3717,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
return false;
}
- if (salvage_wallet) {
- // Recover readable keypairs:
- CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
- std::string backup_filename;
- if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
- return false;
- }
- }
-
- return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string);
+ return WalletBatch::VerifyDatabaseFile(wallet_path, error_string);
}
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 9d312e8ee5..67331dc3be 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -631,7 +631,6 @@ private:
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<int64_t> m_scanning_start{0};
std::atomic<double> m_scanning_progress{0};
- std::mutex mutexScanning;
friend class WalletRescanReserver;
//! the current wallet version: clients below this version are not able to load the wallet
@@ -922,7 +921,7 @@ public:
uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
- void transactionRemovedFromMempool(const CTransactionRef &ptx) override;
+ void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
struct Balance {
@@ -1136,7 +1135,7 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
//! Verify wallet naming and perform salvage on the wallet if required
- static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
+ static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
@@ -1287,13 +1286,11 @@ public:
bool reserve()
{
assert(!m_could_reserve);
- std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
- if (m_wallet.fScanningWallet) {
+ if (m_wallet.fScanningWallet.exchange(true)) {
return false;
}
m_wallet.m_scanning_start = GetTimeMillis();
m_wallet.m_scanning_progress = 0;
- m_wallet.fScanningWallet = true;
m_could_reserve = true;
return true;
}
@@ -1305,7 +1302,6 @@ public:
~WalletRescanReserver()
{
- std::lock_guard<std::mutex> lock(m_wallet.mutexScanning);
if (m_could_reserve) {
m_wallet.fScanningWallet = false;
}
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 331408ef48..cb516f70f0 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -18,8 +18,6 @@
#include <atomic>
#include <string>
-#include <boost/thread.hpp>
-
namespace DBKeys {
const std::string ACENTRY{"acentry"};
const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
@@ -672,6 +670,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return true;
}
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr)
+{
+ CWalletScanState dummy_wss;
+ LOCK(pwallet->cs_wallet);
+ return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr);
+}
+
bool WalletBatch::IsKeyType(const std::string& strType)
{
return (strType == DBKeys::KEY ||
@@ -738,11 +743,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
pwallet->WalletLogPrintf("%s\n", strErr);
}
pcursor->close();
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (...) {
+ } catch (...) {
result = DBErrors::CORRUPT;
}
@@ -880,11 +881,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
}
}
pcursor->close();
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (...) {
+ } catch (...) {
result = DBErrors::CORRUPT;
}
@@ -976,53 +973,14 @@ void MaybeCompactWalletDB()
fOneThread = false;
}
-//
-// Try to (very carefully!) recover wallet file if there is a problem.
-//
-bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
-{
- return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
-}
-
-bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
-{
- // recover without a key filter callback
- // results in recovering all record types
- return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
-}
-
-bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
-{
- CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
- CWalletScanState dummyWss;
- std::string strType, strErr;
- bool fReadOK;
- {
- // Required in LoadKeyMetadata():
- LOCK(dummyWallet->cs_wallet);
- fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
- dummyWss, strType, strErr);
- }
- if (!IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
- return false;
- }
- if (!fReadOK)
- {
- LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
- return false;
- }
-
- return true;
-}
-
bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr)
{
return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
}
-bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr)
+bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, bilingual_str& errorStr)
{
- return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warnings, errorStr, WalletBatch::Recover);
+ return BerkeleyBatch::VerifyDatabaseFile(wallet_path, errorStr);
}
bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index a2788ed6c4..b95ed24d12 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -261,18 +261,12 @@ public:
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
- /* Try to (very carefully!) recover wallet database (with a possible key type filter) */
- static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
- /* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */
- static bool Recover(const fs::path& wallet_path, std::string& out_backup_filename);
- /* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
- static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
static bool VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& wallet_path, std::vector<bilingual_str>& warnings, bilingual_str& errorStr);
+ static bool VerifyDatabaseFile(const fs::path& wallet_path, bilingual_str& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
@@ -292,4 +286,7 @@ private:
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
void MaybeCompactWalletDB();
+//! Unserialize a given Key-Value pair and load it into the wallet
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
+
#endif // BITCOIN_WALLET_WALLETDB_H
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 522efaa884..be07c28503 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -5,6 +5,7 @@
#include <fs.h>
#include <util/system.h>
#include <util/translation.h>
+#include <wallet/salvage.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -103,6 +104,27 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
+static bool SalvageWallet(const fs::path& path)
+{
+ // Create a Database handle to allow for the db to be initialized before recovery
+ std::unique_ptr<WalletDatabase> database = WalletDatabase::Create(path);
+
+ // Initialize the environment before recovery
+ bilingual_str error_string;
+ try {
+ WalletBatch::VerifyEnvironment(path, error_string);
+ } catch (const fs::filesystem_error& e) {
+ error_string = Untranslated(strprintf("Error loading wallet. %s", fsbridge::get_filesystem_error_message(e)));
+ }
+ if (!error_string.original.empty()) {
+ tfm::format(std::cerr, "Failed to open wallet for salvage :%s\n", error_string.original);
+ return false;
+ }
+
+ // Perform the recovery
+ return RecoverDatabaseFile(path);
+}
+
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
{
fs::path path = fs::absolute(name, GetWalletDir());
@@ -113,7 +135,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
WalletShowInfo(wallet_instance.get());
wallet_instance->Flush(true);
}
- } else if (command == "info") {
+ } else if (command == "info" || command == "salvage") {
if (!fs::exists(path)) {
tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
return false;
@@ -123,10 +145,15 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
tfm::format(std::cerr, "%s\nError loading %s. Is wallet being used by other process?\n", error.original, name);
return false;
}
- std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
- if (!wallet_instance) return false;
- WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush(true);
+
+ if (command == "info") {
+ std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
+ if (!wallet_instance) return false;
+ WalletShowInfo(wallet_instance.get());
+ wallet_instance->Flush(true);
+ } else if (command == "salvage") {
+ return SalvageWallet(path);
+ }
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
return false;
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 467c3d0f65..501bf7e637 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -6,66 +6,71 @@
#include <warnings.h>
#include <sync.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
-static RecursiveMutex cs_warnings;
-static std::string strMiscWarning GUARDED_BY(cs_warnings);
-static bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
-static bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
+#include <vector>
-void SetMiscWarning(const std::string& strWarning)
+static Mutex g_warnings_mutex;
+static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex);
+static bool fLargeWorkForkFound GUARDED_BY(g_warnings_mutex) = false;
+static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
+
+void SetMiscWarning(const bilingual_str& warning)
{
- LOCK(cs_warnings);
- strMiscWarning = strWarning;
+ LOCK(g_warnings_mutex);
+ g_misc_warnings = warning;
}
void SetfLargeWorkForkFound(bool flag)
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
fLargeWorkForkFound = flag;
}
bool GetfLargeWorkForkFound()
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
return fLargeWorkForkFound;
}
void SetfLargeWorkInvalidChainFound(bool flag)
{
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
fLargeWorkInvalidChainFound = flag;
}
-std::string GetWarnings(bool verbose)
+bilingual_str GetWarnings(bool verbose)
{
- std::string warnings_concise;
- std::string warnings_verbose;
- const std::string warning_separator = "<hr />";
+ bilingual_str warnings_concise;
+ std::vector<bilingual_str> warnings_verbose;
- LOCK(cs_warnings);
+ LOCK(g_warnings_mutex);
// Pre-release build warning
if (!CLIENT_VERSION_IS_RELEASE) {
- warnings_concise = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications";
- warnings_verbose = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated;
+ warnings_concise = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ warnings_verbose.emplace_back(warnings_concise);
}
// Misc warnings like out of disk space and clock is wrong
- if (strMiscWarning != "") {
- warnings_concise = strMiscWarning;
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning;
+ if (!g_misc_warnings.empty()) {
+ warnings_concise = g_misc_warnings;
+ warnings_verbose.emplace_back(warnings_concise);
}
if (fLargeWorkForkFound) {
- warnings_concise = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated;
+ warnings_concise = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ warnings_verbose.emplace_back(warnings_concise);
} else if (fLargeWorkInvalidChainFound) {
- warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
- warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated;
+ warnings_concise = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ warnings_verbose.emplace_back(warnings_concise);
+ }
+
+ if (verbose) {
+ return Join(warnings_verbose, Untranslated("<hr />"));
}
- if (verbose) return warnings_verbose;
- else return warnings_concise;
+ return warnings_concise;
}
diff --git a/src/warnings.h b/src/warnings.h
index 83b1add1ee..28546eb753 100644
--- a/src/warnings.h
+++ b/src/warnings.h
@@ -8,16 +8,18 @@
#include <string>
-void SetMiscWarning(const std::string& strWarning);
+struct bilingual_str;
+
+void SetMiscWarning(const bilingual_str& warning);
void SetfLargeWorkForkFound(bool flag);
bool GetfLargeWorkForkFound();
void SetfLargeWorkInvalidChainFound(bool flag);
/** Format a string that describes several potential problems detected by the core.
* @param[in] verbose bool
- * - if true, get all warnings, translated (where possible), separated by <hr />
+ * - if true, get all warnings separated by <hr />
* - if false, get the most important warning
* @returns the warning string
*/
-std::string GetWarnings(bool verbose);
+bilingual_str GetWarnings(bool verbose);
#endif // BITCOIN_WARNINGS_H
diff --git a/test/README.md b/test/README.md
index 0210907878..b036a66f67 100644
--- a/test/README.md
+++ b/test/README.md
@@ -261,6 +261,7 @@ Use the `-v` option for verbose output.
| Lint test | Dependency | Version [used by CI](../ci/lint/04_install.sh) | Installation
|-----------|:----------:|:-------------------------------------------:|--------------
| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8) | [3.7.8](https://github.com/bitcoin/bitcoin/pull/15257) | `pip3 install flake8==3.7.8`
+| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy) | [0.700](https://github.com/bitcoin/bitcoin/pull/18210) | `pip3 install mypy==0.700`
| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [0.6.0](https://github.com/bitcoin/bitcoin/pull/15166) | [details...](https://github.com/koalaman/shellcheck#installing)
| [`lint-shell.sh`](lint/lint-shell.sh) | [yq](https://github.com/kislyuk/yq) | default | `pip3 install yq`
| [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell) | [1.15.0](https://github.com/bitcoin/bitcoin/pull/16186) | `pip3 install codespell==1.15.0`
diff --git a/test/config.ini.in b/test/config.ini.in
index 9687206ee1..be1bfe8752 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -7,6 +7,7 @@
[environment]
PACKAGE_NAME=@PACKAGE_NAME@
+PACKAGE_BUGREPORT=@PACKAGE_BUGREPORT@
SRCDIR=@abs_top_srcdir@
BUILDDIR=@abs_top_builddir@
EXEEXT=@EXEEXT@
diff --git a/test/functional/README.md b/test/functional/README.md
index 004e0afb1d..aff5f714f2 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -26,10 +26,12 @@ don't have test cases for.
The Travis linter also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126).
- See [the python lint script](/test/lint/lint-python.sh) that checks for violations that
could lead to bugs and issues in the test code.
+- Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability
+ and to detect possible bugs earlier.
- Avoid wildcard imports
- Use a module-level docstring to describe what the test is testing, and how it
is testing it.
-- When subclassing the BitcoinTestFramwork, place overrides for the
+- When subclassing the BitcoinTestFramework, place overrides for the
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
the subclass, then locally-defined helper methods, then the `run_test()` method.
- Use `'{}'.format(x)` for string formatting, not `'%s' % x`.
@@ -45,7 +47,7 @@ don't have test cases for.
- `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
- `tool` for tests for tools, eg `tool_wallet.py`
- `wallet` for tests for wallet features, eg `wallet_keypool.py`
-- use an underscore to separate words
+- Use an underscore to separate words
- exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py`
- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py`
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index ae5721bec2..6e72db1d96 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -21,6 +21,7 @@ Invalid tx cases not covered here can be found by running:
"""
import abc
+from typing import Optional
from test_framework.messages import (
COutPoint,
CTransaction,
@@ -56,7 +57,7 @@ class BadTxTemplate:
__metaclass__ = abc.ABCMeta
# The expected error code given by bitcoind upon submission of the tx.
- reject_reason = ""
+ reject_reason = "" # type: Optional[str]
# Only specified if it differs from mempool acceptance error.
block_reject_reason = ""
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 1a7c656274..a4dc455d57 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -71,7 +71,7 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
conf.write('[testnet]\n')
self.restart_node(0)
- self.nodes[0].stop_node(expected_stderr='Warning: ' + inc_conf_file_path + ':1 Section [testnot] is not recognized.' + os.linesep + 'Warning: ' + inc_conf_file2_path + ':1 Section [testnet] is not recognized.')
+ self.nodes[0].stop_node(expected_stderr='Warning: ' + inc_conf_file_path + ':1 Section [testnot] is not recognized.' + os.linesep + inc_conf_file2_path + ':1 Section [testnet] is not recognized.')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 5bbdb8cda1..7b38e09bf9 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -256,7 +256,11 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.log.debug("Mining longer tip")
block_hashes = []
while current_height + 1 > self.nodes[3].getblockcount():
- block_hashes.extend(self.nodes[3].generate(min(10, current_height + 1 - self.nodes[3].getblockcount())))
+ block_hashes.extend(self.nodes[3].generatetoaddress(
+ nblocks=min(10, current_height + 1 - self.nodes[3].getblockcount()),
+ # new address to avoid mining a block that has just been invalidated
+ address=self.nodes[3].getnewaddress(),
+ ))
self.log.debug("Syncing %d new blocks...", len(block_hashes))
self.sync_node3blocks(block_hashes)
utxo_list = self.nodes[3].listunspent()
@@ -281,5 +285,6 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
if self.restart_counts[i] == 0:
self.log.warning("Node %d never crashed during utxo flush!", i)
+
if __name__ == "__main__":
ChainstateWriteCrashTest().main()
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 9579a1715d..7eabf86cad 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -138,8 +138,7 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.log.info("Restarting node 0 with noban permission and 1MB maxuploadtarget")
- self.stop_node(0)
- self.start_node(0, ["-whitelist=noban@127.0.0.1", "-maxuploadtarget=1"])
+ self.restart_node(0, ["-whitelist=noban@127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
self.nodes[0].add_p2p_connection(TestP2PConn())
@@ -152,9 +151,9 @@ class MaxUploadTest(BitcoinTestFramework):
getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
self.nodes[0].p2p.send_and_ping(getdata_request)
- assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist
+ assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the noban permission
- self.log.info("Peer still connected after trying to download old block (whitelisted)")
+ self.log.info("Peer still connected after trying to download old block (noban permission)")
if __name__ == '__main__':
MaxUploadTest().main()
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 47200b6cc6..33a308ad1b 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -19,7 +19,7 @@ from test_framework.util import (
# Windows disallow control characters (0-31) and /\?%:|"<>
FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128
-FILE_CHAR_BLACKLIST = '/\\?%*:|"<>' if os.name == 'nt' else '/'
+FILE_CHAR_BLOCKLIST = '/\\?%*:|"<>' if os.name == 'nt' else '/'
def notify_outputname(walletname, txid):
@@ -32,7 +32,7 @@ class NotificationsTest(BitcoinTestFramework):
self.setup_clean_chain = True
def setup_network(self):
- self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHAR_BLACKLIST)
+ self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHAR_BLOCKLIST)
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify")
@@ -125,12 +125,7 @@ class NotificationsTest(BitcoinTestFramework):
# Bump tx2 as bump2 and generate a block on node 0 while
# disconnected, then reconnect and check for notifications on node 1
- # about newly confirmed bump2 and newly conflicted tx2. Currently
- # only the bump2 notification is sent. Ideally, notifications would
- # be sent both for bump2 and tx2, which was the previous behavior
- # before being broken by an accidental change in PR
- # https://github.com/bitcoin/bitcoin/pull/16624. The bug is reported
- # in issue https://github.com/bitcoin/bitcoin/issues/18325.
+ # about newly confirmed bump2 and newly conflicted tx2.
disconnect_nodes(self.nodes[0], 1)
bump2 = self.nodes[0].bumpfee(tx2)["txid"]
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
@@ -138,7 +133,7 @@ class NotificationsTest(BitcoinTestFramework):
assert_equal(tx2 in self.nodes[1].getrawmempool(), True)
connect_nodes(self.nodes[0], 1)
self.sync_blocks()
- self.expect_wallet_notify([bump2])
+ self.expect_wallet_notify([bump2, tx2])
assert_equal(self.nodes[1].gettransaction(bump2)["confirmations"], 1)
# TODO: add test for `-alertnotify` large fork notifications
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 24c357091f..2298485640 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -20,6 +20,7 @@ from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash16
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_is_hex_string,
assert_raises_rpc_error,
connect_nodes,
hex_str_to_bytes,
@@ -188,6 +189,14 @@ class SegWitTest(BitcoinTestFramework):
assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"]
assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) == tx.serialize_without_witness().hex()
+ # Coinbase contains the witness commitment nonce, check that RPC shows us
+ coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0]
+ coinbase_tx = self.nodes[2].gettransaction(txid=coinbase_txid, verbose=True)
+ witnesses = coinbase_tx["decoded"]["vin"][0]["txinwitness"]
+ assert_equal(len(witnesses), 1)
+ assert_is_hex_string(witnesses[0])
+ assert_equal(witnesses[0], '00'*32)
+
self.log.info("Verify witness txs without witness data are invalid after the fork")
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False)
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False)
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 3969da2eb0..5d00648aed 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -84,7 +84,9 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
# disconnect nodes & make a txn that remains in the unbroadcast set.
- disconnect_nodes(self.nodes[0], 2)
+ disconnect_nodes(self.nodes[0], 1)
+ assert(len(self.nodes[0].getpeerinfo()) == 0)
+ assert(len(self.nodes[0].p2ps) == 0)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
connect_nodes(self.nodes[0], 2)
@@ -157,8 +159,10 @@ class MempoolPersistTest(BitcoinTestFramework):
# clear out mempool
node0.generate(1)
- # disconnect nodes to make a txn that remains in the unbroadcast set.
- disconnect_nodes(node0, 1)
+ # ensure node0 doesn't have any connections
+ # make a transaction that will remain in the unbroadcast set
+ assert(len(node0.getpeerinfo()) == 0)
+ assert(len(node0.p2ps) == 0)
node0.sendtoaddress(self.nodes[1].getnewaddress(), Decimal("12"))
# shutdown, then startup with wallet disabled
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index dedf5b8a47..365d011157 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -16,6 +16,7 @@ from test_framework.util import (
disconnect_nodes,
)
+MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
class MempoolUnbroadcastTest(BitcoinTestFramework):
def set_test_params(self):
@@ -72,7 +73,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
connect_nodes(node, 1)
# fast forward into the future & ensure that the second node has the txns
- node.mockscheduler(15 * 60) # 15 min in seconds
+ node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
self.sync_mempools(timeout=30)
mempool = self.nodes[1].getrawmempool()
assert rpc_tx_hsh in mempool
@@ -86,15 +87,16 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.log.info("Add another connection & ensure transactions aren't broadcast again")
conn = node.add_p2p_connection(P2PTxInvStore())
- node.mockscheduler(15 * 60)
- time.sleep(5)
+ node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
+ time.sleep(2) # allow sufficient time for possibility of broadcast
assert_equal(len(conn.get_invs()), 0)
+ disconnect_nodes(node, 1)
+ node.disconnect_p2ps()
+
def test_txn_removal(self):
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
node = self.nodes[0]
- disconnect_nodes(node, 1)
- node.disconnect_p2ps
# since the node doesn't have any connections, it will not receive
# any GETDATAs & thus the transaction will remain in the unbroadcast set.
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index 1bda167c87..63d1ccfb36 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -18,24 +18,25 @@ from test_framework.blocktools import (
from test_framework.messages import (
CBlock,
CBlockHeader,
- BLOCK_HEADER_SIZE
-)
-from test_framework.mininode import (
- P2PDataStore,
+ BLOCK_HEADER_SIZE,
)
+from test_framework.mininode import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
connect_nodes,
)
-from test_framework.script import CScriptNum
def assert_template(node, block, expect, rehash=True):
if rehash:
block.hashMerkleRoot = block.calc_merkle_root()
- rsp = node.getblocktemplate(template_request={'data': block.serialize().hex(), 'mode': 'proposal', 'rules': ['segwit']})
+ rsp = node.getblocktemplate(template_request={
+ 'data': block.serialize().hex(),
+ 'mode': 'proposal',
+ 'rules': ['segwit'],
+ })
assert_equal(rsp, expect)
@@ -87,16 +88,9 @@ class MiningTest(BitcoinTestFramework):
next_height = int(tmpl["height"])
coinbase_tx = create_coinbase(height=next_height)
# sequence numbers must not be max for nLockTime to have effect
- coinbase_tx.vin[0].nSequence = 2 ** 32 - 2
+ coinbase_tx.vin[0].nSequence = 2**32 - 2
coinbase_tx.rehash()
- # round-trip the encoded bip34 block height commitment
- assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height)
- # round-trip negative and multi-byte CScriptNums to catch python regression
- assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500)
- assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500)
- assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1)
-
block = CBlock()
block.nVersion = tmpl["version"]
block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
@@ -124,7 +118,11 @@ class MiningTest(BitcoinTestFramework):
assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, bad_block.serialize().hex())
self.log.info("getblocktemplate: Test truncated final transaction")
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': block.serialize()[:-1].hex(), 'mode': 'proposal', 'rules': ['segwit']})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
+ 'data': block.serialize()[:-1].hex(),
+ 'mode': 'proposal',
+ 'rules': ['segwit'],
+ })
self.log.info("getblocktemplate: Test duplicate transaction")
bad_block = copy.deepcopy(block)
@@ -143,7 +141,7 @@ class MiningTest(BitcoinTestFramework):
self.log.info("getblocktemplate: Test nonfinal transaction")
bad_block = copy.deepcopy(block)
- bad_block.vtx[0].nLockTime = 2 ** 32 - 1
+ bad_block.vtx[0].nLockTime = 2**32 - 1
bad_block.vtx[0].rehash()
assert_template(node, bad_block, 'bad-txns-nonfinal')
assert_submitblock(bad_block, 'bad-txns-nonfinal')
@@ -153,7 +151,11 @@ class MiningTest(BitcoinTestFramework):
bad_block_sn = bytearray(block.serialize())
assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
bad_block_sn[BLOCK_HEADER_SIZE] += 1
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': bad_block_sn.hex(), 'mode': 'proposal', 'rules': ['segwit']})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
+ 'data': bad_block_sn.hex(),
+ 'mode': 'proposal',
+ 'rules': ['segwit'],
+ })
self.log.info("getblocktemplate: Test bad bits")
bad_block = copy.deepcopy(block)
@@ -168,7 +170,7 @@ class MiningTest(BitcoinTestFramework):
self.log.info("getblocktemplate: Test bad timestamps")
bad_block = copy.deepcopy(block)
- bad_block.nTime = 2 ** 31 - 1
+ bad_block.nTime = 2**31 - 1
assert_template(node, bad_block, 'time-too-new')
assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
bad_block.nTime = 0
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index 9ff76b4b3d..6d947ac660 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -13,6 +13,7 @@ from test_framework.messages import (
hash256,
msg_getcfcheckpt,
msg_getcfheaders,
+ msg_getcfilters,
ser_uint256,
uint256_from_str,
)
@@ -25,6 +26,21 @@ from test_framework.util import (
wait_until,
)
+class CFiltersClient(P2PInterface):
+ def __init__(self):
+ super().__init__()
+ # Store the cfilters received.
+ self.cfilters = []
+
+ def pop_cfilters(self):
+ cfilters = self.cfilters
+ self.cfilters = []
+ return cfilters
+
+ def on_cfilter(self, message):
+ """Store cfilters received in a list."""
+ self.cfilters.append(message)
+
class CompactFiltersTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -37,8 +53,8 @@ class CompactFiltersTest(BitcoinTestFramework):
def run_test(self):
# Node 0 supports COMPACT_FILTERS, node 1 does not.
- node0 = self.nodes[0].add_p2p_connection(P2PInterface())
- node1 = self.nodes[1].add_p2p_connection(P2PInterface())
+ node0 = self.nodes[0].add_p2p_connection(CFiltersClient())
+ node1 = self.nodes[1].add_p2p_connection(CFiltersClient())
# Nodes 0 & 1 share the same first 999 blocks in the chain.
self.nodes[0].generate(999)
@@ -112,7 +128,8 @@ class CompactFiltersTest(BitcoinTestFramework):
)
node0.send_and_ping(request)
response = node0.last_message['cfheaders']
- assert_equal(len(response.hashes), 1000)
+ main_cfhashes = response.hashes
+ assert_equal(len(main_cfhashes), 1000)
assert_equal(
compute_last_header(response.prev_header, response.hashes),
int(main_cfcheckpt, 16)
@@ -126,12 +143,50 @@ class CompactFiltersTest(BitcoinTestFramework):
)
node0.send_and_ping(request)
response = node0.last_message['cfheaders']
- assert_equal(len(response.hashes), 1000)
+ stale_cfhashes = response.hashes
+ assert_equal(len(stale_cfhashes), 1000)
assert_equal(
compute_last_header(response.prev_header, response.hashes),
int(stale_cfcheckpt, 16)
)
+ self.log.info("Check that peers can fetch cfilters.")
+ stop_hash = self.nodes[0].getblockhash(10)
+ request = msg_getcfilters(
+ filter_type=FILTER_TYPE_BASIC,
+ start_height=1,
+ stop_hash=int(stop_hash, 16)
+ )
+ node0.send_message(request)
+ node0.sync_with_ping()
+ response = node0.pop_cfilters()
+ assert_equal(len(response), 10)
+
+ self.log.info("Check that cfilter responses are correct.")
+ for cfilter, cfhash, height in zip(response, main_cfhashes, range(1, 11)):
+ block_hash = self.nodes[0].getblockhash(height)
+ assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
+ assert_equal(cfilter.block_hash, int(block_hash, 16))
+ computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
+ assert_equal(computed_cfhash, cfhash)
+
+ self.log.info("Check that peers can fetch cfilters for stale blocks.")
+ request = msg_getcfilters(
+ filter_type=FILTER_TYPE_BASIC,
+ start_height=1000,
+ stop_hash=int(stale_block_hash, 16)
+ )
+ node0.send_message(request)
+ node0.sync_with_ping()
+ response = node0.pop_cfilters()
+ assert_equal(len(response), 1)
+
+ cfilter = response[0]
+ assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
+ assert_equal(cfilter.block_hash, int(stale_block_hash, 16))
+ computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
+ assert_equal(computed_cfhash, stale_cfhashes[999])
+
self.log.info("Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.")
requests = [
msg_getcfcheckpt(
@@ -143,6 +198,11 @@ class CompactFiltersTest(BitcoinTestFramework):
start_height=1000,
stop_hash=int(main_block_hash, 16)
),
+ msg_getcfilters(
+ filter_type=FILTER_TYPE_BASIC,
+ start_height=1000,
+ stop_hash=int(main_block_hash, 16)
+ ),
]
for request in requests:
node1 = self.nodes[1].add_p2p_connection(P2PInterface())
@@ -151,6 +211,12 @@ class CompactFiltersTest(BitcoinTestFramework):
self.log.info("Check that invalid requests result in disconnection.")
requests = [
+ # Requesting too many filters results in disconnection.
+ msg_getcfilters(
+ filter_type=FILTER_TYPE_BASIC,
+ start_height=0,
+ stop_hash=int(main_block_hash, 16)
+ ),
# Requesting too many filter headers results in disconnection.
msg_getcfheaders(
filter_type=FILTER_TYPE_BASIC,
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 15955a938c..5726a73e40 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -16,13 +16,15 @@ from test_framework.messages import (
msg_filterclear,
msg_filterload,
msg_getdata,
+ msg_mempool,
+ msg_version,
)
-from test_framework.mininode import P2PInterface
+from test_framework.mininode import P2PInterface, mininode_lock
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
from test_framework.test_framework import BitcoinTestFramework
-class FilterNode(P2PInterface):
+class P2PBloomFilter(P2PInterface):
# This is a P2SH watch-only wallet
watch_script_pubkey = 'a914ffffffffffffffffffffffffffffffffffffffff87'
# The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added
@@ -34,6 +36,11 @@ class FilterNode(P2PInterface):
nFlags=1,
)
+ def __init__(self):
+ super().__init__()
+ self._tx_received = False
+ self._merkleblock_received = False
+
def on_inv(self, message):
want = msg_getdata()
for i in message.inv:
@@ -46,10 +53,30 @@ class FilterNode(P2PInterface):
self.send_message(want)
def on_merkleblock(self, message):
- self.merkleblock_received = True
+ self._merkleblock_received = True
def on_tx(self, message):
- self.tx_received = True
+ self._tx_received = True
+
+ @property
+ def tx_received(self):
+ with mininode_lock:
+ return self._tx_received
+
+ @tx_received.setter
+ def tx_received(self, value):
+ with mininode_lock:
+ self._tx_received = value
+
+ @property
+ def merkleblock_received(self):
+ with mininode_lock:
+ return self._merkleblock_received
+
+ @merkleblock_received.setter
+ def merkleblock_received(self, value):
+ with mininode_lock:
+ self._merkleblock_received = value
class FilterTest(BitcoinTestFramework):
@@ -64,95 +91,144 @@ class FilterTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def test_size_limits(self, filter_node):
+ def test_size_limits(self, filter_peer):
self.log.info('Check that too large filter is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1)))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1)))
self.log.info('Check that max size filter is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE)))
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE)))
+ filter_peer.send_and_ping(msg_filterclear())
self.log.info('Check that filter with too many hash functions is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1))
self.log.info('Check that filter with max hash functions is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS))
+ filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS))
# Don't send filterclear until next two filteradd checks are done
self.log.info('Check that max size data element to add to the filter is accepted')
with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE)))
+ filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE)))
self.log.info('Check that too large data element to add to the filter is rejected')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1)))
+ filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1)))
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterclear())
- def run_test(self):
- filter_node = self.nodes[0].add_p2p_connection(FilterNode())
+ def test_msg_mempool(self):
+ self.log.info("Check that a node with bloom filters enabled services p2p mempool messages")
+ filter_peer = P2PBloomFilter()
- self.test_size_limits(filter_node)
+ self.log.info("Create a tx relevant to the peer before connecting")
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
+ txid = self.nodes[0].sendtoaddress(filter_address, 90)
- self.log.info('Add filtered P2P connection to the node')
- filter_node.send_and_ping(filter_node.watch_filter_init)
- filter_address = self.nodes[0].decodescript(filter_node.watch_script_pubkey)['addresses'][0]
+ self.log.info("Send a mempool msg after connecting and check that the tx is received")
+ self.nodes[0].add_p2p_connection(filter_peer)
+ filter_peer.send_and_ping(filter_peer.watch_filter_init)
+ self.nodes[0].p2p.send_message(msg_mempool())
+ filter_peer.wait_for_tx(txid)
+
+ def test_frelay_false(self, filter_peer):
+ self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")
+ filter_peer.tx_received = False
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
+ self.nodes[0].sendtoaddress(filter_address, 90)
+ # Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays
+ filter_peer.sync_with_ping()
+ assert not filter_peer.tx_received
+
+ # Clear the mempool so that this transaction does not impact subsequent tests
+ self.nodes[0].generate(1)
+
+ def test_filter(self, filter_peer):
+ # Set the bloomfilter using filterload
+ filter_peer.send_and_ping(filter_peer.watch_filter_init)
+ # If fRelay is not already True, sending filterload sets it to True
+ assert self.nodes[0].getpeerinfo()[0]['relaytxes']
+ filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block')
block_hash = self.nodes[0].generatetoaddress(1, filter_address)[0]
txid = self.nodes[0].getblock(block_hash)['tx'][0]
- filter_node.wait_for_merkleblock(block_hash)
- filter_node.wait_for_tx(txid)
+ filter_peer.wait_for_merkleblock(block_hash)
+ filter_peer.wait_for_tx(txid)
self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block')
- filter_node.tx_received = False
+ filter_peer.tx_received = False
block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
- filter_node.wait_for_merkleblock(block_hash)
- assert not filter_node.tx_received
+ filter_peer.wait_for_merkleblock(block_hash)
+ assert not filter_peer.tx_received
self.log.info('Check that we not receive a tx if the filter does not match a mempool tx')
- filter_node.merkleblock_received = False
- filter_node.tx_received = False
+ filter_peer.merkleblock_received = False
+ filter_peer.tx_received = False
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90)
- filter_node.sync_with_ping()
- filter_node.sync_with_ping()
- assert not filter_node.merkleblock_received
- assert not filter_node.tx_received
+ filter_peer.sync_with_ping()
+ filter_peer.sync_with_ping()
+ assert not filter_peer.merkleblock_received
+ assert not filter_peer.tx_received
- self.log.info('Check that we receive a tx in reply to a mempool msg if the filter matches a mempool tx')
- filter_node.merkleblock_received = False
+ self.log.info('Check that we receive a tx if the filter matches a mempool tx')
+ filter_peer.merkleblock_received = False
txid = self.nodes[0].sendtoaddress(filter_address, 90)
- filter_node.wait_for_tx(txid)
- assert not filter_node.merkleblock_received
+ filter_peer.wait_for_tx(txid)
+ assert not filter_peer.merkleblock_received
self.log.info('Check that after deleting filter all txs get relayed again')
- filter_node.send_and_ping(msg_filterclear())
+ filter_peer.send_and_ping(msg_filterclear())
for _ in range(5):
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7)
- filter_node.wait_for_tx(txid)
+ filter_peer.wait_for_tx(txid)
self.log.info('Check that request for filtered blocks is ignored if no filter is set')
- filter_node.merkleblock_received = False
- filter_node.tx_received = False
+ filter_peer.merkleblock_received = False
+ filter_peer.tx_received = False
with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']):
block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0]
- filter_node.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])
- filter_node.sync_with_ping()
- assert not filter_node.merkleblock_received
- assert not filter_node.tx_received
+ filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])
+ filter_peer.sync_with_ping()
+ assert not filter_peer.merkleblock_received
+ assert not filter_peer.tx_received
self.log.info('Check that sending "filteradd" if no filter is set is treated as misbehavior')
with self.nodes[0].assert_debug_log(['Misbehaving']):
- filter_node.send_and_ping(msg_filteradd(data=b'letsmisbehave'))
+ filter_peer.send_and_ping(msg_filteradd(data=b'letsmisbehave'))
self.log.info("Check that division-by-zero remote crash bug [CVE-2013-5700] is fixed")
- filter_node.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))
- filter_node.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))
+ filter_peer.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))
+ filter_peer.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))
+ self.nodes[0].disconnect_p2ps()
+ def run_test(self):
+ filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter())
+ self.log.info('Test filter size limits')
+ self.test_size_limits(filter_peer)
+
+ self.log.info('Test BIP 37 for a node with fRelay = True (default)')
+ self.test_filter(filter_peer)
+ self.nodes[0].disconnect_p2ps()
+
+ self.log.info('Test BIP 37 for a node with fRelay = False')
+ # Add peer but do not send version yet
+ filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False)
+ # Send version with fRelay=False
+ filter_peer_without_nrelay.wait_until(lambda: filter_peer_without_nrelay.is_connected, timeout=10)
+ version_without_fRelay = msg_version()
+ version_without_fRelay.nRelay = 0
+ filter_peer_without_nrelay.send_message(version_without_fRelay)
+ filter_peer_without_nrelay.wait_for_verack()
+ assert not self.nodes[0].getpeerinfo()[0]['relaytxes']
+ self.test_frelay_false(filter_peer_without_nrelay)
+ self.test_filter(filter_peer_without_nrelay)
+
+ self.log.info('Test msg_mempool')
+ self.test_msg_mempool()
if __name__ == '__main__':
FilterTest().main()
diff --git a/test/functional/p2p_mempool.py b/test/functional/p2p_mempool.py
deleted file mode 100755
index a8fcb181e6..0000000000
--- a/test/functional/p2p_mempool.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-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.
-"""Test p2p mempool message.
-
-Test that nodes are disconnected if they send mempool messages when bloom
-filters are not enabled.
-"""
-
-from test_framework.messages import msg_mempool
-from test_framework.mininode import P2PInterface
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-class P2PMempoolTests(BitcoinTestFramework):
- def set_test_params(self):
- self.setup_clean_chain = True
- self.num_nodes = 1
- self.extra_args = [["-peerbloomfilters=0"]]
-
- def run_test(self):
- # Add a p2p connection
- self.nodes[0].add_p2p_connection(P2PInterface())
-
- #request mempool
- self.nodes[0].p2p.send_message(msg_mempool())
- self.nodes[0].p2p.wait_for_disconnect()
-
- #mininode must be disconnected at this point
- assert_equal(len(self.nodes[0].getpeerinfo()), 0)
-
-if __name__ == '__main__':
- P2PMempoolTests().main()
diff --git a/test/functional/p2p_nobloomfilter_messages.py b/test/functional/p2p_nobloomfilter_messages.py
new file mode 100755
index 0000000000..41af74ebb8
--- /dev/null
+++ b/test/functional/p2p_nobloomfilter_messages.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-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.
+"""Test invalid p2p messages for nodes with bloom filters disabled.
+
+Test that, when bloom filters are not enabled, nodes are disconnected if:
+1. They send a p2p mempool message
+2. They send a p2p filterload message
+3. They send a p2p filteradd message
+"""
+
+from test_framework.messages import msg_mempool, msg_filteradd, msg_filterload
+from test_framework.mininode import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class P2PNobloomfilterMessages(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [["-peerbloomfilters=0"]]
+
+ def test_message_causes_disconnect(self, message):
+ # Add a p2p connection that sends a message and check that it disconnects
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer.send_message(message)
+ peer.wait_for_disconnect()
+ assert_equal(len(self.nodes[0].getpeerinfo()), 0)
+
+ def run_test(self):
+ self.log.info("Test that node is disconnected if it sends mempool message")
+ self.test_message_causes_disconnect(msg_mempool())
+
+ self.log.info("Test that node is disconnected if it sends filterload message")
+ self.test_message_causes_disconnect(msg_filterload())
+
+ self.log.info("Test that node is disconnected if it sends filteradd message")
+ self.test_message_causes_disconnect(msg_filteradd(data=b'\xcc'))
+
+if __name__ == '__main__':
+ P2PNobloomfilterMessages().main()
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 9c8c36c89e..ed3429a037 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -42,9 +42,6 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
def disconnect_all(self):
disconnect_nodes(self.nodes[0], 1)
- disconnect_nodes(self.nodes[1], 0)
- disconnect_nodes(self.nodes[2], 1)
- disconnect_nodes(self.nodes[2], 0)
disconnect_nodes(self.nodes[0], 2)
disconnect_nodes(self.nodes[1], 2)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 8a989097b4..8803086213 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -295,7 +295,7 @@ class SegWitTest(BitcoinTestFramework):
return func_wrapper
- @subtest
+ @subtest # type: ignore
def test_non_witness_transaction(self):
"""See if sending a regular transaction works, and create a utxo to use in later tests."""
# Mine a block with an anyone-can-spend coinbase,
@@ -324,7 +324,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.append(UTXO(tx.sha256, 0, 49 * 100000000))
self.nodes[0].generate(1)
- @subtest
+ @subtest # type: ignore
def test_unnecessary_witness_before_segwit_activation(self):
"""Verify that blocks with witnesses are rejected before activation."""
@@ -355,7 +355,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_block_relay(self):
"""Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG.
@@ -451,7 +451,7 @@ class SegWitTest(BitcoinTestFramework):
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
assert block4.sha256 not in self.old_node.getdataset
- @subtest
+ @subtest # type: ignore
def test_v0_outputs_arent_spendable(self):
"""Test that v0 outputs aren't spendable before segwit activation.
@@ -533,7 +533,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
- @subtest
+ @subtest # type: ignore
def test_getblocktemplate_before_lockin(self):
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
@@ -559,7 +559,7 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_blocks()
- @subtest
+ @subtest # type: ignore
def test_witness_tx_relay_before_segwit_activation(self):
# Generate a transaction that doesn't require a witness, but send it
@@ -601,7 +601,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx_hash, 0, tx_value))
- @subtest
+ @subtest # type: ignore
def test_standardness_v0(self):
"""Test V0 txout standardness.
@@ -679,7 +679,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
assert_equal(len(self.nodes[1].getrawmempool()), 0)
- @subtest
+ @subtest # type: ignore
def advance_to_segwit_active(self):
"""Mine enough blocks to activate segwit."""
assert not softfork_active(self.nodes[0], 'segwit')
@@ -690,7 +690,7 @@ class SegWitTest(BitcoinTestFramework):
assert softfork_active(self.nodes[0], 'segwit')
self.segwit_active = True
- @subtest
+ @subtest # type: ignore
def test_p2sh_witness(self):
"""Test P2SH wrapped witness programs."""
@@ -759,7 +759,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_witness_commitments(self):
"""Test witness commitments.
@@ -849,7 +849,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_block_malleability(self):
# Make sure that a block that has too big a virtual size
@@ -889,7 +889,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
- @subtest
+ @subtest # type: ignore
def test_witness_block_size(self):
# TODO: Test that non-witness carrying blocks can't exceed 1MB
# Skipping this test for now; this is covered in p2p-fullblocktest.py
@@ -967,7 +967,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_submit_block(self):
"""Test that submitblock adds the nonce automatically when possible."""
block = self.build_next_block()
@@ -1003,7 +1003,7 @@ class SegWitTest(BitcoinTestFramework):
# Tip should not advance!
assert self.nodes[0].getbestblockhash() != block_2.hash
- @subtest
+ @subtest # type: ignore
def test_extra_witness_data(self):
"""Test extra witness data in a transaction."""
@@ -1076,7 +1076,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_max_witness_push_length(self):
"""Test that witness stack can only allow up to 520 byte pushes."""
@@ -1113,7 +1113,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_max_witness_program_length(self):
"""Test that witness outputs greater than 10kB can't be spent."""
@@ -1161,7 +1161,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_witness_input_length(self):
"""Test that vin length must match vtxinwit length."""
@@ -1243,7 +1243,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_tx_relay_after_segwit_activation(self):
"""Test transaction relay after segwit activation.
@@ -1336,7 +1336,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_segwit_versions(self):
"""Test validity of future segwit version transactions.
@@ -1418,7 +1418,7 @@ class SegWitTest(BitcoinTestFramework):
# Add utxo to our list
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_premature_coinbase_witness_spend(self):
block = self.build_next_block()
@@ -1453,7 +1453,7 @@ class SegWitTest(BitcoinTestFramework):
test_witness_block(self.nodes[0], self.test_node, block2, accepted=True)
self.sync_blocks()
- @subtest
+ @subtest # type: ignore
def test_uncompressed_pubkey(self):
"""Test uncompressed pubkey validity in segwit transactions.
@@ -1558,7 +1558,7 @@ class SegWitTest(BitcoinTestFramework):
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_signature_version_1(self):
key = ECKey()
@@ -1740,7 +1740,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(len(tx.vout)):
self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue))
- @subtest
+ @subtest # type: ignore
def test_non_standard_witness_blinding(self):
"""Test behavior of unnecessary witnesses in transactions does not blind the node for the transaction"""
@@ -1794,7 +1794,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
- @subtest
+ @subtest # type: ignore
def test_non_standard_witness(self):
"""Test detection of non-standard P2WSH witness"""
pad = chr(1).encode('latin-1')
@@ -1894,7 +1894,7 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
- @subtest
+ @subtest # type: ignore
def test_upgrade_after_activation(self):
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
@@ -1916,7 +1916,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash))
height -= 1
- @subtest
+ @subtest # type: ignore
def test_witness_sigops(self):
"""Test sigop counting is correct inside witnesses."""
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 56e9ecfcc2..3c81a4a4e2 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -3,21 +3,21 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test multisig RPCs"""
+import binascii
+import decimal
+import itertools
+import json
+import os
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create, drop_origins
+from test_framework.key import ECPubKey, ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_raises_rpc_error,
assert_equal,
)
-from test_framework.key import ECPubKey, ECKey, bytes_to_wif
-
-import binascii
-import decimal
-import itertools
-import json
-import os
+from test_framework.wallet_util import bytes_to_wif
class RpcCreateMultiSigTest(BitcoinTestFramework):
def set_test_params(self):
diff --git a/test/functional/rpc_getaddressinfo_label_deprecation.py b/test/functional/rpc_getaddressinfo_label_deprecation.py
index 4c6b2fe5cf..09545ebce7 100755
--- a/test/functional/rpc_getaddressinfo_label_deprecation.py
+++ b/test/functional/rpc_getaddressinfo_label_deprecation.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
Test deprecation of the RPC getaddressinfo `label` field. It has been
-superceded by the `labels` field.
+superseded by the `labels` field.
"""
from test_framework.test_framework import BitcoinTestFramework
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index 027ae368e7..9b981b864e 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -18,6 +18,8 @@ class HelpRpcTest(BitcoinTestFramework):
def run_test(self):
self.test_categories()
self.dump_help()
+ if self.is_wallet_compiled():
+ self.wallet_help()
def test_categories(self):
node = self.nodes[0]
@@ -53,6 +55,11 @@ class HelpRpcTest(BitcoinTestFramework):
# Make sure the node can generate the help at runtime without crashing
f.write(self.nodes[0].help(call))
+ def wallet_help(self):
+ assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help('getnewaddress')
+ self.restart_node(0, extra_args=['-nowallet=1'])
+ assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help('getnewaddress')
+
if __name__ == '__main__':
HelpRpcTest().main()
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 376bb35f07..58d8c4abe1 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -28,6 +28,7 @@ from test_framework.messages import (
NODE_WITNESS,
)
+
def assert_net_servicesnames(servicesflag, servicenames):
"""Utility that checks if all flags are correctly decoded in
`getpeerinfo` and `getnetworkinfo`.
@@ -40,6 +41,7 @@ def assert_net_servicesnames(servicesflag, servicenames):
servicesflag_generated |= getattr(test_framework.messages, 'NODE_' + servicename)
assert servicesflag_generated == servicesflag
+
class NetTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -57,6 +59,7 @@ class NetTest(BitcoinTestFramework):
self._test_getnetworkinfo()
self._test_getaddednodeinfo()
self._test_getpeerinfo()
+ self.test_service_flags()
self._test_getnodeaddresses()
def _test_connection_count(self):
@@ -139,6 +142,11 @@ class NetTest(BitcoinTestFramework):
for info in peer_info:
assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"])
+ def test_service_flags(self):
+ self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63))
+ assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
+ self.nodes[0].disconnect_p2ps()
+
def _test_getnodeaddresses(self):
self.nodes[0].add_p2p_connection(P2PInterface())
@@ -174,5 +182,6 @@ class NetTest(BitcoinTestFramework):
node_addresses = self.nodes[0].getnodeaddresses(LARGE_REQUEST_COUNT)
assert_greater_than(LARGE_REQUEST_COUNT, len(node_addresses))
+
if __name__ == '__main__':
NetTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 51d136d26a..9b07c39606 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -43,10 +43,8 @@ class PSBTTest(BitcoinTestFramework):
online_node = self.nodes[1]
# Disconnect offline node from others
+ # Topology of test network is linear, so this one call is enough
disconnect_nodes(offline_node, 1)
- disconnect_nodes(online_node, 0)
- disconnect_nodes(offline_node, 2)
- disconnect_nodes(mining_node, 0)
# Create watchonly on online_node
online_node.createwallet(wallet_name='wonline', disable_private_keys=True)
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 8f410f233e..9506b63f82 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -5,12 +5,15 @@
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
import enum
+import unittest
from .script import hash256, hash160, sha256, CScript, OP_0
from .util import hex_str_to_bytes
from . import segwit_addr
+from test_framework.util import assert_equal
+
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
# Coins sent to this address can be spent with a witness stack of just OP_TRUE
@@ -41,7 +44,32 @@ def byte_to_base58(b, version):
str = str[2:]
return result
-# TODO: def base58_decode
+
+def base58_to_byte(s, verify_checksum=True):
+ if not s:
+ return b''
+ n = 0
+ for c in s:
+ n *= 58
+ assert c in chars
+ digit = chars.index(c)
+ n += digit
+ h = '%x' % n
+ if len(h) % 2:
+ h = '0' + h
+ res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
+ pad = 0
+ for c in s:
+ if c == chars[0]:
+ pad += 1
+ else:
+ break
+ res = b'\x00' * pad + res
+ if verify_checksum:
+ assert_equal(hash256(res[:-4])[:4], res[-4:])
+
+ return res[1:-4], int(res[0])
+
def keyhash_to_p2pkh(hash, main = False):
assert len(hash) == 20
@@ -100,3 +128,22 @@ def check_script(script):
if (type(script) is bytes or type(script) is CScript):
return script
assert False
+
+
+class TestFrameworkScript(unittest.TestCase):
+ def test_base58encodedecode(self):
+ def check_base58(data, version):
+ self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
+
+ check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
+ check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
+ check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
+ check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
+ check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
+ check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index d741b00ba0..afc1995009 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
+import unittest
+
from .address import (
key_to_p2sh_p2wpkh,
key_to_p2wpkh,
@@ -217,3 +219,9 @@ def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=Tru
tx_to_witness = ToHex(tx)
return node.sendrawtransaction(tx_to_witness)
+
+class TestFrameworkBlockTools(unittest.TestCase):
+ def test_create_coinbase(self):
+ height = 20
+ coinbase_tx = create_coinbase(height=height)
+ assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), height)
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index f2d6fba4a6..912c0ca978 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -8,8 +8,6 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for
anything but tests."""
import random
-from .address import byte_to_base58
-
def modinv(a, n):
"""Compute the modular inverse of a modulo n
@@ -386,14 +384,3 @@ class ECKey():
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
-
-def bytes_to_wif(b, compressed=True):
- if compressed:
- b += b'\x01'
- return byte_to_base58(b, 239)
-
-def generate_wif_key():
- # Makes a WIF privkey for imports
- k = ECKey()
- k.generate()
- return bytes_to_wif(k.get_bytes(), k.is_compressed)
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index d178e79541..4d1dd4422e 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1516,6 +1516,57 @@ class msg_no_witness_blocktxn(msg_blocktxn):
def serialize(self):
return self.block_transactions.serialize(with_witness=False)
+
+class msg_getcfilters:
+ __slots__ = ("filter_type", "start_height", "stop_hash")
+ msgtype = b"getcfilters"
+
+ def __init__(self, filter_type, start_height, stop_hash):
+ self.filter_type = filter_type
+ self.start_height = start_height
+ self.stop_hash = stop_hash
+
+ def deserialize(self, f):
+ self.filter_type = struct.unpack("<B", f.read(1))[0]
+ self.start_height = struct.unpack("<I", f.read(4))[0]
+ self.stop_hash = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<B", self.filter_type)
+ r += struct.pack("<I", self.start_height)
+ r += ser_uint256(self.stop_hash)
+ return r
+
+ def __repr__(self):
+ return "msg_getcfilters(filter_type={:#x}, start_height={}, stop_hash={:x})".format(
+ self.filter_type, self.start_height, self.stop_hash)
+
+class msg_cfilter:
+ __slots__ = ("filter_type", "block_hash", "filter_data")
+ msgtype = b"cfilter"
+
+ def __init__(self, filter_type=None, block_hash=None, filter_data=None):
+ self.filter_type = filter_type
+ self.block_hash = block_hash
+ self.filter_data = filter_data
+
+ def deserialize(self, f):
+ self.filter_type = struct.unpack("<B", f.read(1))[0]
+ self.block_hash = deser_uint256(f)
+ self.filter_data = deser_string(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<B", self.filter_type)
+ r += ser_uint256(self.block_hash)
+ r += ser_string(self.filter_data)
+ return r
+
+ def __repr__(self):
+ return "msg_cfilter(filter_type={:#x}, block_hash={:x})".format(
+ self.filter_type, self.block_hash)
+
class msg_getcfheaders:
__slots__ = ("filter_type", "start_height", "stop_hash")
msgtype = b"getcfheaders"
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index d1e982ac3e..45063aaff2 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -31,8 +31,9 @@ from test_framework.messages import (
msg_block,
MSG_BLOCK,
msg_blocktxn,
- msg_cfheaders,
msg_cfcheckpt,
+ msg_cfheaders,
+ msg_cfilter,
msg_cmpctblock,
msg_feefilter,
msg_filteradd,
@@ -69,8 +70,9 @@ MESSAGEMAP = {
b"addr": msg_addr,
b"block": msg_block,
b"blocktxn": msg_blocktxn,
- b"cfheaders": msg_cfheaders,
b"cfcheckpt": msg_cfcheckpt,
+ b"cfheaders": msg_cfheaders,
+ b"cfilter": msg_cfilter,
b"cmpctblock": msg_cmpctblock,
b"feefilter": msg_feefilter,
b"filteradd": msg_filteradd,
@@ -332,8 +334,9 @@ class P2PInterface(P2PConnection):
def on_addr(self, message): pass
def on_block(self, message): pass
def on_blocktxn(self, message): pass
- def on_cfheaders(self, message): pass
def on_cfcheckpt(self, message): pass
+ def on_cfheaders(self, message): pass
+ def on_cfilter(self, message): pass
def on_cmpctblock(self, message): pass
def on_feefilter(self, message): pass
def on_filteradd(self, message): pass
@@ -655,6 +658,8 @@ class P2PTxInvStore(P2PInterface):
# save txid
self.tx_invs_received[i.hash] += 1
+ super().on_inv(message)
+
def get_invs(self):
with mininode_lock:
return list(self.tx_invs_received.keys())
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 9102266456..cc5f8307d3 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -9,6 +9,7 @@ This file is modified from python-bitcoinlib.
import hashlib
import struct
import unittest
+from typing import List, Dict
from .messages import (
CTransaction,
@@ -21,7 +22,7 @@ from .messages import (
)
MAX_SCRIPT_ELEMENT_SIZE = 520
-OPCODE_NAMES = {}
+OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
def hash160(s):
return hashlib.new('ripemd160', sha256(s)).digest()
@@ -37,7 +38,7 @@ def bn2vch(v):
# Serialize to bytes
return encoded_v.to_bytes(n_bytes, 'little')
-_opcode_instances = []
+_opcode_instances = [] # type: List[CScriptOp]
class CScriptOp(int):
"""A single script opcode"""
__slots__ = ()
@@ -731,3 +732,9 @@ class TestFrameworkScript(unittest.TestCase):
self.assertEqual(bn2vch(0xFFFFFFFF), bytes([0xFF, 0xFF, 0xFF, 0xFF, 0x00]))
self.assertEqual(bn2vch(123456789), bytes([0x15, 0xCD, 0x5B, 0x07]))
self.assertEqual(bn2vch(-54321), bytes([0x31, 0xD4, 0x80]))
+
+ def test_cscriptnum_encoding(self):
+ # round-trip negative and multi-byte CScriptNums
+ values = [0, 1, -1, -2, 127, 128, -255, 256, (1 << 15) - 1, -(1 << 16), (1 << 24) - 1, (1 << 31), 1 - (1 << 32), 1 << 40, 1500, -1500]
+ for value in values:
+ self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 716fa1d845..9f5e9e5f0d 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -91,6 +91,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
This class also contains various public and private helper methods."""
+ chain = None # type: str
+ setup_clean_chain = None # type: bool
+
def __init__(self):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
self.chain = 'regtest'
@@ -290,7 +293,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
exit_code = TEST_EXIT_SKIPPED
else:
self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir)
+ self.log.error("")
self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir))
+ self.log.error("")
+ self.log.error("If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.")
+ self.log.error(self.config['environment']['PACKAGE_BUGREPORT'])
+ self.log.error("")
exit_code = TEST_EXIT_FAILED
# Logging.shutdown will not remove stream- and filehandlers, so we must
# do it explicitly. Handlers are removed so the next test run can apply
@@ -402,7 +410,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Public helper methods. These can be accessed by the subclass test scripts.
- def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
+ def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
"""Instantiate TestNode objects.
Should only be called once after the nodes have been specified in
@@ -523,7 +531,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
Split the network of four nodes into nodes 0/1 and 2/3.
"""
disconnect_nodes(self.nodes[1], 2)
- disconnect_nodes(self.nodes[2], 1)
self.sync_all(self.nodes[:2])
self.sync_all(self.nodes[2:])
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 6dfea7efd2..52306c8c3d 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -381,7 +381,21 @@ def set_node_times(nodes, t):
node.setmocktime(t)
def disconnect_nodes(from_connection, node_num):
- for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
+ def get_peer_ids():
+ result = []
+ for peer in from_connection.getpeerinfo():
+ if "testnode{}".format(node_num) in peer['subver']:
+ result.append(peer['id'])
+ return result
+
+ peer_ids = get_peer_ids()
+ if not peer_ids:
+ logger.warning("disconnect_nodes: {} and {} were not connected".format(
+ from_connection.index,
+ node_num
+ ))
+ return
+ for peer_id in peer_ids:
try:
from_connection.disconnectnode(nodeid=peer_id)
except JSONRPCException as e:
@@ -392,7 +406,7 @@ def disconnect_nodes(from_connection, node_num):
raise
# wait to disconnect
- wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
+ wait_until(lambda: not get_peer_ids(), timeout=5)
def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 1b6686ff45..b9c0fb6691 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -6,6 +6,7 @@
from collections import namedtuple
from test_framework.address import (
+ byte_to_base58,
key_to_p2pkh,
key_to_p2sh_p2wpkh,
key_to_p2wpkh,
@@ -13,10 +14,7 @@ from test_framework.address import (
script_to_p2sh_p2wsh,
script_to_p2wsh,
)
-from test_framework.key import (
- bytes_to_wif,
- ECKey,
-)
+from test_framework.key import ECKey
from test_framework.script import (
CScript,
OP_0,
@@ -120,3 +118,14 @@ def test_address(node, address, **kwargs):
raise AssertionError("key {} unexpectedly returned in getaddressinfo.".format(key))
elif addr_info[key] != value:
raise AssertionError("key {} value {} did not match expected value {}".format(key, addr_info[key], value))
+
+def bytes_to_wif(b, compressed=True):
+ if compressed:
+ b += b'\x01'
+ return byte_to_base58(b, 239)
+
+def generate_wif_key():
+ # Makes a WIF privkey for imports
+ k = ECKey()
+ k.generate()
+ return bytes_to_wif(k.get_bytes(), k.is_compressed)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 2053d91321..8b11dc8376 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -42,7 +42,7 @@ except UnicodeDecodeError:
if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
if os.name == 'nt':
import ctypes
- kernel32 = ctypes.windll.kernel32
+ kernel32 = ctypes.windll.kernel32 # type: ignore
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
@@ -67,6 +67,8 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
TEST_FRAMEWORK_MODULES = [
+ "address",
+ "blocktools",
"script",
]
@@ -162,7 +164,7 @@ BASE_SCRIPTS = [
'wallet_keypool.py',
'wallet_keypool.py --descriptors',
'wallet_descriptor.py',
- 'p2p_mempool.py',
+ 'p2p_nobloomfilter_messages.py',
'p2p_filter.py',
'rpc_setban.py',
'p2p_blocksonly.py',
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 039ce7daee..524e1593ba 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -203,6 +203,14 @@ class ToolWalletTest(BitcoinTestFramework):
assert_equal(shasum_after, shasum_before)
self.log.debug('Wallet file shasum unchanged\n')
+ def test_salvage(self):
+ # TODO: Check salvage actually salvages and doesn't break things. https://github.com/bitcoin/bitcoin/issues/7463
+ self.log.info('Check salvage')
+ self.start_node(0, ['-wallet=salvage'])
+ self.stop_node(0)
+
+ self.assert_tool_output('', '-wallet=salvage', 'salvage')
+
def run_test(self):
self.wallet_path = os.path.join(self.nodes[0].datadir, self.chain, 'wallets', 'wallet.dat')
self.test_invalid_tool_commands_and_args()
@@ -211,7 +219,7 @@ class ToolWalletTest(BitcoinTestFramework):
self.test_tool_wallet_info_after_transaction()
self.test_tool_wallet_create_on_existing_wallet()
self.test_getwalletinfo_on_different_wallet()
-
+ self.test_salvage()
if __name__ == '__main__':
ToolWalletTest().main()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 9e295af330..797c903dd3 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -404,8 +404,6 @@ class WalletTest(BitcoinTestFramework):
'-reindex',
'-zapwallettxes=1',
'-zapwallettxes=2',
- # disabled until issue is fixed: https://github.com/bitcoin/bitcoin/issues/7463
- # '-salvagewallet',
]
chainlimit = 6
for m in maintenance:
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 5b083a5398..c441b75652 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -11,7 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
- assert_raises_rpc_error
+ assert_raises_rpc_error,
)
@@ -32,11 +32,11 @@ class WalletHDTest(BitcoinTestFramework):
# create an internal key
change_addr = self.nodes[1].getrawchangeaddress()
- change_addrV= self.nodes[1].getaddressinfo(change_addr)
+ change_addrV = self.nodes[1].getaddressinfo(change_addr)
if self.options.descriptors:
assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/0")
else:
- assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key
+ assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key
# Import a non-HD private key in the HD wallet
non_hd_add = 'bcrt1qmevj8zfx0wdvp05cqwkmr6mxkfx60yezwjksmt'
@@ -58,7 +58,7 @@ class WalletHDTest(BitcoinTestFramework):
if self.options.descriptors:
assert_equal(hd_info["hdkeypath"], "m/84'/1'/0'/0/" + str(i))
else:
- assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
+ assert_equal(hd_info["hdkeypath"], "m/0'/0'/" + str(i) + "'")
assert_equal(hd_info["hdmasterfingerprint"], hd_fingerprint)
self.nodes[0].sendtoaddress(hd_add, 1)
self.nodes[0].generate(1)
@@ -67,11 +67,11 @@ class WalletHDTest(BitcoinTestFramework):
# create an internal key (again)
change_addr = self.nodes[1].getrawchangeaddress()
- change_addrV= self.nodes[1].getaddressinfo(change_addr)
+ change_addrV = self.nodes[1].getaddressinfo(change_addr)
if self.options.descriptors:
assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/1")
else:
- assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key
+ assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key
self.sync_all()
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1)
@@ -82,7 +82,10 @@ class WalletHDTest(BitcoinTestFramework):
# otherwise node1 would auto-recover all funds in flag the keypool keys as used
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "blocks"))
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "chainstate"))
- shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, self.chain, 'wallets', "wallet.dat"))
+ shutil.copyfile(
+ os.path.join(self.nodes[1].datadir, "hd.bak"),
+ os.path.join(self.nodes[1].datadir, self.chain, 'wallets', "wallet.dat"),
+ )
self.start_node(1)
# Assert that derivation is deterministic
@@ -93,7 +96,7 @@ class WalletHDTest(BitcoinTestFramework):
if self.options.descriptors:
assert_equal(hd_info_2["hdkeypath"], "m/84'/1'/0'/0/" + str(i))
else:
- assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'")
+ assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/" + str(i) + "'")
assert_equal(hd_info_2["hdmasterfingerprint"], hd_fingerprint)
assert_equal(hd_add, hd_add_2)
connect_nodes(self.nodes[0], 1)
@@ -108,7 +111,10 @@ class WalletHDTest(BitcoinTestFramework):
self.stop_node(1)
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "blocks"))
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "chainstate"))
- shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, self.chain, "wallets", "wallet.dat"))
+ shutil.copyfile(
+ os.path.join(self.nodes[1].datadir, "hd.bak"),
+ os.path.join(self.nodes[1].datadir, self.chain, "wallets", "wallet.dat"),
+ )
self.start_node(1, extra_args=self.extra_args[1])
connect_nodes(self.nodes[0], 1)
self.sync_all()
@@ -142,8 +148,9 @@ class WalletHDTest(BitcoinTestFramework):
new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
assert orig_masterkeyid != new_masterkeyid
addr = self.nodes[1].getnewaddress()
- assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool
- self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key
+ # Make sure the new address is the first from the keypool
+ assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'')
+ self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key
# Set a new HD seed on node 1 without flushing the keypool
new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
@@ -153,13 +160,15 @@ class WalletHDTest(BitcoinTestFramework):
assert orig_masterkeyid != new_masterkeyid
addr = self.nodes[1].getnewaddress()
assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdseedid'])
- assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool
+ # Make sure the new address continues previous keypool
+ assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'')
# Check that the next address is from the new seed
self.nodes[1].keypoolrefill(1)
next_addr = self.nodes[1].getnewaddress()
assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdseedid'])
- assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool
+ # Make sure the new address is not from previous keypool
+ assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'')
assert next_addr != addr
# Sethdseed parameter validity
@@ -185,13 +194,13 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[1].createwallet(wallet_name='restore', blank=True)
restore_rpc = self.nodes[1].get_wallet_rpc('restore')
- restore_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
- restore_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
+ restore_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
+ restore_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
self.nodes[1].createwallet(wallet_name='restore2', blank=True)
restore2_rpc = self.nodes[1].get_wallet_rpc('restore2')
- restore2_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
- restore2_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
+ restore2_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
+ restore2_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
# Check persistence of inactive seed by reloading restore. restore2 is still loaded to test the case where the wallet is not reloaded
restore_rpc.unloadwallet()
@@ -201,8 +210,8 @@ class WalletHDTest(BitcoinTestFramework):
# Empty origin keypool and get an address that is beyond the initial keypool
origin_rpc.getnewaddress()
origin_rpc.getnewaddress()
- last_addr = origin_rpc.getnewaddress() # Last address of initial keypool
- addr = origin_rpc.getnewaddress() # First address beyond initial keypool
+ last_addr = origin_rpc.getnewaddress() # Last address of initial keypool
+ addr = origin_rpc.getnewaddress() # First address beyond initial keypool
# Check that the restored seed has last_addr but does not have addr
info = restore_rpc.getaddressinfo(last_addr)
@@ -222,6 +231,7 @@ class WalletHDTest(BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(addr, 1)
origin_rpc.sendrawtransaction(self.nodes[0].gettransaction(txid)['hex'])
self.nodes[0].generate(1)
+ self.sync_blocks()
origin_rpc.gettransaction(txid)
assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', restore_rpc.gettransaction, txid)
out_of_kp_txid = txid
@@ -232,6 +242,7 @@ class WalletHDTest(BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(last_addr, 1)
origin_rpc.sendrawtransaction(self.nodes[0].gettransaction(txid)['hex'])
self.nodes[0].generate(1)
+ self.sync_blocks()
origin_rpc.gettransaction(txid)
restore_rpc.gettransaction(txid)
assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', restore_rpc.gettransaction, out_of_kp_txid)
@@ -266,5 +277,6 @@ class WalletHDTest(BitcoinTestFramework):
info = restore2_rpc.getaddressinfo(addr)
assert_equal(info['ismine'], False)
+
if __name__ == '__main__':
- WalletHDTest().main ()
+ WalletHDTest().main()
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index f8d1720469..fb4a1f9792 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -134,6 +134,33 @@ class WalletLabelsTest(BitcoinTestFramework):
# in the label. This is a no-op.
change_label(node, labels[2].addresses[0], labels[2], labels[2])
+ self.log.info('Check watchonly labels')
+ node.createwallet(wallet_name='watch_only', disable_private_keys=True, descriptors=False)
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ BECH32_VALID = {
+ '✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn2cjv3',
+ '✔️_VER16_PROG03': 'bcrt1sqqqqqjq8pdp',
+ '✔️_VER16_PROB02': 'bcrt1sqqqqqjq8pv',
+ }
+ BECH32_INVALID = {
+ '❌_VER15_PROG41': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzc7xyq',
+ '❌_VER16_PROB01': 'bcrt1sqqpl9r5c',
+ }
+ for l in BECH32_VALID:
+ ad = BECH32_VALID[l]
+ wallet_watch_only.importaddress(label=l, rescan=False, address=ad)
+ node.generatetoaddress(1, ad)
+ assert_equal(wallet_watch_only.getaddressesbylabel(label=l), {ad: {'purpose': 'receive'}})
+ assert_equal(wallet_watch_only.getreceivedbylabel(label=l), 0)
+ for l in BECH32_INVALID:
+ ad = BECH32_INVALID[l]
+ assert_raises_rpc_error(
+ -5,
+ "Invalid Bitcoin address or script",
+ lambda: wallet_watch_only.importaddress(label=l, rescan=False, address=ad),
+ )
+
+
class Label:
def __init__(self, name):
# Label name
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 580a61f9f3..ff9ff34185 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -122,10 +122,6 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
- self.log.info("Do not allow -salvagewallet with multiwallet")
- self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
- self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
-
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
os.rename(wallet_dir(), wallet_dir2)
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index b384998d56..3417616d77 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -49,16 +49,21 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
block.solve()
node.submitblock(ToHex(block))
- # Transaction should not be rebroadcast
node.syncwithvalidationinterfacequeue()
- node.p2ps[1].sync_with_ping()
- assert_equal(node.p2ps[1].tx_invs_received[txid], 0)
+ now = int(time.time())
+
+ # Transaction should not be rebroadcast within first 12 hours
+ # Leave 2 mins for buffer
+ twelve_hrs = 12 * 60 * 60
+ two_min = 2 * 60
+ node.setmocktime(now + twelve_hrs - two_min)
+ time.sleep(2) # ensure enough time has passed for rebroadcast attempt to occur
+ assert_equal(txid in node.p2ps[1].get_invs(), False)
self.log.info("Bump time & check that transaction is rebroadcast")
# Transaction should be rebroadcast approximately 24 hours in the future,
# but can range from 12-36. So bump 36 hours to be sure.
- rebroadcast_time = int(time.time()) + 36 * 60 * 60
- node.setmocktime(rebroadcast_time)
+ node.setmocktime(now + 36 * 60 * 60)
wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock)
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index ad23206c90..5e1a804d33 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -31,7 +31,6 @@ class TxnMallTest(BitcoinTestFramework):
# Start with split network:
super().setup_network()
disconnect_nodes(self.nodes[1], 2)
- disconnect_nodes(self.nodes[2], 1)
def run_test(self):
if self.options.segwit:
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index 1891cd9190..cac58aeaf2 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -29,7 +29,6 @@ class TxnMallTest(BitcoinTestFramework):
# Start with split network:
super().setup_network()
disconnect_nodes(self.nodes[1], 2)
- disconnect_nodes(self.nodes[2], 1)
def run_test(self):
# All nodes should start with 1,250 BTC:
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index bd9c8337ac..5404565b94 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -67,9 +67,9 @@ EXPECTED_BOOST_INCLUDES=(
boost/signals2/last_value.hpp
boost/signals2/signal.hpp
boost/test/unit_test.hpp
- boost/thread.hpp
boost/thread/condition_variable.hpp
boost/thread/mutex.hpp
+ boost/thread/shared_mutex.hpp
boost/thread/thread.hpp
boost/variant.hpp
boost/variant/apply_visitor.hpp
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index 86ac5a930f..decea38c4f 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -7,6 +7,7 @@
# Check for specified flake8 warnings in python files.
export LC_ALL=C
+export MYPY_CACHE_DIR="${BASE_ROOT_DIR}/test/.mypy_cache"
enabled=(
E101 # indentation contains mixed spaces and tabs
@@ -89,10 +90,20 @@ elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then
exit 0
fi
-PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") $(
+EXIT_CODE=0
+
+if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") $(
if [[ $# == 0 ]]; then
git ls-files "*.py"
else
echo "$@"
fi
-)
+); then
+ EXIT_CODE=1
+fi
+
+if ! mypy --ignore-missing-imports $(git ls-files "test/functional/*.py"); then
+ EXIT_CODE=1
+fi
+
+exit $EXIT_CODE
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 70eea34363..b8fe75c5c5 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -1,6 +1,35 @@
# ThreadSanitizer suppressions
# ============================
+# double locks (TODO fix)
+mutex:g_genesis_wait_mutex
+mutex:Interrupt
+mutex:CThreadInterrupt
+mutex:CConnman::Interrupt
+mutex:CConnman::WakeMessageHandler
+mutex:CConnman::ThreadOpenConnections
+mutex:CConnman::ThreadOpenAddedConnections
+mutex:CConnman::SocketHandler
+mutex:UpdateTip
+mutex:PeerLogicValidation::UpdatedBlockTip
+mutex:g_best_block_mutex
+# race (TODO fix)
+race:CConnman::WakeMessageHandler
+race:CConnman::ThreadMessageHandler
+race:fHaveGenesis
+race:ProcessNewBlock
+race:ThreadImport
+race:WalletBatch::WriteHDChain
+race:BerkeleyDatabase
+race:zmq::*
+race:bitcoin-qt
+# deadlock (TODO fix)
+deadlock:CConnman::ForNode
+deadlock:CConnman::GetNodeStats
+deadlock:CChainState::ConnectTip
+deadlock:UpdateTip
+deadlock:wallet_tests::CreateWalletFromFile
+
# WalletBatch (unidentified deadlock)
deadlock:WalletBatch