diff --git a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
index 96bb5843758b3..6c45d4dbd8a0f 100644
--- a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
+++ b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
@@ -24,6 +24,7 @@
+
@@ -78,6 +79,7 @@
+
diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
index 5c4b777d5164b..bb1a780bfab12 100644
--- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj
+++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
@@ -16,6 +16,7 @@
+
diff --git a/ci/test/00_setup_env_android.sh b/ci/test/00_setup_env_android.sh
index f78a84eeac47d..4ef3ae1ceb97d 100755
--- a/ci/test/00_setup_env_android.sh
+++ b/ci/test/00_setup_env_android.sh
@@ -16,7 +16,7 @@ export RUN_FUNCTIONAL_TESTS=false
export ANDROID_API_LEVEL=28
export ANDROID_BUILD_TOOLS_VERSION=28.0.3
-export ANDROID_NDK_VERSION=21.1.6352462
+export ANDROID_NDK_VERSION=22.1.7171670
export ANDROID_TOOLS_URL=https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip
export ANDROID_HOME="${DEPENDS_DIR}/SDKs/android"
export ANDROID_NDK_HOME="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}"
diff --git a/contrib/guix/INSTALL.md b/contrib/guix/INSTALL.md
index 86f91cc87bf0b..63aa3e02b2cc0 100644
--- a/contrib/guix/INSTALL.md
+++ b/contrib/guix/INSTALL.md
@@ -79,8 +79,8 @@ Guix v1.2.0 is available as a distribution package starting in [Debian
21.04](https://packages.ubuntu.com/hirsute/guix).
Note that if you intend on using Guix without using any substitutes (more
-details [here][security-model]), v1.2.0 has a known problems when building
-GnuTLS from source. Solutions and workarounds are documented
+details [here][security-model]), v1.2.0 has a known problem when building GnuTLS
+from source. Solutions and workarounds are documented
[here](#gnutls-test-suite-fail-status-request-revoked).
@@ -124,7 +124,7 @@ particular commit of Guix). Previous experience with using autotools-style build
systems to build packages from source will be helpful. *hic sunt dracones.*
I strongly urge you to at least skim through the entire section once before you
-start issuing commands, as it will save you a lot of unncessary pain and
+start issuing commands, as it will save you a lot of unnecessary pain and
anguish.
### Installing common build tools
@@ -165,7 +165,7 @@ packaged and installable without manually building and installing.
For reference, the graphic below outlines Guix v1.3.0's dependency graph:
-
+
#### Guile
@@ -270,23 +270,11 @@ Note that these environment variables are used to check for packages during
`./configure`, so they should be set as soon as possible should you want to use
a prefix other than `/usr`.
-
-
-
-
-
-
-
-
-
-
-
-
#### Building and installing source-built packages
-***IMPORTANT**: A few dependencies have non-obvious quirks/erratas which are documented in the
-sub-sections immediately below. Please read these sections before proceeding to
-build and install these packages.*
+***IMPORTANT**: A few dependencies have non-obvious quirks/errata which are
+documented in the sub-sections immediately below. Please read these sections
+before proceeding to build and install these packages.*
Although you should always refer to the README or INSTALL files for the most
accurate information, most of these dependencies use autoconf-style build
diff --git a/contrib/guix/guix-build b/contrib/guix/guix-build
index f6da8435e99e4..5a908a507d068 100755
--- a/contrib/guix/guix-build
+++ b/contrib/guix/guix-build
@@ -190,7 +190,7 @@ fi
# Services database must have basic entries
################
-if ! getent services http https ftp; then
+if ! getent services http https ftp > /dev/null 2>&1; then
cat << EOF
ERR: Your system's C library can not find service database entries for at least
one of the following services: http, https, ftp.
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index bc3391e0896e2..356bd700701ed 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -227,7 +227,6 @@ GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}.tar.gz"
# Create the source tarball if not already there
if [ ! -e "$GIT_ARCHIVE" ]; then
mkdir -p "$(dirname "$GIT_ARCHIVE")"
- touch "${DIST_ARCHIVE_BASE}"/SKIPATTEST.TAG
git archive --prefix="${DISTNAME}/" --output="$GIT_ARCHIVE" HEAD
fi
diff --git a/contrib/install_db4.sh b/contrib/install_db4.sh
index 4037936404bae..dd4d862dee504 100755
--- a/contrib/install_db4.sh
+++ b/contrib/install_db4.sh
@@ -221,10 +221,10 @@ EOF
# The packaged config.guess and config.sub are ancient (2009) and can cause build issues.
# Replace them with modern versions.
# See https://github.com/bitcoin/bitcoin/issues/16064
-CONFIG_GUESS_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=55eaf3e779455c4e5cc9f82efb5278be8f8f900b'
-CONFIG_GUESS_HASH='2d1ff7bca773d2ec3c6217118129220fa72d8adda67c7d2bf79994b3129232c1'
-CONFIG_SUB_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=55eaf3e779455c4e5cc9f82efb5278be8f8f900b'
-CONFIG_SUB_HASH='3a4befde9bcdf0fdb2763fc1bfa74e8696df94e1ad7aac8042d133c8ff1d2e32'
+CONFIG_GUESS_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=4550d2f15b3a7ce2451c1f29500b9339430c877f'
+CONFIG_GUESS_HASH='c8f530e01840719871748a8071113435bdfdf75b74c57e78e47898edea8754ae'
+CONFIG_SUB_URL='https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=4550d2f15b3a7ce2451c1f29500b9339430c877f'
+CONFIG_SUB_HASH='3969f7d5f6967ccc6f792401b8ef3916a1d1b1d0f0de5a4e354c95addb8b800e'
rm -f "dist/config.guess"
rm -f "dist/config.sub"
diff --git a/contrib/tracing/README.md b/contrib/tracing/README.md
new file mode 100644
index 0000000000000..047354cda17b1
--- /dev/null
+++ b/contrib/tracing/README.md
@@ -0,0 +1,241 @@
+Example scripts for User-space, Statically Defined Tracing (USDT)
+=================================================================
+
+This directory contains scripts showcasing User-space, Statically Defined
+Tracing (USDT) support for Bitcoin Core on Linux using. For more information on
+USDT support in Bitcoin Core see the [USDT documentation].
+
+[USDT documentation]: ../../doc/tracing.md
+
+
+Examples for the two main eBPF front-ends, [bpftrace] and
+[BPF Compiler Collection (BCC)], with support for USDT, are listed. BCC is used
+for complex tools and daemons and `bpftrace` is preferred for one-liners and
+shorter scripts.
+
+[bpftrace]: https://github.com/iovisor/bpftrace
+[BPF Compiler Collection (BCC)]: https://github.com/iovisor/bcc
+
+
+To develop and run bpftrace and BCC scripts you need to install the
+corresponding packages. See [installing bpftrace] and [installing BCC] for more
+information. For development there exist a [bpftrace Reference Guide], a
+[BCC Reference Guide], and a [bcc Python Developer Tutorial].
+
+[installing bpftrace]: https://github.com/iovisor/bpftrace/blob/master/INSTALL.md
+[installing BCC]: https://github.com/iovisor/bcc/blob/master/INSTALL.md
+[bpftrace Reference Guide]: https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
+[BCC Reference Guide]: https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
+[bcc Python Developer Tutorial]: https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md
+
+## Examples
+
+The bpftrace examples contain a relative path to the `bitcoind` binary. By
+default, the scripts should be run from the repository-root and assume a
+self-compiled `bitcoind` binary. The paths in the examples can be changed, for
+example, to point to release builds if needed. See the
+[Bitcoin Core USDT documentation] on how to list available tracepoints in your
+`bitcoind` binary.
+
+[Bitcoin Core USDT documentation]: ../../doc/tracing.md#listing-available-tracepoints
+
+**WARNING: eBPF programs require root privileges to be loaded into a Linux
+kernel VM. This means the bpftrace and BCC examples must be executed with root
+privileges. Make sure to carefully review any scripts that you run with root
+privileges first!**
+
+### log_p2p_traffic.bt
+
+A bpftrace script logging information about inbound and outbound P2P network
+messages. Based on the `net:inbound_message` and `net:outbound_message`
+tracepoints.
+
+By default, `bpftrace` limits strings to 64 bytes due to the limited stack size
+in the eBPF VM. For example, Tor v3 addresses exceed the string size limit which
+results in the port being cut off during logging. The string size limit can be
+increased with the `BPFTRACE_STRLEN` environment variable (`BPFTRACE_STRLEN=70`
+works fine).
+
+```
+$ bpftrace contrib/tracing/log_p2p_traffic.bt
+```
+
+Output
+```
+outbound 'ping' msg to peer 11 (outbound-full-relay, [2a02:b10c:f747:1:ef:fake:ipv6:addr]:8333) with 8 bytes
+inbound 'pong' msg from peer 11 (outbound-full-relay, [2a02:b10c:f747:1:ef:fake:ipv6:addr]:8333) with 8 bytes
+inbound 'inv' msg from peer 16 (outbound-full-relay, XX.XX.XXX.121:8333) with 37 bytes
+outbound 'getdata' msg to peer 16 (outbound-full-relay, XX.XX.XXX.121:8333) with 37 bytes
+inbound 'tx' msg from peer 16 (outbound-full-relay, XX.XX.XXX.121:8333) with 222 bytes
+outbound 'inv' msg to peer 9 (outbound-full-relay, faketorv3addressa2ufa6odvoi3s77j4uegey0xb10csyfyve2t33curbyd.onion:8333) with 37 bytes
+outbound 'inv' msg to peer 7 (outbound-full-relay, XX.XX.XXX.242:8333) with 37 bytes
+…
+```
+
+### p2p_monitor.py
+
+A BCC Python script using curses for an interactive P2P message monitor. Based
+on the `net:inbound_message` and `net:outbound_message` tracepoints.
+
+Inbound and outbound traffic is listed for each peer together with information
+about the connection. Peers can be selected individually to view recent P2P
+messages.
+
+```
+$ python3 contrib/tracing/p2p_monitor.py ./src/bitcoind
+```
+
+Lists selectable peers and traffic and connection information.
+```
+ P2P Message Monitor
+ Navigate with UP/DOWN or J/K and select a peer with ENTER or SPACE to see individual P2P messages
+
+ PEER OUTBOUND INBOUND TYPE ADDR
+ 0 46 398 byte 61 1407590 byte block-relay-only XX.XX.XXX.196:8333
+ 11 1156 253570 byte 3431 2394924 byte outbound-full-relay XXX.X.XX.179:8333
+ 13 3425 1809620 byte 1236 305458 byte inbound XXX.X.X.X:60380
+ 16 1046 241633 byte 1589 1199220 byte outbound-full-relay 4faketorv2pbfu7x.onion:8333
+ 19 577 181679 byte 390 148951 byte outbound-full-relay kfake4vctorjv2o2.onion:8333
+ 20 11 1248 byte 13 1283 byte block-relay-only [2600:fake:64d9:b10c:4436:aaaa:fe:bb]:8333
+ 21 11 1248 byte 13 1299 byte block-relay-only XX.XXX.X.155:8333
+ 22 5 103 byte 1 102 byte feeler XX.XX.XXX.173:8333
+ 23 11 1248 byte 12 1255 byte block-relay-only XX.XXX.XXX.220:8333
+ 24 3 103 byte 1 102 byte feeler XXX.XXX.XXX.64:8333
+…
+```
+
+Showing recent P2P messages between our node and a selected peer.
+
+```
+ ----------------------------------------------------------------------
+ | PEER 16 (4faketorv2pbfu7x.onion:8333) |
+ | OUR NODE outbound-full-relay PEER |
+ | <--- sendcmpct (9 bytes) |
+ | inv (37 byte) ---> |
+ | <--- ping (8 bytes) |
+ | pong (8 byte) ---> |
+ | inv (37 byte) ---> |
+ | <--- addr (31 bytes) |
+ | inv (37 byte) ---> |
+ | <--- getheaders (1029 bytes) |
+ | headers (1 byte) ---> |
+ | <--- feefilter (8 bytes) |
+ | <--- pong (8 bytes) |
+ | <--- headers (82 bytes) |
+ | <--- addr (30003 bytes) |
+ | inv (1261 byte) ---> |
+ | … |
+
+```
+
+### log_raw_p2p_msgs.py
+
+A BCC Python script showcasing eBPF and USDT limitations when passing data
+larger than about 32kb. Based on the `net:inbound_message` and
+`net:outbound_message` tracepoints.
+
+Bitcoin P2P messages can be larger than 32kb (e.g. `tx`, `block`, ...). The
+eBPF VM's stack is limited to 512 bytes, and we can't allocate more than about
+32kb for a P2P message in the eBPF VM. The **message data is cut off** when the
+message is larger than MAX_MSG_DATA_LENGTH (see script). This can be detected
+in user-space by comparing the data length to the message length variable. The
+message is cut off when the data length is smaller than the message length.
+A warning is included with the printed message data.
+
+Data is submitted to user-space (i.e. to this script) via a ring buffer. The
+throughput of the ring buffer is limited. Each p2p_message is about 32kb in
+size. In- or outbound messages submitted to the ring buffer in rapid
+succession fill the ring buffer faster than it can be read. Some messages are
+lost. BCC prints: `Possibly lost 2 samples` on lost messages.
+
+
+```
+$ python3 contrib/tracing/log_raw_p2p_msgs.py ./src/bitcoind
+```
+
+```
+Logging raw P2P messages.
+Messages larger that about 32kb will be cut off!
+Some messages might be lost!
+ outbound msg 'inv' from peer 4 (outbound-full-relay, XX.XXX.XX.4:8333) with 253 bytes: 0705000000be2245c8f844c9f763748e1a7…
+…
+Warning: incomplete message (only 32568 out of 53552 bytes)! inbound msg 'tx' from peer 32 (outbound-full-relay, XX.XXX.XXX.43:8333) with 53552 bytes: 020000000001fd3c01939c85ad6756ed9fc…
+…
+Possibly lost 2 samples
+```
+
+### connectblock_benchmark.bt
+
+A `bpftrace` script to benchmark the `ConnectBlock()` function during, for
+example, a blockchain re-index. Based on the `validation:block_connected` USDT
+tracepoint.
+
+The script takes three positional arguments. The first two arguments, the start,
+and end height indicate between which blocks the benchmark should be run. The
+third acts as a duration threshold in milliseconds. When the `ConnectBlock()`
+function takes longer than the threshold, information about the block, is
+printed. For more details, see the header comment in the script.
+
+By default, `bpftrace` limits strings to 64 bytes due to the limited stack size
+in the kernel VM. Block hashes as zero-terminated hex strings are 65 bytes which
+exceed the string limit. The string size limit can be set to 65 bytes with the
+environment variable `BPFTRACE_STRLEN`.
+
+The following command can be used to benchmark, for example, `ConnectBlock()`
+between height 20000 and 38000 on SigNet while logging all blocks that take
+longer than 25ms to connect.
+
+```
+$ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 20000 38000 25
+```
+
+In a different terminal, starting Bitcoin Core in SigNet mode and with
+re-indexing enabled.
+
+```
+$ ./src/bitcoind -signet -reindex
+```
+
+This produces the following output.
+```
+Attaching 5 probes...
+ConnectBlock Benchmark between height 20000 and 38000 inclusive
+Logging blocks taking longer than 25 ms to connect.
+Starting Connect Block Benchmark between height 20000 and 38000.
+BENCH 39 blk/s 59 tx/s 59 inputs/s 20 sigops/s (height 20038)
+Block 20492 (000000f555653bb05e2f3c6e79925e01a20dd57033f4dc7c354b46e34735d32b) 20 tx 2319 ins 2318 sigops took 38 ms
+BENCH 1840 blk/s 2117 tx/s 4478 inputs/s 2471 sigops/s (height 21879)
+BENCH 1816 blk/s 4972 tx/s 4982 inputs/s 125 sigops/s (height 23695)
+BENCH 2095 blk/s 2890 tx/s 2910 inputs/s 152 sigops/s (height 25790)
+BENCH 1684 blk/s 3979 tx/s 4053 inputs/s 288 sigops/s (height 27474)
+BENCH 1155 blk/s 3216 tx/s 3252 inputs/s 115 sigops/s (height 28629)
+BENCH 1797 blk/s 2488 tx/s 2503 inputs/s 111 sigops/s (height 30426)
+BENCH 1849 blk/s 6318 tx/s 6569 inputs/s 12189 sigops/s (height 32275)
+BENCH 946 blk/s 20209 tx/s 20775 inputs/s 83809 sigops/s (height 33221)
+Block 33406 (0000002adfe4a15cfcd53bd890a89bbae836e5bb7f38bac566f61ad4548c87f6) 25 tx 2045 ins 2090 sigops took 29 ms
+Block 33687 (00000073231307a9828e5607ceb8156b402efe56747271a4442e75eb5b77cd36) 52 tx 1797 ins 1826 sigops took 26 ms
+BENCH 582 blk/s 21581 tx/s 27673 inputs/s 60345 sigops/s (height 33803)
+BENCH 1035 blk/s 19735 tx/s 19776 inputs/s 51355 sigops/s (height 34838)
+Block 35625 (0000006b00b347390c4768ea9df2655e9ff4b120f29d78594a2a702f8a02c997) 20 tx 3374 ins 3371 sigops took 49 ms
+BENCH 887 blk/s 17857 tx/s 22191 inputs/s 24404 sigops/s (height 35725)
+Block 35937 (000000d816d13d6e39b471cd4368db60463a764ba1f29168606b04a22b81ea57) 75 tx 3943 ins 3940 sigops took 61 ms
+BENCH 823 blk/s 16298 tx/s 21031 inputs/s 18440 sigops/s (height 36548)
+Block 36583 (000000c3e260556dbf42968aae3f904dba8b8c1ff96a6f6e3aa5365d2e3ad317) 24 tx 2198 ins 2194 sigops took 34 ms
+Block 36700 (000000b3b173de9e65a3cfa738d976af6347aaf83fa17ab3f2a4d2ede3ddfac4) 73 tx 1615 ins 1611 sigops took 31 ms
+Block 36832 (0000007859578c02c1ac37dabd1b9ec19b98f350b56935f5dd3a41e9f79f836e) 34 tx 1440 ins 1436 sigops took 26 ms
+BENCH 613 blk/s 16718 tx/s 25074 inputs/s 23022 sigops/s (height 37161)
+Block 37870 (000000f5c1086291ba2d943fb0c3bc82e71c5ee341ee117681d1456fbf6c6c38) 25 tx 1517 ins 1514 sigops took 29 ms
+BENCH 811 blk/s 16031 tx/s 20921 inputs/s 18696 sigops/s (height 37972)
+
+Took 14055 ms to connect the blocks between height 20000 and 38000.
+
+Histogram of block connection times in milliseconds (ms).
+@durations:
+[0] 16838 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
+[1] 882 |@@ |
+[2, 4) 236 | |
+[4, 8) 23 | |
+[8, 16) 9 | |
+[16, 32) 9 | |
+[32, 64) 4 | |
+```
diff --git a/contrib/tracing/connectblock_benchmark.bt b/contrib/tracing/connectblock_benchmark.bt
new file mode 100755
index 0000000000000..d268eff7f8b8c
--- /dev/null
+++ b/contrib/tracing/connectblock_benchmark.bt
@@ -0,0 +1,150 @@
+#!/usr/bin/env bpftrace
+
+/*
+
+ USAGE:
+
+ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt
+
+ - The environment variable BPFTRACE_STRLEN needs to be set to 65 chars as
+ strings are limited to 64 chars by default. Hex strings with Bitcoin block
+ hashes are 64 hex chars + 1 null-termination char.
+ - sets the height at which the benchmark should start. Setting
+ the start height to 0 starts the benchmark immediately, even before the
+ first block is connected.
+ - sets the height after which the benchmark should end. Setting
+ the end height to 0 disables the benchmark. The script only logs blocks
+ over .
+ - Threshold
+
+ This script requires a 'bitcoind' binary compiled with eBPF support and the
+ 'validation:block_connected' USDT. By default, it's assumed that 'bitcoind' is
+ located in './src/bitcoind'. This can be modified in the script below.
+
+ EXAMPLES:
+
+ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 300000 680000 1000
+
+ When run together 'bitcoind -reindex', this benchmarks the time it takes to
+ connect the blocks between height 300.000 and 680.000 (inclusive) and prints
+ details about all blocks that take longer than 1000ms to connect. Prints a
+ histogram with block connection times when the benchmark is finished.
+
+
+ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 0 0 500
+
+ When running together 'bitcoind', all newly connected blocks that
+ take longer than 500ms to connect are logged. A histogram with block
+ connection times is shown when the script is terminated.
+
+*/
+
+BEGIN
+{
+ $start_height = $1;
+ $end_height = $2;
+ $logging_threshold_ms = $3;
+
+ if ($end_height < $start_height) {
+ printf("Error: start height (%d) larger than end height (%d)!\n", $start_height, $end_height);
+ exit();
+ }
+
+ if ($end_height > 0) {
+ printf("ConnectBlock benchmark between height %d and %d inclusive\n", $start_height, $end_height);
+ } else {
+ printf("ConnectBlock logging starting at height %d\n", $start_height);
+ }
+
+ if ($logging_threshold_ms > 0) {
+ printf("Logging blocks taking longer than %d ms to connect.\n", $3);
+ }
+
+ if ($start_height == 0) {
+ @start = nsecs;
+ }
+}
+
+/*
+ Attaches to the 'validation:block_connected' USDT and collects stats when the
+ connected block is between the start and end height (or the end height is
+ unset).
+*/
+usdt:./src/bitcoind:validation:block_connected /arg1 >= $1 && (arg1 <= $2 || $2 == 0 )/
+{
+ $height = arg1;
+ $transactions = arg2;
+ $inputs = arg3;
+ $sigops = arg4;
+ $duration = (uint64) arg5;
+
+ @height = $height;
+
+ @blocks = @blocks + 1;
+ @transactions = @transactions + $transactions;
+ @inputs = @inputs + $inputs;
+ @sigops = @sigops + $sigops;
+
+ @durations = hist($duration / 1000);
+
+ if ($height == $1 && $height != 0) {
+ @start = nsecs;
+ printf("Starting Connect Block Benchmark between height %d and %d.\n", $1, $2);
+ }
+
+ if ($2 > 0 && $height >= $2) {
+ @end = nsecs;
+ $duration = @end - @start;
+ printf("\nTook %d ms to connect the blocks between height %d and %d.\n", $duration / 1000000, $1, $2);
+ exit();
+ }
+}
+
+/*
+ Attaches to the 'validation:block_connected' USDT and logs information about
+ blocks where the time it took to connect the block is above the
+ .
+*/
+usdt:./src/bitcoind:validation:block_connected / (uint64) arg5 / 1000> $3 /
+{
+ $hash_str = str(arg0);
+ $height = (int32) arg1;
+ $transactions = (uint64) arg2;
+ $inputs = (int32) arg3;
+ $sigops = (int64) arg4;
+ $duration = (int64) arg5;
+
+ printf("Block %d (%s) %4d tx %5d ins %5d sigops took %4d ms\n", $height, $hash_str, $transactions, $inputs, $sigops, (uint64) $duration / 1000);
+}
+
+
+/*
+ Prints stats about the blocks, transactions, inputs, and sigops processed in
+ the last second (if any).
+*/
+interval:s:1 {
+ if (@blocks > 0) {
+ printf("BENCH %4d blk/s %6d tx/s %7d inputs/s %8d sigops/s (height %d)\n", @blocks, @transactions, @inputs, @sigops, @height);
+
+ zero(@blocks);
+ zero(@transactions);
+ zero(@inputs);
+ zero(@sigops);
+ }
+}
+
+END
+{
+ printf("\nHistogram of block connection times in milliseconds (ms).\n");
+ print(@durations);
+
+ clear(@durations);
+ clear(@blocks);
+ clear(@transactions);
+ clear(@inputs);
+ clear(@sigops);
+ clear(@height);
+ clear(@start);
+ clear(@end);
+}
+
diff --git a/contrib/tracing/log_p2p_traffic.bt b/contrib/tracing/log_p2p_traffic.bt
new file mode 100755
index 0000000000000..f62956aa5e1c3
--- /dev/null
+++ b/contrib/tracing/log_p2p_traffic.bt
@@ -0,0 +1,28 @@
+#!/usr/bin/env bpftrace
+
+BEGIN
+{
+ printf("Logging P2P traffic\n")
+}
+
+usdt:./src/bitcoind:net:inbound_message
+{
+ $peer_id = (int64) arg0;
+ $peer_addr = str(arg1);
+ $peer_type = str(arg2);
+ $msg_type = str(arg3);
+ $msg_len = arg4;
+ printf("inbound '%s' msg from peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type, $peer_addr, $msg_len);
+}
+
+usdt:./src/bitcoind:net:outbound_message
+{
+ $peer_id = (int64) arg0;
+ $peer_addr = str(arg1);
+ $peer_type = str(arg2);
+ $msg_type = str(arg3);
+ $msg_len = arg4;
+
+ printf("outbound '%s' msg to peer %d (%s, %s) with %d bytes\n", $msg_type, $peer_id, $peer_type, $peer_addr, $msg_len);
+}
+
diff --git a/contrib/tracing/log_raw_p2p_msgs.py b/contrib/tracing/log_raw_p2p_msgs.py
new file mode 100755
index 0000000000000..b5b57556324e3
--- /dev/null
+++ b/contrib/tracing/log_raw_p2p_msgs.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+
+""" Demonstration of eBPF limitations and the effect on USDT with the
+ net:inbound_message and net:outbound_message tracepoints. """
+
+# This script shows a limitation of eBPF when data larger than 32kb is passed to
+# user-space. It uses BCC (https://github.com/iovisor/bcc) to load a sandboxed
+# eBPF program into the Linux kernel (root privileges are required). The eBPF
+# program attaches to two statically defined tracepoints. The tracepoint
+# 'net:inbound_message' is called when a new P2P message is received, and
+# 'net:outbound_message' is called on outbound P2P messages. The eBPF program
+# submits the P2P messages to this script via a BPF ring buffer. The submitted
+# messages are printed.
+
+# eBPF Limitations:
+#
+# Bitcoin P2P messages can be larger than 32kb (e.g. tx, block, ...). The eBPF
+# VM's stack is limited to 512 bytes, and we can't allocate more than about 32kb
+# for a P2P message in the eBPF VM. The message data is cut off when the message
+# is larger than MAX_MSG_DATA_LENGTH (see definition below). This can be detected
+# in user-space by comparing the data length to the message length variable. The
+# message is cut off when the data length is smaller than the message length.
+# A warning is included with the printed message data.
+#
+# Data is submitted to user-space (i.e. to this script) via a ring buffer. The
+# throughput of the ring buffer is limited. Each p2p_message is about 32kb in
+# size. In- or outbound messages submitted to the ring buffer in rapid
+# succession fill the ring buffer faster than it can be read. Some messages are
+# lost.
+#
+# BCC prints: "Possibly lost 2 samples" on lost messages.
+
+import sys
+from bcc import BPF, USDT
+
+# BCC: The C program to be compiled to an eBPF program (by BCC) and loaded into
+# a sandboxed Linux kernel VM.
+program = """
+#include
+
+#define MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
+
+// Maximum possible allocation size
+// from include/linux/percpu.h in the Linux kernel
+#define PCPU_MIN_UNIT_SIZE (32 << 10)
+
+// Tor v3 addresses are 62 chars + 6 chars for the port (':12345').
+#define MAX_PEER_ADDR_LENGTH 62 + 6
+#define MAX_PEER_CONN_TYPE_LENGTH 20
+#define MAX_MSG_TYPE_LENGTH 20
+#define MAX_MSG_DATA_LENGTH PCPU_MIN_UNIT_SIZE - 200
+
+struct p2p_message
+{
+ u64 peer_id;
+ char peer_addr[MAX_PEER_ADDR_LENGTH];
+ char peer_conn_type[MAX_PEER_CONN_TYPE_LENGTH];
+ char msg_type[MAX_MSG_TYPE_LENGTH];
+ u64 msg_size;
+ u8 msg[MAX_MSG_DATA_LENGTH];
+};
+
+// We can't store the p2p_message struct on the eBPF stack as it is limited to
+// 512 bytes and P2P message can be bigger than 512 bytes. However, we can use
+// an BPF-array with a length of 1 to allocate up to 32768 bytes (this is
+// defined by PCPU_MIN_UNIT_SIZE in include/linux/percpu.h in the Linux kernel).
+// Also see https://github.com/iovisor/bcc/issues/2306
+BPF_ARRAY(msg_arr, struct p2p_message, 1);
+
+// Two BPF perf buffers for pushing data (here P2P messages) to user-space.
+BPF_PERF_OUTPUT(inbound_messages);
+BPF_PERF_OUTPUT(outbound_messages);
+
+int trace_inbound_message(struct pt_regs *ctx) {
+ int idx = 0;
+ struct p2p_message *msg = msg_arr.lookup(&idx);
+
+ // lookup() does not return a NULL pointer. However, the BPF verifier
+ // requires an explicit check that that the `msg` pointer isn't a NULL
+ // pointer. See https://github.com/iovisor/bcc/issues/2595
+ if (msg == NULL) return 1;
+
+ bpf_usdt_readarg(1, ctx, &msg->peer_id);
+ bpf_usdt_readarg_p(2, ctx, &msg->peer_addr, MAX_PEER_ADDR_LENGTH);
+ bpf_usdt_readarg_p(3, ctx, &msg->peer_conn_type, MAX_PEER_CONN_TYPE_LENGTH);
+ bpf_usdt_readarg_p(4, ctx, &msg->msg_type, MAX_MSG_TYPE_LENGTH);
+ bpf_usdt_readarg(5, ctx, &msg->msg_size);
+ bpf_usdt_readarg_p(6, ctx, &msg->msg, MIN(msg->msg_size, MAX_MSG_DATA_LENGTH));
+
+ inbound_messages.perf_submit(ctx, msg, sizeof(*msg));
+ return 0;
+};
+
+int trace_outbound_message(struct pt_regs *ctx) {
+ int idx = 0;
+ struct p2p_message *msg = msg_arr.lookup(&idx);
+
+ // lookup() does not return a NULL pointer. However, the BPF verifier
+ // requires an explicit check that that the `msg` pointer isn't a NULL
+ // pointer. See https://github.com/iovisor/bcc/issues/2595
+ if (msg == NULL) return 1;
+
+ bpf_usdt_readarg(1, ctx, &msg->peer_id);
+ bpf_usdt_readarg_p(2, ctx, &msg->peer_addr, MAX_PEER_ADDR_LENGTH);
+ bpf_usdt_readarg_p(3, ctx, &msg->peer_conn_type, MAX_PEER_CONN_TYPE_LENGTH);
+ bpf_usdt_readarg_p(4, ctx, &msg->msg_type, MAX_MSG_TYPE_LENGTH);
+ bpf_usdt_readarg(5, ctx, &msg->msg_size);
+ bpf_usdt_readarg_p(6, ctx, &msg->msg, MIN(msg->msg_size, MAX_MSG_DATA_LENGTH));
+
+ outbound_messages.perf_submit(ctx, msg, sizeof(*msg));
+ return 0;
+};
+"""
+
+
+def print_message(event, inbound):
+ print(f"%s %s msg '%s' from peer %d (%s, %s) with %d bytes: %s" %
+ (
+ f"Warning: incomplete message (only %d out of %d bytes)!" % (
+ len(event.msg), event.msg_size) if len(event.msg) < event.msg_size else "",
+ "inbound" if inbound else "outbound",
+ event.msg_type.decode("utf-8"),
+ event.peer_id,
+ event.peer_conn_type.decode("utf-8"),
+ event.peer_addr.decode("utf-8"),
+ event.msg_size,
+ bytes(event.msg[:event.msg_size]).hex(),
+ )
+ )
+
+
+def main(bitcoind_path):
+ bitcoind_with_usdts = USDT(path=str(bitcoind_path))
+
+ # attaching the trace functions defined in the BPF program to the tracepoints
+ bitcoind_with_usdts.enable_probe(
+ probe="inbound_message", fn_name="trace_inbound_message")
+ bitcoind_with_usdts.enable_probe(
+ probe="outbound_message", fn_name="trace_outbound_message")
+ bpf = BPF(text=program, usdt_contexts=[bitcoind_with_usdts])
+
+ # BCC: perf buffer handle function for inbound_messages
+ def handle_inbound(_, data, size):
+ """ Inbound message handler.
+
+ Called each time a message is submitted to the inbound_messages BPF table."""
+
+ event = bpf["inbound_messages"].event(data)
+ print_message(event, True)
+
+ # BCC: perf buffer handle function for outbound_messages
+
+ def handle_outbound(_, data, size):
+ """ Outbound message handler.
+
+ Called each time a message is submitted to the outbound_messages BPF table."""
+
+ event = bpf["outbound_messages"].event(data)
+ print_message(event, False)
+
+ # BCC: add handlers to the inbound and outbound perf buffers
+ bpf["inbound_messages"].open_perf_buffer(handle_inbound)
+ bpf["outbound_messages"].open_perf_buffer(handle_outbound)
+
+ print("Logging raw P2P messages.")
+ print("Messages larger that about 32kb will be cut off!")
+ print("Some messages might be lost!")
+ while True:
+ try:
+ bpf.perf_buffer_poll()
+ except KeyboardInterrupt:
+ exit()
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("USAGE:", sys.argv[0], "path/to/bitcoind")
+ exit()
+ path = sys.argv[1]
+ main(path)
diff --git a/contrib/tracing/p2p_monitor.py b/contrib/tracing/p2p_monitor.py
new file mode 100755
index 0000000000000..14e3e3a80123a
--- /dev/null
+++ b/contrib/tracing/p2p_monitor.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python3
+
+""" Interactive bitcoind P2P network traffic monitor utilizing USDT and the
+ net:inbound_message and net:outbound_message tracepoints. """
+
+# This script demonstrates what USDT for Bitcoin Core can enable. It uses BCC
+# (https://github.com/iovisor/bcc) to load a sandboxed eBPF program into the
+# Linux kernel (root privileges are required). The eBPF program attaches to two
+# statically defined tracepoints. The tracepoint 'net:inbound_message' is called
+# when a new P2P message is received, and 'net:outbound_message' is called on
+# outbound P2P messages. The eBPF program submits the P2P messages to
+# this script via a BPF ring buffer.
+
+import sys
+import curses
+from curses import wrapper, panel
+from bcc import BPF, USDT
+
+# BCC: The C program to be compiled to an eBPF program (by BCC) and loaded into
+# a sandboxed Linux kernel VM.
+program = """
+#include
+
+// Tor v3 addresses are 62 chars + 6 chars for the port (':12345').
+// I2P addresses are 60 chars + 6 chars for the port (':12345').
+#define MAX_PEER_ADDR_LENGTH 62 + 6
+#define MAX_PEER_CONN_TYPE_LENGTH 20
+#define MAX_MSG_TYPE_LENGTH 20
+
+struct p2p_message
+{
+ u64 peer_id;
+ char peer_addr[MAX_PEER_ADDR_LENGTH];
+ char peer_conn_type[MAX_PEER_CONN_TYPE_LENGTH];
+ char msg_type[MAX_MSG_TYPE_LENGTH];
+ u64 msg_size;
+};
+
+
+// Two BPF perf buffers for pushing data (here P2P messages) to user space.
+BPF_PERF_OUTPUT(inbound_messages);
+BPF_PERF_OUTPUT(outbound_messages);
+
+int trace_inbound_message(struct pt_regs *ctx) {
+ struct p2p_message msg = {};
+
+ bpf_usdt_readarg(1, ctx, &msg.peer_id);
+ bpf_usdt_readarg_p(2, ctx, &msg.peer_addr, MAX_PEER_ADDR_LENGTH);
+ bpf_usdt_readarg_p(3, ctx, &msg.peer_conn_type, MAX_PEER_CONN_TYPE_LENGTH);
+ bpf_usdt_readarg_p(4, ctx, &msg.msg_type, MAX_MSG_TYPE_LENGTH);
+ bpf_usdt_readarg(5, ctx, &msg.msg_size);
+
+ inbound_messages.perf_submit(ctx, &msg, sizeof(msg));
+ return 0;
+};
+
+int trace_outbound_message(struct pt_regs *ctx) {
+ struct p2p_message msg = {};
+
+ bpf_usdt_readarg(1, ctx, &msg.peer_id);
+ bpf_usdt_readarg_p(2, ctx, &msg.peer_addr, MAX_PEER_ADDR_LENGTH);
+ bpf_usdt_readarg_p(3, ctx, &msg.peer_conn_type, MAX_PEER_CONN_TYPE_LENGTH);
+ bpf_usdt_readarg_p(4, ctx, &msg.msg_type, MAX_MSG_TYPE_LENGTH);
+ bpf_usdt_readarg(5, ctx, &msg.msg_size);
+
+ outbound_messages.perf_submit(ctx, &msg, sizeof(msg));
+ return 0;
+};
+"""
+
+
+class Message:
+ """ A P2P network message. """
+ msg_type = ""
+ size = 0
+ data = bytes()
+ inbound = False
+
+ def __init__(self, msg_type, size, inbound):
+ self.msg_type = msg_type
+ self.size = size
+ self.inbound = inbound
+
+
+class Peer:
+ """ A P2P network peer. """
+ id = 0
+ address = ""
+ connection_type = ""
+ last_messages = list()
+
+ total_inbound_msgs = 0
+ total_inbound_bytes = 0
+ total_outbound_msgs = 0
+ total_outbound_bytes = 0
+
+ def __init__(self, id, address, connection_type):
+ self.id = id
+ self.address = address
+ self.connection_type = connection_type
+ self.last_messages = list()
+
+ def add_message(self, message):
+ self.last_messages.append(message)
+ if len(self.last_messages) > 25:
+ self.last_messages.pop(0)
+ if message.inbound:
+ self.total_inbound_bytes += message.size
+ self.total_inbound_msgs += 1
+ else:
+ self.total_outbound_bytes += message.size
+ self.total_outbound_msgs += 1
+
+
+def main(bitcoind_path):
+ peers = dict()
+
+ bitcoind_with_usdts = USDT(path=str(bitcoind_path))
+
+ # attaching the trace functions defined in the BPF program to the tracepoints
+ bitcoind_with_usdts.enable_probe(
+ probe="inbound_message", fn_name="trace_inbound_message")
+ bitcoind_with_usdts.enable_probe(
+ probe="outbound_message", fn_name="trace_outbound_message")
+ bpf = BPF(text=program, usdt_contexts=[bitcoind_with_usdts])
+
+ # BCC: perf buffer handle function for inbound_messages
+ def handle_inbound(_, data, size):
+ """ Inbound message handler.
+
+ Called each time a message is submitted to the inbound_messages BPF table."""
+ event = bpf["inbound_messages"].event(data)
+ if event.peer_id not in peers:
+ peer = Peer(event.peer_id, event.peer_addr.decode(
+ "utf-8"), event.peer_conn_type.decode("utf-8"))
+ peers[peer.id] = peer
+ peers[event.peer_id].add_message(
+ Message(event.msg_type.decode("utf-8"), event.msg_size, True))
+
+ # BCC: perf buffer handle function for outbound_messages
+ def handle_outbound(_, data, size):
+ """ Outbound message handler.
+
+ Called each time a message is submitted to the outbound_messages BPF table."""
+ event = bpf["outbound_messages"].event(data)
+ if event.peer_id not in peers:
+ peer = Peer(event.peer_id, event.peer_addr.decode(
+ "utf-8"), event.peer_conn_type.decode("utf-8"))
+ peers[peer.id] = peer
+ peers[event.peer_id].add_message(
+ Message(event.msg_type.decode("utf-8"), event.msg_size, False))
+
+ # BCC: add handlers to the inbound and outbound perf buffers
+ bpf["inbound_messages"].open_perf_buffer(handle_inbound)
+ bpf["outbound_messages"].open_perf_buffer(handle_outbound)
+
+ wrapper(loop, bpf, peers)
+
+
+def loop(screen, bpf, peers):
+ screen.nodelay(1)
+ cur_list_pos = 0
+ win = curses.newwin(30, 70, 2, 7)
+ win.erase()
+ win.border(ord("|"), ord("|"), ord("-"), ord("-"),
+ ord("-"), ord("-"), ord("-"), ord("-"))
+ info_panel = panel.new_panel(win)
+ info_panel.hide()
+
+ ROWS_AVALIABLE_FOR_LIST = curses.LINES - 5
+ scroll = 0
+
+ while True:
+ try:
+ # BCC: poll the perf buffers for new events or timeout after 50ms
+ bpf.perf_buffer_poll(timeout=50)
+
+ ch = screen.getch()
+ if (ch == curses.KEY_DOWN or ch == ord("j")) and cur_list_pos < len(
+ peers.keys()) -1 and info_panel.hidden():
+ cur_list_pos += 1
+ if cur_list_pos >= ROWS_AVALIABLE_FOR_LIST:
+ scroll += 1
+ if (ch == curses.KEY_UP or ch == ord("k")) and cur_list_pos > 0 and info_panel.hidden():
+ cur_list_pos -= 1
+ if scroll > 0:
+ scroll -= 1
+ if ch == ord('\n') or ch == ord(' '):
+ if info_panel.hidden():
+ info_panel.show()
+ else:
+ info_panel.hide()
+ screen.erase()
+ render(screen, peers, cur_list_pos, scroll, ROWS_AVALIABLE_FOR_LIST, info_panel)
+ curses.panel.update_panels()
+ screen.refresh()
+ except KeyboardInterrupt:
+ exit()
+
+
+def render(screen, peers, cur_list_pos, scroll, ROWS_AVALIABLE_FOR_LIST, info_panel):
+ """ renders the list of peers and details panel
+
+ This code is unrelated to USDT, BCC and BPF.
+ """
+ header_format = "%6s %-20s %-20s %-22s %-67s"
+ row_format = "%6s %-5d %9d byte %-5d %9d byte %-22s %-67s"
+
+ screen.addstr(0, 1, (" P2P Message Monitor "), curses.A_REVERSE)
+ screen.addstr(
+ 1, 0, (" Navigate with UP/DOWN or J/K and select a peer with ENTER or SPACE to see individual P2P messages"), curses.A_NORMAL)
+ screen.addstr(3, 0,
+ header_format % ("PEER", "OUTBOUND", "INBOUND", "TYPE", "ADDR"), curses.A_BOLD | curses.A_UNDERLINE)
+ peer_list = sorted(peers.keys())[scroll:ROWS_AVALIABLE_FOR_LIST+scroll]
+ for i, peer_id in enumerate(peer_list):
+ peer = peers[peer_id]
+ screen.addstr(i + 4, 0,
+ row_format % (peer.id, peer.total_outbound_msgs, peer.total_outbound_bytes,
+ peer.total_inbound_msgs, peer.total_inbound_bytes,
+ peer.connection_type, peer.address),
+ curses.A_REVERSE if i + scroll == cur_list_pos else curses.A_NORMAL)
+ if i + scroll == cur_list_pos:
+ info_window = info_panel.window()
+ info_window.erase()
+ info_window.border(
+ ord("|"), ord("|"), ord("-"), ord("-"),
+ ord("-"), ord("-"), ord("-"), ord("-"))
+
+ info_window.addstr(
+ 1, 1, f"PEER {peer.id} ({peer.address})".center(68), curses.A_REVERSE | curses.A_BOLD)
+ info_window.addstr(
+ 2, 1, f" OUR NODE{peer.connection_type:^54}PEER ",
+ curses.A_BOLD)
+ for i, msg in enumerate(peer.last_messages):
+ if msg.inbound:
+ info_window.addstr(
+ i + 3, 1, "%68s" %
+ (f"<--- {msg.msg_type} ({msg.size} bytes) "), curses.A_NORMAL)
+ else:
+ info_window.addstr(
+ i + 3, 1, " %s (%d byte) --->" %
+ (msg.msg_type, msg.size), curses.A_NORMAL)
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("USAGE:", sys.argv[0], "path/to/bitcoind")
+ exit()
+ path = sys.argv[1]
+ main(path)
diff --git a/contrib/verifybinaries/verify.py b/contrib/verifybinaries/verify.py
index 6cbaf2dc0c7c0..51c151add826f 100755
--- a/contrib/verifybinaries/verify.py
+++ b/contrib/verifybinaries/verify.py
@@ -2,7 +2,7 @@
# 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.
-"""Script for verifying Bitoin Core release binaries
+"""Script for verifying Bitcoin Core release binaries
This script attempts to download the signature file SHA256SUMS.asc from
bitcoincore.org and bitcoin.org and compares them.
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk
index dad317193c024..0af5412d94c4e 100644
--- a/depends/packages/libevent.mk
+++ b/depends/packages/libevent.mk
@@ -16,6 +16,10 @@ define $(package)_set_vars
$(package)_cppflags_mingw32=-D_WIN32_WINNT=0x0601
endef
+define $(package)_preprocess_cmds
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux
+endef
+
define $(package)_config_cmds
$($(package)_autoconf)
endef
diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk
index 885207fce9af5..d169eb6723184 100644
--- a/depends/packages/native_cctools.mk
+++ b/depends/packages/native_cctools.mk
@@ -16,6 +16,10 @@ define $(package)_set_vars
$(package)_cxx=$(clangxx_prog)
endef
+define $(package)_preprocess_cmds
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub cctools
+endef
+
define $(package)_config_cmds
$($(package)_autoconf)
endef
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 0c3ef8c82f1f5..9004b064d6e9e 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -9,7 +9,7 @@ $(package)_qt_libs=corelib network widgets gui plugins testlib
$(package)_linguist_tools = lrelease lupdate lconvert
$(package)_patches = qt.pro qttools_src.pro
$(package)_patches += fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch
-$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
+$(package)_patches += support_new_android_ndks.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
$(package)_patches+= no_sdk_version_check.patch
$(package)_patches+= fix_lib_paths.patch fix_android_pch.patch
$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch fix_limits_header.patch
@@ -224,7 +224,7 @@ define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_android_qmake_conf.patch && \
+ patch -p1 -i $($(package)_patch_dir)/support_new_android_ndks.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_pch.patch && \
patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \
diff --git a/depends/packages/sqlite.mk b/depends/packages/sqlite.mk
index 5b3a61b239c64..af5e0d09c9dc8 100644
--- a/depends/packages/sqlite.mk
+++ b/depends/packages/sqlite.mk
@@ -9,6 +9,10 @@ $(package)_config_opts=--disable-shared --disable-readline --disable-dynamic-ext
$(package)_config_opts_linux=--with-pic
endef
+define $(package)_preprocess_cmds
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
+endef
+
define $(package)_config_cmds
$($(package)_autoconf)
endef
diff --git a/depends/patches/qt/fix_android_pch.patch b/depends/patches/qt/fix_android_pch.patch
index bed6e4bb63597..195e1c5e592a1 100644
--- a/depends/patches/qt/fix_android_pch.patch
+++ b/depends/patches/qt/fix_android_pch.patch
@@ -1,6 +1,6 @@
--- old/qtbase/mkspecs/common/android-base-head.conf
+++ new/qtbase/mkspecs/common/android-base-head.conf
-@@ -73,6 +73,6 @@ CROSS_COMPILE = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLS_PREFIX-
+@@ -72,6 +72,6 @@ CROSS_COMPILE = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLS_PREFIX-
QMAKE_PCH_OUTPUT_EXT = .gch
QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
diff --git a/depends/patches/qt/fix_android_qmake_conf.patch b/depends/patches/qt/fix_android_qmake_conf.patch
deleted file mode 100644
index 3a8753fd1d74c..0000000000000
--- a/depends/patches/qt/fix_android_qmake_conf.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- old/qtbase/mkspecs/android-clang/qmake.conf
-+++ new/qtbase/mkspecs/android-clang/qmake.conf
-@@ -47,7 +47,7 @@ ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so
- ANDROID_USE_LLVM = true
-
- exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \
-- ANDROID_CXX_STL_LIBS = -lc++
-+ ANDROID_CXX_STL_LIBS = -lc++_shared
- else: \
- ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "")
diff --git a/depends/patches/qt/support_new_android_ndks.patch b/depends/patches/qt/support_new_android_ndks.patch
new file mode 100644
index 0000000000000..85c8ae2132af5
--- /dev/null
+++ b/depends/patches/qt/support_new_android_ndks.patch
@@ -0,0 +1,122 @@
+Follow Google's BuildSystemMaintainers doc to support future NDK releases.
+
+Upstream commit:
+ - Qt 5.14: 9b14950ff600a4ce5a8698b67ab38907c50417f1
+
+--- old/qtbase/mkspecs/android-clang/qmake.conf
++++ new/qtbase/mkspecs/android-clang/qmake.conf
+@@ -14,43 +14,29 @@
+ QMAKE_CC = $$NDK_LLVM_PATH/bin/clang
+ QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++
+
++# Follow https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md
++
+ equals(ANDROID_TARGET_ARCH, armeabi-v7a): \
+- QMAKE_CFLAGS += -target armv7-none-linux-androideabi
+-else: equals(ANDROID_TARGET_ARCH, armeabi): \
+- QMAKE_CFLAGS += -target armv5te-none-linux-androideabi
++ QMAKE_CFLAGS = -target armv7a-linux-androideabi$$replace(ANDROID_PLATFORM, "android-", "")
+ else: equals(ANDROID_TARGET_ARCH, arm64-v8a): \
+- QMAKE_CFLAGS += -target aarch64-none-linux-android
++ QMAKE_CFLAGS = -target aarch64-linux-android$$replace(ANDROID_PLATFORM, "android-", "")
+ else: equals(ANDROID_TARGET_ARCH, x86): \
+- QMAKE_CFLAGS += -target i686-none-linux-android -mstackrealign
++ QMAKE_CFLAGS = -target i686-linux-android$$replace(ANDROID_PLATFORM, "android-", "") -mstackrealign
+ else: equals(ANDROID_TARGET_ARCH, x86_64): \
+- QMAKE_CFLAGS += -target x86_64-none-linux-android
+-else: equals(ANDROID_TARGET_ARCH, mips): \
+- QMAKE_CFLAGS += -target mipsel-none-linux-android
+-else: equals(ANDROID_TARGET_ARCH, mips64): \
+- QMAKE_CFLAGS += -target mips64el-none-linux-android
+-
+-QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH -fno-limit-debug-info
+-
+-QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++
+-equals(ANDROID_TARGET_ARCH, armeabi-v7a): QMAKE_LINK += -Wl,--exclude-libs,libunwind.a
+-
+-QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \
+- -isystem $$NDK_ROOT/sysroot/usr/include/$$NDK_TOOLS_PREFIX \
+- -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++/include \
+- -isystem $$NDK_ROOT/sources/android/support/include \
+- -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++abi/include
++ QMAKE_CFLAGS = -target x86_64-linux-android$$replace(ANDROID_PLATFORM, "android-", "")
+
+-ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH
++QMAKE_CFLAGS += -fno-limit-debug-info
+
+-ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so
++QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS
+
+-ANDROID_USE_LLVM = true
++ANDROID_STDCPP_PATH = $$NDK_LLVM_PATH/sysroot/usr/lib/$$NDK_TOOLS_PREFIX/libc++_shared.so
+
+-exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \
+- ANDROID_CXX_STL_LIBS = -lc++
+-else: \
+- ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "")
++ANDROID_USE_LLVM = true
+
+-QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz
++QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz
++QMAKE_LIBDIR_POST =
++QMAKE_LFLAGS =
++QMAKE_LIBS_PRIVATE =
++ANDROID_CXX_STL_LIBS =
+
+ include(../common/android-base-tail.conf)
+
+--- old/qtbase/mkspecs/common/android-base-head.conf
++++ new/qtbase/mkspecs/common/android-base-head.conf
+@@ -64,7 +58,6 @@
+ }
+
+ CONFIG += $$ANDROID_PLATFORM
+-QMAKE_CFLAGS = -D__ANDROID_API__=$$replace(ANDROID_PLATFORM, "android-", "")
+
+ ANDROID_PLATFORM_ROOT_PATH = $$NDK_ROOT/platforms/$$ANDROID_PLATFORM/arch-$$ANDROID_ARCHITECTURE/
+
+--- old/qtbase/mkspecs/common/android-base-tail.conf
++++ new/qtbase/mkspecs/common/android-base-tail.conf
+@@ -6,22 +6,17 @@
+ QMAKE_CFLAGS += -fstack-protector-strong -DANDROID
+
+ equals(ANDROID_TARGET_ARCH, armeabi-v7a): \
+- QMAKE_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -fno-builtin-memmove
++ QMAKE_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp
+ else: equals(ANDROID_TARGET_ARCH, armeabi): \
+- QMAKE_CFLAGS += -march=armv5te -mtune=xscale -msoft-float -fno-builtin-memmove
+-# -fno-builtin-memmove is used to workaround https://code.google.com/p/android/issues/detail?id=81692
++ QMAKE_CFLAGS += -march=armv5te -mtune=xscale -msoft-float
+
+ QMAKE_CFLAGS_WARN_ON = -Wall -W
+ QMAKE_CFLAGS_WARN_OFF =
+ equals(ANDROID_TARGET_ARCH, armeabi-v7a) | equals(ANDROID_TARGET_ARCH, armeabi) {
+ CONFIG += optimize_size
+ QMAKE_CFLAGS_DEBUG = -g -marm -O0
+- equals(ANDROID_TARGET_ARCH, armeabi):if(equals(NDK_TOOLCHAIN_VERSION, 4.8)|equals(NDK_TOOLCHAIN_VERSION, 4.9)) {
+- DEFINES += QT_OS_ANDROID_GCC_48_WORKAROUND
+- } else {
+- QMAKE_CFLAGS_RELEASE += -mthumb
+- QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -mthumb
+- }
++ QMAKE_CFLAGS_RELEASE += -mthumb
++ QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -mthumb
+ }
+
+ QMAKE_CFLAGS_SHLIB = -fPIC
+@@ -61,15 +56,12 @@
+ QMAKE_RANLIB = $${CROSS_COMPILE}ranlib
+
+ QMAKE_INCDIR_POST =
+-QMAKE_LIBDIR_POST = $$ANDROID_SOURCES_CXX_STL_LIBDIR
+ QMAKE_INCDIR_X11 =
+ QMAKE_LIBDIR_X11 =
+ QMAKE_INCDIR_OPENGL =
+ QMAKE_LIBDIR_OPENGL =
+
+ QMAKE_LINK_SHLIB = $$QMAKE_LINK
+-QMAKE_LFLAGS = --sysroot=$$ANDROID_PLATFORM_ROOT_PATH
+-equals(ANDROID_TARGET_ARCH, x86_64) QMAKE_LFLAGS += -L$$ANDROID_PLATFORM_ROOT_PATH/usr/lib64
+ QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared
+ QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared
+ QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 73c0bf877950e..4a56114109e49 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -48,6 +48,7 @@ Optional dependencies:
univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure)
libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0)
sqlite3 | SQLite DB | Optional, wallet storage (only needed when wallet enabled)
+ systemtap | Tracing (USDT) | Optional, statically defined tracepoints
For the versions used, see [dependencies.md](dependencies.md)
@@ -107,6 +108,10 @@ ZMQ dependencies (provides ZMQ API):
sudo apt-get install libzmq3-dev
+User-Space, Statically Defined Tracing (USDT) dependencies:
+
+ sudo apt install systemtap-sdt-dev
+
GUI dependencies:
If you want to build bitcoin-qt, make sure that the required packages for Qt development
@@ -162,6 +167,10 @@ ZMQ dependencies (provides ZMQ API):
sudo dnf install zeromq-devel
+User-Space, Statically Defined Tracing (USDT) dependencies:
+
+ sudo dnf install systemtap
+
GUI dependencies:
If you want to build bitcoin-qt, make sure that the required packages for Qt development
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 66c5a76b3b8b2..b7634718e8d61 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -24,6 +24,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| Qt | [5.12.11](https://download.qt.io/official_releases/qt/) | [5.9.5](https://github.com/bitcoin/bitcoin/issues/20104) | No | | |
| SQLite | [3.32.1](https://sqlite.org/download.html) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) | | | |
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
+| systemtap ([tracing](tracing.md))| | | | | |
| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | |
| zlib | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
@@ -41,6 +42,7 @@ Some dependencies are not needed in all configurations. The following are some f
* SQLite is not needed with `--disable-wallet` or `--without-sqlite`.
* Qt is not needed with `--without-gui`.
* If the qrencode dependency is absent, QR support won't be added. To force an error when that happens, pass `--with-qrencode`.
+* If the systemtap dependency is absent, USDT support won't compiled in.
* ZeroMQ is needed only with the `--with-zmq` option.
#### Other
diff --git a/doc/release-notes.md b/doc/release-notes.md
index dc28ccb9edd5f..cf9edd9b0865a 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,5 +1,3 @@
-# Release notes now being edited on https://github.com/bitcoin-core/bitcoin-devwiki/wiki/22.0-Release-Notes-draft
-
*After branching off for a major version release of Bitcoin Core, use this
template to create the initial release notes draft.*
@@ -8,7 +6,7 @@ template to create the initial release notes draft.*
for the process.*
*Create the draft, named* "*version* Release Notes Draft"
-*(e.g. "0.20.0 Release Notes Draft"), as a collaborative wiki in:*
+*(e.g. "22.0 Release Notes Draft"), as a collaborative wiki in:*
https://github.com/bitcoin-core/bitcoin-devwiki/wiki/
@@ -53,90 +51,15 @@ 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 22.0 onwards, macOS versions earlier than 10.14 are no longer supported.
-
Notable changes
===============
P2P and network changes
-----------------------
-- This release removes support for Tor version 2 hidden services in favor of Tor
- v3 only, as the Tor network [dropped support for Tor
- v2](https://blog.torproject.org/v2-deprecation-timeline) with the release of
- Tor version 0.4.6. Henceforth, Bitcoin Core ignores Tor v2 addresses; it
- neither rumors them over the network to other peers, nor stores them in memory
- or to `peers.dat`. (#22050)
-
-- Added NAT-PMP port mapping support via
- [`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html). (#18077)
-
Updated RPCs
------------
-- Due to [BIP 350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
- being implemented, behavior for all RPCs that accept addresses is changed when
- a native witness version 1 (or higher) is passed. These now require a Bech32m
- encoding instead of a Bech32 one, and Bech32m encoding will be used for such
- addresses in RPC output as well. No version 1 addresses should be created
- for mainnet until consensus rules are adopted that give them meaning
- (e.g. through [BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)).
- Once that happens, Bech32m is expected to be used for them, so this shouldn't
- affect any production systems, but may be observed on other networks where such
- addresses already have meaning (like signet). (#20861)
-
-- The `getpeerinfo` RPC returns two new boolean fields, `bip152_hb_to` and
- `bip152_hb_from`, that respectively indicate whether we selected a peer to be
- in compact blocks high-bandwidth mode or whether a peer selected us as a
- compact blocks high-bandwidth peer. High-bandwidth peers send new block
- announcements via a `cmpctblock` message rather than the usual inv/headers
- announcements. See BIP 152 for more details. (#19776)
-
-- `getpeerinfo` no longer returns the following fields: `addnode`, `banscore`,
- and `whitelisted`, which were previously deprecated in 0.21. Instead of
- `addnode`, the `connection_type` field returns manual. Instead of
- `whitelisted`, the `permissions` field indicates if the peer has special
- privileges. The `banscore` field has simply been removed. (#20755)
-
-- The following RPCs: `gettxout`, `getrawtransaction`, `decoderawtransaction`,
- `decodescript`, `gettransaction`, and REST endpoints: `/rest/tx`,
- `/rest/getutxos`, `/rest/block` deprecated the following fields (which are no
- longer returned in the responses by default): `addresses`, `reqSigs`.
- The `-deprecatedrpc=addresses` flag must be passed for these fields to be
- included in the RPC response. This flag/option will be available only for this major release, after which
- the deprecation will be removed entirely. Note that these fields are attributes of
- the `scriptPubKey` object returned in the RPC response. However, in the response
- of `decodescript` these fields are top-level attributes, and included again as attributes
- of the `scriptPubKey` object. (#20286)
-
-- When creating a hex-encoded bitcoin transaction using the `bitcoin-tx` utility
- with the `-json` option set, the following fields: `addresses`, `reqSigs` are no longer
- returned in the tx output of the response. (#20286)
-
-- The `listbanned` RPC now returns two new numeric fields: `ban_duration` and `time_remaining`.
- Respectively, these new fields indicate the duration of a ban and the time remaining until a ban expires,
- both in seconds. Additionally, the `ban_created` field is repositioned to come before `banned_until`. (#21602)
-
-- The `getnodeaddresses` RPC now returns a "network" field indicating the
- network type (ipv4, ipv6, onion, or i2p) for each address. (#21594)
-
-- `getnodeaddresses` now also accepts a "network" argument (ipv4, ipv6, onion,
- or i2p) to return only addresses of the specified network. (#21843)
-
-- The `testmempoolaccept` RPC now accepts multiple transactions (still experimental at the moment,
- API may be unstable). This is intended for testing transaction packages with dependency
- relationships; it is not recommended for batch-validating independent transactions. In addition to
- mempool policy, package policies apply: the list cannot contain more than 25 transactions or have a
- total size exceeding 101K virtual bytes, and cannot conflict with (spend the same inputs as) each other or
- the mempool, even if it would be a valid BIP125 replace-by-fee. There are some known limitations to
- the accuracy of the test accept: it's possible for `testmempoolaccept` to return "allowed"=True for a
- group of transactions, but "too-long-mempool-chain" if they are actually submitted. (#20833)
-
-- `addmultisigaddress` and `createmultisig` now support up to 20 keys for
- Segwit addresses. (#20867)
-
-Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section below.
-
New RPCs
--------
@@ -146,48 +69,17 @@ Build System
New settings
------------
-- The `-natpmp` option has been added to use NAT-PMP to map the listening port.
- If both UPnP and NAT-PMP are enabled, a successful allocation from UPnP
- prevails over one from NAT-PMP. (#18077)
-
Updated settings
----------------
-Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below.
-
-- Passing an invalid `-rpcauth` argument now cause bitcoind to fail to start. (#20461)
-
Tools and Utilities
-------------------
-- A new CLI `-addrinfo` command returns the number of addresses known to the
- node per network type (including Tor v2 versus v3) and total. This can be
- useful to see if the node knows enough addresses in a network to use options
- like `-onlynet=` or to upgrade to this release of Bitcoin Core 22.0
- that supports Tor v3 only. (#21595)
-
-- A new `-rpcwaittimeout` argument to `bitcoin-cli` sets the timeout
- in seconds to use with `-rpcwait`. If the timeout expires,
- `bitcoin-cli` will report a failure. (#21056)
+- Update `-getinfo` to return data in a user-friendly format that also reduces vertical space. (#21832)
Wallet
------
-- A new `listdescriptors` RPC is available to inspect the contents of descriptor-enabled wallets.
- The RPC returns public versions of all imported descriptors, including their timestamp and flags.
- For ranged descriptors, it also returns the range boundaries and the next index to generate addresses from. (#20226)
-
-- The `bumpfee` RPC is not available with wallets that have private keys
- disabled. `psbtbumpfee` can be used instead. (#20891)
-
-- The `fundrawtransaction`, `send` and `walletcreatefundedpsbt` RPCs now support an `include_unsafe` option
- that when `true` allows using unsafe inputs to fund the transaction.
- Note that the resulting transaction may become invalid if one of the unsafe inputs disappears.
- If that happens, the transaction must be funded with different inputs and republished. (#21359)
-
-- We now support up to 20 keys in `multi()` and `sortedmulti()` descriptors
- under `wsh()`. (#20867)
-
GUI changes
-----------
@@ -197,18 +89,7 @@ Low-level changes
RPC
---
-- The RPC server can process a limited number of simultaneous RPC requests.
- Previously, if this limit was exceeded, the RPC server would respond with
- [status code 500 (`HTTP_INTERNAL_SERVER_ERROR`)](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_server_errors).
- Now it returns status code 503 (`HTTP_SERVICE_UNAVAILABLE`). (#18335)
-
-- Error codes have been updated to be more accurate for the following error cases (#18466):
- - `signmessage` now returns RPC_INVALID_ADDRESS_OR_KEY (-5) if the
- passed address is invalid. Previously returned RPC_TYPE_ERROR (-3).
- - `verifymessage` now returns RPC_INVALID_ADDRESS_OR_KEY (-5) if the
- passed address is invalid. Previously returned RPC_TYPE_ERROR (-3).
- - `verifymessage` now returns RPC_TYPE_ERROR (-3) if the passed signature
- is malformed. Previously returned RPC_INVALID_ADDRESS_OR_KEY (-5).
+- `getblockchaininfo` now returns a new `time` field, that provides the chain tip time. (#22407)
Tests
-----
diff --git a/doc/tracing.md b/doc/tracing.md
new file mode 100644
index 0000000000000..1242a0d25002b
--- /dev/null
+++ b/doc/tracing.md
@@ -0,0 +1,266 @@
+# User-space, Statically Defined Tracing (USDT) for Bitcoin Core
+
+Bitcoin Core includes statically defined tracepoints to allow for more
+observability during development, debugging, code review, and production usage.
+These tracepoints make it possible to keep track of custom statistics and
+enable detailed monitoring of otherwise hidden internals. They have
+little to no performance impact when unused.
+
+```
+eBPF and USDT Overview
+======================
+
+ ┌──────────────────┐ ┌──────────────┐
+ │ tracing script │ │ bitcoind │
+ │==================│ 2. │==============│
+ │ eBPF │ tracing │ hooks │ │
+ │ code │ logic │ into┌─┤►tracepoint 1─┼───┐ 3.
+ └────┬───┴──▲──────┘ ├─┤►tracepoint 2 │ │ pass args
+ 1. │ │ 4. │ │ ... │ │ to eBPF
+ User compiles │ │ pass data to │ └──────────────┘ │ program
+ Space & loads │ │ tracing script │ │
+ ─────────────────┼──────┼─────────────────┼────────────────────┼───
+ Kernel │ │ │ │
+ Space ┌──┬─▼──────┴─────────────────┴────────────┐ │
+ │ │ eBPF program │◄──────┘
+ │ └───────────────────────────────────────┤
+ │ eBPF kernel Virtual Machine (sandboxed) │
+ └──────────────────────────────────────────┘
+
+1. The tracing script compiles the eBPF code and loads the eBPF program into a kernel VM
+2. The eBPF program hooks into one or more tracepoints
+3. When the tracepoint is called, the arguments are passed to the eBPF program
+4. The eBPF program processes the arguments and returns data to the tracing script
+```
+
+The Linux kernel can hook into the tracepoints during runtime and pass data to
+sandboxed [eBPF] programs running in the kernel. These eBPF programs can, for
+example, collect statistics or pass data back to user-space scripts for further
+processing.
+
+[eBPF]: https://ebpf.io/
+
+The two main eBPF front-ends with support for USDT are [bpftrace] and
+[BPF Compiler Collection (BCC)]. BCC is used for complex tools and daemons and
+`bpftrace` is preferred for one-liners and shorter scripts. Examples for both can
+be found in [contrib/tracing].
+
+[bpftrace]: https://github.com/iovisor/bpftrace
+[BPF Compiler Collection (BCC)]: https://github.com/iovisor/bcc
+[contrib/tracing]: ../contrib/tracing/
+
+## Tracepoint documentation
+
+The currently available tracepoints are listed here.
+
+### Context `net`
+
+#### Tracepoint `net:inbound_message`
+
+Is called when a message is received from a peer over the P2P network. Passes
+information about our peer, the connection and the message as arguments.
+
+Arguments passed:
+1. Peer ID as `int64`
+2. Peer Address and Port (IPv4, IPv6, Tor v3, I2P, ...) as `pointer to C-style String` (max. length 68 characters)
+3. Connection Type (inbound, feeler, outbound-full-relay, ...) as `pointer to C-style String` (max. length 20 characters)
+4. Message Type (inv, ping, getdata, addrv2, ...) as `pointer to C-style String` (max. length 20 characters)
+5. Message Size in bytes as `uint64`
+6. Message Bytes as `pointer to unsigned chars` (i.e. bytes)
+
+Note: The message is passed to the tracepoint in full, however, due to space
+limitations in the eBPF kernel VM it might not be possible to pass the message
+to user-space in full. Messages longer than a 32kb might be cut off. This can
+be detected in tracing scripts by comparing the message size to the length of
+the passed message.
+
+#### Tracepoint `net:outbound_message`
+
+Is called when a message is send to a peer over the P2P network. Passes
+information about our peer, the connection and the message as arguments.
+
+Arguments passed:
+1. Peer ID as `int64`
+2. Peer Address and Port (IPv4, IPv6, Tor v3, I2P, ...) as `pointer to C-style String` (max. length 68 characters)
+3. Connection Type (inbound, feeler, outbound-full-relay, ...) as `pointer to C-style String` (max. length 20 characters)
+4. Message Type (inv, ping, getdata, addrv2, ...) as `pointer to C-style String` (max. length 20 characters)
+5. Message Size in bytes as `uint64`
+6. Message Bytes as `pointer to unsigned chars` (i.e. bytes)
+
+Note: The message is passed to the tracepoint in full, however, due to space
+limitations in the eBPF kernel VM it might not be possible to pass the message
+to user-space in full. Messages longer than a 32kb might be cut off. This can
+be detected in tracing scripts by comparing the message size to the length of
+the passed message.
+
+### Context `validation`
+
+#### Tracepoint `validation:block_connected`
+
+Is called *after* a block is connected to the chain. Can, for example, be used
+to benchmark block connections together with `-reindex`.
+
+Arguments passed:
+1. Block Header Hash as `pointer to C-style String` (64 characters)
+2. Block Height as `int32`
+3. Transactions in the Block as `uint64`
+4. Inputs spend in the Block as `int32`
+5. SigOps in the Block (excluding coinbase SigOps) `uint64`
+6. Time it took to connect the Block in microseconds (µs) as `uint64`
+7. Block Header Hash as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
+
+Note: The 7th argument can't be accessed by bpftrace and is purposefully chosen
+to be the block header hash as bytes. See [bpftrace argument limit] for more
+details.
+
+[bpftrace argument limit]: #bpftrace-argument-limit
+
+## Adding tracepoints to Bitcoin Core
+
+To add a new tracepoint, `#include ` in the compilation unit where
+the tracepoint is inserted. Use one of the `TRACEx` macros listed below
+depending on the number of arguments passed to the tracepoint. Up to 12
+arguments can be provided. The `context` and `event` specify the names by which
+the tracepoint is referred to. Please use `snake_case` and try to make sure that
+the tracepoint names make sense even without detailed knowledge of the
+implementation details. Do not forget to update the tracepoint list in this
+document.
+
+```c
+#define TRACE(context, event)
+#define TRACE1(context, event, a)
+#define TRACE2(context, event, a, b)
+#define TRACE3(context, event, a, b, c)
+#define TRACE4(context, event, a, b, c, d)
+#define TRACE5(context, event, a, b, c, d, e)
+#define TRACE6(context, event, a, b, c, d, e, f)
+#define TRACE7(context, event, a, b, c, d, e, f, g)
+#define TRACE8(context, event, a, b, c, d, e, f, g, h)
+#define TRACE9(context, event, a, b, c, d, e, f, g, h, i)
+#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j)
+#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
+#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
+```
+
+For example:
+
+```C++
+TRACE6(net, inbound_message,
+ pnode->GetId(),
+ pnode->GetAddrName().c_str(),
+ pnode->ConnectionTypeAsString().c_str(),
+ sanitizedType.c_str(),
+ msg.data.size(),
+ msg.data.data()
+);
+```
+
+### Guidelines and best practices
+
+#### Clear motivation and use-case
+Tracepoints need a clear motivation and use-case. The motivation should
+outweigh the impact on, for example, code readability. There is no point in
+adding tracepoints that don't end up being used.
+
+#### Provide an example
+When adding a new tracepoint, provide an example. Examples can show the use case
+and help reviewers testing that the tracepoint works as intended. The examples
+can be kept simple but should give others a starting point when working with
+the tracepoint. See existing examples in [contrib/tracing/].
+
+[contrib/tracing/]: ../contrib/tracing/
+
+#### No expensive computations for tracepoints
+Data passed to the tracepoint should be inexpensive to compute. Although the
+tracepoint itself only has overhead when enabled, the code to compute arguments
+is always run - even if the tracepoint is not used. For example, avoid
+serialization and parsing.
+
+#### Semi-stable API
+Tracepoints should have a semi-stable API. Users should be able to rely on the
+tracepoints for scripting. This means tracepoints need to be documented, and the
+argument order ideally should not change. If there is an important reason to
+change argument order, make sure to document the change and update the examples
+using the tracepoint.
+
+#### eBPF Virtual Machine limits
+Keep the eBPF Virtual Machine limits in mind. eBPF programs receiving data from
+the tracepoints run in a sandboxed Linux kernel VM. This VM has a limited stack
+size of 512 bytes. Check if it makes sense to pass larger amounts of data, for
+example, with a tracing script that can handle the passed data.
+
+#### `bpftrace` argument limit
+While tracepoints can have up to 12 arguments, bpftrace scripts currently only
+support reading from the first six arguments (`arg0` till `arg5`) on `x86_64`.
+bpftrace currently lacks real support for handling and printing binary data,
+like block header hashes and txids. When a tracepoint passes more than six
+arguments, then string and integer arguments should preferably be placed in the
+first six argument fields. Binary data can be placed in later arguments. The BCC
+supports reading from all 12 arguments.
+
+#### Strings as C-style String
+Generally, strings should be passed into the `TRACEx` macros as pointers to
+C-style strings (a null-terminated sequence of characters). For C++
+`std::strings`, [`c_str()`] can be used. It's recommended to document the
+maximum expected string size if known.
+
+
+[`c_str()`]: https://www.cplusplus.com/reference/string/string/c_str/
+
+
+## Listing available tracepoints
+
+Multiple tools can list the available tracepoints in a `bitcoind` binary with
+USDT support.
+
+### GDB - GNU Project Debugger
+
+To list probes in Bitcoin Core, use `info probes` in `gdb`:
+
+```
+$ gdb ./src/bitcoind
+…
+(gdb) info probes
+Type Provider Name Where Semaphore Object
+stap net inbound_message 0x000000000014419e /src/bitcoind
+stap net outbound_message 0x0000000000107c05 /src/bitcoind
+stap validation block_connected 0x00000000002fb10c /src/bitcoind
+…
+```
+
+### With `readelf`
+
+The `readelf` tool can be used to display the USDT tracepoints in Bitcoin Core.
+Look for the notes with the description `NT_STAPSDT`.
+
+```
+$ readelf -n ./src/bitcoind | grep NT_STAPSDT -A 4 -B 2
+Displaying notes found in: .note.stapsdt
+ Owner Data size Description
+ stapsdt 0x0000005d NT_STAPSDT (SystemTap probe descriptors)
+ Provider: net
+ Name: outbound_message
+ Location: 0x0000000000107c05, Base: 0x0000000000579c90, Semaphore: 0x0000000000000000
+ Arguments: -8@%r12 8@%rbx 8@%rdi 8@192(%rsp) 8@%rax 8@%rdx
+…
+```
+
+### With `tplist`
+
+The `tplist` tool is provided by BCC (see [Installing BCC]). It displays kernel
+tracepoints or USDT probes and their formats (for more information, see the
+[`tplist` usage demonstration]). There are slight binary naming differences
+between distributions. For example, on
+[Ubuntu the binary is called `tplist-bpfcc`][ubuntu binary].
+
+[Installing BCC]: https://github.com/iovisor/bcc/blob/master/INSTALL.md
+[`tplist` usage demonstration]: https://github.com/iovisor/bcc/blob/master/tools/tplist_example.txt
+[ubuntu binary]: https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu---binary
+
+```
+$ tplist -l ./src/bitcoind -v
+b'net':b'outbound_message' [sema 0x0]
+ 1 location(s)
+ 6 argument(s)
+…
+```
diff --git a/src/Makefile.am b/src/Makefile.am
index 407fdf5a8fd47..a8d6591e98b54 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -842,9 +842,11 @@ EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
if BUILD_MULTIPROCESS
LIBBITCOIN_IPC=libbitcoin_ipc.a
libbitcoin_ipc_a_SOURCES = \
+ ipc/capnp/context.h \
ipc/capnp/init-types.h \
ipc/capnp/protocol.cpp \
ipc/capnp/protocol.h \
+ ipc/context.h \
ipc/exception.h \
ipc/interfaces.cpp \
ipc/process.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index a1821cafe3512..6f450bbc74ee2 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -40,9 +40,9 @@ QT_MOC_CPP = \
qt/moc_askpassphrasedialog.cpp \
qt/moc_createwalletdialog.cpp \
qt/moc_bantablemodel.cpp \
+ qt/moc_bitcoin.cpp \
qt/moc_bitcoinaddressvalidator.cpp \
qt/moc_bitcoinamountfield.cpp \
- qt/moc_bitcoin.cpp \
qt/moc_bitcoingui.cpp \
qt/moc_bitcoinunits.cpp \
qt/moc_clientmodel.cpp \
@@ -51,6 +51,7 @@ QT_MOC_CPP = \
qt/moc_csvmodelwriter.cpp \
qt/moc_editaddressdialog.cpp \
qt/moc_guiutil.cpp \
+ qt/moc_initexecutor.cpp \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
@@ -109,9 +110,9 @@ BITCOIN_QT_H = \
qt/addresstablemodel.h \
qt/askpassphrasedialog.h \
qt/bantablemodel.h \
+ qt/bitcoin.h \
qt/bitcoinaddressvalidator.h \
qt/bitcoinamountfield.h \
- qt/bitcoin.h \
qt/bitcoingui.h \
qt/bitcoinunits.h \
qt/clientmodel.h \
@@ -122,6 +123,7 @@ BITCOIN_QT_H = \
qt/editaddressdialog.h \
qt/guiconstants.h \
qt/guiutil.h \
+ qt/initexecutor.h \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
@@ -227,6 +229,7 @@ BITCOIN_QT_BASE_CPP = \
qt/clientmodel.cpp \
qt/csvmodelwriter.cpp \
qt/guiutil.cpp \
+ qt/initexecutor.cpp \
qt/intro.cpp \
qt/modaloverlay.cpp \
qt/networkstyle.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index fc2fd80166032..a07a1bb002828 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -152,6 +152,7 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/psbt_wallet_tests.cpp \
+ wallet/test/spend_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/walletdb_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
@@ -170,6 +171,8 @@ endif
BITCOIN_TEST_SUITE += \
+ wallet/test/util.cpp \
+ wallet/test/util.h \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
wallet/test/init_test_fixture.cpp \
diff --git a/src/addrman.h b/src/addrman.h
index 6f081b8dc14e0..1fc64ac07fdc2 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -536,12 +536,12 @@ class CAddrMan
}
//! Mark an entry as accessible.
- void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
- Good_(addr, test_before_evict, nTime);
+ Good_(addr, /* test_before_evict */ true, nTime);
Check();
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 7a5f9455112d7..1ec6411e32cd3 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
#include
@@ -28,6 +29,10 @@
#include
#include
+#ifndef WIN32
+#include
+#endif
+
#include
#include
#include
@@ -48,6 +53,9 @@ static constexpr int8_t UNKNOWN_NETWORK{-1};
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std::string DEFAULT_NBLOCKS = "1";
+/** Default -color setting. */
+static const std::string DEFAULT_COLOR_SETTING{"auto"};
+
static void SetupCliArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
@@ -66,6 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions(argsman);
+ argsman.AddArg("-color=", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcconnect=", strprintf("Send commands to node running on (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -338,7 +347,9 @@ class GetinfoRequestHandler: public BaseRequestHandler
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO]["result"].isNull()) {
+ result.pushKV("has_wallet", true);
result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
+ result.pushKV("walletname", batch[ID_WALLETINFO]["result"]["walletname"]);
if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
}
@@ -873,6 +884,100 @@ static void GetWalletBalances(UniValue& result)
result.pushKV("balances", balances);
}
+/**
+ * ParseGetInfoResult takes in -getinfo result in UniValue object and parses it
+ * into a user friendly UniValue string to be printed on the console.
+ * @param[out] result Reference to UniValue result containing the -getinfo output.
+ */
+static void ParseGetInfoResult(UniValue& result)
+{
+ if (!find_value(result, "error").isNull()) return;
+
+ std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
+ bool should_colorize = false;
+
+#ifndef WIN32
+ if (isatty(fileno(stdout))) {
+ // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
+ should_colorize = true;
+ }
+#endif
+
+ if (gArgs.IsArgSet("-color")) {
+ const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
+ if (color == "always") {
+ should_colorize = true;
+ } else if (color == "never") {
+ should_colorize = false;
+ } else if (color != "auto") {
+ throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
+ }
+ }
+
+ if (should_colorize) {
+ RESET = "\x1B[0m";
+ GREEN = "\x1B[32m";
+ BLUE = "\x1B[34m";
+ YELLOW = "\x1B[33m";
+ MAGENTA = "\x1B[35m";
+ CYAN = "\x1B[36m";
+ }
+
+ std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
+ result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
+ result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
+ result_string += strprintf("Verification progress: %.4f%%\n", result["verificationprogress"].get_real() * 100);
+ result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
+
+ result_string += strprintf(
+ "%sNetwork: in %s, out %s, total %s%s\n",
+ GREEN,
+ result["connections"]["in"].getValStr(),
+ result["connections"]["out"].getValStr(),
+ result["connections"]["total"].getValStr(),
+ RESET);
+ result_string += strprintf("Version: %s\n", result["version"].getValStr());
+ result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
+ const std::string proxy = result["proxy"].getValStr();
+ result_string += strprintf("Proxy: %s\n", proxy.empty() ? "N/A" : proxy);
+ result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
+
+ if (!result["has_wallet"].isNull()) {
+ const std::string walletname = result["walletname"].getValStr();
+ result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
+
+ result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
+ if (!result["unlocked_until"].isNull()) {
+ result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
+ }
+ result_string += strprintf("Transaction fee rate (-paytxfee) (%s/kvB): %s\n\n", CURRENCY_UNIT, result["paytxfee"].getValStr());
+ }
+ if (!result["balance"].isNull()) {
+ result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
+ }
+
+ if (!result["balances"].isNull()) {
+ result_string += strprintf("%sBalances%s\n", CYAN, RESET);
+
+ size_t max_balance_length{10};
+
+ for (const std::string& wallet : result["balances"].getKeys()) {
+ max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
+ }
+
+ for (const std::string& wallet : result["balances"].getKeys()) {
+ result_string += strprintf("%*s %s\n",
+ max_balance_length,
+ result["balances"][wallet].getValStr(),
+ wallet.empty() ? "\"\"" : wallet);
+ }
+ result_string += "\n";
+ }
+
+ result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, result["warnings"].getValStr());
+ result.setStr(result_string);
+}
+
/**
* Call RPC getnewaddress.
* @returns getnewaddress response as a UniValue object.
@@ -994,9 +1099,13 @@ static int CommandLineRPC(int argc, char *argv[])
UniValue result = find_value(reply, "result");
const UniValue& error = find_value(reply, "error");
if (error.isNull()) {
- if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
- GetWalletBalances(result); // fetch multiwallet balances and append to result
+ if (gArgs.GetBoolArg("-getinfo", false)) {
+ if (!gArgs.IsArgSet("-rpcwallet")) {
+ GetWalletBalances(result); // fetch multiwallet balances and append to result
+ }
+ ParseGetInfoResult(result);
}
+
ParseResult(result, strPrint);
} else {
ParseError(error, strPrint, nRet);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index eb6c382f23e76..a8b45685d0e5d 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -490,11 +490,8 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
{
if (args.IsArgSet("-segwitheight")) {
int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight);
- if (height < -1 || height >= std::numeric_limits::max()) {
- throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range. Use -1 to disable segwit.", height));
- } else if (height == -1) {
- LogPrintf("Segwit disabled for testing\n");
- height = std::numeric_limits::max();
+ if (height < 0 || height >= std::numeric_limits::max()) {
+ throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range.", height));
}
consensus.SegwitHeight = static_cast(height);
}
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index e71b4bc859375..79c1bc25bc175 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-segwitheight=", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-segwitheight=", "Set the activation height of segwit. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
diff --git a/src/init.cpp b/src/init.cpp
index 3915fa8fcbc00..75394d96b1da9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -715,7 +715,7 @@ namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
-ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
+ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED | NODE_WITNESS);
int64_t peer_connect_timeout;
std::set g_enabled_filter_types;
@@ -1588,12 +1588,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
- if (DeploymentEnabled(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
- // Advertise witness capabilities.
- // The option to not set NODE_WITNESS is only used in the tests and should be removed.
- nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
- }
-
// ********************************************************* Step 11: import blocks
if (!CheckDiskSpace(gArgs.GetDataDirNet())) {
diff --git a/src/interfaces/ipc.h b/src/interfaces/ipc.h
index e9e6c78053d3e..963649fc9ab2e 100644
--- a/src/interfaces/ipc.h
+++ b/src/interfaces/ipc.h
@@ -9,6 +9,10 @@
#include
#include
+namespace ipc {
+struct Context;
+} // namespace ipc
+
namespace interfaces {
class Init;
@@ -58,6 +62,9 @@ class Ipc
addCleanup(typeid(Interface), &iface, std::move(cleanup));
}
+ //! IPC context struct accessor (see struct definition for more description).
+ virtual ipc::Context& context() = 0;
+
protected:
//! Internal implementation of public addCleanup method (above) as a
//! type-erased virtual function, since template functions can't be virtual.
diff --git a/src/ipc/capnp/context.h b/src/ipc/capnp/context.h
new file mode 100644
index 0000000000000..06e16144941ba
--- /dev/null
+++ b/src/ipc/capnp/context.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_IPC_CAPNP_CONTEXT_H
+#define BITCOIN_IPC_CAPNP_CONTEXT_H
+
+#include
+
+namespace ipc {
+namespace capnp {
+//! Cap'n Proto context struct. Generally the parent ipc::Context struct should
+//! be used instead of this struct to give all IPC protocols access to
+//! application state, so there aren't unnecessary differences between IPC
+//! protocols. But this specialized struct can be used to pass capnp-specific
+//! function and object types to capnp hooks.
+struct Context : ipc::Context
+{
+};
+} // namespace capnp
+} // namespace ipc
+
+#endif // BITCOIN_IPC_CAPNP_CONTEXT_H
diff --git a/src/ipc/capnp/protocol.cpp b/src/ipc/capnp/protocol.cpp
index 74c66c899acf4..37b57a95251bd 100644
--- a/src/ipc/capnp/protocol.cpp
+++ b/src/ipc/capnp/protocol.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
+#include
#include
#include
#include
@@ -54,7 +55,7 @@ class CapnpProtocol : public Protocol
{
assert(!m_loop);
mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
- m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ m_loop.emplace(exe_name, &IpcLogFn, &m_context);
mp::ServeStream(*m_loop, fd, init);
m_loop->loop();
m_loop.reset();
@@ -63,13 +64,14 @@ class CapnpProtocol : public Protocol
{
mp::ProxyTypeRegister::types().at(type)(iface).cleanup.emplace_back(std::move(cleanup));
}
+ Context& context() override { return m_context; }
void startLoop(const char* exe_name)
{
if (m_loop) return;
std::promise promise;
m_loop_thread = std::thread([&] {
util::ThreadRename("capnp-loop");
- m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ m_loop.emplace(exe_name, &IpcLogFn, &m_context);
{
std::unique_lock lock(m_loop->m_mutex);
m_loop->addClient(lock);
@@ -80,6 +82,7 @@ class CapnpProtocol : public Protocol
});
promise.get_future().wait();
}
+ Context m_context;
std::thread m_loop_thread;
std::optional m_loop;
};
diff --git a/src/ipc/context.h b/src/ipc/context.h
new file mode 100644
index 0000000000000..924d7d7450f20
--- /dev/null
+++ b/src/ipc/context.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_IPC_CONTEXT_H
+#define BITCOIN_IPC_CONTEXT_H
+
+namespace ipc {
+//! Context struct used to give IPC protocol implementations or implementation
+//! hooks access to application state, in case they need to run extra code that
+//! isn't needed within a single process, like code copying global state from an
+//! existing process to a new process when it's initialized, or code dealing
+//! with shared objects that are created or destroyed remotely.
+struct Context
+{
+};
+} // namespace ipc
+
+#endif // BITCOIN_IPC_CONTEXT_H
diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp
index ad4b78ed81262..580590fde93e1 100644
--- a/src/ipc/interfaces.cpp
+++ b/src/ipc/interfaces.cpp
@@ -60,6 +60,7 @@ class IpcImpl : public interfaces::Ipc
{
m_protocol->addCleanup(type, iface, std::move(cleanup));
}
+ Context& context() override { return m_protocol->context(); }
const char* m_exe_name;
const char* m_process_argv0;
interfaces::Init& m_init;
diff --git a/src/ipc/protocol.h b/src/ipc/protocol.h
index af955b000786e..4cd892e411786 100644
--- a/src/ipc/protocol.h
+++ b/src/ipc/protocol.h
@@ -12,6 +12,8 @@
#include
namespace ipc {
+struct Context;
+
//! IPC protocol interface for calling IPC methods over sockets.
//!
//! There may be different implementations of this interface for different IPC
@@ -33,6 +35,9 @@ class Protocol
//! Add cleanup callback to interface that will run when the interface is
//! deleted.
virtual void addCleanup(std::type_index type, void* iface, std::function cleanup) = 0;
+
+ //! Context accessor.
+ virtual Context& context() = 0;
};
} // namespace ipc
diff --git a/src/net.cpp b/src/net.cpp
index 3a1bb138ab51a..194410c3d403c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#ifdef WIN32
@@ -658,7 +659,7 @@ bool CNode::ReceiveMsgBytes(Span msg_bytes, bool& complete)
i->second += result->m_raw_message_size;
// push the message to the process queue,
- vRecvMsg.push_back(std::move(*result));
+ m_recv_msg_most_recent = vRecvMsg.insert_after(m_recv_msg_most_recent, std::move(*result));
complete = true;
}
@@ -1576,14 +1577,18 @@ void CConnman::SocketHandler()
if (notify) {
size_t nSizeAdded = 0;
auto it(pnode->vRecvMsg.begin());
- for (; it != pnode->vRecvMsg.end(); ++it) {
+ // it2 will hold the before end iterator.
+ auto it2 = it;
+ for (; it != pnode->vRecvMsg.end(); it2 = it++) {
// vRecvMsg contains only completed CNetMessage
// the single possible partially deserialized message are held by TransportDeserializer
nSizeAdded += it->m_raw_message_size;
}
{
LOCK(pnode->cs_vProcessMsg);
- pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
+ pnode->vProcessMsg.splice_after(pnode->m_process_msg_most_recent, pnode->vRecvMsg, pnode->vRecvMsg.before_begin(), it);
+ pnode->m_process_msg_most_recent = it2;
+ pnode->m_recv_msg_most_recent = pnode->vRecvMsg.before_begin();
pnode->nProcessQueueSize += nSizeAdded;
pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
}
@@ -2974,15 +2979,20 @@ unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion)
: nTimeConnected(GetTimeSeconds()),
- addr(addrIn),
- addrBind(addrBindIn),
- m_inbound_onion(inbound_onion),
- nKeyedNetGroup(nKeyedNetGroupIn),
- id(idIn),
- nLocalHostNonce(nLocalHostNonceIn),
- m_conn_type(conn_type_in),
- nLocalServices(nLocalServicesIn)
-{
+ addr(addrIn),
+ addrBind(addrBindIn),
+ m_inbound_onion(inbound_onion),
+ nKeyedNetGroup(nKeyedNetGroupIn),
+ // Don't relay addr messages to peers that we connect to as block-relay-only
+ // peers (to prevent adversaries from inferring these links from addr
+ // traffic).
+ id(idIn),
+ nLocalHostNonce(nLocalHostNonceIn),
+ m_conn_type(conn_type_in),
+ nLocalServices(nLocalServicesIn)
+{
+ m_recv_msg_most_recent = vRecvMsg.before_begin();
+ m_process_msg_most_recent = vProcessMsg.before_begin();
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
@@ -3017,11 +3027,20 @@ bool CConnman::NodeFullyConnected(const CNode* pnode)
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
size_t nMessageSize = msg.data.size();
- LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.m_type), nMessageSize, pnode->GetId());
+ LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
if (gArgs.GetBoolArg("-capturemessages", false)) {
CaptureMessage(pnode->addr, msg.m_type, msg.data, /* incoming */ false);
}
+ TRACE6(net, outbound_message,
+ pnode->GetId(),
+ pnode->GetAddrName().c_str(),
+ pnode->ConnectionTypeAsString().c_str(),
+ msg.m_type.c_str(),
+ msg.data.size(),
+ msg.data.data()
+ );
+
// make sure we use the appropriate network transport format
std::vector serializedHeader;
pnode->m_serializer->prepareForTransport(msg, serializedHeader);
diff --git a/src/net.h b/src/net.h
index a8836dfcb4363..151bcdd944d92 100644
--- a/src/net.h
+++ b/src/net.h
@@ -37,6 +37,13 @@
#include
#include
#include
+#include
+#include
+
+#ifndef WIN32
+#include
+#endif
+
class CScheduler;
class CNode;
@@ -414,7 +421,8 @@ class CNode
Mutex cs_vRecv;
RecursiveMutex cs_vProcessMsg;
- std::list vProcessMsg GUARDED_BY(cs_vProcessMsg);
+ std::forward_list vProcessMsg GUARDED_BY(cs_vProcessMsg);
+ std::forward_list::iterator m_process_msg_most_recent GUARDED_BY(cs_vProcessMsg);
size_t nProcessQueueSize{0};
RecursiveMutex cs_sendProcessing;
@@ -693,7 +701,8 @@ class CNode
//! service advertisements.
const ServiceFlags nLocalServices;
- std::list vRecvMsg; // Used only by SocketHandler thread
+ std::forward_list vRecvMsg; // Used only by SocketHandler thread
+ std::forward_list::iterator m_recv_msg_most_recent;
mutable RecursiveMutex cs_addrName;
std::string addrName GUARDED_BY(cs_addrName);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 44f9879a232e9..bc5354bf8767b 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -34,6 +34,7 @@
#include // For NDEBUG compile time check
#include
#include
+#include
#include
#include
@@ -877,7 +878,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
}
m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
- uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
+ uint64_t nCMPCTBLOCKVersion = 2;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
@@ -1280,14 +1281,20 @@ void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const s
if (peer == nullptr) return;
LOCK(peer->m_misbehavior_mutex);
+ const int score_before{peer->m_misbehavior_score};
peer->m_misbehavior_score += howmuch;
+ const int score_now{peer->m_misbehavior_score};
+
const std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (peer->m_misbehavior_score >= DISCOURAGEMENT_THRESHOLD && peer->m_misbehavior_score - howmuch < DISCOURAGEMENT_THRESHOLD) {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
+ std::string warning;
+
+ if (score_now >= DISCOURAGEMENT_THRESHOLD && score_before < DISCOURAGEMENT_THRESHOLD) {
+ warning = " DISCOURAGE THRESHOLD EXCEEDED";
peer->m_should_discourage = true;
- } else {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
}
+
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s%s\n",
+ pnode, score_before, score_now, warning, message_prefixed);
}
bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
@@ -1973,7 +1980,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
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 (State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
@@ -2688,8 +2695,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// they may wish to request compact blocks from us
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
- if (pfrom.GetLocalServices() & NODE_WITNESS)
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
@@ -2707,7 +2713,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) {
+ if (nCMPCTBLOCKVersion == 1 || nCMPCTBLOCKVersion == 2) {
LOCK(cs_main);
// fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
@@ -2721,10 +2727,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.m_bip152_highbandwidth_from = 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 == 2);
}
}
return;
@@ -4038,18 +4041,29 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic& interrupt
// Don't bother if send buffer is too full to respond anyway
if (pfrom->fPauseSend) return false;
- std::list msgs;
+ std::forward_list msgs;
{
LOCK(pfrom->cs_vProcessMsg);
if (pfrom->vProcessMsg.empty()) return false;
// Just take one message
- msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
+ msgs.splice_after(msgs.before_begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.before_begin());
+ if (pfrom->vProcessMsg.empty())
+ pfrom->m_process_msg_most_recent = pfrom->vProcessMsg.before_begin();
pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size;
pfrom->fPauseRecv = pfrom->nProcessQueueSize > m_connman.GetReceiveFloodSize();
fMoreWork = !pfrom->vProcessMsg.empty();
}
CNetMessage& msg(msgs.front());
+ TRACE6(net, inbound_message,
+ pfrom->GetId(),
+ pfrom->GetAddrName().c_str(),
+ pfrom->ConnectionTypeAsString().c_str(),
+ msg.m_command.c_str(),
+ msg.m_recv.size(),
+ msg.m_recv.data()
+ );
+
if (gArgs.GetBoolArg("-capturemessages", false)) {
CaptureMessage(pfrom->addr, msg.m_command, MakeUCharSpan(msg.m_recv), /* incoming */ true);
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 442c813a5a60a..4ab4e388d9dc4 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -7,12 +7,19 @@
#endif
#include
-#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -20,6 +27,11 @@
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
#ifdef ENABLE_WALLET
#include
@@ -27,18 +39,6 @@
#include
#endif // ENABLE_WALLET
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
#include
#include
@@ -155,54 +155,11 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
}
}
-BitcoinCore::BitcoinCore(interfaces::Node& node) :
- QObject(), m_node(node)
-{
-}
-
-void BitcoinCore::handleRunawayException(const std::exception *e)
-{
- PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
-}
-
-void BitcoinCore::initialize()
-{
- try
- {
- util::ThreadRename("qt-init");
- qDebug() << __func__ << ": Running initialization in thread";
- interfaces::BlockAndHeaderTipInfo tip_info;
- bool rv = m_node.appInitMain(&tip_info);
- Q_EMIT initializeResult(rv, tip_info);
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
-}
-
-void BitcoinCore::shutdown()
-{
- try
- {
- qDebug() << __func__ << ": Running Shutdown in thread";
- m_node.appShutdown();
- qDebug() << __func__ << ": Shutdown finished";
- Q_EMIT shutdownResult();
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
-}
-
static int qt_argc = 1;
static const char* qt_argv = "bitcoin-qt";
BitcoinApplication::BitcoinApplication():
QApplication(qt_argc, const_cast(&qt_argv)),
- coreThread(nullptr),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
@@ -230,13 +187,7 @@ void BitcoinApplication::setupPlatformStyle()
BitcoinApplication::~BitcoinApplication()
{
- if(coreThread)
- {
- qDebug() << __func__ << ": Stopping thread";
- coreThread->quit();
- coreThread->wait();
- qDebug() << __func__ << ": Stopped thread";
- }
+ m_executor.reset();
delete window;
window = nullptr;
@@ -291,22 +242,15 @@ bool BitcoinApplication::baseInitialize()
void BitcoinApplication::startThread()
{
- if(coreThread)
- return;
- coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore(node());
- executor->moveToThread(coreThread);
+ assert(!m_executor);
+ m_executor.emplace(node());
/* communication to and from thread */
- connect(executor, &BitcoinCore::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(executor, &BitcoinCore::shutdownResult, this, &BitcoinApplication::shutdownResult);
- connect(executor, &BitcoinCore::runawayException, this, &BitcoinApplication::handleRunawayException);
- connect(this, &BitcoinApplication::requestedInitialize, executor, &BitcoinCore::initialize);
- connect(this, &BitcoinApplication::requestedShutdown, executor, &BitcoinCore::shutdown);
- /* make sure executor object is deleted in its own thread */
- connect(coreThread, &QThread::finished, executor, &QObject::deleteLater);
-
- coreThread->start();
+ connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &BitcoinApplication::shutdownResult);
+ connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
+ connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
+ connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
}
void BitcoinApplication::parameterSetup()
@@ -339,7 +283,6 @@ void BitcoinApplication::requestShutdown()
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
- startThread();
window->hide();
// Must disconnect node signals otherwise current thread can deadlock since
// no event loop is running.
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index f9fab0534ba94..ed2f26b7f384d 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -9,11 +9,14 @@
#include
#endif
-#include
+#include
+#include
+
#include
#include
+#include
-#include
+#include
class BitcoinGUI;
class ClientModel;
@@ -26,31 +29,6 @@ class WalletController;
class WalletModel;
-/** Class encapsulating Bitcoin Core startup and shutdown.
- * Allows running startup and shutdown in a different thread from the UI thread.
- */
-class BitcoinCore: public QObject
-{
- Q_OBJECT
-public:
- explicit BitcoinCore(interfaces::Node& node);
-
-public Q_SLOTS:
- void initialize();
- void shutdown();
-
-Q_SIGNALS:
- void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
- void shutdownResult();
- void runawayException(const QString &message);
-
-private:
- /// Pass fatal exception message to UI thread
- void handleRunawayException(const std::exception *e);
-
- interfaces::Node& m_node;
-};
-
/** Main Bitcoin application object */
class BitcoinApplication: public QApplication
{
@@ -112,7 +90,7 @@ public Q_SLOTS:
void windowShown(BitcoinGUI* window);
private:
- QThread *coreThread;
+ std::optional m_executor;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp
new file mode 100644
index 0000000000000..7060f74dab81c
--- /dev/null
+++ b/src/qt/initexecutor.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2014-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+InitExecutor::InitExecutor(interfaces::Node& node)
+ : QObject(), m_node(node)
+{
+ this->moveToThread(&m_thread);
+ m_thread.start();
+}
+
+InitExecutor::~InitExecutor()
+{
+ qDebug() << __func__ << ": Stopping thread";
+ m_thread.quit();
+ m_thread.wait();
+ qDebug() << __func__ << ": Stopped thread";
+}
+
+void InitExecutor::handleRunawayException(const std::exception* e)
+{
+ PrintExceptionContinue(e, "Runaway exception");
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
+}
+
+void InitExecutor::initialize()
+{
+ try {
+ util::ThreadRename("qt-init");
+ qDebug() << __func__ << ": Running initialization in thread";
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+}
+
+void InitExecutor::shutdown()
+{
+ try {
+ qDebug() << __func__ << ": Running Shutdown in thread";
+ m_node.appShutdown();
+ qDebug() << __func__ << ": Shutdown finished";
+ Q_EMIT shutdownResult();
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+}
diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h
new file mode 100644
index 0000000000000..319ce40465d45
--- /dev/null
+++ b/src/qt/initexecutor.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2014-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_INITEXECUTOR_H
+#define BITCOIN_QT_INITEXECUTOR_H
+
+#include
+
+#include
+
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+/** Class encapsulating Bitcoin Core startup and shutdown.
+ * Allows running startup and shutdown in a different thread from the UI thread.
+ */
+class InitExecutor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit InitExecutor(interfaces::Node& node);
+ ~InitExecutor();
+
+public Q_SLOTS:
+ void initialize();
+ void shutdown();
+
+Q_SIGNALS:
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
+ void shutdownResult();
+ void runawayException(const QString& message);
+
+private:
+ /// Pass fatal exception message to UI thread
+ void handleRunawayException(const std::exception* e);
+
+ interfaces::Node& m_node;
+ QThread m_thread;
+};
+
+#endif // BITCOIN_QT_INITEXECUTOR_H
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index eb86f027ef942..7d66f67f8a993 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c4a89c9772abf..f8a4d4ccab053 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1377,23 +1377,24 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
}
- if (ThresholdState::STARTED == thresholdState)
- {
+ const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
+ if (has_signal) {
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
bip9.pushKV("since", since_height);
- if (ThresholdState::STARTED == thresholdState)
- {
+ if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
- statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
- statsUV.pushKV("possible", statsStruct.possible);
+ if (ThresholdState::LOCKED_IN != thresholdState) {
+ statsUV.pushKV("threshold", statsStruct.threshold);
+ statsUV.pushKV("possible", statsStruct.possible);
+ }
bip9.pushKV("statistics", statsUV);
}
bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
@@ -1422,7 +1423,8 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
{RPCResult::Type::NUM, "difficulty", "the current difficulty"},
- {RPCResult::Type::NUM, "mediantime", "median time for the current best block"},
+ {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
{RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
@@ -1439,18 +1441,18 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)",
{
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)",
+ {RPCResult::Type::OBJ, "statistics", "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
{
- {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
- {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"},
+ {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
+ {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
{RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
{RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"},
+ {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
}},
}},
{RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
@@ -1478,6 +1480,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
+ obj.pushKV("time", (int64_t)tip->nTime);
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 617dfec98f845..6dfccd9023cc0 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -74,12 +74,10 @@ static RPCHelpMan getrawtransaction()
"getrawtransaction",
"\nReturn the raw transaction data.\n"
- "\nBy default this function only works for mempool transactions. When called with a blockhash\n"
- "argument, getrawtransaction will return the transaction if the specified block is available and\n"
- "the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n"
- "will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n"
- "is in a block in the blockchain.\n"
-
+ "\nBy default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n"
+ "and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block.\n"
+ "If -txindex is not enabled and a blockhash argument is passed, it will return the transaction if\n"
+ "the specified block is available and the transaction is found in that block.\n"
"\nHint: Use gettransaction for wallet transactions.\n"
"\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index ef48f89965817..dd7c0a4a05101 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1807,16 +1807,16 @@ bool GenericTransactionSignatureChecker::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker;
template class GenericTransactionSignatureChecker;
-static bool ExecuteWitnessScript(const Span& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
+static bool ExecuteWitnessScript(const Span& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
{
std::vector stack{stack_span.begin(), stack_span.end()};
if (sigversion == SigVersion::TAPSCRIPT) {
// OP_SUCCESSx processing overrides everything, including stack element size limits
- CScript::const_iterator pc = scriptPubKey.begin();
- while (pc < scriptPubKey.end()) {
+ CScript::const_iterator pc = exec_script.begin();
+ while (pc < exec_script.end()) {
opcodetype opcode;
- if (!scriptPubKey.GetOp(pc, opcode)) {
+ if (!exec_script.GetOp(pc, opcode)) {
// Note how this condition would not be reached if an unknown OP_SUCCESSx was found
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
@@ -1839,7 +1839,7 @@ static bool ExecuteWitnessScript(const Span& stack_span, const CS
}
// Run the script interpreter.
- if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, execdata, serror)) return false;
+ if (!EvalScript(stack, exec_script, flags, checker, sigversion, execdata, serror)) return false;
// Scripts inside witness implicitly require cleanstack behaviour
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 37ae7139968a4..93136a0b79d7e 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -139,6 +139,10 @@ enum : uint32_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
+
+ // Constants to point to the highest flag in use. Add new flags above this line.
+ //
+ SCRIPT_VERIFY_END_MARKER
};
bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror);
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 1103292c1a873..5e5c5eba69cfe 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -76,7 +76,7 @@ class CAddrManTest : public CAddrMan
{
int64_t nLastSuccess = 1;
// Set last good connection in the deep past.
- Good(addr, true, nLastSuccess);
+ Good(addr, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index db0b461873076..92c34e74d9861 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -44,6 +44,17 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
addr_man.m_asmap.clear();
}
}
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
+ const auto ser_version{fuzzed_data_provider.ConsumeIntegral()};
+ ds.SetVersion(ser_version);
+ try {
+ ds >> addr_man;
+ } catch (const std::ios_base::failure&) {
+ addr_man.Clear();
+ }
+ }
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
@@ -80,7 +91,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
[&] {
const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider);
if (opt_service) {
- addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+ addr_man.Good(*opt_service, ConsumeTime(fuzzed_data_provider));
}
},
[&] {
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 182aabc79b86b..1986b5e4c8751 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -109,7 +109,9 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
BanMan ban_man_read{banlist_file, /* client_interface */ nullptr, /* default_ban_time */ 0};
banmap_t banmap_read;
ban_man_read.GetBanned(banmap_read);
- assert(banmap == banmap_read);
+ // Assert temporarily disabled to allow the remainder of the fuzz test to run while a
+ // fix is being worked on. See https://github.com/bitcoin/bitcoin/pull/22517
+ (void)(banmap == banmap_read);
}
}
fs::remove(banlist_file.string() + ".dat");
diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp
index 51956bbe9ec29..447f32ed16cf4 100644
--- a/src/test/fuzz/prevector.cpp
+++ b/src/test/fuzz/prevector.cpp
@@ -206,10 +206,14 @@ class prevector_tester
FUZZ_TARGET(prevector)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{3000};
+
FuzzedDataProvider prov(buffer.data(), buffer.size());
prevector_tester<8, int> test;
- while (prov.remaining_bytes()) {
+ while (--limit_max_ops >= 0 && prov.remaining_bytes()) {
switch (prov.ConsumeIntegralInRange(0, 13 + 3 * (test.size() > 0))) {
case 0:
test.insert(prov.ConsumeIntegralInRange(0, test.size()), prov.ConsumeIntegral());
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 07059cce76a87..3b33115e72b04 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -16,12 +16,16 @@
FUZZ_TARGET(rolling_bloom_filter)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{3000};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
CRollingBloomFilter rolling_bloom_filter{
fuzzed_data_provider.ConsumeIntegralInRange(1, 1000),
0.999 / fuzzed_data_provider.ConsumeIntegralInRange(1, std::numeric_limits::max())};
- while (fuzzed_data_provider.remaining_bytes() > 0) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.remaining_bytes() > 0) {
CallOneOf(
fuzzed_data_provider,
[&] {
@@ -32,13 +36,10 @@ FUZZ_TARGET(rolling_bloom_filter)
assert(present);
},
[&] {
- const std::optional u256 = ConsumeDeserializable(fuzzed_data_provider);
- if (!u256) {
- return;
- }
- (void)rolling_bloom_filter.contains(*u256);
- rolling_bloom_filter.insert(*u256);
- const bool present = rolling_bloom_filter.contains(*u256);
+ const uint256 u256{ConsumeUInt256(fuzzed_data_provider)};
+ (void)rolling_bloom_filter.contains(u256);
+ rolling_bloom_filter.insert(u256);
+ const bool present = rolling_bloom_filter.contains(u256);
assert(present);
},
[&] {
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index bab34ea3402a8..dadf772bc1f23 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -112,6 +112,10 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const CChainState& chain
FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{300};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
@@ -142,7 +146,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
return c.out.nValue;
};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
{
// Total supply is the mempool fee + all outpoints
CAmount supply_now{WITH_LOCK(tx_pool.cs, return tx_pool.GetTotalFee())};
@@ -285,6 +289,10 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
{
+ // Pick an arbitrary upper bound to limit the runtime and avoid timeouts on
+ // inputs.
+ int limit_max_ops{300};
+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
@@ -305,7 +313,7 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
CTxMemPool tx_pool_{/* estimator */ nullptr, /* check_ratio */ 1};
MockedTxPool& tx_pool = *static_cast(&tx_pool_);
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
if (fuzzed_data_provider.ConsumeBool()) {
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 23195c0a26c5f..1924ea55b1e41 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -112,10 +112,15 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
- // If we add many more flags, this loop can get too expensive, but we can
- // rewrite in the future to randomly pick a set of flags to evaluate.
- for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) {
+
+ FastRandomContext insecure_rand(true);
+
+ for (int count = 0; count < 10000; ++count) {
TxValidationState state;
+
+ // Randomly selects flag combinations
+ uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1);
+
// Filter out incompatible flag choices
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
// CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 28d79670786bb..6231a2ace6a19 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -16,14 +16,17 @@ void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, Span msg_by
if (complete) {
size_t nSizeAdded = 0;
auto it(node.vRecvMsg.begin());
- for (; it != node.vRecvMsg.end(); ++it) {
+ auto it2 = it;
+ for (; it != node.vRecvMsg.end(); it2 = it++) {
// vRecvMsg contains only completed CNetMessage
// the single possible partially deserialized message are held by TransportDeserializer
nSizeAdded += it->m_raw_message_size;
}
{
LOCK(node.cs_vProcessMsg);
- node.vProcessMsg.splice(node.vProcessMsg.end(), node.vRecvMsg, node.vRecvMsg.begin(), it);
+ node.vProcessMsg.splice_after(node.m_process_msg_most_recent, node.vRecvMsg, node.vRecvMsg.before_begin(), it);
+ node.m_process_msg_most_recent = it2;
+ node.m_recv_msg_most_recent = node.vRecvMsg.before_begin();
node.nProcessQueueSize += nSizeAdded;
node.fPauseRecv = node.nProcessQueueSize > nReceiveFloodSize;
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index a0499fa51fe7e..bb296456bafc6 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -132,28 +132,35 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
bool TorControlConnection::Connect(const std::string& tor_control_center, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
{
- if (b_conn)
+ if (b_conn) {
Disconnect();
- // Parse tor_control_center address:port
- struct sockaddr_storage connect_to_addr;
- int connect_to_addrlen = sizeof(connect_to_addr);
- if (evutil_parse_sockaddr_port(tor_control_center.c_str(),
- (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
+ }
+
+ CService control_service;
+ if (!Lookup(tor_control_center, control_service, 9051, fNameLookup)) {
+ LogPrintf("tor: Failed to look up control center %s\n", tor_control_center);
+ return false;
+ }
+
+ struct sockaddr_storage control_address;
+ socklen_t control_address_len = sizeof(control_address);
+ if (!control_service.GetSockAddr(reinterpret_cast(&control_address), &control_address_len)) {
LogPrintf("tor: Error parsing socket address %s\n", tor_control_center);
return false;
}
// Create a new socket, set up callbacks and enable notification bits
b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
- if (!b_conn)
+ if (!b_conn) {
return false;
+ }
bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr, TorControlConnection::eventcb, this);
bufferevent_enable(b_conn, EV_READ|EV_WRITE);
this->connected = _connected;
this->disconnected = _disconnected;
// Finally, connect to tor_control_center
- if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
+ if (bufferevent_socket_connect(b_conn, reinterpret_cast(&control_address), control_address_len) < 0) {
LogPrintf("tor: Error connecting to address %s\n", tor_control_center);
return false;
}
diff --git a/src/util/epochguard.h b/src/util/epochguard.h
index 1570ec4eb4c80..3e63e093daa19 100644
--- a/src/util/epochguard.h
+++ b/src/util/epochguard.h
@@ -40,6 +40,9 @@ class LOCKABLE Epoch
Epoch() = default;
Epoch(const Epoch&) = delete;
Epoch& operator=(const Epoch&) = delete;
+ Epoch(Epoch&&) = delete;
+ Epoch& operator=(Epoch&&) = delete;
+ ~Epoch() = default;
bool guarded() const { return m_guarded; }
@@ -51,6 +54,13 @@ class LOCKABLE Epoch
// only allow modification via Epoch member functions
friend class Epoch;
Marker& operator=(const Marker&) = delete;
+
+ public:
+ Marker() = default;
+ Marker(const Marker&) = default;
+ Marker(Marker&&) = delete;
+ Marker& operator=(Marker&&) = delete;
+ ~Marker() = default;
};
class SCOPED_LOCKABLE Guard
diff --git a/src/validation.cpp b/src/validation.cpp
index f81c27e8e3f0c..20d641bf40089 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -48,6 +48,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1158,6 +1159,20 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
{
LOCK(cs_main);
+ if (mempool && !block_index) {
+ CTransactionRef ptx = mempool->get(hash);
+ if (ptx) return ptx;
+ }
+ if (g_txindex) {
+ CTransactionRef tx;
+ uint256 block_hash;
+ if (g_txindex->FindTx(hash, block_hash, tx)) {
+ if (!block_index || block_index->GetBlockHash() == block_hash) {
+ hashBlock = block_hash;
+ return tx;
+ }
+ }
+ }
if (block_index) {
CBlock block;
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
@@ -1168,15 +1183,6 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
}
}
}
- return nullptr;
- }
- if (mempool) {
- CTransactionRef ptx = mempool->get(hash);
- if (ptx) return ptx;
- }
- if (g_txindex) {
- CTransactionRef tx;
- if (g_txindex->FindTx(hash, hashBlock, tx)) return tx;
}
return nullptr;
}
@@ -1645,13 +1651,8 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity()
*pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception
{
- flags |= SCRIPT_VERIFY_P2SH;
- }
-
- // Enforce WITNESS rules whenever P2SH is in effect (and the segwit
- // deployment is defined).
- if (flags & SCRIPT_VERIFY_P2SH && DeploymentEnabled(consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
- flags |= SCRIPT_VERIFY_WITNESS;
+ // Enforce WITNESS rules whenever P2SH is in effect
+ flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS;
}
// Enforce the DERSIG (BIP66) rule
@@ -1997,6 +1998,16 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal);
+ TRACE7(validation, block_connected,
+ block.GetHash().ToString().c_str(),
+ pindex->nHeight,
+ block.vtx.size(),
+ nInputs,
+ nSigOpsCost,
+ GetTimeMicros() - nTimeStart, // in microseconds (µs)
+ block.GetHash().data()
+ );
+
return true;
}
@@ -3101,25 +3112,23 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc
std::vector commitment;
int commitpos = GetWitnessCommitmentIndex(block);
std::vector ret(32, 0x00);
- if (DeploymentEnabled(consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
- if (commitpos == NO_WITNESS_COMMITMENT) {
- uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
- CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
- CTxOut out;
- out.nValue = 0;
- out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
- out.scriptPubKey[0] = OP_RETURN;
- out.scriptPubKey[1] = 0x24;
- out.scriptPubKey[2] = 0xaa;
- out.scriptPubKey[3] = 0x21;
- out.scriptPubKey[4] = 0xa9;
- out.scriptPubKey[5] = 0xed;
- memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
- commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end());
- CMutableTransaction tx(*block.vtx[0]);
- tx.vout.push_back(out);
- block.vtx[0] = MakeTransactionRef(std::move(tx));
- }
+ if (commitpos == NO_WITNESS_COMMITMENT) {
+ uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
+ CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
+ CTxOut out;
+ out.nValue = 0;
+ out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
+ out.scriptPubKey[0] = OP_RETURN;
+ out.scriptPubKey[1] = 0x24;
+ out.scriptPubKey[2] = 0xaa;
+ out.scriptPubKey[3] = 0x21;
+ out.scriptPubKey[4] = 0xa9;
+ out.scriptPubKey[5] = 0xed;
+ memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
+ commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end());
+ CMutableTransaction tx(*block.vtx[0]);
+ tx.vout.push_back(out);
+ block.vtx[0] = MakeTransactionRef(std::move(tx));
}
UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
return commitment;
diff --git a/src/validation.h b/src/validation.h
index 18a09d4aa3b4c..d3e4f3b98322e 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -141,15 +141,16 @@ void StartScriptCheckWorkerThreads(int threads_num);
/** Stop all of the script checking worker threads */
void StopScriptCheckWorkerThreads();
/**
- * Return transaction from the block at block_index.
- * If block_index is not provided, fall back to mempool.
- * If mempool is not provided or the tx couldn't be found in mempool, fall back to g_txindex.
+ * Return transaction with a given hash.
+ * If mempool is provided and block_index is not provided, check it first for the tx.
+ * If -txindex is available, check it next for the tx.
+ * Finally, if block_index is provided, check for tx by reading entire block from disk.
*
* @param[in] block_index The block to read from disk, or nullptr
- * @param[in] mempool If block_index is not provided, look in the mempool, if provided
+ * @param[in] mempool If provided, check mempool for tx
* @param[in] hash The txid
* @param[in] consensusParams The params
- * @param[out] hashBlock The hash of block_index, if the tx was found via block_index
+ * @param[out] hashBlock The block hash, if the tx was found via -txindex or block_index
* @returns The tx if found, otherwise nullptr
*/
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock);
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
new file mode 100644
index 0000000000000..66e7de4273c92
--- /dev/null
+++ b/src/wallet/test/spend_tests.cpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)
+
+BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
+{
+ CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+ auto wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
+
+ // Check that a subtract-from-recipient transaction slightly less than the
+ // coinbase input amount does not create a change output (because it would
+ // be uneconomical to add and spend the output), and make sure it pays the
+ // leftover input amount which would have been change to the recipient
+ // instead of the miner.
+ auto check_tx = [&wallet](CAmount leftover_input_amount) {
+ CRecipient recipient{GetScriptForRawPubKey({}), 50 * COIN - leftover_input_amount, true /* subtract fee */};
+ CTransactionRef tx;
+ CAmount fee;
+ int change_pos = -1;
+ bilingual_str error;
+ CCoinControl coin_control;
+ coin_control.m_feerate.emplace(10000);
+ coin_control.fOverrideFeeRate = true;
+ FeeCalculation fee_calc;
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
+ BOOST_CHECK_EQUAL(tx->vout.size(), 1);
+ BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
+ BOOST_CHECK_GT(fee, 0);
+ return fee;
+ };
+
+ // Send full input amount to recipient, check that only nonzero fee is
+ // subtracted (to_reduce == fee).
+ const CAmount fee{check_tx(0)};
+
+ // Send slightly less than full input amount to recipient, check leftover
+ // input amount is paid to recipient not the miner (to_reduce == fee - 123)
+ BOOST_CHECK_EQUAL(fee, check_tx(123));
+
+ // Send full input minus fee amount to recipient, check leftover input
+ // amount is paid to recipient not the miner (to_reduce == 0)
+ BOOST_CHECK_EQUAL(fee, check_tx(fee));
+
+ // Send full input minus more than the fee amount to recipient, check
+ // leftover input amount is paid to recipient not the miner (to_reduce ==
+ // -123). This overpays the recipient instead of overpaying the miner more
+ // than double the neccesary fee.
+ BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
new file mode 100644
index 0000000000000..c3061b93c061c
--- /dev/null
+++ b/src/wallet/test/util.cpp
@@ -0,0 +1,38 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+std::unique_ptr CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
+{
+ auto wallet = std::make_unique(&chain, "", CreateMockWalletDatabase());
+ {
+ LOCK2(wallet->cs_wallet, ::cs_main);
+ wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
+ }
+ wallet->LoadWallet();
+ {
+ auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
+ LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
+ spk_man->AddKeyPubKey(key, key.GetPubKey());
+ }
+ WalletRescanReserver reserver(*wallet);
+ reserver.reserve();
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
+ BOOST_CHECK_EQUAL(result.last_scanned_block, cchain.Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, cchain.Height());
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ return wallet;
+}
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
new file mode 100644
index 0000000000000..288c111571c4d
--- /dev/null
+++ b/src/wallet/test/util.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_TEST_UTIL_H
+#define BITCOIN_WALLET_TEST_UTIL_H
+
+#include
+
+class CChain;
+class CKey;
+class CWallet;
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+std::unique_ptr CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
+
+#endif // BITCOIN_WALLET_TEST_UTIL_H
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index a0070b8dd321f..75a08b6f74ca6 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
@@ -480,20 +481,7 @@ class ListCoinsTestingSetup : public TestChain100Setup
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = std::make_unique(m_node.chain.get(), "", CreateMockWalletDatabase());
- {
- LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- }
- wallet->LoadWallet();
- AddKey(*wallet, coinbaseKey);
- WalletRescanReserver reserver(*wallet);
- reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
- BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
- BOOST_CHECK(result.last_failed_block.IsNull());
+ wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
}
~ListCoinsTestingSetup()
diff --git a/test/functional/feature_presegwit_node_upgrade.py b/test/functional/feature_presegwit_node_upgrade.py
new file mode 100755
index 0000000000000..0428588da396e
--- /dev/null
+++ b/test/functional/feature_presegwit_node_upgrade.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test a pre-segwit node upgrading to segwit consensus"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ softfork_active,
+)
+
+class SegwitUpgradeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [["-segwitheight=10"]]
+
+ def run_test(self):
+ """A pre-segwit node with insufficiently validated blocks needs to redownload blocks"""
+
+ self.log.info("Testing upgrade behaviour for pre-segwit node to segwit rules")
+ node = self.nodes[0]
+
+ # Node hasn't been used or connected yet
+ assert_equal(node.getblockcount(), 0)
+
+ assert not softfork_active(node, "segwit")
+
+ # Generate 8 blocks without witness data
+ node.generate(8)
+ assert_equal(node.getblockcount(), 8)
+
+ self.stop_node(0)
+ # Restarting the node (with segwit activation height set to 5) should result in a shutdown
+ # because the blockchain consists of 3 insufficiently validated blocks per segwit consensus rules.
+ node.assert_start_raises_init_error(
+ extra_args=["-segwitheight=5"],
+ expected_msg=": Witness data for blocks after height 5 requires validation. Please restart with -reindex..\nPlease restart with -reindex or -reindex-chainstate to recover.")
+
+ # As directed, the user restarts the node with -reindex
+ self.start_node(0, extra_args=["-reindex", "-segwitheight=5"])
+
+ # With the segwit consensus rules, the node is able to validate only up to block 4
+ assert_equal(node.getblockcount(), 4)
+
+ # The upgraded node should now have segwit activated
+ assert softfork_active(node, "segwit")
+
+
+if __name__ == '__main__':
+ SegwitUpgradeTest().main()
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 22eec59600dff..dfa448a1a867a 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -5,6 +5,7 @@
"""Test bitcoin-cli"""
from decimal import Decimal
+import re
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
@@ -29,6 +30,41 @@
WALLET_NOT_LOADED = 'Requested wallet does not exist or is not loaded'
WALLET_NOT_SPECIFIED = 'Wallet file not specified'
+
+def cli_get_info_string_to_dict(cli_get_info_string):
+ """Helper method to convert human-readable -getinfo into a dictionary"""
+ cli_get_info = {}
+ lines = cli_get_info_string.splitlines()
+ line_idx = 0
+ ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
+ while line_idx < len(lines):
+ # Remove ansi colour code
+ line = ansi_escape.sub('', lines[line_idx])
+ if "Balances" in line:
+ # When "Balances" appears in a line, all of the following lines contain "balance: wallet" until an empty line
+ cli_get_info["Balances"] = {}
+ while line_idx < len(lines) and not (lines[line_idx + 1] == ''):
+ line_idx += 1
+ balance, wallet = lines[line_idx].strip().split(" ")
+ # Remove right justification padding
+ wallet = wallet.strip()
+ if wallet == '""':
+ # Set default wallet("") to empty string
+ wallet = ''
+ cli_get_info["Balances"][wallet] = balance.strip()
+ elif ": " in line:
+ key, value = line.split(": ")
+ if key == 'Wallet' and value == '""':
+ # Set default wallet("") to empty string
+ value = ''
+ if key == "Proxy" and value == "N/A":
+ # Set N/A to empty string to represent no proxy
+ value = ''
+ cli_get_info[key.strip()] = value.strip()
+ line_idx += 1
+ return cli_get_info
+
+
class TestBitcoinCli(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -67,37 +103,43 @@ def run_test(self):
self.log.info("Test -getinfo with arguments fails")
assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
+ self.log.info("Test -getinfo with -color=never does not return ANSI escape codes")
+ assert "\u001b[0m" not in self.nodes[0].cli('-getinfo', '-color=never').send_cli()
+
+ self.log.info("Test -getinfo with -color=always returns ANSI escape codes")
+ assert "\u001b[0m" in self.nodes[0].cli('-getinfo', '-color=always').send_cli()
+
+ self.log.info("Test -getinfo with invalid value for -color option")
+ assert_raises_process_error(1, "Invalid value for -color option. Valid values: always, auto, never.", self.nodes[0].cli('-getinfo', '-color=foo').send_cli)
+
self.log.info("Test -getinfo returns expected network and blockchain info")
if self.is_wallet_compiled():
self.nodes[0].encryptwallet(password)
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+
network_info = self.nodes[0].getnetworkinfo()
blockchain_info = self.nodes[0].getblockchaininfo()
- assert_equal(cli_get_info['version'], network_info['version'])
- assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
- assert_equal(cli_get_info['headers'], blockchain_info['headers'])
- assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
- assert_equal(
- cli_get_info['connections'],
- {
- 'in': network_info['connections_in'],
- 'out': network_info['connections_out'],
- 'total': network_info['connections']
- }
- )
- assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
- assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
- assert_equal(cli_get_info['chain'], blockchain_info['chain'])
+ assert_equal(int(cli_get_info['Version']), network_info['version'])
+ assert_equal(cli_get_info['Verification progress'], "%.4f%%" % (blockchain_info['verificationprogress'] * 100))
+ assert_equal(int(cli_get_info['Blocks']), blockchain_info['blocks'])
+ assert_equal(int(cli_get_info['Headers']), blockchain_info['headers'])
+ assert_equal(int(cli_get_info['Time offset (s)']), network_info['timeoffset'])
+ expected_network_info = f"in {network_info['connections_in']}, out {network_info['connections_out']}, total {network_info['connections']}"
+ assert_equal(cli_get_info["Network"], expected_network_info)
+ assert_equal(cli_get_info['Proxy'], network_info['networks'][0]['proxy'])
+ assert_equal(Decimal(cli_get_info['Difficulty']), blockchain_info['difficulty'])
+ assert_equal(cli_get_info['Chain'], blockchain_info['chain'])
if self.is_wallet_compiled():
self.log.info("Test -getinfo and bitcoin-cli getwalletinfo return expected wallet info")
- assert_equal(cli_get_info['balance'], BALANCE)
- assert 'balances' not in cli_get_info.keys()
+ assert_equal(Decimal(cli_get_info['Balance']), BALANCE)
+ assert 'Balances' not in cli_get_info_string
wallet_info = self.nodes[0].getwalletinfo()
- assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
- assert_equal(cli_get_info['unlocked_until'], wallet_info['unlocked_until'])
- assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
- assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
+ assert_equal(int(cli_get_info['Keypool size']), wallet_info['keypoolsize'])
+ assert_equal(int(cli_get_info['Unlocked until']), wallet_info['unlocked_until'])
+ assert_equal(Decimal(cli_get_info['Transaction fee rate (-paytxfee) (BTC/kvB)']), wallet_info['paytxfee'])
+ assert_equal(Decimal(cli_get_info['Min tx relay fee rate (BTC/kvB)']), network_info['relayfee'])
assert_equal(self.nodes[0].cli.getwalletinfo(), wallet_info)
# Setup to test -getinfo, -generate, and -rpcwallet= with multiple wallets.
@@ -120,44 +162,57 @@ def run_test(self):
self.log.info("Test -getinfo with multiple wallets and -rpcwallet returns specified wallet balance")
for i in range(len(wallets)):
- cli_get_info = self.nodes[0].cli('-getinfo', '-rpcwallet={}'.format(wallets[i])).send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[i])
+ cli_get_info_string = self.nodes[0].cli('-getinfo', '-rpcwallet={}'.format(wallets[i])).send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info["Wallet"], wallets[i])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[i])
self.log.info("Test -getinfo with multiple wallets and -rpcwallet=non-existing-wallet returns no balances")
- cli_get_info_keys = self.nodes[0].cli('-getinfo', '-rpcwallet=does-not-exist').send_cli().keys()
- assert 'balance' not in cli_get_info_keys
- assert 'balances' not in cli_get_info_keys
+ cli_get_info_string = self.nodes[0].cli('-getinfo', '-rpcwallet=does-not-exist').send_cli()
+ assert 'Balance' not in cli_get_info_string
+ assert 'Balances' not in cli_get_info_string
self.log.info("Test -getinfo with multiple wallets returns all loaded wallet names and balances")
assert_equal(set(self.nodes[0].listwallets()), set(wallets))
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balance' not in cli_get_info.keys()
- assert_equal(cli_get_info['balances'], {k: v for k, v in zip(wallets, amounts)})
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info
+ for k, v in zip(wallets, amounts):
+ assert_equal(Decimal(cli_get_info['Balances'][k]), v)
# Unload the default wallet and re-verify.
self.nodes[0].unloadwallet(wallets[0])
assert wallets[0] not in self.nodes[0].listwallets()
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balance' not in cli_get_info.keys()
- assert_equal(cli_get_info['balances'], {k: v for k, v in zip(wallets[1:], amounts[1:])})
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info
+ assert 'Balances' in cli_get_info_string
+ for k, v in zip(wallets[1:], amounts[1:]):
+ assert_equal(Decimal(cli_get_info['Balances'][k]), v)
+ assert wallets[0] not in cli_get_info
self.log.info("Test -getinfo after unloading all wallets except a non-default one returns its balance")
self.nodes[0].unloadwallet(wallets[2])
assert_equal(self.nodes[0].listwallets(), [wallets[1]])
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[1])
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info['Wallet'], wallets[1])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[1])
self.log.info("Test -getinfo with -rpcwallet=remaining-non-default-wallet returns only its balance")
- cli_get_info = self.nodes[0].cli('-getinfo', rpcwallet2).send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[1])
+ cli_get_info_string = self.nodes[0].cli('-getinfo', rpcwallet2).send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info['Wallet'], wallets[1])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[1])
self.log.info("Test -getinfo with -rpcwallet=unloaded wallet returns no balances")
- cli_get_info_keys = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli().keys()
- assert 'balance' not in cli_get_info_keys
- assert 'balances' not in cli_get_info_keys
+ cli_get_info_string = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli()
+ cli_get_info_keys = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info_keys
+ assert 'Balances' not in cli_get_info_string
# Test bitcoin-cli -generate.
n1 = 3
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index ba467c1517385..01fc02f27e922 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -13,6 +13,7 @@
from test_framework.blocktools import (
create_coinbase,
+ get_witness_script,
NORMAL_GBT_REQUEST_PARAMS,
TIME_GENESIS_BLOCK,
)
@@ -20,6 +21,7 @@
CBlock,
CBlockHeader,
BLOCK_HEADER_SIZE,
+ ser_uint256,
)
from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
@@ -49,6 +51,9 @@ def set_test_params(self):
self.setup_clean_chain = True
self.supports_cli = False
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
def mine_chain(self):
self.log.info('Create some old blocks')
for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
@@ -89,7 +94,21 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
assert_equal(mining_info['pooledtx'], 0)
- # Mine a block to leave initial block download
+ self.log.info("getblocktemplate: Test default witness commitment")
+ txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16)
+ tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+
+ # Check that default_witness_commitment is present.
+ assert 'default_witness_commitment' in tmpl
+ witness_commitment = tmpl['default_witness_commitment']
+
+ # Check that default_witness_commitment is correct.
+ witness_root = CBlock.get_merkle_root([ser_uint256(0),
+ ser_uint256(txid)])
+ script = get_witness_script(witness_root, 0)
+ assert_equal(witness_commitment, script.hex())
+
+ # Mine a block to leave initial block download and clear the mempool
node.generatetoaddress(1, node.get_deterministic_priv_key().address)
tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
self.log.info("getblocktemplate: Test capability advertised")
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index ead9d852febf3..db96e6bdcfe8f 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -9,11 +9,10 @@
import struct
import time
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
from test_framework.key import ECKey
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
- CBlock,
CBlockHeader,
CInv,
COutPoint,
@@ -206,24 +205,17 @@ def request_block(self, blockhash, inv_type, timeout=60):
class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 3
+ self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
["-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT), "-whitelist=noban@127.0.0.1"],
["-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
- ["-acceptnonstdtxn=1", "-segwitheight=-1"],
]
self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def setup_network(self):
- self.setup_nodes()
- self.connect_nodes(0, 1)
- self.connect_nodes(0, 2)
- self.sync_all()
-
# Helper functions
def build_next_block(self, version=4):
@@ -264,7 +256,6 @@ def run_test(self):
self.test_non_witness_transaction()
self.test_v0_outputs_arent_spendable()
self.test_block_relay()
- self.test_getblocktemplate_before_lockin()
self.test_unnecessary_witness_before_segwit_activation()
self.test_witness_tx_relay_before_segwit_activation()
self.test_standardness_v0()
@@ -292,7 +283,6 @@ def run_test(self):
self.test_signature_version_1()
self.test_non_standard_witness_blinding()
self.test_non_standard_witness()
- self.test_upgrade_after_activation()
self.test_witness_sigops()
self.test_superfluous_witness()
self.test_wtxid_relay()
@@ -482,11 +472,6 @@ def test_v0_outputs_arent_spendable(self):
witness, and so can't be spent before segwit activation (the point at which
blocks are permitted to contain witnesses)."""
- # node2 doesn't need to be connected for this test.
- # (If it's connected, node0 may propagate an invalid block to it over
- # compact blocks and the nodes would have inconsistent tips.)
- self.disconnect_nodes(0, 2)
-
# Create two outputs, a p2wsh and p2sh-p2wsh
witness_program = CScript([OP_TRUE])
script_pubkey = script_to_p2wsh_script(witness_program)
@@ -544,37 +529,9 @@ def test_v0_outputs_arent_spendable(self):
# TODO: support multiple acceptable reject reasons.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
- self.connect_nodes(0, 2)
-
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
- @subtest # type: ignore
- def test_getblocktemplate_before_lockin(self):
- txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
-
- for node in [self.nodes[0], self.nodes[2]]:
- gbt_results = node.getblocktemplate({"rules": ["segwit"]})
- if node == self.nodes[2]:
- # If this is a non-segwit node, we should not get a witness
- # commitment.
- assert 'default_witness_commitment' not in gbt_results
- else:
- # For segwit-aware nodes, check the witness
- # commitment is correct.
- assert 'default_witness_commitment' in gbt_results
- witness_commitment = gbt_results['default_witness_commitment']
-
- # Check that default_witness_commitment is present.
- witness_root = CBlock.get_merkle_root([ser_uint256(0),
- ser_uint256(txid)])
- script = get_witness_script(witness_root, 0)
- assert_equal(witness_commitment, script.hex())
-
- # Clear out the mempool
- self.nodes[0].generate(1)
- self.sync_blocks()
-
@subtest # type: ignore
def test_witness_tx_relay_before_segwit_activation(self):
@@ -1927,39 +1884,6 @@ def test_non_standard_witness(self):
self.utxo.pop(0)
- @subtest # type: ignore
- def test_upgrade_after_activation(self):
- """Test the behavior of starting up a segwit-aware node after the softfork has activated."""
-
- # All nodes are caught up and node 2 is a pre-segwit node that will soon upgrade.
- for n in range(2):
- assert_equal(self.nodes[n].getblockcount(), self.nodes[2].getblockcount())
- assert softfork_active(self.nodes[n], "segwit")
- assert SEGWIT_HEIGHT < self.nodes[2].getblockcount()
- assert 'segwit' not in self.nodes[2].getblockchaininfo()['softforks']
-
- # Restarting node 2 should result in a shutdown because the blockchain consists of
- # insufficiently validated blocks per segwit consensus rules.
- self.stop_node(2)
- self.nodes[2].assert_start_raises_init_error(
- extra_args=[f"-segwitheight={SEGWIT_HEIGHT}"],
- expected_msg=f": Witness data for blocks after height {SEGWIT_HEIGHT} requires validation. Please restart with -reindex..\nPlease restart with -reindex or -reindex-chainstate to recover.",
- )
-
- # As directed, the user restarts the node with -reindex
- self.start_node(2, extra_args=["-reindex", f"-segwitheight={SEGWIT_HEIGHT}"])
-
- # With the segwit consensus rules, the node is able to validate only up to SEGWIT_HEIGHT - 1
- assert_equal(self.nodes[2].getblockcount(), SEGWIT_HEIGHT - 1)
- self.connect_nodes(0, 2)
-
- # We reconnect more than 100 blocks, give it plenty of time
- # sync_blocks() also verifies the best block hash is the same for all nodes
- self.sync_blocks(timeout=240)
-
- # The upgraded node should now have segwit activated
- assert softfork_active(self.nodes[2], "segwit")
-
@subtest # type: ignore
def test_witness_sigops(self):
"""Test sigop counting is correct inside witnesses."""
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 90715cae2689b..f7290ff229163 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -93,11 +93,14 @@ def _test_getblockchaininfo(self):
'pruned',
'size_on_disk',
'softforks',
+ 'time',
'verificationprogress',
'warnings',
]
res = self.nodes[0].getblockchaininfo()
+ assert isinstance(res['time'], int)
+
# result should have these additional pruning keys if manual pruning is enabled
assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 3ff74dc5a476d..9d4a5525d14a2 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -515,6 +515,15 @@ def run_test(self):
assert_equal(testres['allowed'], True)
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000')
+ self.log.info('sendrawtransaction/testmempoolaccept with tx that is already in the chain')
+ self.nodes[2].generate(1)
+ self.sync_blocks()
+ for node in self.nodes:
+ testres = node.testmempoolaccept([rawTxSigned['hex']])[0]
+ assert_equal(testres['allowed'], False)
+ assert_equal(testres['reject-reason'], 'txn-already-known')
+ assert_raises_rpc_error(-27, 'Transaction already in block chain', node.sendrawtransaction, rawTxSigned['hex'])
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 725706947f147..32da2202db7dc 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -297,6 +297,7 @@
'wallet_startup.py',
'p2p_i2p_ports.py',
'feature_config_args.py',
+ 'feature_presegwit_node_upgrade.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
'rpc_addresses_deprecation.py',
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index 01e4ef47a7b1d..e92bb402b5888 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -112,7 +112,11 @@ def download_binary(tag, args) -> int:
tarballHash = hasher.hexdigest()
if tarballHash not in SHA256_SUMS or SHA256_SUMS[tarballHash] != tarball:
- print("Checksum did not match")
+ if tarball in SHA256_SUMS.values():
+ print("Checksum did not match")
+ return 1
+
+ print("Checksum for given version doesn't exist")
return 1
print("Checksum matched")