aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml14
-rw-r--r--build_msvc/README.md2
-rw-r--r--contrib/devtools/README.md10
-rw-r--r--contrib/devtools/headerssync-params.py357
-rw-r--r--depends/packages/qt.mk12
-rw-r--r--depends/patches/qt/dont_hardcode_x86_64.patch119
-rw-r--r--depends/patches/qt/fix-macos-linker.patch4
-rw-r--r--depends/patches/qt/fix_android_jni_static.patch2
-rw-r--r--depends/patches/qt/fix_montery_include.patch21
-rw-r--r--depends/patches/qt/memory_resource.patch2
-rw-r--r--depends/patches/qt/no-xlib.patch20
-rw-r--r--doc/dependencies.md2
-rw-r--r--doc/release-process.md8
-rw-r--r--src/bench/crypto_hash.cpp127
-rw-r--r--src/crypto/sha256.cpp45
-rw-r--r--src/crypto/sha256.h14
-rw-r--r--src/headerssync.cpp10
-rw-r--r--src/net.cpp10
-rw-r--r--src/net.h2
-rw-r--r--src/net_processing.cpp30
-rw-r--r--src/net_processing.h3
-rw-r--r--src/node/blockstorage.cpp5
-rw-r--r--src/policy/fees.cpp5
-rw-r--r--src/policy/fees.h4
-rw-r--r--src/qt/addressbookpage.cpp26
-rw-r--r--src/qt/addressbookpage.h1
-rw-r--r--src/qt/addresstablemodel.cpp2
-rw-r--r--src/qt/addresstablemodel.h2
-rw-r--r--src/qt/forms/debugwindow.ui148
-rw-r--r--src/qt/rpcconsole.cpp23
-rw-r--r--src/test/fuzz/fees.cpp3
-rw-r--r--src/test/policy_fee_tests.cpp3
-rw-r--r--src/test/util/setup_common.cpp1
-rwxr-xr-xtest/functional/feature_assumeutxo.py15
-rwxr-xr-xtest/functional/feature_init.py24
-rwxr-xr-xtest/functional/p2p_v2_transport.py28
-rwxr-xr-xtest/functional/test_framework/test_framework.py12
-rwxr-xr-xtest/functional/wallet_backwards_compatibility.py341
-rwxr-xr-xtest/functional/wallet_createwallet.py6
-rwxr-xr-xtest/functional/wallet_descriptor.py4
-rwxr-xr-xtest/functional/wallet_dump.py2
-rwxr-xr-xtest/functional/wallet_encryption.py6
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py6
-rwxr-xr-xtest/functional/wallet_keypool.py6
-rwxr-xr-xtest/get_previous_releases.py9
45 files changed, 1037 insertions, 459 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 28a027b780..a76a757129 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -105,8 +105,8 @@ jobs:
CCACHE_MAXSIZE: '200M'
CI_CCACHE_VERSION: '4.7.5'
CI_QT_CONF: '-release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml'
- CI_QT_DIR: 'qt-everywhere-src-5.15.5'
- CI_QT_URL: 'https://download.qt.io/official_releases/qt/5.15/5.15.5/single/qt-everywhere-opensource-src-5.15.5.zip'
+ CI_QT_DIR: 'qt-everywhere-src-5.15.10'
+ CI_QT_URL: 'https://download.qt.io/official_releases/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.zip'
PYTHONUTF8: 1
TEST_RUNNER_TIMEOUT_FACTOR: 40
@@ -286,6 +286,10 @@ jobs:
run: py -3 test\util\rpcauth-test.py
- name: Run functional tests
- env:
- TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }}
- run: py -3 test\functional\test_runner.py --jobs $env:NUMBER_OF_PROCESSORS --ci --quiet --tmpdirprefix=$env:RUNNER_TEMP --combinedlogslen=99999999 --timeout-factor=$env:TEST_RUNNER_TIMEOUT_FACTOR $env:TEST_RUNNER_EXTRA
+ # Don't run functional tests for pull requests.
+ # The test suit regularly fails to complete in windows native github
+ # actions as a child process stops making progress. The root cause has
+ # not yet been determined.
+ # Discussed in https://github.com/bitcoin/bitcoin/pull/28509
+ if: github.event_name != 'pull_request'
+ run: py -3 test\functional\test_runner.py --jobs $env:NUMBER_OF_PROCESSORS --ci --quiet --tmpdirprefix=$env:RUNNER_TEMP --combinedlogslen=99999999 --timeout-factor=$env:TEST_RUNNER_TIMEOUT_FACTOR --extended
diff --git a/build_msvc/README.md b/build_msvc/README.md
index ba6171fee7..8206620c3b 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -32,7 +32,7 @@ Qt
---------------------
To build Bitcoin Core with the GUI, a static build of Qt is required.
-1. Download a single ZIP archive of Qt source code from https://download.qt.io/official_releases/qt/ (e.g., [`qt-everywhere-opensource-src-5.15.5.zip`](https://download.qt.io/official_releases/qt/5.15/5.15.5/single/qt-everywhere-opensource-src-5.15.5.zip)), and expand it into a dedicated folder. The following instructions assume that this folder is `C:\dev\qt-source`.
+1. Download a single ZIP archive of Qt source code from https://download.qt.io/official_releases/qt/ (e.g., [`qt-everywhere-opensource-src-5.15.10.zip`](https://download.qt.io/official_releases/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.zip)), and expand it into a dedicated folder. The following instructions assume that this folder is `C:\dev\qt-source`.
2. Open "x64 Native Tools Command Prompt for VS 2022", and input the following commands:
```cmd
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index 54b1a85588..8bbf39b67f 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -90,6 +90,16 @@ example:
BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py
```
+headerssync-params.py
+=====================
+
+A script to generate optimal parameters for the headerssync module (src/headerssync.cpp). It takes no command-line
+options, as all its configuration is set at the top of the file. It runs many times faster inside PyPy. Invocation:
+
+```bash
+pypy3 contrib/devtools/headerssync-params.py
+```
+
gen-bitcoin-conf.sh
===================
diff --git a/contrib/devtools/headerssync-params.py b/contrib/devtools/headerssync-params.py
new file mode 100644
index 0000000000..f0088d6cb9
--- /dev/null
+++ b/contrib/devtools/headerssync-params.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""Script to find the optimal parameters for the headerssync module through simulation."""
+
+from math import log, exp, sqrt
+from datetime import datetime, timedelta
+import random
+
+# Parameters:
+
+# Aim for still working fine at some point in the future. [datetime]
+TIME = datetime(2026, 5, 25)
+
+# Expected block interval. [timedelta]
+BLOCK_INTERVAL = timedelta(seconds=600)
+
+# The number of headers corresponding to the minchainwork parameter. [headers]
+MINCHAINWORK_HEADERS = 784000
+
+# Combined processing bandwidth from all attackers to one victim. [bit/s]
+# 6 Gbit/s is approximately the speed at which a single thread of a Ryzen 5950X CPU thread can hash
+# headers. In practice, the victim's network bandwidth and network processing overheads probably
+# impose a far lower number, but it's a useful upper bound.
+ATTACK_BANDWIDTH = 6000000000
+
+# How much additional permanent memory usage are attackers (jointly) allowed to cause in the victim,
+# expressed as fraction of the normal memory usage due to mainchain growth, for the duration the
+# attack is sustained. [unitless]
+# 0.2 means that attackers, while they keep up the attack, can cause permanent memory usage due to
+# headers storage to grow at 1.2 header per BLOCK_INTERVAL.
+ATTACK_FRACTION = 0.2
+
+# When this is set, the mapping from period size to memory usage (at optimal buffer size for that
+# period) is assumed to be convex. This greatly speeds up the computation, and does not appear
+# to influence the outcome. Set to False for a stronger guarantee to get the optimal result.
+ASSUME_CONVEX = True
+
+# Explanation:
+#
+# The headerssync module implements a DoS protection against low-difficulty header spam which does
+# not rely on checkpoints. In short it works as follows:
+#
+# - (initial) header synchronization is split into two phases:
+# - A commitment phase, in which headers are downloaded from the peer, and a very compact
+# commitment to them is remembered in per-peer memory. The commitment phase ends when the
+# received chain's combined work reaches a predetermined threshold.
+# - A redownload phase, during which the headers are downloaded a second time from the same peer,
+# and compared against the commitment constructed in the first phase. If there is a match, the
+# redownloaded headers are fed to validation and accepted into permanent storage.
+#
+# This separation guarantees that no headers are accepted into permanent storage without
+# requiring the peer to first prove the chain actually has sufficient work.
+#
+# - To actually implement this commitment mechanism, the following approach is used:
+# - Keep a *1 bit* commitment (constructed using a salted hash function), for every block whose
+# height is a multiple of {period} plus an offset value. If RANDOMIZE_OFFSET, the offset,
+# like the salt, is chosen randomly when the synchronization starts and kept fixed afterwards.
+# - When redownloading, headers are fed through a per-peer queue that holds {bufsize} headers,
+# before passing them to validation. All the headers in this queue are verified against the
+# commitment bits created in the first phase before any header is released from it. This means
+# {bufsize/period} bits are checked "on top of" each header before actually processing it,
+# which results in a commitment structure with roughly {bufsize/period} bits of security, as
+# once a header is modified, due to the prevhash inclusion, all future headers necessarily
+# change as well.
+#
+# The question is what these {period} and {bufsize} parameters need to be set to. This program
+# exhaustively tests a range of values to find the optimal choice, taking into account:
+#
+# - Minimizing the (maximum of) two scenarios that trigger per-peer memory usage:
+#
+# - When downloading a (likely honest) chain that reaches the chainwork threshold after {n}
+# blocks, and then redownloads them, we will consume per-peer memory that is sufficient to
+# store {n/period} commitment bits and {bufsize} headers. We only consider attackers without
+# sufficient hashpower (as otherwise they are from a PoW perspective not attackers), which
+# means {n} is restricted to the honest chain's length before reaching minchainwork.
+#
+# - When downloading a (likely false) chain of {n} headers that never reaches the chainwork
+# threshold, we will consume per-peer memory that is sufficient to store {n/period}
+# commitment bits. Such a chain may be very long, by exploiting the timewarp bug to avoid
+# ramping up difficulty. There is however an absolute limit on how long such a chain can be: 6
+# blocks per second since genesis, due to the increasing MTP consensus rule.
+#
+# - Not gratuitously preventing synchronizing any valid chain, however difficult such a chain may
+# be to construct. In particular, the above scenario with an enormous timewarp-expoiting chain
+# cannot simply be ignored, as it is legal that the honest main chain is like that. We however
+# do not bother minimizing the memory usage in that case (because a billion-header long honest
+# chain will inevitably use far larger amounts of memory than designed for).
+#
+# - Keep the rate at which attackers can get low-difficulty headers accepted to the block index
+# negligible. Specifically, the possibility exists for an attacker to send the honest main
+# chain's headers during the commitment phase, but then start deviating at an attacker-chosen
+# point by sending novel low-difficulty headers instead. Depending on how high we set the
+# {bufsize/period} ratio, we can make the probability that such a header makes it in
+# arbitrarily small, but at the cost of higher memory during the redownload phase. It turns out,
+# some rate of memory usage growth is expected anyway due to chain growth, so permitting the
+# attacker to increase that rate by a small factor isn't concerning. The attacker may start
+# somewhat later than genesis, as long as the difficulty doesn't get too high. This reduces
+# the attacker bandwidth required at the cost of higher PoW needed for constructing the
+# alternate chain. This trade-off is ignored here, as it results in at most a small constant
+# factor in attack rate.
+
+
+# System properties:
+
+# Headers in the redownload buffer are stored without prevhash. [bits]
+COMPACT_HEADER_SIZE = 48 * 8
+
+# How many bits a header uses in P2P protocol. [bits]
+NET_HEADER_SIZE = 81 * 8
+
+# How many headers are sent at once. [headers]
+HEADER_BATCH_COUNT = 2000
+
+# Whether or not the offset of which blocks heights get checksummed is randomized.
+RANDOMIZE_OFFSET = True
+
+# Timestamp of the genesis block
+GENESIS_TIME = datetime(2009, 1, 3)
+
+# Derived values:
+
+# What rate of headers worth of RAM attackers are allowed to cause in the victim. [headers/s]
+LIMIT_HEADERRATE = ATTACK_FRACTION / BLOCK_INTERVAL.total_seconds()
+
+# How many headers can attackers (jointly) send a victim per second. [headers/s]
+NET_HEADERRATE = ATTACK_BANDWIDTH / NET_HEADER_SIZE
+
+# What fraction of headers sent by attackers can at most be accepted by a victim [unitless]
+LIMIT_FRACTION = LIMIT_HEADERRATE / NET_HEADERRATE
+
+# How many headers we permit attackers to cause being accepted per attack. [headers/attack]
+ATTACK_HEADERS = LIMIT_FRACTION * MINCHAINWORK_HEADERS
+
+
+def find_max_headers(when):
+ """Compute the maximum number of headers a valid Bitcoin chain can have at given time."""
+ # When exploiting the timewarp attack, this can be up to 6 per second since genesis.
+ return 6 * ((when - GENESIS_TIME) // timedelta(seconds=1))
+
+
+def lambert_w(value):
+ """Solve the equation x*exp(x)=value (x > 0, value > 0)."""
+ # Initial approximation.
+ approx = max(log(value), 0.0)
+ for _ in range(10):
+ # Newton-Rhapson iteration steps.
+ approx += (value * exp(-approx) - approx) / (approx + 1.0)
+ return approx
+
+
+def attack_rate(period, bufsize, limit=None):
+ """Compute maximal accepted headers per attack in (period, bufsize) configuration.
+
+ If limit is provided, the computation is stopped early when the result is known to exceed the
+ value in limit.
+ """
+
+ max_rate = None
+ max_honest = None
+ # Let the current batch 0 being received be the first one in which the attacker starts lying.
+ # They will only ever start doing so right after a commitment block, but where that is can be
+ # in a number of places. Let honest be the number of honest headers in this current batch,
+ # preceding the forged ones.
+ for honest in range(HEADER_BATCH_COUNT):
+ # The number of headers the attack under consideration will on average get accepted.
+ # This is the number being computed.
+ rate = 0
+
+ # Iterate over the possible alignments of commitments w.r.t. the first batch. In case
+ # the alignments are randomized, try all values. If not, the attacker can know/choose
+ # the alignment, and will always start forging right after a commitment.
+ if RANDOMIZE_OFFSET:
+ align_choices = list(range(period))
+ else:
+ align_choices = [(honest - 1) % period]
+ # Now loop over those possible alignment values, computing the average attack rate
+ # over them by dividing each contribution by len(align_choices).
+ for align in align_choices:
+ # These state variables capture the situation after receiving the first batch.
+ # - The number of headers received after the last commitment for an honest block:
+ after_good_commit = HEADER_BATCH_COUNT - honest + ((honest - align - 1) % period)
+ # - The number of forged headers in the redownload buffer:
+ forged_in_buf = HEADER_BATCH_COUNT - honest
+
+ # Now iterate over the next batches of headers received, adding contributions to the
+ # rate variable.
+ while True:
+ # Process the first HEADER_BATCH_COUNT headers in the buffer:
+ accept_forged_headers = max(forged_in_buf - bufsize, 0)
+ forged_in_buf -= accept_forged_headers
+ if accept_forged_headers:
+ # The probability the attack has not been detected yet at this point:
+ prob = 0.5 ** (after_good_commit // period)
+ # Update attack rate, divided by align_choices to average over the alignments.
+ rate += accept_forged_headers * prob / len(align_choices)
+ # If this means we exceed limit, bail out early (performance optimization).
+ if limit is not None and rate >= limit:
+ return rate, None
+ # If the maximal term being added is negligible compared to rate, stop
+ # iterating.
+ if HEADER_BATCH_COUNT * prob < 1.0e-16 * rate * len(align_choices):
+ break
+ # Update state from a new incoming batch (which is all forged)
+ after_good_commit += HEADER_BATCH_COUNT
+ forged_in_buf += HEADER_BATCH_COUNT
+
+ if max_rate is None or rate > max_rate:
+ max_rate = rate
+ max_honest = honest
+
+ return max_rate, max_honest
+
+
+def memory_usage(period, bufsize, when):
+ """How much memory (max,mainchain,timewarp) does the (period,bufsize) configuration need?"""
+
+ # Per-peer memory usage for a timewarp chain that never meets minchainwork
+ mem_timewarp = find_max_headers(when) // period
+ # Per-peer memory usage for being fed the main chain
+ mem_mainchain = (MINCHAINWORK_HEADERS // period) + bufsize * COMPACT_HEADER_SIZE
+ # Maximum per-peer memory usage
+ max_mem = max(mem_timewarp, mem_mainchain)
+
+ return max_mem, mem_mainchain, mem_timewarp
+
+def find_bufsize(period, attack_headers, when, max_mem=None, min_bufsize=1):
+ """Determine how big bufsize needs to be given a specific period length.
+
+ Given a period, find the smallest value of bufsize such that the attack rate against the
+ (period, bufsize) configuration is below attack_headers. If max_mem is provided, and no
+ such bufsize exists that needs less than max_mem bits of memory, None is returned.
+ min_bufsize is the minimal result to be considered."""
+
+ if max_mem is None:
+ succ_buf = min_bufsize - 1
+ fail_buf = min_bufsize
+ # First double iteratively until an upper bound for failure is found.
+ while True:
+ if attack_rate(period, fail_buf, attack_headers)[0] < attack_headers:
+ break
+ succ_buf, fail_buf = fail_buf, 3 * fail_buf - 2 * succ_buf
+ else:
+ # If a long low-work header chain exists that exceeds max_mem already, give up.
+ if find_max_headers(when) // period > max_mem:
+ return None
+ # Otherwise, verify that the maximal buffer size that permits a mainchain sync with less
+ # than max_mem memory is sufficient to get the attack rate below attack_headers. If not,
+ # also give up.
+ max_buf = (max_mem - (MINCHAINWORK_HEADERS // period)) // COMPACT_HEADER_SIZE
+ if max_buf < min_bufsize:
+ return None
+ if attack_rate(period, max_buf, attack_headers)[0] >= attack_headers:
+ return None
+ # If it is sufficient, that's an upper bound to start our search.
+ succ_buf = min_bufsize - 1
+ fail_buf = max_buf
+
+ # Then perform a bisection search to narrow it down.
+ while fail_buf > succ_buf + 1:
+ try_buf = (succ_buf + fail_buf) // 2
+ if attack_rate(period, try_buf, attack_headers)[0] >= attack_headers:
+ succ_buf = try_buf
+ else:
+ fail_buf = try_buf
+ return fail_buf
+
+
+def optimize(when):
+ """Find the best (period, bufsize) configuration."""
+
+ # When period*bufsize = memory_scale, the per-peer memory for a mainchain sync and a maximally
+ # long low-difficulty header sync are equal.
+ memory_scale = (find_max_headers(when) - MINCHAINWORK_HEADERS) / COMPACT_HEADER_SIZE
+ # Compute approximation for {bufsize/period}, using a formula for a simplified problem.
+ approx_ratio = lambert_w(log(4) * memory_scale / ATTACK_HEADERS**2) / log(4)
+ # Use those for a first attempt.
+ print("Searching configurations:")
+ period = int(sqrt(memory_scale / approx_ratio) + 0.5)
+ bufsize = find_bufsize(period, ATTACK_HEADERS, when)
+ mem = memory_usage(period, bufsize, when)
+ best = (period, bufsize, mem)
+ maps = [(period, bufsize), (MINCHAINWORK_HEADERS + 1, None)]
+ print(f"- Initial: period={period}, buffer={bufsize}, mem={mem[0] / 8192:.3f} KiB")
+
+ # Consider all period values between 1 and MINCHAINWORK_HEADERS, except the one just tried.
+ periods = [iv for iv in range(1, MINCHAINWORK_HEADERS + 1) if iv != period]
+ # Iterate, picking a random element from periods, computing its corresponding bufsize, and
+ # then using the result to shrink the period.
+ while True:
+ # Remove all periods whose memory usage for low-work long chain sync exceed the best
+ # memory usage we've found so far.
+ periods = [p for p in periods if find_max_headers(when) // p < best[2][0]]
+ # Stop if there is nothing left to try.
+ if len(periods) == 0:
+ break
+ # Pick a random remaining option for period size, and compute corresponding bufsize.
+ period = periods.pop(random.randrange(len(periods)))
+ # The buffer size (at a given attack level) cannot shrink as the period grows. Find the
+ # largest period smaller than the selected one we know the buffer size for, and use that
+ # as a lower bound to find_bufsize.
+ min_bufsize = max([(p, b) for p, b in maps if p < period] + [(0,0)])[1]
+ bufsize = find_bufsize(period, ATTACK_HEADERS, when, best[2][0], min_bufsize)
+ if bufsize is not None:
+ # We found a (period, bufsize) configuration with better memory usage than our best
+ # so far. Remember it for future lower bounds.
+ maps.append((period, bufsize))
+ mem = memory_usage(period, bufsize, when)
+ assert mem[0] <= best[2][0]
+ if ASSUME_CONVEX:
+ # Remove all periods that are on the other side of the former best as the new
+ # best.
+ periods = [p for p in periods if (p < best[0]) == (period < best[0])]
+ best = (period, bufsize, mem)
+ print(f"- New best: period={period}, buffer={bufsize}, mem={mem[0] / 8192:.3f} KiB")
+ else:
+ # The (period, bufsize) configuration we found is worse than what we already had.
+ if ASSUME_CONVEX:
+ # Remove all periods that are on the other side of the tried configuration as the
+ # best one.
+ periods = [p for p in periods if (p < period) == (best[0] < period)]
+
+ # Return the result.
+ period, bufsize, _ = best
+ return period, bufsize
+
+
+def analyze(when):
+ """Find the best configuration and print it out."""
+
+ period, bufsize = optimize(when)
+ # Compute accurate statistics for the best found configuration.
+ _, mem_mainchain, mem_timewarp = memory_usage(period, bufsize, when)
+ headers_per_attack, _ = attack_rate(period, bufsize)
+ attack_volume = NET_HEADER_SIZE * MINCHAINWORK_HEADERS
+ # And report them.
+ print()
+ print("Optimal configuration:")
+ print()
+ print("//! Store one header commitment per HEADER_COMMITMENT_PERIOD blocks.")
+ print(f"constexpr size_t HEADER_COMMITMENT_PERIOD{{{period}}};")
+ print()
+ print("//! Only feed headers to validation once this many headers on top have been")
+ print("//! received and validated against commitments.")
+ print(f"constexpr size_t REDOWNLOAD_BUFFER_SIZE{{{bufsize}}};"
+ f" // {bufsize}/{period} = ~{bufsize/period:.1f} commitments")
+ print()
+ print("Properties:")
+ print(f"- Per-peer memory for mainchain sync: {mem_mainchain / 8192:.3f} KiB")
+ print(f"- Per-peer memory for timewarp attack: {mem_timewarp / 8192:.3f} KiB")
+ print(f"- Attack rate: {1/headers_per_attack:.1f} attacks for 1 header of memory growth")
+ print(f" (where each attack costs {attack_volume / 8388608:.3f} MiB bandwidth)")
+
+
+analyze(TIME)
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 7b4ee64776..86df58f9d2 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -1,9 +1,9 @@
package=qt
-$(package)_version=5.15.5
+$(package)_version=5.15.10
$(package)_download_path=https://download.qt.io/official_releases/qt/5.15/$($(package)_version)/submodules
$(package)_suffix=everywhere-opensource-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
-$(package)_sha256_hash=0c42c799aa7c89e479a07c451bf5a301e291266ba789e81afc18f95049524edc
+$(package)_sha256_hash=c0d06cb18d20f10bf7ad53552099e097ec39362d30a5d6f104724f55fa1c8fb9
$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon libxcb_util libxcb_util_render libxcb_util_keysyms libxcb_util_image libxcb_util_wm
$(package)_qt_libs=corelib network widgets gui plugins testlib
$(package)_linguist_tools = lrelease lupdate lconvert
@@ -12,8 +12,6 @@ $(package)_patches += qttools_src.pro
$(package)_patches += mac-qmake.conf
$(package)_patches += fix_qt_pkgconfig.patch
$(package)_patches += no-xlib.patch
-$(package)_patches += dont_hardcode_x86_64.patch
-$(package)_patches += fix_montery_include.patch
$(package)_patches += fix_android_jni_static.patch
$(package)_patches += dont_hardcode_pwd.patch
$(package)_patches += qtbase-moc-ignore-gcc-macro.patch
@@ -26,10 +24,10 @@ $(package)_patches += fix-macos-linker.patch
$(package)_patches += memory_resource.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
-$(package)_qttranslations_sha256_hash=c92af4171397a0ed272330b4fa0669790fcac8d050b07c8b8cc565ebeba6735e
+$(package)_qttranslations_sha256_hash=38b942bc7e62794dd072945c8a92bb9dfffed24070aea300327a3bb42f855609
$(package)_qttools_file_name=qttools-$($(package)_suffix)
-$(package)_qttools_sha256_hash=6d0778b71b2742cb527561791d1d3d255366163d54a10f78c683a398f09ffc6c
+$(package)_qttools_sha256_hash=66f46c9729c831dce431778a9c561cca32daceaede1c7e58568d7a5898167dae
$(package)_extra_sources = $($(package)_qttranslations_file_name)
$(package)_extra_sources += $($(package)_qttools_file_name)
@@ -245,9 +243,7 @@ define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \
patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \
- patch -p1 -i $($(package)_patch_dir)/dont_hardcode_x86_64.patch && \
patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_montery_include.patch && \
patch -p1 -i $($(package)_patch_dir)/use_android_ndk23.patch && \
patch -p1 -i $($(package)_patch_dir)/memory_resource.patch && \
patch -p1 -i $($(package)_patch_dir)/rcc_hardcode_timestamp.patch && \
diff --git a/depends/patches/qt/dont_hardcode_x86_64.patch b/depends/patches/qt/dont_hardcode_x86_64.patch
deleted file mode 100644
index a66426877a..0000000000
--- a/depends/patches/qt/dont_hardcode_x86_64.patch
+++ /dev/null
@@ -1,119 +0,0 @@
-macOS: Don't hard-code x86_64 as the architecture when using qmake
-
-Upstream commit:
- - Qt 6.1: 9082cc8e8d5a6441dabe5e7a95bc0cd9085b95fe
-
-For other Qt branches see
-https://codereview.qt-project.org/q/I70db7e4c27f0d3da5d0af33cb491d72c312d3fa8
-
-
---- old/qtbase/configure.json
-+++ new/qtbase/configure.json
-@@ -244,11 +244,18 @@
-
- "testTypeDependencies": {
- "linkerSupportsFlag": [ "use_bfd_linker", "use_gold_linker", "use_lld_linker" ],
-- "verifySpec": [ "shared", "use_bfd_linker", "use_gold_linker", "use_lld_linker", "compiler-flags", "qmakeargs", "commit" ],
-+ "verifySpec": [
-+ "shared",
-+ "use_bfd_linker", "use_gold_linker", "use_lld_linker",
-+ "compiler-flags", "qmakeargs",
-+ "simulator_and_device",
-+ "thread",
-+ "commit" ],
- "compile": [ "verifyspec" ],
- "detectPkgConfig": [ "cross_compile", "machineTuple" ],
- "library": [ "pkg-config", "compiler-flags" ],
-- "getPkgConfigVariable": [ "pkg-config" ]
-+ "getPkgConfigVariable": [ "pkg-config" ],
-+ "architecture" : [ "verifyspec" ]
- },
-
- "testTypeAliases": {
-@@ -762,7 +769,7 @@
- },
- "architecture": {
- "label": "Architecture",
-- "output": [ "architecture" ]
-+ "output": [ "architecture", "commitConfig" ]
- },
- "pkg-config": {
- "label": "Using pkg-config",
-diff --git a/configure.pri b/configure.pri
-index 49755f7abfd..8be9b10d7d4 100644
---- old/qtbase/configure.pri
-+++ new/qtbase/configure.pri
-@@ -662,6 +662,13 @@ defineTest(qtConfOutput_commitOptions) {
- write_file($$QT_BUILD_TREE/mkspecs/qdevice.pri, $${currentConfig}.output.devicePro)|error()
- }
-
-+# Output is written after configuring each Qt module,
-+# but some tests within a module might depend on the
-+# configuration output of previous tests.
-+defineTest(qtConfOutput_commitConfig) {
-+ qtConfProcessOutput()
-+}
-+
- # type (empty or 'host'), option name, default value
- defineTest(processQtPath) {
- out_var = config.rel_input.$${2}
-diff --git a/mkspecs/common/macx.conf b/mkspecs/common/macx.conf
-index d16b77acb8e..4ba0a8eaa36 100644
---- old/qtbase/mkspecs/common/macx.conf
-+++ new/qtbase/mkspecs/common/macx.conf
-@@ -6,7 +6,6 @@ QMAKE_PLATFORM += macos osx macx
- QMAKE_MAC_SDK = macosx
-
- QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13
--QMAKE_APPLE_DEVICE_ARCHS = x86_64
-
- # Should be 10.15, but as long as the CI builds with
- # older SDKs we have to keep this.
-diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf
-index 92a9112bca6..d888731ec8d 100644
---- old/qtbase/mkspecs/features/mac/default_post.prf
-+++ new/qtbase/mkspecs/features/mac/default_post.prf
-@@ -95,6 +95,11 @@ app_extension_api_only {
- QMAKE_LFLAGS += $$QMAKE_CFLAGS_APPLICATION_EXTENSION
- }
-
-+# Non-universal builds do not set QMAKE_APPLE_DEVICE_ARCHS,
-+# so we pick it up from what the arch test resolved instead.
-+isEmpty(QMAKE_APPLE_DEVICE_ARCHS): \
-+ QMAKE_APPLE_DEVICE_ARCHS = $$QT_ARCH
-+
- macx-xcode {
- qmake_pkginfo_typeinfo.name = QMAKE_PKGINFO_TYPEINFO
- !isEmpty(QMAKE_PKGINFO_TYPEINFO): \
-@@ -150,9 +155,6 @@ macx-xcode {
- simulator: VALID_SIMULATOR_ARCHS = $$QMAKE_APPLE_SIMULATOR_ARCHS
- VALID_ARCHS = $$VALID_DEVICE_ARCHS $$VALID_SIMULATOR_ARCHS
-
-- isEmpty(VALID_ARCHS): \
-- error("QMAKE_APPLE_DEVICE_ARCHS or QMAKE_APPLE_SIMULATOR_ARCHS must contain at least one architecture")
--
- single_arch: VALID_ARCHS = $$first(VALID_ARCHS)
-
- ACTIVE_ARCHS = $(filter $(EXPORT_VALID_ARCHS), $(ARCHS))
-diff --git a/mkspecs/features/toolchain.prf b/mkspecs/features/toolchain.prf
-index efbe7c1e55b..8add6dc8043 100644
---- old/qtbase/mkspecs/features/toolchain.prf
-+++ new/qtbase/mkspecs/features/toolchain.prf
-@@ -182,9 +182,14 @@ isEmpty($${target_prefix}.INCDIRS) {
- # UIKit simulator platforms will see the device SDK's sysroot in
- # QMAKE_DEFAULT_*DIRS, because they're handled in a single build pass.
- darwin {
-- # Clang doesn't pick up the architecture from the sysroot, and will
-- # default to the host architecture, so we need to manually set it.
-- cxx_flags += -arch $$QMAKE_APPLE_DEVICE_ARCHS
-+ uikit {
-+ # Clang doesn't automatically pick up the architecture, just because
-+ # we're passing the iOS sysroot below, and we will end up building the
-+ # test for the host architecture, resulting in linker errors when
-+ # linking against the iOS libraries. We work around this by passing
-+ # the architecture explicitly.
-+ cxx_flags += -arch $$first(QMAKE_APPLE_DEVICE_ARCHS)
-+ }
-
- uikit:macx-xcode: \
- cxx_flags += -isysroot $$sdk_path_device.value
diff --git a/depends/patches/qt/fix-macos-linker.patch b/depends/patches/qt/fix-macos-linker.patch
index db056de4d9..e439685656 100644
--- a/depends/patches/qt/fix-macos-linker.patch
+++ b/depends/patches/qt/fix-macos-linker.patch
@@ -29,7 +29,7 @@ https://codereview.qt-project.org/q/I2347b26e2df0828471373b0e15b8c9089274c65d
--- old/qtbase/mkspecs/features/toolchain.prf
+++ new/qtbase/mkspecs/features/toolchain.prf
-@@ -283,9 +283,12 @@ isEmpty($${target_prefix}.INCDIRS) {
+@@ -288,9 +288,12 @@ isEmpty($${target_prefix}.INCDIRS) {
}
}
}
@@ -44,7 +44,7 @@ https://codereview.qt-project.org/q/I2347b26e2df0828471373b0e15b8c9089274c65d
QMAKE_DEFAULT_LIBDIRS = $$unique(QMAKE_DEFAULT_LIBDIRS)
} else: ghs {
cmd = $$QMAKE_CXX $$QMAKE_CXXFLAGS -$${LITERAL_HASH} -o /tmp/fake_output /tmp/fake_input.cpp
-@@ -407,7 +410,7 @@ isEmpty($${target_prefix}.INCDIRS) {
+@@ -412,7 +415,7 @@ isEmpty($${target_prefix}.INCDIRS) {
QMAKE_DEFAULT_INCDIRS = $$split(INCLUDE, $$QMAKE_DIRLIST_SEP)
}
diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch
index 936b82e152..7dbd68fe9c 100644
--- a/depends/patches/qt/fix_android_jni_static.patch
+++ b/depends/patches/qt/fix_android_jni_static.patch
@@ -1,6 +1,6 @@
--- old/qtbase/src/plugins/platforms/android/androidjnimain.cpp
+++ new/qtbase/src/plugins/platforms/android/androidjnimain.cpp
-@@ -943,6 +943,14 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
+@@ -980,6 +980,14 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}
diff --git a/depends/patches/qt/fix_montery_include.patch b/depends/patches/qt/fix_montery_include.patch
deleted file mode 100644
index 38b700addf..0000000000
--- a/depends/patches/qt/fix_montery_include.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From dece6f5840463ae2ddf927d65eb1b3680e34a547
-From: Øystein Heskestad <oystein.heskestad@qt.io>
-Date: Wed, 27 Oct 2021 13:07:46 +0200
-Subject: [PATCH] Add missing macOS header file that was indirectly included before
-
-See: https://bugreports.qt.io/browse/QTBUG-97855
-
-Upstream Commits:
- - Qt 6.2: c884bf138a21dd7320e35cef34d24e22e74d7ce0
-
-diff --git a/qtbase/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/qtbase/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
-index e070ba97..07c75b04 100644
---- a/qtbase/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
-+++ b/qtbase/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
-@@ -40,6 +40,7 @@
- #ifndef QIOSURFACEGRAPHICSBUFFER_H
- #define QIOSURFACEGRAPHICSBUFFER_H
-
-+#include <CoreGraphics/CGColorSpace.h>
- #include <qpa/qplatformgraphicsbuffer.h>
- #include <private/qcore_mac_p.h>
diff --git a/depends/patches/qt/memory_resource.patch b/depends/patches/qt/memory_resource.patch
index e41d68db30..650c328528 100644
--- a/depends/patches/qt/memory_resource.patch
+++ b/depends/patches/qt/memory_resource.patch
@@ -17,7 +17,7 @@ and https://bugreports.qt.io/browse/QTBUG-114316
--- a/qtbase/src/corelib/global/qcompilerdetection.h
+++ b/qtbase/src/corelib/global/qcompilerdetection.h
-@@ -1041,16 +1041,22 @@
+@@ -1050,16 +1050,22 @@
# endif // !_HAS_CONSTEXPR
# endif // !__GLIBCXX__ && !_LIBCPP_VERSION
# endif // Q_OS_QNX
diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch
index d6846aaca2..0f7965d2ea 100644
--- a/depends/patches/qt/no-xlib.patch
+++ b/depends/patches/qt/no-xlib.patch
@@ -4,12 +4,7 @@ Date: Thu, 18 Jul 2019 17:22:05 -0400
Subject: [PATCH] Wrap xlib related code blocks in #if's
They are not necessary to compile QT.
----
- qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp | 8 ++++++++
- 1 file changed, 8 insertions(+)
-diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
-index 7c62c2e2b3..c05c6c0a07 100644
--- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -49,7 +49,9 @@
@@ -22,7 +17,7 @@ index 7c62c2e2b3..c05c6c0a07 100644
#include <xcb/xfixes.h>
#include <xcb/xcb_image.h>
-@@ -391,6 +391,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
+@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
xcb_flush(xcb_connection());
}
@@ -30,7 +25,7 @@ index 7c62c2e2b3..c05c6c0a07 100644
static int cursorIdForShape(int cshape)
{
int cursorId = 0;
-@@ -444,6 +445,7 @@ static int cursorIdForShape(int cshape)
+@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape)
}
return cursorId;
}
@@ -38,7 +33,7 @@ index 7c62c2e2b3..c05c6c0a07 100644
xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
{
-@@ -556,7 +558,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
+@@ -558,7 +562,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
{
xcb_connection_t *conn = xcb_connection();
@@ -47,8 +42,8 @@ index 7c62c2e2b3..c05c6c0a07 100644
+#endif
xcb_cursor_t cursor = XCB_NONE;
- // Try Xcursor first
-@@ -586,6 +590,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+ #if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
+@@ -590,6 +596,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
// Non-standard X11 cursors are created from bitmaps
cursor = createNonStandardCursor(cshape);
@@ -56,7 +51,7 @@ index 7c62c2e2b3..c05c6c0a07 100644
// Create a glpyh cursor if everything else failed
if (!cursor && cursorId) {
cursor = xcb_generate_id(conn);
-@@ -593,6 +598,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+@@ -597,6 +604,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
cursorId, cursorId + 1,
0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0);
}
@@ -64,6 +59,3 @@ index 7c62c2e2b3..c05c6c0a07 100644
if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
const char *name = cursorNames[cshape].front();
---
-2.22.0
-
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 804f796abe..cb79b30cdf 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -30,7 +30,7 @@ You can find installation instructions in the `build-*.md` file for your platfor
| [Fontconfig](../depends/packages/fontconfig.mk) | [link](https://www.freedesktop.org/wiki/Software/fontconfig/) | [2.12.6](https://github.com/bitcoin/bitcoin/pull/23495) | 2.6 | Yes |
| [FreeType](../depends/packages/freetype.mk) | [link](https://freetype.org) | [2.11.0](https://github.com/bitcoin/bitcoin/commit/01544dd78ccc0b0474571da854e27adef97137fb) | 2.3.0 | Yes |
| [qrencode](../depends/packages/qrencode.mk) | [link](https://fukuchi.org/works/qrencode/) | [4.1.1](https://github.com/bitcoin/bitcoin/pull/27312) | | No |
-| [Qt](../depends/packages/qt.mk) | [link](https://download.qt.io/official_releases/qt/) | [5.15.5](https://github.com/bitcoin/bitcoin/pull/25719) | [5.11.3](https://github.com/bitcoin/bitcoin/pull/24132) | No |
+| [Qt](../depends/packages/qt.mk) | [link](https://download.qt.io/official_releases/qt/) | [5.15.10](https://github.com/bitcoin/bitcoin/pull/28561) | [5.11.3](https://github.com/bitcoin/bitcoin/pull/24132) | No |
### Networking
| Dependency | Releases | Version used | Minimum required | Runtime |
diff --git a/doc/release-process.md b/doc/release-process.md
index bdef57243b..468efeb7e1 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -43,6 +43,14 @@ Release Process
- On mainnet, the selected value must not be orphaned, so it may be useful to set the height two blocks back from the tip.
- Testnet should be set with a height some tens of thousands back from the tip, due to reorgs there.
- `nMinimumChainWork` with the "chainwork" value of RPC `getblockheader` using the same height as that selected for the previous step.
+* Consider updating the headers synchronization tuning parameters to account for the chainparams updates.
+ The optimal values change very slowly, so this isn't strictly necessary every release, but doing so doesn't hurt.
+ - Update configuration variables in [`contrib/devtools/headerssync-params.py`](contrib/devtools/headerssync-params.py):
+ - Set `TIME` to the software's expected supported lifetime -- after this time, its ability to defend against a high bandwidth timewarp attacker will begin to degrade.
+ - Set `MINCHAINWORK_HEADERS` to the height used for the `nMinimumChainWork` calculation above.
+ - Check that the other variables still look reasonable.
+ - Run the script. It works fine in CPython, but PyPy is much faster (seconds instead of minutes): `pypy3 contrib/devtools/headerssync-params.py`.
+ - Paste the output defining `HEADER_COMMITMENT_PERIOD` and `REDOWNLOAD_BUFFER_SIZE` into the top of [`src/headerssync.cpp`](/src/headerssync.cpp).
- Clear the release notes and move them to the wiki (see "Write the release notes" below).
- Translations on Transifex:
- Pull translations from Transifex into the master branch.
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index cf8d807d7b..1685a120b4 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -13,6 +13,7 @@
#include <crypto/siphash.h>
#include <hash.h>
#include <random.h>
+#include <tinyformat.h>
#include <uint256.h>
/* Number of bytes to hash per iteration */
@@ -36,13 +37,48 @@ static void SHA1(benchmark::Bench& bench)
});
}
-static void SHA256(benchmark::Bench& bench)
+static void SHA256_STANDARD(benchmark::Bench& bench)
{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::STANDARD)));
uint8_t hash[CSHA256::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
bench.batch(in.size()).unit("byte").run([&] {
CSHA256().Write(in.data(), in.size()).Finalize(hash);
});
+ SHA256AutoDetect();
+}
+
+static void SHA256_SSE4(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4)));
+ uint8_t hash[CSHA256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(BUFFER_SIZE,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256().Write(in.data(), in.size()).Finalize(hash);
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256_AVX2(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_AVX2)));
+ uint8_t hash[CSHA256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(BUFFER_SIZE,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256().Write(in.data(), in.size()).Finalize(hash);
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256_SHANI(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_SHANI)));
+ uint8_t hash[CSHA256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(BUFFER_SIZE,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256().Write(in.data(), in.size()).Finalize(hash);
+ });
+ SHA256AutoDetect();
}
static void SHA3_256_1M(benchmark::Bench& bench)
@@ -54,22 +90,92 @@ static void SHA3_256_1M(benchmark::Bench& bench)
});
}
-static void SHA256_32b(benchmark::Bench& bench)
+static void SHA256_32b_STANDARD(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::STANDARD)));
+ std::vector<uint8_t> in(32,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256()
+ .Write(in.data(), in.size())
+ .Finalize(in.data());
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256_32b_SSE4(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4)));
+ std::vector<uint8_t> in(32,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256()
+ .Write(in.data(), in.size())
+ .Finalize(in.data());
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256_32b_AVX2(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_AVX2)));
+ std::vector<uint8_t> in(32,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ CSHA256()
+ .Write(in.data(), in.size())
+ .Finalize(in.data());
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256_32b_SHANI(benchmark::Bench& bench)
{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_SHANI)));
std::vector<uint8_t> in(32,0);
bench.batch(in.size()).unit("byte").run([&] {
CSHA256()
.Write(in.data(), in.size())
.Finalize(in.data());
});
+ SHA256AutoDetect();
+}
+
+static void SHA256D64_1024_STANDARD(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::STANDARD)));
+ std::vector<uint8_t> in(64 * 1024, 0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ SHA256D64(in.data(), in.data(), 1024);
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256D64_1024_SSE4(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4)));
+ std::vector<uint8_t> in(64 * 1024, 0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ SHA256D64(in.data(), in.data(), 1024);
+ });
+ SHA256AutoDetect();
+}
+
+static void SHA256D64_1024_AVX2(benchmark::Bench& bench)
+{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_AVX2)));
+ std::vector<uint8_t> in(64 * 1024, 0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ SHA256D64(in.data(), in.data(), 1024);
+ });
+ SHA256AutoDetect();
}
-static void SHA256D64_1024(benchmark::Bench& bench)
+static void SHA256D64_1024_SHANI(benchmark::Bench& bench)
{
+ bench.name(strprintf("%s using the '%s' SHA256 implementation", __func__, SHA256AutoDetect(sha256_implementation::USE_SSE4_AND_SHANI)));
std::vector<uint8_t> in(64 * 1024, 0);
bench.batch(in.size()).unit("byte").run([&] {
SHA256D64(in.data(), in.data(), 1024);
});
+ SHA256AutoDetect();
}
static void SHA512(benchmark::Bench& bench)
@@ -152,13 +258,22 @@ static void MuHashPrecompute(benchmark::Bench& bench)
BENCHMARK(BenchRIPEMD160, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA1, benchmark::PriorityLevel::HIGH);
-BENCHMARK(SHA256, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_STANDARD, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_SSE4, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_AVX2, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_SHANI, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA512, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA3_256_1M, benchmark::PriorityLevel::HIGH);
-BENCHMARK(SHA256_32b, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_32b_STANDARD, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_32b_SSE4, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_32b_AVX2, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256_32b_SHANI, benchmark::PriorityLevel::HIGH);
BENCHMARK(SipHash_32b, benchmark::PriorityLevel::HIGH);
-BENCHMARK(SHA256D64_1024, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256D64_1024_STANDARD, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256D64_1024_SSE4, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256D64_1024_AVX2, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256D64_1024_SHANI, benchmark::PriorityLevel::HIGH);
BENCHMARK(FastRandom_32bit, benchmark::PriorityLevel::HIGH);
BENCHMARK(FastRandom_1bit, benchmark::PriorityLevel::HIGH);
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index a4eef36480..5eacaa44e1 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -579,9 +579,15 @@ bool AVXEnabled()
} // namespace
-std::string SHA256AutoDetect()
+std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implementation)
{
std::string ret = "standard";
+ Transform = sha256::Transform;
+ TransformD64 = sha256::TransformD64;
+ TransformD64_2way = nullptr;
+ TransformD64_4way = nullptr;
+ TransformD64_8way = nullptr;
+
#if defined(USE_ASM) && defined(HAVE_GETCPUID)
bool have_sse4 = false;
bool have_xsave = false;
@@ -592,7 +598,9 @@ std::string SHA256AutoDetect()
uint32_t eax, ebx, ecx, edx;
GetCPUID(1, 0, eax, ebx, ecx, edx);
- have_sse4 = (ecx >> 19) & 1;
+ if (use_implementation & sha256_implementation::USE_SSE4) {
+ have_sse4 = (ecx >> 19) & 1;
+ }
have_xsave = (ecx >> 27) & 1;
have_avx = (ecx >> 28) & 1;
if (have_xsave && have_avx) {
@@ -600,8 +608,12 @@ std::string SHA256AutoDetect()
}
if (have_sse4) {
GetCPUID(7, 0, eax, ebx, ecx, edx);
- have_avx2 = (ebx >> 5) & 1;
- have_x86_shani = (ebx >> 29) & 1;
+ if (use_implementation & sha256_implementation::USE_AVX2) {
+ have_avx2 = (ebx >> 5) & 1;
+ }
+ if (use_implementation & sha256_implementation::USE_SHANI) {
+ have_x86_shani = (ebx >> 29) & 1;
+ }
}
#if defined(ENABLE_X86_SHANI) && !defined(BUILD_BITCOIN_INTERNAL)
@@ -637,27 +649,28 @@ std::string SHA256AutoDetect()
#if defined(ENABLE_ARM_SHANI) && !defined(BUILD_BITCOIN_INTERNAL)
bool have_arm_shani = false;
-
+ if (use_implementation & sha256_implementation::USE_SHANI) {
#if defined(__linux__)
#if defined(__arm__) // 32-bit
- if (getauxval(AT_HWCAP2) & HWCAP2_SHA2) {
- have_arm_shani = true;
- }
+ if (getauxval(AT_HWCAP2) & HWCAP2_SHA2) {
+ have_arm_shani = true;
+ }
#endif
#if defined(__aarch64__) // 64-bit
- if (getauxval(AT_HWCAP) & HWCAP_SHA2) {
- have_arm_shani = true;
- }
+ if (getauxval(AT_HWCAP) & HWCAP_SHA2) {
+ have_arm_shani = true;
+ }
#endif
#endif
#if defined(MAC_OSX)
- int val = 0;
- size_t len = sizeof(val);
- if (sysctlbyname("hw.optional.arm.FEAT_SHA256", &val, &len, nullptr, 0) == 0) {
- have_arm_shani = val != 0;
- }
+ int val = 0;
+ size_t len = sizeof(val);
+ if (sysctlbyname("hw.optional.arm.FEAT_SHA256", &val, &len, nullptr, 0) == 0) {
+ have_arm_shani = val != 0;
+ }
#endif
+ }
if (have_arm_shani) {
Transform = sha256_arm_shani::Transform;
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index 7625508665..b1348631d3 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -26,10 +26,22 @@ public:
CSHA256& Reset();
};
+namespace sha256_implementation {
+enum UseImplementation : uint8_t {
+ STANDARD = 0,
+ USE_SSE4 = 1 << 0,
+ USE_AVX2 = 1 << 1,
+ USE_SHANI = 1 << 2,
+ USE_SSE4_AND_AVX2 = USE_SSE4 | USE_AVX2,
+ USE_SSE4_AND_SHANI = USE_SSE4 | USE_SHANI,
+ USE_ALL = USE_SSE4 | USE_AVX2 | USE_SHANI,
+};
+}
+
/** Autodetect the best available SHA256 implementation.
* Returns the name of the implementation.
*/
-std::string SHA256AutoDetect();
+std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implementation = sha256_implementation::USE_ALL);
/** Compute multiple double-SHA256's of 64-byte blobs.
* output: pointer to a blocks*32 byte output buffer
diff --git a/src/headerssync.cpp b/src/headerssync.cpp
index f891063cd2..1b5d7305e8 100644
--- a/src/headerssync.cpp
+++ b/src/headerssync.cpp
@@ -9,15 +9,15 @@
#include <util/check.h>
#include <util/vector.h>
-// The two constants below are computed using the simulation script on
-// https://gist.github.com/sipa/016ae445c132cdf65a2791534dfb7ae1
+// The two constants below are computed using the simulation script in
+// contrib/devtools/headerssync-params.py.
-//! Store a commitment to a header every HEADER_COMMITMENT_PERIOD blocks.
-constexpr size_t HEADER_COMMITMENT_PERIOD{584};
+//! Store one header commitment per HEADER_COMMITMENT_PERIOD blocks.
+constexpr size_t HEADER_COMMITMENT_PERIOD{600};
//! Only feed headers to validation once this many headers on top have been
//! received and validated against commitments.
-constexpr size_t REDOWNLOAD_BUFFER_SIZE{13959}; // 13959/584 = ~23.9 commitments
+constexpr size_t REDOWNLOAD_BUFFER_SIZE{14308}; // 14308/600 = ~23.8 commitments
// Our memory analysis assumes 48 bytes for a CompressedHeader (so we should
// re-calculate parameters if we compress further)
diff --git a/src/net.cpp b/src/net.cpp
index 13f4430424..994abd986d 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1098,10 +1098,10 @@ void V2Transport::ProcessReceivedMaybeV1Bytes() noexcept
AssertLockNotHeld(m_send_mutex);
Assume(m_recv_state == RecvState::KEY_MAYBE_V1);
// We still have to determine if this is a v1 or v2 connection. The bytes being received could
- // be the beginning of either a v1 packet (network magic + "version\x00"), or of a v2 public
- // key. BIP324 specifies that a mismatch with this 12-byte string should trigger sending of the
- // key.
- std::array<uint8_t, V1_PREFIX_LEN> v1_prefix = {0, 0, 0, 0, 'v', 'e', 'r', 's', 'i', 'o', 'n', 0};
+ // be the beginning of either a v1 packet (network magic + "version\x00\x00\x00\x00\x00"), or
+ // of a v2 public key. BIP324 specifies that a mismatch with this 16-byte string should trigger
+ // sending of the key.
+ std::array<uint8_t, V1_PREFIX_LEN> v1_prefix = {0, 0, 0, 0, 'v', 'e', 'r', 's', 'i', 'o', 'n', 0, 0, 0, 0, 0};
std::copy(std::begin(Params().MessageStart()), std::end(Params().MessageStart()), v1_prefix.begin());
Assume(m_recv_buffer.size() <= v1_prefix.size());
if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), v1_prefix.begin())) {
@@ -1295,7 +1295,7 @@ size_t V2Transport::GetMaxBytesToProcess() noexcept
// receive buffer.
Assume(m_recv_buffer.size() <= V1_PREFIX_LEN);
// As long as we're not sure if this is a v1 or v2 connection, don't receive more than what
- // is strictly necessary to distinguish the two (12 bytes). If we permitted more than
+ // is strictly necessary to distinguish the two (16 bytes). If we permitted more than
// the v1 header size (24 bytes), we may not be able to feed the already-received bytes
// back into the m_v1_fallback V1 transport.
return V1_PREFIX_LEN - m_recv_buffer.size();
diff --git a/src/net.h b/src/net.h
index 2f7b832fba..e8e31f72e4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -473,7 +473,7 @@ private:
/** The length of the V1 prefix to match bytes initially received by responders with to
* determine if their peer is speaking V1 or V2. */
- static constexpr size_t V1_PREFIX_LEN = 12;
+ static constexpr size_t V1_PREFIX_LEN = 16;
// The sender side and receiver side of V2Transport are state machines that are transitioned
// through, based on what has been received. The receive state corresponds to the contents of,
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 06086d6804..a8d1553eed 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -696,6 +696,10 @@ private:
/** Send `feefilter` message. */
void MaybeSendFeefilter(CNode& node, Peer& peer, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
+ FastRandomContext m_rng GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
+
+ FeeFilterRounder m_fee_filter_rounder GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
+
const CChainParams& m_chainparams;
CConnman& m_connman;
AddrMan& m_addrman;
@@ -1053,7 +1057,7 @@ private:
bool SetupAddressRelay(const CNode& node, Peer& peer) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
void AddAddressKnown(Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
- void PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
+ void PushAddress(Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
};
const CNodeState* PeerManagerImpl::State(NodeId pnode) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1085,7 +1089,7 @@ void PeerManagerImpl::AddAddressKnown(Peer& peer, const CAddress& addr)
peer.m_addr_known->insert(addr.GetKey());
}
-void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand)
+void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr)
{
// Known checking here is only to save space from duplicates.
// Before sending, we'll filter it again for known addresses that were
@@ -1093,7 +1097,7 @@ void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomCo
assert(peer.m_addr_known);
if (addr.IsValid() && !peer.m_addr_known->contains(addr.GetKey()) && IsAddrCompatible(peer, addr)) {
if (peer.m_addrs_to_send.size() >= MAX_ADDR_TO_SEND) {
- peer.m_addrs_to_send[insecure_rand.randrange(peer.m_addrs_to_send.size())] = addr;
+ peer.m_addrs_to_send[m_rng.randrange(peer.m_addrs_to_send.size())] = addr;
} else {
peer.m_addrs_to_send.push_back(addr);
}
@@ -1877,7 +1881,9 @@ std::unique_ptr<PeerManager> PeerManager::make(CConnman& connman, AddrMan& addrm
PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, Options opts)
- : m_chainparams(chainman.GetParams()),
+ : m_rng{opts.deterministic_rng},
+ m_fee_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}, m_rng},
+ m_chainparams(chainman.GetParams()),
m_connman(connman),
m_addrman(addrman),
m_banman(banman),
@@ -2183,7 +2189,6 @@ void PeerManagerImpl::RelayAddress(NodeId originator,
const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY)
.Write(hash_addr)
.Write(time_addr)};
- FastRandomContext insecure_rand;
// Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers.
unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1;
@@ -2207,7 +2212,7 @@ void PeerManagerImpl::RelayAddress(NodeId originator,
};
for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
- PushAddress(*best[i].second, addr, insecure_rand);
+ PushAddress(*best[i].second, addr);
}
}
@@ -3793,7 +3798,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr);
uint64_t num_proc = 0;
uint64_t num_rate_limit = 0;
- Shuffle(vAddr.begin(), vAddr.end(), FastRandomContext());
+ Shuffle(vAddr.begin(), vAddr.end(), m_rng);
for (CAddress& addr : vAddr)
{
if (interruptMsgProc)
@@ -4735,9 +4740,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
} else {
vAddr = m_connman.GetAddresses(pfrom, MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
}
- FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- PushAddress(*peer, addr, insecure_rand);
+ PushAddress(*peer, addr);
}
return;
}
@@ -5339,8 +5343,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
}
if (std::optional<CService> local_service = GetLocalAddrForPeer(node)) {
CAddress local_addr{*local_service, peer.m_our_services, Now<NodeSeconds>()};
- FastRandomContext insecure_rand;
- PushAddress(peer, local_addr, insecure_rand);
+ PushAddress(peer, local_addr);
}
peer.m_next_local_addr_send = GetExponentialRand(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -5419,14 +5422,13 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
if (pto.IsBlockOnlyConn()) return;
CAmount currentFilter = m_mempool.GetMinFee().GetFeePerK();
- static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
if (m_chainman.IsInitialBlockDownload()) {
// Received tx-inv messages are discarded when the active
// chainstate is in IBD, so tell the peer to not send them.
currentFilter = MAX_MONEY;
} else {
- static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
+ static const CAmount MAX_FILTER{m_fee_filter_rounder.round(MAX_MONEY)};
if (peer.m_fee_filter_sent == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
@@ -5434,7 +5436,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
}
}
if (current_time > peer.m_next_send_feefilter) {
- CAmount filterToSend = g_filter_rounder.round(currentFilter);
+ CAmount filterToSend = m_fee_filter_rounder.round(currentFilter);
// We always have a fee filter of at least the min relay fee
filterToSend = std::max(filterToSend, m_mempool.m_min_relay_feerate.GetFeePerK());
if (filterToSend != peer.m_fee_filter_sent) {
diff --git a/src/net_processing.h b/src/net_processing.h
index 837e308617..80d07648a4 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -58,6 +58,9 @@ public:
uint32_t max_extra_txs{DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN};
//! Whether all P2P messages are captured to disk
bool capture_messages{false};
+ //! Whether or not the internal RNG behaves deterministically (this is
+ //! a test-only option).
+ bool deterministic_rng{false};
};
static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman,
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 5e61ed3100..706b62ea9b 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -410,8 +410,13 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
CBlockIndexHeightOnlyComparator());
+ CBlockIndex* previous_index{nullptr};
for (CBlockIndex* pindex : vSortedByHeight) {
if (m_interrupt) return false;
+ if (previous_index && pindex->nHeight > previous_index->nHeight + 1) {
+ return error("%s: block index is non-contiguous, index of height %d missing", __func__, previous_index->nHeight + 1);
+ }
+ previous_index = pindex;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 553c88fddc..87bfa4cfc3 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -1054,8 +1054,9 @@ static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
return fee_set;
}
-FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
- : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)}
+FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee, FastRandomContext& rng)
+ : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)},
+ insecure_rand{rng}
{
}
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 8ed13482e9..69bda195be 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -320,7 +320,7 @@ private:
public:
/** Create new FeeFilterRounder */
- explicit FeeFilterRounder(const CFeeRate& min_incremental_fee);
+ explicit FeeFilterRounder(const CFeeRate& min_incremental_fee, FastRandomContext& rng);
/** Quantize a minimum fee for privacy purpose before broadcast. */
CAmount round(CAmount currentMinFee) EXCLUSIVE_LOCKS_REQUIRED(!m_insecure_rand_mutex);
@@ -328,7 +328,7 @@ public:
private:
const std::set<double> m_fee_set;
Mutex m_insecure_rand_mutex;
- FastRandomContext insecure_rand GUARDED_BY(m_insecure_rand_mutex);
+ FastRandomContext& insecure_rand GUARDED_BY(m_insecure_rand_mutex);
};
#endif // BITCOIN_POLICY_FEES_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index b888fc43e2..05e58191fc 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -81,9 +81,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
}
- switch(mode)
- {
- case ForSelection:
+ if (mode == ForSelection) {
switch(tab)
{
case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
@@ -94,14 +92,6 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
ui->tableView->setFocus();
ui->closeButton->setText(tr("C&hoose"));
ui->exportButton->hide();
- break;
- case ForEditing:
- switch(tab)
- {
- case SendingTab: setWindowTitle(tr("Sending addresses")); break;
- case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
- }
- break;
}
switch(tab)
{
@@ -164,6 +154,7 @@ void AddressBookPage::setModel(AddressTableModel *_model)
connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
selectionChanged();
+ this->updateWindowsTitleWithWalletName();
}
void AddressBookPage::on_copyAddress_clicked()
@@ -328,3 +319,16 @@ void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int
newAddressToSelect.clear();
}
}
+
+void AddressBookPage::updateWindowsTitleWithWalletName()
+{
+ const QString walletName = this->model->GetWalletDisplayName();
+
+ if (mode == ForEditing) {
+ switch(tab)
+ {
+ case SendingTab: setWindowTitle(tr("Sending addresses - %1").arg(walletName)); break;
+ case ReceivingTab: setWindowTitle(tr("Receiving addresses - %1").arg(walletName)); break;
+ }
+ }
+}
diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h
index 283209d00c..b649da4ac8 100644
--- a/src/qt/addressbookpage.h
+++ b/src/qt/addressbookpage.h
@@ -56,6 +56,7 @@ private:
AddressBookSortFilterProxyModel *proxyModel;
QMenu *contextMenu;
QString newAddressToSelect;
+ void updateWindowsTitleWithWalletName();
private Q_SLOTS:
/** Delete currently selected address entry */
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index e4689e4389..c52ef7cd67 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -451,3 +451,5 @@ void AddressTableModel::emitDataChanged(int idx)
{
Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
+
+QString AddressTableModel::GetWalletDisplayName() const { return walletModel->getDisplayName(); };
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index 599aa89cad..44808364ec 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -87,6 +87,8 @@ public:
OutputType GetDefaultAddressType() const;
+ QString GetWalletDisplayName() const;
+
private:
WalletModel* const walletModel;
AddressTablePriv *priv = nullptr;
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index f1b66341d1..60e9bcde33 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1085,6 +1085,58 @@
</widget>
</item>
<item row="2" column="0">
+ <widget class="QLabel" name="peerTransportTypeLabel">
+ <property name="toolTip">
+ <string>The transport layer version: %1</string>
+ </property>
+ <property name="text">
+ <string>Transport</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="peerTransportType">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="peerSessionIdLabel">
+ <property name="toolTip">
+ <string>The BIP324 session ID string in hex, if any.</string>
+ </property>
+ <property name="text">
+ <string>Session ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="peerSessionId">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
<widget class="QLabel" name="peerNetworkLabel">
<property name="toolTip">
<string>The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</string>
@@ -1094,7 +1146,7 @@
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="4" column="1">
<widget class="QLabel" name="peerNetwork">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1110,14 +1162,14 @@
</property>
</widget>
</item>
- <item row="3" column="0">
+ <item row="5" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Version</string>
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="5" column="1">
<widget class="QLabel" name="peerVersion">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1133,14 +1185,14 @@
</property>
</widget>
</item>
- <item row="4" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>User Agent</string>
</property>
</widget>
</item>
- <item row="4" column="1">
+ <item row="6" column="1">
<widget class="QLabel" name="peerSubversion">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1156,14 +1208,14 @@
</property>
</widget>
</item>
- <item row="5" column="0">
+ <item row="7" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Services</string>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="7" column="1">
<widget class="QLabel" name="peerServices">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1182,7 +1234,7 @@
</property>
</widget>
</item>
- <item row="6" column="0">
+ <item row="8" column="0">
<widget class="QLabel" name="peerRelayTxesLabel">
<property name="toolTip">
<string>Whether we relay transactions to this peer.</string>
@@ -1192,7 +1244,7 @@
</property>
</widget>
</item>
- <item row="6" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="peerRelayTxes">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1208,7 +1260,7 @@
</property>
</widget>
</item>
- <item row="7" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="peerHighBandwidthLabel">
<property name="toolTip">
<string>High bandwidth BIP152 compact block relay: %1</string>
@@ -1218,7 +1270,7 @@
</property>
</widget>
</item>
- <item row="7" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="peerHighBandwidth">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1234,14 +1286,14 @@
</property>
</widget>
</item>
- <item row="8" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Starting Block</string>
</property>
</widget>
</item>
- <item row="8" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="peerHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1257,14 +1309,14 @@
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Synced Headers</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="peerSyncHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1280,14 +1332,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Synced Blocks</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="12" column="1">
<widget class="QLabel" name="peerCommonHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1303,14 +1355,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Connection Time</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="13" column="1">
<widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1326,7 +1378,7 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="peerLastBlockLabel">
<property name="toolTip">
<string>Elapsed time since a novel block passing initial validity checks was received from this peer.</string>
@@ -1336,7 +1388,7 @@
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerLastBlock">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1352,7 +1404,7 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="peerLastTxLabel">
<property name="toolTip">
<string extracomment="Tooltip text for the Last Transaction field in the peer details area.">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
@@ -1362,7 +1414,7 @@
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerLastTx">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1378,14 +1430,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1401,14 +1453,14 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1424,14 +1476,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="18" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="18" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1447,14 +1499,14 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="19" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="19" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1470,14 +1522,14 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="20" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="20" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1493,7 +1545,7 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="21" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1503,7 +1555,7 @@
</property>
</widget>
</item>
- <item row="19" column="1">
+ <item row="21" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1519,14 +1571,14 @@
</property>
</widget>
</item>
- <item row="20" column="0">
+ <item row="22" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="20" column="1">
+ <item row="22" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1542,14 +1594,14 @@
</property>
</widget>
</item>
- <item row="21" column="0">
+ <item row="23" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="21" column="1">
+ <item row="23" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1565,7 +1617,7 @@
</property>
</widget>
</item>
- <item row="22" column="0">
+ <item row="24" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1575,7 +1627,7 @@
</property>
</widget>
</item>
- <item row="22" column="1">
+ <item row="24" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1591,7 +1643,7 @@
</property>
</widget>
</item>
- <item row="23" column="0">
+ <item row="25" column="0">
<widget class="QLabel" name="peerAddrRelayEnabledLabel">
<property name="toolTip">
<string extracomment="Tooltip text for the Address Relay field in the peer details area, which displays whether we relay addresses to this peer (Yes/No).">Whether we relay addresses to this peer.</string>
@@ -1601,7 +1653,7 @@
</property>
</widget>
</item>
- <item row="23" column="1">
+ <item row="25" column="1">
<widget class="QLabel" name="peerAddrRelayEnabled">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1617,7 +1669,7 @@
</property>
</widget>
</item>
- <item row="24" column="0">
+ <item row="26" column="0">
<widget class="QLabel" name="peerAddrProcessedLabel">
<property name="toolTip">
<string extracomment="Tooltip text for the Addresses Processed field in the peer details area, which displays the total number of addresses received from this peer that were processed (excludes addresses that were dropped due to rate-limiting).">The total number of addresses received from this peer that were processed (excludes addresses that were dropped due to rate-limiting).</string>
@@ -1627,7 +1679,7 @@
</property>
</widget>
</item>
- <item row="24" column="1">
+ <item row="26" column="1">
<widget class="QLabel" name="peerAddrProcessed">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1643,7 +1695,7 @@
</property>
</widget>
</item>
- <item row="25" column="0">
+ <item row="27" column="0">
<widget class="QLabel" name="peerAddrRateLimitedLabel">
<property name="toolTip">
<string extracomment="Tooltip text for the Addresses Rate-Limited field in the peer details area, which displays the total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.">The total number of addresses received from this peer that were dropped (not processed) due to rate-limiting.</string>
@@ -1653,7 +1705,7 @@
</property>
</widget>
</item>
- <item row="25" column="1">
+ <item row="27" column="1">
<widget class="QLabel" name="peerAddrRateLimited">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1669,7 +1721,7 @@
</property>
</widget>
</item>
- <item row="26" column="0">
+ <item row="28" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index cb4ecfb6fb..998a4e5cbe 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -12,6 +12,7 @@
#include <chainparams.h>
#include <common/system.h>
#include <interfaces/node.h>
+#include <node/connection_types.h>
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
@@ -514,8 +515,17 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
/*: Explanatory text for a short-lived outbound peer connection that is used
to request addresses from a peer. */
tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
- const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
- ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
+ const QString connection_types_list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
+ ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(connection_types_list));
+ const std::vector<QString> TRANSPORT_TYPE_DOC{
+ //: Explanatory text for "detecting" transport type.
+ tr("detecting: peer could be v1 or v2"),
+ //: Explanatory text for v1 transport type.
+ tr("v1: unencrypted, plaintext transport protocol"),
+ //: Explanatory text for v2 transport type.
+ tr("v2: BIP324 encrypted transport protocol")};
+ const QString transport_types_list{"<ul><li>" + Join(TRANSPORT_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
+ ui->peerTransportTypeLabel->setToolTip(ui->peerTransportTypeLabel->toolTip().arg(transport_types_list));
const QString hb_list{"<ul><li>\""
+ ts.to + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\""
+ ts.from + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\""
@@ -1191,6 +1201,15 @@ void RPCConsole::updateDetailWidget()
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
}
ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /*prepend_direction=*/true));
+ ui->peerTransportType->setText(QString::fromStdString(TransportTypeAsString(stats->nodeStats.m_transport_type)));
+ if (stats->nodeStats.m_transport_type == TransportProtocolType::V2) {
+ ui->peerSessionIdLabel->setVisible(true);
+ ui->peerSessionId->setVisible(true);
+ ui->peerSessionId->setText(QString::fromStdString(stats->nodeStats.m_session_id));
+ } else {
+ ui->peerSessionIdLabel->setVisible(false);
+ ui->peerSessionId->setVisible(false);
+ }
ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
if (stats->nodeStats.m_permission_flags == NetPermissionFlags::None) {
ui->peerPermissions->setText(ts.na);
diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp
index deb0ed65ca..38a8c6798e 100644
--- a/src/test/fuzz/fees.cpp
+++ b/src/test/fuzz/fees.cpp
@@ -17,7 +17,8 @@ FUZZ_TARGET(fees)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const CFeeRate minimal_incremental_fee{ConsumeMoney(fuzzed_data_provider)};
- FeeFilterRounder fee_filter_rounder{minimal_incremental_fee};
+ FastRandomContext rng{/*fDeterministic=*/true};
+ FeeFilterRounder fee_filter_rounder{minimal_incremental_fee, rng};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
const CAmount current_minimum_fee = ConsumeMoney(fuzzed_data_provider);
const CAmount rounded_fee = fee_filter_rounder.round(current_minimum_fee);
diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp
index 25fb5343e3..29d70cb5f8 100644
--- a/src/test/policy_fee_tests.cpp
+++ b/src/test/policy_fee_tests.cpp
@@ -13,7 +13,8 @@ BOOST_AUTO_TEST_SUITE(policy_fee_tests)
BOOST_AUTO_TEST_CASE(FeeRounder)
{
- FeeFilterRounder fee_rounder{CFeeRate{1000}};
+ FastRandomContext rng{/*fDeterministic=*/true};
+ FeeFilterRounder fee_rounder{CFeeRate{1000}, rng};
// check that 1000 rounds to 974 or 1071
std::set<CAmount> results;
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 2947bc3fcb..e27d5a27ad 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -256,6 +256,7 @@ TestingSetup::TestingSetup(
m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); // Deterministic randomness for tests.
PeerManager::Options peerman_opts;
ApplyArgsManOptions(*m_node.args, peerman_opts);
+ peerman_opts.deterministic_rng = true;
m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
m_node.banman.get(), *m_node.chainman,
*m_node.mempool, peerman_opts);
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index be1aa18993..be0715df32 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -142,7 +142,10 @@ class AssumeutxoTest(BitcoinTestFramework):
f"-stopatheight={PAUSE_HEIGHT}", *self.extra_args[1]])
# Finally connect the nodes and let them sync.
- self.connect_nodes(0, 1)
+ #
+ # Set `wait_for_connect=False` to avoid a race between performing connection
+ # assertions and the -stopatheight tripping.
+ self.connect_nodes(0, 1, wait_for_connect=False)
n1.wait_until_stopped(timeout=5)
@@ -156,7 +159,15 @@ class AssumeutxoTest(BitcoinTestFramework):
self.connect_nodes(0, 1)
self.log.info(f"Ensuring snapshot chain syncs to tip. ({FINAL_HEIGHT})")
- wait_until_helper(lambda: n1.getchainstates()['snapshot']['blocks'] == FINAL_HEIGHT)
+
+ def check_for_final_height():
+ chainstates = n1.getchainstates()
+ # The background validation may have completed before we run our first
+ # check, so accept a final blockheight from either chainstate type.
+ cs = chainstates.get('snapshot') or chainstates.get('normal')
+ return cs['blocks'] == FINAL_HEIGHT
+
+ wait_until_helper(check_for_final_height)
self.sync_blocks(nodes=(n0, n1))
self.log.info("Ensuring background validation completes")
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index 64ca312b84..94f5116f9b 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -5,6 +5,7 @@
"""Stress tests related to node initialization."""
import os
from pathlib import Path
+import shutil
from test_framework.test_framework import BitcoinTestFramework, SkipTest
from test_framework.test_node import ErrorMatch
@@ -47,7 +48,7 @@ class InitStressTest(BitcoinTestFramework):
def start_expecting_error(err_fragment):
node.assert_start_raises_init_error(
- extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'],
+ extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1', '-checkblocks=200', '-checklevel=4'],
expected_msg=err_fragment,
match=ErrorMatch.PARTIAL_REGEX,
)
@@ -101,9 +102,9 @@ class InitStressTest(BitcoinTestFramework):
}
files_to_perturb = {
- 'blocks/index/*.ldb': 'Error opening block database.',
+ 'blocks/index/*.ldb': 'Error loading block database.',
'chainstate/*.ldb': 'Error opening block database.',
- 'blocks/blk*.dat': 'Error opening block database.',
+ 'blocks/blk*.dat': 'Corrupted block database detected.',
}
for file_patt, err_fragment in files_to_delete.items():
@@ -124,18 +125,31 @@ class InitStressTest(BitcoinTestFramework):
check_clean_start()
self.stop_node(0)
+ self.log.info("Test startup errors after perturbing certain essential files")
for file_patt, err_fragment in files_to_perturb.items():
+ shutil.copytree(node.chain_path / "blocks", node.chain_path / "blocks_bak")
+ shutil.copytree(node.chain_path / "chainstate", node.chain_path / "chainstate_bak")
target_files = list(node.chain_path.glob(file_patt))
for target_file in target_files:
self.log.info(f"Perturbing file to ensure failure {target_file}")
- with open(target_file, "rb") as tf_read, open(target_file, "wb") as tf_write:
+ with open(target_file, "rb") as tf_read:
contents = tf_read.read()
tweaked_contents = bytearray(contents)
- tweaked_contents[50:250] = b'1' * 200
+ # Since the genesis block is not checked by -checkblocks, the
+ # perturbation window must be chosen such that a higher block
+ # in blk*.dat is affected.
+ tweaked_contents[150:350] = b'1' * 200
+ with open(target_file, "wb") as tf_write:
tf_write.write(bytes(tweaked_contents))
start_expecting_error(err_fragment)
+ shutil.rmtree(node.chain_path / "blocks")
+ shutil.rmtree(node.chain_path / "chainstate")
+ shutil.move(node.chain_path / "blocks_bak", node.chain_path / "blocks")
+ shutil.move(node.chain_path / "chainstate_bak", node.chain_path / "chainstate")
+
+
if __name__ == '__main__':
InitStressTest().main()
diff --git a/test/functional/p2p_v2_transport.py b/test/functional/p2p_v2_transport.py
index 2455bf2e2d..dd564fed88 100755
--- a/test/functional/p2p_v2_transport.py
+++ b/test/functional/p2p_v2_transport.py
@@ -5,10 +5,16 @@
"""
Test v2 transport
"""
+import socket
from test_framework.messages import NODE_P2P_V2
+from test_framework.p2p import MAGIC_BYTES
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import (
+ assert_equal,
+ p2p_port,
+)
+
class V2TransportTest(BitcoinTestFramework):
def set_test_params(self):
@@ -123,5 +129,25 @@ class V2TransportTest(BitcoinTestFramework):
self.sync_all()
assert_equal(self.nodes[4].getblockcount(), 11)
+ # Check v1 prefix detection
+ V1_PREFIX = MAGIC_BYTES["regtest"] + b"version\x00\x00\x00\x00\x00"
+ assert_equal(len(V1_PREFIX), 16)
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ num_peers = len(self.nodes[0].getpeerinfo())
+ s.connect(("127.0.0.1", p2p_port(0)))
+ self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers + 1)
+ s.sendall(V1_PREFIX[:-1])
+ assert_equal(self.nodes[0].getpeerinfo()[-1]["transport_protocol_type"], "detecting")
+ s.sendall(bytes([V1_PREFIX[-1]])) # send out last prefix byte
+ self.wait_until(lambda: self.nodes[0].getpeerinfo()[-1]["transport_protocol_type"] == "v1")
+
+ # Check wrong network prefix detection (hits if the next 12 bytes correspond to a v1 version message)
+ wrong_network_magic_prefix = MAGIC_BYTES["signet"] + V1_PREFIX[4:]
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.connect(("127.0.0.1", p2p_port(0)))
+ with self.nodes[0].assert_debug_log("V2 transport error: V1 peer with wrong MessageStart"):
+ s.sendall(wrong_network_magic_prefix + b"somepayload")
+
+
if __name__ == '__main__':
V2TransportTest().main()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index ab7fed335c..a34c34713e 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -586,7 +586,14 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def wait_for_node_exit(self, i, timeout):
self.nodes[i].process.wait(timeout)
- def connect_nodes(self, a, b, *, peer_advertises_v2=None):
+ def connect_nodes(self, a, b, *, peer_advertises_v2=None, wait_for_connect: bool = True):
+ """
+ Kwargs:
+ wait_for_connect: if True, block until the nodes are verified as connected. You might
+ want to disable this when using -stopatheight with one of the connected nodes,
+ since there will be a race between the actual connection and performing
+ the assertions before one node shuts down.
+ """
from_connection = self.nodes[a]
to_connection = self.nodes[b]
from_num_peers = 1 + len(from_connection.getpeerinfo())
@@ -603,6 +610,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# compatibility with older clients
from_connection.addnode(ip_port, "onetry")
+ if not wait_for_connect:
+ return
+
# poll until version handshake complete to avoid race conditions
# with transaction relaying
# See comments in net_processing:
diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py
index 49e36b21c5..4d6e6024c5 100755
--- a/test/functional/wallet_backwards_compatibility.py
+++ b/test/functional/wallet_backwards_compatibility.py
@@ -33,11 +33,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 11
+ self.num_nodes = 12
# Add new version after each release:
self.extra_args = [
["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to receive coins, swap wallets, etc
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v25.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v24.0.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0
@@ -58,6 +59,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
+ 250000,
240001,
230000,
220000,
@@ -72,20 +74,72 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.start_nodes()
self.import_deterministic_coinbase_privkeys()
- def nodes_wallet_dir(self, node):
- if node.version < 170000:
- return node.chain_path
- return node.wallets_path
+ def split_version(self, node):
+ major = node.version // 10000
+ minor = (node.version % 10000) // 100
+ patch = (node.version % 100)
+ return (major, minor, patch)
+
+ def major_version_equals(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major == major
+
+ def major_version_less_than(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major < major
+
+ def major_version_at_least(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major >= major
+
+ def test_v19_addmultisigaddress(self):
+ if not self.is_bdb_compiled():
+ return
+ # Specific test for addmultisigaddress using v19
+ # See #18075
+ self.log.info("Testing 0.19 addmultisigaddress case (#18075)")
+ node_master = self.nodes[1]
+ node_v19 = self.nodes[self.num_nodes - 4]
+ node_v19.rpc.createwallet(wallet_name="w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+ # Use addmultisigaddress (see #18075)
+ address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+ node_v19.unloadwallet("w1_v19")
+
+ # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
+ shutil.copytree(
+ os.path.join(node_v19.wallets_path, "w1_v19"),
+ os.path.join(node_master.wallets_path, "w1_v19")
+ )
+ node_master.loadwallet("w1_v19")
+ wallet = node_master.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+
+ # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("w1_v19")
+ shutil.rmtree(os.path.join(node_v19.wallets_path, "w1_v19"))
+ shutil.copytree(
+ os.path.join(node_master.wallets_path, "w1_v19"),
+ os.path.join(node_v19.wallets_path, "w1_v19")
+ )
+ node_v19.loadwallet("w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
def run_test(self):
node_miner = self.nodes[0]
node_master = self.nodes[1]
- node_v19 = self.nodes[self.num_nodes - 4]
- node_v18 = self.nodes[self.num_nodes - 3]
+ node_v21 = self.nodes[self.num_nodes - 6]
node_v17 = self.nodes[self.num_nodes - 2]
node_v16 = self.nodes[self.num_nodes - 1]
- legacy_nodes = self.nodes[2:]
+ legacy_nodes = self.nodes[2:] # Nodes that support legacy wallets
+ legacy_only_nodes = self.nodes[-5:] # Nodes that only support legacy wallets
+ descriptors_nodes = self.nodes[2:-5] # Nodes that support descriptor wallets
self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress())
@@ -121,24 +175,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Abandon transaction, but don't confirm
node_master.abandontransaction(tx3_id)
- # w1_v19: regular wallet, created with v0.19
- node_v19.rpc.createwallet(wallet_name="w1_v19")
- wallet = node_v19.get_wallet_rpc("w1_v19")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
- # Use addmultisigaddress (see #18075)
- address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
- assert wallet.getaddressinfo(address_18075)["solvable"]
- node_v19.unloadwallet("w1_v19")
-
- # w1_v18: regular wallet, created with v0.18
- node_v18.rpc.createwallet(wallet_name="w1_v18")
- wallet = node_v18.get_wallet_rpc("w1_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
-
# w2: wallet with private keys disabled, created on master: update this
# test when default wallets private keys disabled can no longer be
# opened by older versions.
@@ -158,9 +194,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Unload wallets and copy to older nodes:
node_master_wallets_dir = node_master.wallets_path
- node_v19_wallets_dir = node_v19.wallets_path
- node_v17_wallets_dir = node_v17.wallets_path
- node_v16_wallets_dir = node_v16.chain_path
node_master.unloadwallet("w1")
node_master.unloadwallet("w2")
node_master.unloadwallet("w3")
@@ -168,24 +201,35 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
for node in legacy_nodes:
# Copy wallets to previous version
for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(self.nodes_wallet_dir(node), wallet)
- )
-
- if not self.options.descriptors:
- # Descriptor wallets break compatibility, only run this test for legacy wallet
- # Load modern wallet with older nodes
- for node in legacy_nodes:
- for wallet_name in ["w1", "w2", "w3"]:
- if node.version < 170000:
- # loadwallet was introduced in v0.17.0
- continue
- if node.version < 180000 and wallet_name == "w3":
- # Blank wallets were introduced in v0.18.0. We test the loading error below.
- continue
- node.loadwallet(wallet_name)
- wallet = node.get_wallet_rpc(wallet_name)
+ dest = node.wallets_path / wallet
+ source = node_master_wallets_dir / wallet
+ if self.major_version_equals(node, 16):
+ # 0.16 node expect the wallet to be in the wallet dir but as a plain file rather than in directories
+ shutil.copyfile(source / "wallet.dat", dest)
+ else:
+ shutil.copytree(source, dest)
+
+ self.test_v19_addmultisigaddress()
+
+ self.log.info("Test that a wallet made on master can be opened on:")
+ # In descriptors wallet mode, run this test on the nodes that support descriptor wallets
+ # In legacy wallets mode, run this test on the nodes that support legacy wallets
+ for node in descriptors_nodes if self.options.descriptors else legacy_nodes:
+ if self.major_version_less_than(node, 17):
+ # loadwallet was introduced in v0.17.0
+ continue
+ self.log.info(f"- {node.version}")
+ for wallet_name in ["w1", "w2", "w3"]:
+ if self.major_version_less_than(node, 18) and wallet_name == "w3":
+ # Blank wallets were introduced in v0.18.0. We test the loading error below.
+ continue
+ if self.major_version_less_than(node, 22) and wallet_name == "w1" and self.options.descriptors:
+ # Descriptor wallets created after 0.21 have taproot descriptors which 0.21 does not support, tested below
+ continue
+ # Also try to reopen on master after opening on old
+ for n in [node, node_master]:
+ n.loadwallet(wallet_name)
+ wallet = n.get_wallet_rpc(wallet_name)
info = wallet.getwalletinfo()
if wallet_name == "w1":
assert info['private_keys_enabled'] == True
@@ -208,130 +252,133 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
else:
assert info['private_keys_enabled'] == True
assert info['keypoolsize'] == 0
- else:
- for node in legacy_nodes:
- # Descriptor wallets appear to be corrupted wallets to old software
- # and loadwallet is introduced in v0.17.0
- if node.version >= 170000 and node.version < 210000:
- for wallet_name in ["w1", "w2", "w3"]:
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
- # RPC loadwallet failure causes bitcoind to exit, in addition to the RPC
- # call failure, so the following test won't work:
- # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3')
+ # Copy back to master
+ wallet.unloadwallet()
+ if n == node:
+ shutil.rmtree(node_master.wallets_path / wallet_name)
+ shutil.copytree(
+ n.wallets_path / wallet_name,
+ node_master.wallets_path / wallet_name,
+ )
+
+ # Check that descriptor wallets don't work on legacy only nodes
+ if self.options.descriptors:
+ self.log.info("Test descriptor wallet incompatibility on:")
+ for node in legacy_only_nodes:
+ # RPC loadwallet failure causes bitcoind to exit in <= 0.17, in addition to the RPC
+ # call failure, so the following test won't work:
+ # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3')
+ if self.major_version_less_than(node, 18):
+ continue
+ self.log.info(f"- {node.version}")
+ # Descriptor wallets appear to be corrupted wallets to old software
+ assert self.major_version_at_least(node, 18) and self.major_version_less_than(node, 21)
+ for wallet_name in ["w1", "w2", "w3"]:
+ assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
# Instead, we stop node and try to launch it with the wallet:
self.stop_node(node_v17.index)
if self.options.descriptors:
+ self.log.info("Test descriptor wallet incompatibility with 0.17")
# Descriptor wallets appear to be corrupted wallets to old software
node_v17.assert_start_raises_init_error(["-wallet=w1"], "Error: wallet.dat corrupt, salvage failed")
node_v17.assert_start_raises_init_error(["-wallet=w2"], "Error: wallet.dat corrupt, salvage failed")
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: wallet.dat corrupt, salvage failed")
else:
+ self.log.info("Test blank wallet incompatibility with v17")
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core")
self.start_node(node_v17.index)
- if not self.options.descriptors:
- # Descriptor wallets break compatibility, only run this test for legacy wallets
- # Open most recent wallet in v0.16 (no loadwallet RPC)
- self.restart_node(node_v16.index, extra_args=["-wallet=w2"])
- wallet = node_v16.get_wallet_rpc("w2")
- info = wallet.getwalletinfo()
- assert info['keypoolsize'] == 1
-
- # Create upgrade wallet in v0.16
- self.restart_node(node_v16.index, extra_args=["-wallet=u1_v16"])
- wallet = node_v16.get_wallet_rpc("u1_v16")
- v16_addr = wallet.getnewaddress('', "bech32")
- v16_info = wallet.validateaddress(v16_addr)
- v16_pubkey = v16_info['pubkey']
+ # No wallet created in master can be opened in 0.16
+ self.log.info("Test that wallets created in master are too new for 0.16")
self.stop_node(node_v16.index)
+ for wallet_name in ["w1", "w2", "w3"]:
+ if self.options.descriptors:
+ node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: {wallet_name} corrupt, salvage failed")
+ else:
+ node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: Error loading {wallet_name}: Wallet requires newer version of Bitcoin Core")
+
+ # When descriptors are enabled, w1 cannot be opened by 0.21 since it contains a taproot descriptor
+ if self.options.descriptors:
+ self.log.info("Test that 0.21 cannot open wallet containing tr() descriptors")
+ assert_raises_rpc_error(-1, "map::at", node_v21.loadwallet, "w1")
+
+ self.log.info("Test that a wallet can upgrade to and downgrade from master, from:")
+ for node in descriptors_nodes if self.options.descriptors else legacy_nodes:
+ self.log.info(f"- {node.version}")
+ wallet_name = f"up_{node.version}"
+ if self.major_version_less_than(node, 17):
+ # createwallet is only available in 0.17+
+ self.restart_node(node.index, extra_args=[f"-wallet={wallet_name}"])
+ wallet_prev = node.get_wallet_rpc(wallet_name)
+ address = wallet_prev.getnewaddress('', "bech32")
+ addr_info = wallet_prev.validateaddress(address)
+ else:
+ if self.major_version_at_least(node, 21):
+ node.rpc.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors)
+ else:
+ node.rpc.createwallet(wallet_name=wallet_name)
+ wallet_prev = node.get_wallet_rpc(wallet_name)
+ address = wallet_prev.getnewaddress('', "bech32")
+ addr_info = wallet_prev.getaddressinfo(address)
+
+ hdkeypath = addr_info["hdkeypath"].replace("'", "h")
+ pubkey = addr_info["pubkey"]
+
+ # Make a backup of the wallet file
+ backup_path = os.path.join(self.options.tmpdir, f"{wallet_name}.dat")
+ wallet_prev.backupwallet(backup_path)
+
+ # Remove the wallet from old node
+ if self.major_version_at_least(node, 17):
+ wallet_prev.unloadwallet()
+ else:
+ self.stop_node(node.index)
+
+ # Restore the wallet to master
+ load_res = node_master.restorewallet(wallet_name, backup_path)
- self.log.info("Test wallet upgrade path...")
- # u1: regular wallet, created with v0.17
- node_v17.rpc.createwallet(wallet_name="u1_v17")
- wallet = node_v17.get_wallet_rpc("u1_v17")
- address = wallet.getnewaddress("bech32")
- v17_info = wallet.getaddressinfo(address)
- hdkeypath = v17_info["hdkeypath"].replace("'", "h")
- pubkey = v17_info["pubkey"]
-
- if self.is_bdb_compiled():
- # Old wallets are BDB and will only work if BDB is compiled
- # Copy the 0.16 wallet to the last Bitcoin Core version and open it:
- shutil.copyfile(
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16"),
- os.path.join(node_master_wallets_dir, "u1_v16")
- )
- load_res = node_master.loadwallet("u1_v16")
# Make sure this wallet opens with only the migration warning. See https://github.com/bitcoin/bitcoin/pull/19054
- if int(node_master.getnetworkinfo()["version"]) >= 249900:
- # loadwallet#warnings (added in v25) -- only present if there is a warning
+ if not self.options.descriptors:
# Legacy wallets will have only a deprecation warning
assert_equal(load_res["warnings"], ["Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet."])
else:
- # loadwallet#warning (deprecated in v25) -- always present, but empty string if no warning
- assert_equal(load_res["warning"], '')
- wallet = node_master.get_wallet_rpc("u1_v16")
- info = wallet.getaddressinfo(v16_addr)
- descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{v16_pubkey})"
- assert_equal(info["desc"], descsum_create(descriptor))
+ assert "warnings" not in load_res
- # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("u1_v16")
- os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16"))
- shutil.copyfile(
- os.path.join(node_master_wallets_dir, "u1_v16"),
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16")
- )
- self.start_node(node_v16.index, extra_args=["-wallet=u1_v16"])
- wallet = node_v16.get_wallet_rpc("u1_v16")
- info = wallet.validateaddress(v16_addr)
- assert_equal(info, v16_info)
-
- # Copy the 0.17 wallet to the last Bitcoin Core version and open it:
- node_v17.unloadwallet("u1_v17")
- shutil.copytree(
- os.path.join(node_v17_wallets_dir, "u1_v17"),
- os.path.join(node_master_wallets_dir, "u1_v17")
- )
- node_master.loadwallet("u1_v17")
- wallet = node_master.get_wallet_rpc("u1_v17")
+ wallet = node_master.get_wallet_rpc(wallet_name)
info = wallet.getaddressinfo(address)
descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{pubkey})"
assert_equal(info["desc"], descsum_create(descriptor))
- # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("u1_v17")
- shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "u1_v17"),
- os.path.join(node_v17_wallets_dir, "u1_v17")
- )
- node_v17.loadwallet("u1_v17")
- wallet = node_v17.get_wallet_rpc("u1_v17")
- info = wallet.getaddressinfo(address)
- assert_equal(info, v17_info)
-
- # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
- shutil.copytree(
- os.path.join(node_v19_wallets_dir, "w1_v19"),
- os.path.join(node_master_wallets_dir, "w1_v19")
- )
- node_master.loadwallet("w1_v19")
- wallet = node_master.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
-
- # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("w1_v19")
- shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "w1_v19"),
- os.path.join(node_v19_wallets_dir, "w1_v19")
- )
- node_v19.loadwallet("w1_v19")
- wallet = node_v19.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
+ # Make backup so the wallet can be copied back to old node
+ down_wallet_name = f"re_down_{node.version}"
+ down_backup_path = os.path.join(self.options.tmpdir, f"{down_wallet_name}.dat")
+ wallet.backupwallet(down_backup_path)
+ wallet.unloadwallet()
+
+ # Check that no automatic upgrade broke the downgrading the wallet
+ if self.major_version_less_than(node, 17):
+ # loadwallet is only available in 0.17+
+ shutil.copyfile(
+ down_backup_path,
+ node.wallets_path / down_wallet_name
+ )
+ self.start_node(node.index, extra_args=[f"-wallet={down_wallet_name}"])
+ wallet_res = node.get_wallet_rpc(down_wallet_name)
+ info = wallet_res.validateaddress(address)
+ assert_equal(info, addr_info)
+ else:
+ target_dir = node.wallets_path / down_wallet_name
+ os.makedirs(target_dir, exist_ok=True)
+ shutil.copyfile(
+ down_backup_path,
+ target_dir / "wallet.dat"
+ )
+ node.loadwallet(down_wallet_name)
+ wallet_res = node.get_wallet_rpc(down_wallet_name)
+ info = wallet_res.getaddressinfo(address)
+ assert_equal(info, addr_info)
if __name__ == '__main__':
BackwardsCompatibilityTest().main()
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index 75b507c387..eb83e11f36 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -109,7 +109,7 @@ class CreateWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress)
# Now set a seed and it should work. Wallet should also be encrypted
- w4.walletpassphrase('pass', 60)
+ w4.walletpassphrase("pass", 999000)
if self.options.descriptors:
w4.importdescriptors([{
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/0h/*)'),
@@ -142,7 +142,7 @@ class CreateWalletTest(BitcoinTestFramework):
self.nodes[0].createwallet(wallet_name='wblank', disable_private_keys=False, blank=True, passphrase='thisisapassphrase')
wblank = node.get_wallet_rpc('wblank')
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wblank.signmessage, "needanargument", "test")
- wblank.walletpassphrase('thisisapassphrase', 60)
+ wblank.walletpassphrase("thisisapassphrase", 999000)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress)
@@ -151,7 +151,7 @@ class CreateWalletTest(BitcoinTestFramework):
self.nodes[0].createwallet(wallet_name='w6', disable_private_keys=False, blank=False, passphrase='thisisapassphrase')
w6 = node.get_wallet_rpc('w6')
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", w6.signmessage, "needanargument", "test")
- w6.walletpassphrase('thisisapassphrase', 60)
+ w6.walletpassphrase("thisisapassphrase", 999000)
w6.signmessage(w6.getnewaddress('', 'legacy'), "test")
w6.keypoolrefill(1)
# There should only be 1 key for legacy, 3 for descriptors
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 6f563987cc..6af01f8cfd 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -129,7 +129,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
# Encrypt wallet 0
send_wrpc.encryptwallet('pass')
- send_wrpc.walletpassphrase('pass', 10)
+ send_wrpc.walletpassphrase("pass", 999000)
addr = send_wrpc.getnewaddress()
info2 = send_wrpc.getaddressinfo(addr)
assert info1['hdmasterfingerprint'] != info2['hdmasterfingerprint']
@@ -143,7 +143,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
send_wrpc.getnewaddress()
self.log.info("Test that unlock is needed when deriving only hardened keys in an encrypted wallet")
- send_wrpc.walletpassphrase('pass', 10)
+ send_wrpc.walletpassphrase("pass", 999000)
send_wrpc.importdescriptors([{
"desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n",
"timestamp": "now",
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index cf20ff1239..8c68d03f97 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -173,7 +173,7 @@ class WalletDumpTest(BitcoinTestFramework):
# encrypt wallet, restart, unlock and dump
self.nodes[0].encryptwallet('test')
- self.nodes[0].walletpassphrase('test', 100)
+ self.nodes[0].walletpassphrase("test", 999000)
# Should be a no-op:
self.nodes[0].keypoolrefill()
self.nodes[0].dumpwallet(wallet_enc_dump)
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index 88b9ebbddd..e8381ba8f2 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -59,7 +59,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10)
# Test walletlock
- self.nodes[0].walletpassphrase(passphrase, 84600)
+ self.nodes[0].walletpassphrase(passphrase, 999000)
sig = self.nodes[0].signmessage(address, msg)
assert self.nodes[0].verifymessage(address, sig, msg)
self.nodes[0].walletlock()
@@ -68,7 +68,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
# Test passphrase changes
self.nodes[0].walletpassphrasechange(passphrase, passphrase2)
assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10)
- self.nodes[0].walletpassphrase(passphrase2, 10)
+ self.nodes[0].walletpassphrase(passphrase2, 999000)
sig = self.nodes[0].signmessage(address, msg)
assert self.nodes[0].verifymessage(address, sig, msg)
self.nodes[0].walletlock()
@@ -97,7 +97,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
self.nodes[0].walletpassphrasechange(passphrase2, passphrase_with_nulls)
# walletpassphrasechange should not stop at null characters
assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase_with_nulls.partition("\0")[0], 10)
- self.nodes[0].walletpassphrase(passphrase_with_nulls, 10)
+ self.nodes[0].walletpassphrase(passphrase_with_nulls, 999000)
sig = self.nodes[0].signmessage(address, msg)
assert self.nodes[0].verifymessage(address, sig, msg)
self.nodes[0].walletlock()
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index b1829f42af..ca4feefb2b 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -581,7 +581,7 @@ class RawTransactionsTest(BitcoinTestFramework):
wallet.encryptwallet("test")
if self.options.descriptors:
- wallet.walletpassphrase('test', 10)
+ wallet.walletpassphrase("test", 999000)
wallet.importdescriptors([{
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'),
'timestamp': 'now',
@@ -619,7 +619,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", wallet.fundrawtransaction, rawtx)
# Refill the keypool.
- wallet.walletpassphrase("test", 100)
+ wallet.walletpassphrase("test", 999000)
wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address
wallet.walletlock()
@@ -634,7 +634,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert fundedTx["changepos"] != -1
# Now we need to unlock.
- wallet.walletpassphrase("test", 600)
+ wallet.walletpassphrase("test", 999000)
signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex'])
wallet.sendrawtransaction(signedTx['hex'])
self.generate(self.nodes[1], 1)
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index a39db3bfb8..0ba8a46bae 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -85,7 +85,7 @@ class KeyPoolTest(BitcoinTestFramework):
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
- nodes[0].walletpassphrase('test', 12000)
+ nodes[0].walletpassphrase("test", 999000)
nodes[0].keypoolrefill(6)
nodes[0].walletlock()
wi = nodes[0].getwalletinfo()
@@ -131,7 +131,7 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].getnewaddress()
assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getnewaddress)
- nodes[0].walletpassphrase('test', 100)
+ nodes[0].walletpassphrase("test", 999000)
nodes[0].keypoolrefill(100)
wi = nodes[0].getwalletinfo()
if self.options.descriptors:
@@ -170,7 +170,7 @@ class KeyPoolTest(BitcoinTestFramework):
else:
res = w2.importmulti([{'desc': desc, 'timestamp': 'now'}])
assert_equal(res[0]['success'], True)
- w1.walletpassphrase('test', 100)
+ w1.walletpassphrase("test", 999000)
res = w1.sendtoaddress(address=address, amount=0.00010000)
self.generate(nodes[0], 1)
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index 07a5ee8691..459693102b 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -89,6 +89,15 @@ SHA256_SUMS = {
"6b163cef7de4beb07b8cb3347095e0d76a584019b1891135cd1268a1f05b9d88": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-riscv64-linux-gnu.tar.gz"},
"e2f751512f3c0f00eb68ba946d9c829e6cf99422a61e8f5e0a7c109c318674d0": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-apple-darwin.tar.gz"},
"49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-linux-gnu.tar.gz"},
+
+ "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b": {"tag": "v25.0", "tarball": "bitcoin-25.0-aarch64-linux-gnu.tar.gz"},
+ "e537c8630b05e63242d979c3004f851fd73c2a10b5b4fdbb161788427c7b3c0f": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm-linux-gnueabihf.tar.gz"},
+ "3b35075d6c1209743611c705a13575be2668bc069bc6301ce78a2e1e53ebe7cc": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm64-apple-darwin.tar.gz"},
+ "0c8e135a6fd297270d3b65196042d761453493a022b5ff7fb847fc911e938214": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64-linux-gnu.tar.gz"},
+ "fa8af160782f5adfcea570f72b947073c1663b3e9c3cd0f82b216b609fe47573": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64le-linux-gnu.tar.gz"},
+ "fe6e347a66043946920c72c9c4afca301968101e6b82fb90a63d7885ebcceb32": {"tag": "v25.0", "tarball": "bitcoin-25.0-riscv64-linux-gnu.tar.gz"},
+ "5708fc639cdfc27347cccfd50db9b73b53647b36fb5f3a4a93537cbe8828c27f": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-apple-darwin.tar.gz"},
+ "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-linux-gnu.tar.gz"},
}