aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac2
-rw-r--r--contrib/asmap/README.md12
-rwxr-xr-xcontrib/asmap/asmap-tool.py156
-rw-r--r--contrib/asmap/asmap.py (renamed from contrib/seeds/asmap.py)6
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus28
-rwxr-xr-xcontrib/seeds/makeseeds.py5
-rw-r--r--depends/Makefile2
-rw-r--r--depends/builders/darwin.mk4
-rw-r--r--depends/builders/default.mk3
-rw-r--r--depends/config.site.in6
-rw-r--r--depends/funcs.mk3
-rw-r--r--depends/hosts/darwin.mk6
-rw-r--r--depends/hosts/default.mk2
-rw-r--r--depends/packages/native_llvm.mk1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/bench/bip324_ecdh.cpp4
-rw-r--r--src/bench/ccoins_caching.cpp3
-rw-r--r--src/bench/checkqueue.cpp3
-rw-r--r--src/bench/descriptors.cpp4
-rw-r--r--src/bench/ellswift.cpp4
-rw-r--r--src/bench/verify_script.cpp3
-rw-r--r--src/bitcoin-chainstate.cpp1
-rw-r--r--src/bitcoin-tx.cpp19
-rw-r--r--src/bitcoin-wallet.cpp3
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/init.cpp12
-rw-r--r--src/kernel/checks.cpp6
-rw-r--r--src/kernel/context.cpp7
-rw-r--r--src/kernel/context.h5
-rw-r--r--src/key.cpp16
-rw-r--r--src/key.h20
-rw-r--r--src/net_processing.cpp11
-rw-r--r--src/node/context.cpp2
-rw-r--r--src/node/context.h11
-rw-r--r--src/node/interfaces.cpp2
-rw-r--r--src/node/kernel_notifications.cpp1
-rw-r--r--src/qt/forms/debugwindow.ui2
-rw-r--r--src/qt/optionsdialog.cpp6
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/walletmodel.cpp13
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/mining.cpp1
-rw-r--r--src/test/fuzz/bip324.cpp2
-rw-r--r--src/test/fuzz/descriptor_parse.cpp2
-rw-r--r--src/test/fuzz/key.cpp2
-rw-r--r--src/test/fuzz/key_io.cpp2
-rw-r--r--src/test/fuzz/message.cpp2
-rw-r--r--src/test/fuzz/miniscript.cpp2
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp2
-rw-r--r--src/test/fuzz/script_sign.cpp2
-rw-r--r--src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp3
-rw-r--r--src/test/fuzz/txorphan.cpp28
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util/setup_common.h2
-rw-r--r--src/util/subprocess.h76
-rw-r--r--src/validation.cpp4
-rwxr-xr-xtest/functional/feature_assumeutxo.py4
-rwxr-xr-xtest/functional/feature_init.py2
-rwxr-xr-xtest/functional/feature_reindex_readonly.py2
-rwxr-xr-xtest/functional/mempool_limit.py10
-rwxr-xr-xtest/functional/mempool_packages.py26
-rwxr-xr-xtest/functional/p2p_1p1c_network.py7
-rwxr-xr-xtest/functional/p2p_opportunistic_1p1c.py7
-rwxr-xr-xtest/functional/p2p_outbound_eviction.py253
-rwxr-xr-xtest/functional/p2p_tx_download.py6
-rwxr-xr-xtest/functional/rpc_packages.py6
-rw-r--r--test/functional/test_framework/address.py7
-rw-r--r--test/functional/test_framework/mempool_util.py81
-rwxr-xr-xtest/functional/test_framework/test_node.py2
-rw-r--r--test/functional/test_framework/util.py59
-rw-r--r--test/functional/test_framework/wallet.py13
-rwxr-xr-xtest/functional/test_runner.py1
73 files changed, 719 insertions, 305 deletions
diff --git a/Makefile.am b/Makefile.am
index f8bef07c26..2ff6dd0a11 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -126,7 +126,7 @@ $(OSX_ZIP): deploydir
cd $(APP_DIST_DIR) && find . | sort | $(ZIP) -X@ $@
$(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING)
- OTOOL=$(OTOOL) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) $(OSX_VOLNAME) -translations-dir=$(QT_TRANSLATION_DIR)
+ OBJDUMP=$(OBJDUMP) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) $(OSX_VOLNAME) -translations-dir=$(QT_TRANSLATION_DIR)
deploydir: $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt
endif !BUILD_DARWIN
diff --git a/configure.ac b/configure.ac
index ee4400f21a..dfb7b99756 100644
--- a/configure.ac
+++ b/configure.ac
@@ -125,6 +125,7 @@ AC_PATH_PROG([GIT], [git])
AC_PATH_PROG([CCACHE], [ccache])
AC_PATH_PROG([XGETTEXT], [xgettext])
AC_PATH_PROG([HEXDUMP], [hexdump])
+AC_PATH_TOOL([OBJDUMP], [objdump])
AC_PATH_TOOL([OBJCOPY], [objcopy])
AC_PATH_PROG([DOXYGEN], [doxygen])
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
@@ -760,7 +761,6 @@ case $host in
;;
*)
AC_PATH_TOOL([DSYMUTIL], [dsymutil], [dsymutil])
- AC_PATH_TOOL([OTOOL], [otool], [otool])
AC_PATH_PROG([ZIP], [zip], [zip])
dnl libtool will try to strip the static lib, which is a problem for
diff --git a/contrib/asmap/README.md b/contrib/asmap/README.md
new file mode 100644
index 0000000000..5fab4b285e
--- /dev/null
+++ b/contrib/asmap/README.md
@@ -0,0 +1,12 @@
+# ASMap Tool
+
+Tool for performing various operations on textual and binary asmap files,
+particularly encoding/compressing the raw data to the binary format that can
+be used in Bitcoin Core with the `-asmap` option.
+
+Example usage:
+```
+python3 asmap-tool.py encode /path/to/input.file /path/to/output.file
+python3 asmap-tool.py decode /path/to/input.file /path/to/output.file
+python3 asmap-tool.py diff /path/to/first.file /path/to/second.file
+```
diff --git a/contrib/asmap/asmap-tool.py b/contrib/asmap/asmap-tool.py
new file mode 100755
index 0000000000..09c28725e4
--- /dev/null
+++ b/contrib/asmap/asmap-tool.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
+
+import argparse
+import sys
+import ipaddress
+import math
+
+import asmap
+
+def load_file(input_file):
+ try:
+ contents = input_file.read()
+ except OSError as err:
+ sys.exit(f"Input file '{input_file.name}' cannot be read: {err.strerror}.")
+ try:
+ bin_asmap = asmap.ASMap.from_binary(contents)
+ except ValueError:
+ bin_asmap = None
+ txt_error = None
+ entries = None
+ try:
+ txt_contents = str(contents, encoding="utf-8")
+ except UnicodeError:
+ txt_error = "invalid UTF-8"
+ txt_contents = None
+ if txt_contents is not None:
+ entries = []
+ for line in txt_contents.split("\n"):
+ idx = line.find('#')
+ if idx >= 0:
+ line = line[:idx]
+ line = line.lstrip(' ').rstrip(' \t\r\n')
+ if len(line) == 0:
+ continue
+ fields = line.split(' ')
+ if len(fields) != 2:
+ txt_error = f"unparseable line '{line}'"
+ entries = None
+ break
+ prefix, asn = fields
+ if len(asn) <= 2 or asn[:2] != "AS" or any(c < '0' or c > '9' for c in asn[2:]):
+ txt_error = f"invalid ASN '{asn}'"
+ entries = None
+ break
+ try:
+ net = ipaddress.ip_network(prefix)
+ except ValueError:
+ txt_error = f"invalid network '{prefix}'"
+ entries = None
+ break
+ entries.append((asmap.net_to_prefix(net), int(asn[2:])))
+ if entries is not None and bin_asmap is not None and len(contents) > 0:
+ sys.exit(f"Input file '{input_file.name}' is ambiguous.")
+ if entries is not None:
+ state = asmap.ASMap()
+ state.update_multi(entries)
+ return state
+ if bin_asmap is not None:
+ return bin_asmap
+ sys.exit(f"Input file '{input_file.name}' is neither a valid binary asmap file nor valid text input ({txt_error}).")
+
+
+def save_binary(output_file, state, fill):
+ contents = state.to_binary(fill=fill)
+ try:
+ output_file.write(contents)
+ output_file.close()
+ except OSError as err:
+ sys.exit(f"Output file '{output_file.name}' cannot be written to: {err.strerror}.")
+
+def save_text(output_file, state, fill, overlapping):
+ for prefix, asn in state.to_entries(fill=fill, overlapping=overlapping):
+ net = asmap.prefix_to_net(prefix)
+ try:
+ print(f"{net} AS{asn}", file=output_file)
+ except OSError as err:
+ sys.exit(f"Output file '{output_file.name}' cannot be written to: {err.strerror}.")
+ try:
+ output_file.close()
+ except OSError as err:
+ sys.exit(f"Output file '{output_file.name}' cannot be written to: {err.strerror}.")
+
+def main():
+ parser = argparse.ArgumentParser(description="Tool for performing various operations on textual and binary asmap files.")
+ subparsers = parser.add_subparsers(title="valid subcommands", dest="subcommand")
+
+ parser_encode = subparsers.add_parser("encode", help="convert asmap data to binary format")
+ parser_encode.add_argument('-f', '--fill', dest="fill", default=False, action="store_true",
+ help="permit reassigning undefined network ranges arbitrarily to reduce size")
+ parser_encode.add_argument('infile', nargs='?', type=argparse.FileType('rb'), default=sys.stdin.buffer,
+ help="input asmap file (text or binary); default is stdin")
+ parser_encode.add_argument('outfile', nargs='?', type=argparse.FileType('wb'), default=sys.stdout.buffer,
+ help="output binary asmap file; default is stdout")
+
+ parser_decode = subparsers.add_parser("decode", help="convert asmap data to text format")
+ parser_decode.add_argument('-f', '--fill', dest="fill", default=False, action="store_true",
+ help="permit reassigning undefined network ranges arbitrarily to reduce length")
+ parser_decode.add_argument('-n', '--nonoverlapping', dest="overlapping", default=True, action="store_false",
+ help="output strictly non-overall ping network ranges (increases output size)")
+ parser_decode.add_argument('infile', nargs='?', type=argparse.FileType('rb'), default=sys.stdin.buffer,
+ help="input asmap file (text or binary); default is stdin")
+ parser_decode.add_argument('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout,
+ help="output text file; default is stdout")
+
+ parser_diff = subparsers.add_parser("diff", help="compute the difference between two asmap files")
+ parser_diff.add_argument('-i', '--ignore-unassigned', dest="ignore_unassigned", default=False, action="store_true",
+ help="ignore unassigned ranges in the first input (useful when second input is filled)")
+ parser_diff.add_argument('infile1', type=argparse.FileType('rb'),
+ help="first file to compare (text or binary)")
+ parser_diff.add_argument('infile2', type=argparse.FileType('rb'),
+ help="second file to compare (text or binary)")
+
+ args = parser.parse_args()
+ if args.subcommand is None:
+ parser.print_help()
+ elif args.subcommand == "encode":
+ state = load_file(args.infile)
+ save_binary(args.outfile, state, fill=args.fill)
+ elif args.subcommand == "decode":
+ state = load_file(args.infile)
+ save_text(args.outfile, state, fill=args.fill, overlapping=args.overlapping)
+ elif args.subcommand == "diff":
+ state1 = load_file(args.infile1)
+ state2 = load_file(args.infile2)
+ ipv4_changed = 0
+ ipv6_changed = 0
+ for prefix, old_asn, new_asn in state1.diff(state2):
+ if args.ignore_unassigned and old_asn == 0:
+ continue
+ net = asmap.prefix_to_net(prefix)
+ if isinstance(net, ipaddress.IPv4Network):
+ ipv4_changed += 1 << (32 - net.prefixlen)
+ elif isinstance(net, ipaddress.IPv6Network):
+ ipv6_changed += 1 << (128 - net.prefixlen)
+ if new_asn == 0:
+ print(f"# {net} was AS{old_asn}")
+ elif old_asn == 0:
+ print(f"{net} AS{new_asn} # was unassigned")
+ else:
+ print(f"{net} AS{new_asn} # was AS{old_asn}")
+ ipv4_change_str = "" if ipv4_changed == 0 else f" (2^{math.log2(ipv4_changed):.2f})"
+ ipv6_change_str = "" if ipv6_changed == 0 else f" (2^{math.log2(ipv6_changed):.2f})"
+
+ print(
+ f"# {ipv4_changed}{ipv4_change_str} IPv4 addresses changed; "
+ f"{ipv6_changed}{ipv6_change_str} IPv6 addresses changed"
+ )
+ else:
+ parser.print_help()
+ sys.exit("No command provided.")
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/seeds/asmap.py b/contrib/asmap/asmap.py
index 214805b5a5..2ae84a3f31 100644
--- a/contrib/seeds/asmap.py
+++ b/contrib/asmap/asmap.py
@@ -489,12 +489,14 @@ class ASMap:
if ctx not in ret or cand.size < ret[ctx].size:
ret[ctx] = cand
- for ctx in set(left) | set(right):
+ union = set(left) | set(right)
+ sorted_union = sorted(union, key=lambda x: (x is None, x))
+ for ctx in sorted_union:
candidate(ctx, left.get(ctx), right.get(ctx), _BinNode.make_branch)
candidate(ctx, left.get(None), right.get(ctx), _BinNode.make_branch)
candidate(ctx, left.get(ctx), right.get(None), _BinNode.make_branch)
if not hole:
- for ctx in set(ret) - set([None]):
+ for ctx in sorted(set(ret) - set([None])):
candidate(None, ctx, ret[ctx], _BinNode.make_default)
if None in ret:
ret = {ctx:enc for ctx, enc in ret.items()
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index 4b1d72650d..eaa7b896be 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -77,7 +77,7 @@ class FrameworkInfo(object):
bundleBinaryDirectory = "Contents/MacOS"
@classmethod
- def fromOtoolLibraryLine(cls, line: str) -> Optional['FrameworkInfo']:
+ def fromLibraryLine(cls, line: str) -> Optional['FrameworkInfo']:
# Note: line must be trimmed
if line == "":
return None
@@ -88,7 +88,7 @@ class FrameworkInfo(object):
m = cls.reOLine.match(line)
if m is None:
- raise RuntimeError(f"otool line could not be parsed: {line}")
+ raise RuntimeError(f"Line could not be parsed: {line}")
path = m.group(1)
@@ -120,7 +120,7 @@ class FrameworkInfo(object):
break
i += 1
if i == len(parts):
- raise RuntimeError(f"Could not find .framework or .dylib in otool line: {line}")
+ raise RuntimeError(f"Could not find .framework or .dylib in line: {line}")
info.frameworkName = parts[i]
info.frameworkDirectory = "/".join(parts[:i])
@@ -182,24 +182,24 @@ class DeploymentInfo(object):
return False
def getFrameworks(binaryPath: str, verbose: int) -> list[FrameworkInfo]:
+ objdump = os.getenv("OBJDUMP", "objdump")
if verbose:
- print(f"Inspecting with otool: {binaryPath}")
- otoolbin=os.getenv("OTOOL", "otool")
- otool = run([otoolbin, "-L", binaryPath], stdout=PIPE, stderr=PIPE, text=True)
- if otool.returncode != 0:
- sys.stderr.write(otool.stderr)
+ print(f"Inspecting with {objdump}: {binaryPath}")
+ output = run([objdump, "--macho", "--dylibs-used", binaryPath], stdout=PIPE, stderr=PIPE, text=True)
+ if output.returncode != 0:
+ sys.stderr.write(output.stderr)
sys.stderr.flush()
- raise RuntimeError(f"otool failed with return code {otool.returncode}")
+ raise RuntimeError(f"{objdump} failed with return code {output.returncode}")
- otoolLines = otool.stdout.split("\n")
- otoolLines.pop(0) # First line is the inspected binary
+ lines = output.stdout.split("\n")
+ lines.pop(0) # First line is the inspected binary
if ".framework" in binaryPath or binaryPath.endswith(".dylib"):
- otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency.
+ lines.pop(0) # Frameworks and dylibs list themselves as a dependency.
libraries = []
- for line in otoolLines:
+ for line in lines:
line = line.replace("@loader_path", os.path.dirname(binaryPath))
- info = FrameworkInfo.fromOtoolLibraryLine(line.strip())
+ info = FrameworkInfo.fromLibraryLine(line.strip())
if info is not None:
if verbose:
print("Found framework:")
diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py
index 79bb178b21..7fd0f7158e 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -9,11 +9,14 @@
import argparse
import collections
import ipaddress
+from pathlib import Path
import re
import sys
from typing import Union
-from asmap import ASMap, net_to_prefix
+asmap_dir = Path(__file__).parent.parent / "asmap"
+sys.path.append(str(asmap_dir))
+from asmap import ASMap, net_to_prefix # noqa: E402
NSEEDS=512
diff --git a/depends/Makefile b/depends/Makefile
index 005d9696fb..016c8f64ad 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -233,7 +233,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_
-e 's|@RANLIB@|$(host_RANLIB)|' \
-e 's|@NM@|$(host_NM)|' \
-e 's|@STRIP@|$(host_STRIP)|' \
- -e 's|@OTOOL@|$(host_OTOOL)|' \
+ -e 's|@OBJDUMP@|$(host_OBJDUMP)|' \
-e 's|@DSYMUTIL@|$(host_DSYMUTIL)|' \
-e 's|@build_os@|$(build_os)|' \
-e 's|@host_os@|$(host_os)|' \
diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk
index 554bfd2c3e..be04e1d8f3 100644
--- a/depends/builders/darwin.mk
+++ b/depends/builders/darwin.mk
@@ -3,7 +3,7 @@ build_darwin_CXX:=$(shell xcrun -f clang++) -isysroot$(shell xcrun --show-sdk-pa
build_darwin_AR:=$(shell xcrun -f ar)
build_darwin_RANLIB:=$(shell xcrun -f ranlib)
build_darwin_STRIP:=$(shell xcrun -f strip)
-build_darwin_OTOOL:=$(shell xcrun -f otool)
+build_darwin_OBJDUMP:=$(shell xcrun -f objdump)
build_darwin_NM:=$(shell xcrun -f nm)
build_darwin_DSYMUTIL:=$(shell xcrun -f dsymutil)
build_darwin_SHA256SUM=shasum -a 256
@@ -15,7 +15,7 @@ darwin_CXX:=$(shell xcrun -f clang++) -stdlib=libc++ -isysroot$(shell xcrun --sh
darwin_AR:=$(shell xcrun -f ar)
darwin_RANLIB:=$(shell xcrun -f ranlib)
darwin_STRIP:=$(shell xcrun -f strip)
-darwin_OTOOL:=$(shell xcrun -f otool)
+darwin_OBJDUMP:=$(shell xcrun -f objdump)
darwin_NM:=$(shell xcrun -f nm)
darwin_DSYMUTIL:=$(shell xcrun -f dsymutil)
darwin_native_binutils=
diff --git a/depends/builders/default.mk b/depends/builders/default.mk
index 50869cd8a2..2a1709d98a 100644
--- a/depends/builders/default.mk
+++ b/depends/builders/default.mk
@@ -1,6 +1,7 @@
default_build_CC = gcc
default_build_CXX = g++
default_build_AR = ar
+default_build_OBJDUMP = objdump
default_build_TAR = tar
default_build_RANLIB = ranlib
default_build_STRIP = strip
@@ -12,7 +13,7 @@ build_$(build_os)_$1 ?= $$(default_build_$1)
build_$(build_arch)_$(build_os)_$1 ?= $$(build_$(build_os)_$1)
build_$1=$$(build_$(build_arch)_$(build_os)_$1)
endef
-$(foreach var,CC CXX AR TAR RANLIB NM STRIP SHA256SUM DOWNLOAD OTOOL DSYMUTIL TOUCH,$(eval $(call add_build_tool_func,$(var))))
+$(foreach var,CC CXX AR TAR RANLIB NM STRIP SHA256SUM DOWNLOAD OBJDUMP DSYMUTIL TOUCH,$(eval $(call add_build_tool_func,$(var))))
define add_build_flags_func
build_$(build_arch)_$(build_os)_$1 += $(build_$(build_os)_$1)
build_$1=$$(build_$(build_arch)_$(build_os)_$1)
diff --git a/depends/config.site.in b/depends/config.site.in
index 81975f02b9..f6bed6a9d4 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -118,9 +118,9 @@ if test -n "@STRIP@"; then
fi
if test "@host_os@" = darwin; then
- if test -n "@OTOOL@"; then
- OTOOL="@OTOOL@"
- ac_cv_path_OTOOL="${OTOOL}"
+ if test -n "@OBJDUMP@"; then
+ OBJDUMP="@OBJDUMP@"
+ ac_cv_path_OBJDUMP="${OBJDUMP}"
fi
if test -n "@DSYMUTIL@"; then
diff --git a/depends/funcs.mk b/depends/funcs.mk
index d8a72421b4..7f79478cbd 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -180,6 +180,9 @@ $(1)_cmake=env CC="$$($(1)_cc)" \
CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \
LDFLAGS="$$($(1)_ldflags)" \
cmake -DCMAKE_INSTALL_PREFIX:PATH="$$($($(1)_type)_prefix)" \
+ -DCMAKE_AR=`which $$($(1)_ar)` \
+ -DCMAKE_NM=`which $$($(1)_nm)` \
+ -DCMAKE_RANLIB=`which $$($(1)_ranlib)` \
-DCMAKE_INSTALL_LIBDIR=lib/ \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCMAKE_VERBOSE_MAKEFILE:BOOL=$(V) \
diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk
index 639259ace3..2c4ef0644d 100644
--- a/depends/hosts/darwin.mk
+++ b/depends/hosts/darwin.mk
@@ -19,6 +19,8 @@ clang_prog=$(build_prefix)/bin/clang
clangxx_prog=$(clang_prog)++
llvm_config_prog=$(build_prefix)/bin/llvm-config
+darwin_OBJDUMP=$(build_prefix)/bin/$(host)-objdump
+
else
# FORCE_USE_SYSTEM_CLANG is non-empty, so we use the clang from the user's
# system
@@ -37,9 +39,11 @@ clangxx_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v clang++")
llvm_config_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-config")
llvm_lib_dir=$(shell $(llvm_config_prog) --libdir)
+
+darwin_OBJDUMP=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-objdump")
endif
-cctools_TOOLS=AR RANLIB STRIP NM OTOOL DSYMUTIL
+cctools_TOOLS=AR RANLIB STRIP NM DSYMUTIL
# Make-only lowercase function
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
diff --git a/depends/hosts/default.mk b/depends/hosts/default.mk
index 6a6cab6cc6..d82c33f29c 100644
--- a/depends/hosts/default.mk
+++ b/depends/hosts/default.mk
@@ -38,5 +38,5 @@ host_$1 = $$($(host_arch)_$(host_os)_$1)
host_$(release_type)_$1 = $$($(host_arch)_$(host_os)_$(release_type)_$1)
endef
-$(foreach tool,CC CXX AR RANLIB STRIP NM OBJCOPY OTOOL DSYMUTIL,$(eval $(call add_host_tool_func,$(tool))))
+$(foreach tool,CC CXX AR RANLIB STRIP NM OBJCOPY OBJDUMP DSYMUTIL,$(eval $(call add_host_tool_func,$(tool))))
$(foreach flags,CFLAGS CXXFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags))))
diff --git a/depends/packages/native_llvm.mk b/depends/packages/native_llvm.mk
index 09994eb012..1953c91bf4 100644
--- a/depends/packages/native_llvm.mk
+++ b/depends/packages/native_llvm.mk
@@ -18,6 +18,7 @@ define $(package)_stage_cmds
cp -P bin/clang++ $($(package)_staging_prefix_dir)/bin/ && \
cp bin/dsymutil $($(package)_staging_prefix_dir)/bin/$(host)-dsymutil && \
cp bin/llvm-config $($(package)_staging_prefix_dir)/bin/ && \
+ cp bin/llvm-objdump $($(package)_staging_prefix_dir)/bin/$(host)-objdump && \
cp include/llvm-c/ExternC.h $($(package)_staging_prefix_dir)/include/llvm-c && \
cp include/llvm-c/lto.h $($(package)_staging_prefix_dir)/include/llvm-c && \
cp lib/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \
diff --git a/src/Makefile.am b/src/Makefile.am
index 226ee9321f..b749651b72 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -945,7 +945,6 @@ libbitcoinkernel_la_SOURCES = \
kernel/disconnected_transactions.cpp \
kernel/mempool_persist.cpp \
kernel/mempool_removal_reason.cpp \
- key.cpp \
logging.cpp \
node/blockstorage.cpp \
node/chainstate.cpp \
diff --git a/src/bench/bip324_ecdh.cpp b/src/bench/bip324_ecdh.cpp
index fb10c2957e..88f3932ad8 100644
--- a/src/bench/bip324_ecdh.cpp
+++ b/src/bench/bip324_ecdh.cpp
@@ -14,7 +14,7 @@
static void BIP324_ECDH(benchmark::Bench& bench)
{
- ECC_Start();
+ ECC_Context ecc_context{};
FastRandomContext rng;
std::array<std::byte, 32> key_data;
@@ -44,8 +44,6 @@ static void BIP324_ECDH(benchmark::Bench& bench)
// - Copy 16 bytes from the resulting shared secret into the middle of their ellswift key.
std::copy(ret.begin() + 16, ret.end(), their_ellswift_data.begin() + 24);
});
-
- ECC_Stop();
}
BENCHMARK(BIP324_ECDH, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 4a3ec67c2b..05b2f5435c 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -18,7 +18,7 @@
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CCoinsCaching(benchmark::Bench& bench)
{
- ECC_Start();
+ ECC_Context ecc_context{};
FillableSigningProvider keystore;
CCoinsView coinsDummy;
@@ -47,7 +47,6 @@ static void CCoinsCaching(benchmark::Bench& bench)
bool success{AreInputsStandard(tx_1, coins)};
assert(success);
});
- ECC_Stop();
}
BENCHMARK(CCoinsCaching, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 114dd9d39c..c973fe9f71 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -25,7 +25,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
// We shouldn't ever be running with the checkqueue on a single core machine.
if (GetNumCores() <= 1) return;
- ECC_Start();
+ ECC_Context ecc_context{};
struct PrevectorJob {
prevector<PREVECTOR_SIZE, uint8_t> p;
@@ -62,6 +62,5 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
// it is done explicitly here for clarity
control.Wait();
});
- ECC_Stop();
}
BENCHMARK(CCheckQueueSpeedPrevectorJob, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/descriptors.cpp b/src/bench/descriptors.cpp
index fbef1395fb..5d6bcb8ce8 100644
--- a/src/bench/descriptors.cpp
+++ b/src/bench/descriptors.cpp
@@ -12,7 +12,7 @@
static void ExpandDescriptor(benchmark::Bench& bench)
{
- ECC_Start();
+ ECC_Context ecc_context{};
const auto desc_str = "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))";
const std::pair<int64_t, int64_t> range = {0, 1000};
@@ -27,8 +27,6 @@ static void ExpandDescriptor(benchmark::Bench& bench)
assert(success);
}
});
-
- ECC_Stop();
}
BENCHMARK(ExpandDescriptor, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/ellswift.cpp b/src/bench/ellswift.cpp
index 9441b4863e..4780db8e1c 100644
--- a/src/bench/ellswift.cpp
+++ b/src/bench/ellswift.cpp
@@ -9,7 +9,7 @@
static void EllSwiftCreate(benchmark::Bench& bench)
{
- ECC_Start();
+ ECC_Context ecc_context{};
CKey key = GenerateRandomKey();
uint256 entropy = GetRandHash();
@@ -22,8 +22,6 @@ static void EllSwiftCreate(benchmark::Bench& bench)
/* Use the last 32 bytes of the ellswift encoded public key as next entropy. */
std::copy(ret.begin() + 32, ret.begin() + 64, MakeWritableByteSpan(entropy).begin());
});
-
- ECC_Stop();
}
BENCHMARK(EllSwiftCreate, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index ee750bc1f8..f38aa49a23 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -15,7 +15,7 @@
// modified to measure performance of other types of scripts.
static void VerifyScriptBench(benchmark::Bench& bench)
{
- ECC_Start();
+ ECC_Context ecc_context{};
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
const int witnessversion = 0;
@@ -57,7 +57,6 @@ static void VerifyScriptBench(benchmark::Bench& bench)
assert(err == SCRIPT_ERR_OK);
assert(success);
});
- ECC_Stop();
}
static void VerifyNestedIfScript(benchmark::Bench& bench)
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index 642af06e82..4927634233 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -26,6 +26,7 @@
#include <script/sigcache.h>
#include <util/chaintype.h>
#include <util/fs.h>
+#include <util/signalinterrupt.h>
#include <util/task_runner.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 1c5b0c074c..cfac50e090 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -692,21 +692,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
tx = mergedTx;
}
-class Secp256k1Init
-{
-public:
- Secp256k1Init() {
- ECC_Start();
- }
- ~Secp256k1Init() {
- ECC_Stop();
- }
-};
-
static void MutateTx(CMutableTransaction& tx, const std::string& command,
const std::string& commandVal)
{
- std::unique_ptr<Secp256k1Init> ecc;
+ std::unique_ptr<ECC_Context> ecc;
if (command == "nversion")
MutateTxVersion(tx, commandVal);
@@ -726,10 +715,10 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
else if (command == "outpubkey") {
- ecc.reset(new Secp256k1Init());
+ ecc.reset(new ECC_Context());
MutateTxAddOutPubKey(tx, commandVal);
} else if (command == "outmultisig") {
- ecc.reset(new Secp256k1Init());
+ ecc.reset(new ECC_Context());
MutateTxAddOutMultiSig(tx, commandVal);
} else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
@@ -737,7 +726,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxAddOutData(tx, commandVal);
else if (command == "sign") {
- ecc.reset(new Secp256k1Init());
+ ecc.reset(new ECC_Context());
MutateTxSign(tx, commandVal);
}
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index bee052bc46..e6d20b55c2 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -128,10 +128,9 @@ MAIN_FUNCTION
return EXIT_FAILURE;
}
- ECC_Start();
+ ECC_Context ecc_context{};
if (!wallet::WalletTool::ExecuteWalletToolFunc(args, command->command)) {
return EXIT_FAILURE;
}
- ECC_Stop();
return EXIT_SUCCESS;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 7685bdea79..0b89aa42af 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -14,11 +14,13 @@
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/init.h>
+#include <kernel/context.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <noui.h>
#include <util/check.h>
#include <util/exception.h>
+#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/syserror.h>
#include <util/threadnames.h>
@@ -180,6 +182,7 @@ static bool AppInit(NodeContext& node)
}
node.kernel = std::make_unique<kernel::Context>();
+ node.ecc_context = std::make_unique<ECC_Context>();
if (!AppInitSanityChecks(*node.kernel))
{
// InitError will have been called with detailed error, which ends up on console
diff --git a/src/init.cpp b/src/init.cpp
index d2e0a80780..51ade4de93 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -32,6 +32,8 @@
#include <interfaces/chain.h>
#include <interfaces/init.h>
#include <interfaces/node.h>
+#include <kernel/context.h>
+#include <key.h>
#include <logging.h>
#include <mapport.h>
#include <net.h>
@@ -76,6 +78,7 @@
#include <util/fs_helpers.h>
#include <util/moneystr.h>
#include <util/result.h>
+#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/syserror.h>
@@ -372,6 +375,7 @@ void Shutdown(NodeContext& node)
node.chainman.reset();
node.validation_signals.reset();
node.scheduler.reset();
+ node.ecc_context.reset();
node.kernel.reset();
RemovePidFile(*node.args);
@@ -534,9 +538,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
- // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from
- // https://github.com/bitcoin/bitcoin/pull/23542 have become widespread.
- argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, signet: %u, regtest: %u). Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
#if HAVE_SOCKADDR_UN
argsman.AddArg("-proxy=<ip:port|path>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled). May be a local file path prefixed with 'unix:' if the proxy supports it.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
#else
@@ -1085,6 +1087,10 @@ bool AppInitSanityChecks(const kernel::Context& kernel)
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
}
+ if (!ECC_InitSanityCheck()) {
+ return InitError(strprintf(_("Elliptic curve cryptography sanity check failure. %s is shutting down."), PACKAGE_NAME));
+ }
+
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
// and a fork will cause weird behavior to it.
diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp
index 45a5e25093..e4a13ee4cc 100644
--- a/src/kernel/checks.cpp
+++ b/src/kernel/checks.cpp
@@ -4,8 +4,8 @@
#include <kernel/checks.h>
-#include <key.h>
#include <random.h>
+#include <util/result.h>
#include <util/translation.h>
#include <memory>
@@ -14,10 +14,6 @@ namespace kernel {
util::Result<void> SanityChecks(const Context&)
{
- if (!ECC_InitSanityCheck()) {
- return util::Error{Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")};
- }
-
if (!Random_SanityCheck()) {
return util::Error{Untranslated("OS cryptographic RNG sanity check failure. Aborting.")};
}
diff --git a/src/kernel/context.cpp b/src/kernel/context.cpp
index c60f1638d1..bfb17915fd 100644
--- a/src/kernel/context.cpp
+++ b/src/kernel/context.cpp
@@ -5,9 +5,7 @@
#include <kernel/context.h>
#include <crypto/sha256.h>
-#include <key.h>
#include <logging.h>
-#include <pubkey.h>
#include <random.h>
#include <string>
@@ -19,12 +17,7 @@ Context::Context()
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
- ECC_Start();
}
-Context::~Context()
-{
- ECC_Stop();
-}
} // namespace kernel
diff --git a/src/kernel/context.h b/src/kernel/context.h
index ff4df20473..159af12689 100644
--- a/src/kernel/context.h
+++ b/src/kernel/context.h
@@ -5,10 +5,6 @@
#ifndef BITCOIN_KERNEL_CONTEXT_H
#define BITCOIN_KERNEL_CONTEXT_H
-#include <util/signalinterrupt.h>
-
-#include <memory>
-
namespace kernel {
//! Context struct holding the kernel library's logically global state, and
//! passed to external libbitcoin_kernel functions which need access to this
@@ -19,7 +15,6 @@ namespace kernel {
//! should be stored to std::unique_ptr members pointing to opaque types.
struct Context {
Context();
- ~Context();
};
} // namespace kernel
diff --git a/src/key.cpp b/src/key.cpp
index 2bd6396298..e8458f2e3b 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -432,7 +432,8 @@ bool ECC_InitSanityCheck() {
return key.VerifyPubKey(pubkey);
}
-void ECC_Start() {
+/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
+static void ECC_Start() {
assert(secp256k1_context_sign == nullptr);
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
@@ -449,7 +450,8 @@ void ECC_Start() {
secp256k1_context_sign = ctx;
}
-void ECC_Stop() {
+/** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */
+static void ECC_Stop() {
secp256k1_context *ctx = secp256k1_context_sign;
secp256k1_context_sign = nullptr;
@@ -457,3 +459,13 @@ void ECC_Stop() {
secp256k1_context_destroy(ctx);
}
}
+
+ECC_Context::ECC_Context()
+{
+ ECC_Start();
+}
+
+ECC_Context::~ECC_Context()
+{
+ ECC_Stop();
+}
diff --git a/src/key.h b/src/key.h
index 53acd179ba..36d093b7dc 100644
--- a/src/key.h
+++ b/src/key.h
@@ -236,13 +236,21 @@ struct CExtKey {
void SetSeed(Span<const std::byte> seed);
};
-/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
-void ECC_Start();
-
-/** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */
-void ECC_Stop();
-
/** Check that required EC support is available at runtime. */
bool ECC_InitSanityCheck();
+/**
+ * RAII class initializing and deinitializing global state for elliptic curve support.
+ * Only one instance may be initialized at a time.
+ *
+ * In the future global ECC state could be removed, and this class could contain
+ * state and be passed as an argument to ECC key functions.
+ */
+class ECC_Context
+{
+public:
+ ECC_Context();
+ ~ECC_Context();
+};
+
#endif // BITCOIN_KEY_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 57e2f7409b..5d2949af33 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -5441,16 +5441,19 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seco
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {
+ // The outbound peer has sent us a block with at least as much work as our current tip, so reset the timeout if it was set
if (state.m_chain_sync.m_timeout != 0s) {
state.m_chain_sync.m_timeout = 0s;
state.m_chain_sync.m_work_header = nullptr;
state.m_chain_sync.m_sent_getheaders = false;
}
} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
- // Our best block known by this peer is behind our tip, and we're either noticing
- // that for the first time, OR this peer was able to catch up to some earlier point
- // where we checked against our tip.
- // Either way, set a new timeout based on current tip.
+ // At this point we know that the outbound peer has either never sent us a block/header or they have, but its tip is behind ours
+ // AND
+ // we are noticing this for the first time (m_timeout is 0)
+ // OR we noticed this at some point within the last CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds and set a timeout
+ // for them, they caught up to our tip at the time of setting the timer but not to our current one (we've also advanced).
+ // Either way, set a new timeout based on our current tip.
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();
state.m_chain_sync.m_sent_getheaders = false;
diff --git a/src/node/context.cpp b/src/node/context.cpp
index ca56fa0b86..e32d21b383 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -8,6 +8,7 @@
#include <banman.h>
#include <interfaces/chain.h>
#include <kernel/context.h>
+#include <key.h>
#include <net.h>
#include <net_processing.h>
#include <netgroup.h>
@@ -16,6 +17,7 @@
#include <scheduler.h>
#include <txmempool.h>
#include <validation.h>
+#include <validationinterface.h>
namespace node {
NodeContext::NodeContext() = default;
diff --git a/src/node/context.h b/src/node/context.h
index 245f230aec..a7d92989dd 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -5,10 +5,7 @@
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
-#include <kernel/context.h>
-
#include <atomic>
-#include <cassert>
#include <cstdlib>
#include <functional>
#include <memory>
@@ -24,6 +21,7 @@ class ValidationSignals;
class CScheduler;
class CTxMemPool;
class ChainstateManager;
+class ECC_Context;
class NetGroupManager;
class PeerManager;
namespace interfaces {
@@ -32,6 +30,12 @@ class ChainClient;
class Init;
class WalletLoader;
} // namespace interfaces
+namespace kernel {
+struct Context;
+}
+namespace util {
+class SignalInterrupt;
+}
namespace node {
class KernelNotifications;
@@ -49,6 +53,7 @@ class KernelNotifications;
struct NodeContext {
//! libbitcoin_kernel context
std::unique_ptr<kernel::Context> kernel;
+ std::unique_ptr<ECC_Context> ecc_context;
//! Init interface for initializing current process and connecting to other processes.
interfaces::Init* init{nullptr};
//! Interrupt object used to track whether node shutdown was requested.
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 84fc92e69d..c886357a34 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -17,6 +17,7 @@
#include <interfaces/node.h>
#include <interfaces/wallet.h>
#include <kernel/chain.h>
+#include <kernel/context.h>
#include <kernel/mempool_entry.h>
#include <logging.h>
#include <mapport.h>
@@ -99,6 +100,7 @@ public:
if (!AppInitParameterInteraction(args())) return false;
m_context->kernel = std::make_unique<kernel::Context>();
+ m_context->ecc_context = std::make_unique<ECC_Context>();
if (!AppInitSanityChecks(*m_context->kernel)) return false;
if (!AppInitLockDataDirectory()) return false;
diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp
index 6578b383f7..e326d4a1f2 100644
--- a/src/node/kernel_notifications.cpp
+++ b/src/node/kernel_notifications.cpp
@@ -14,6 +14,7 @@
#include <node/abort.h>
#include <node/interface_ui.h>
#include <util/check.h>
+#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 60e9bcde33..eeea53864a 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1113,7 +1113,7 @@
<item row="3" column="0">
<widget class="QLabel" name="peerSessionIdLabel">
<property name="toolTip">
- <string>The BIP324 session ID string in hex, if any.</string>
+ <string>The BIP324 session ID string in hex.</string>
</property>
<property name="text">
<string>Session ID</string>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 15103baf59..ee53a59bb5 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -18,6 +18,7 @@
#include <node/chainstatemanager_args.h>
#include <netbase.h>
#include <txdb.h>
+#include <util/strencodings.h>
#include <chrono>
@@ -480,7 +481,10 @@ QValidator(parent)
QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
- // Validate the proxy
+ uint16_t port{0};
+ std::string hostname;
+ if (!SplitHostPort(input.toStdString(), port, hostname) || port != 0) return QValidator::Invalid;
+
CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
Proxy addrProxy = Proxy(serv, true);
if (addrProxy.IsValid())
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index b55b4cbe64..f261c6409d 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -115,7 +115,7 @@ HelpMessageDialog::~HelpMessageDialog()
void HelpMessageDialog::printToConsole()
{
// On other operating systems, the expected action is to print the message to the console.
- tfm::format(std::cout, "%s\n", qPrintable(text));
+ tfm::format(std::cout, "%s", qPrintable(text));
}
void HelpMessageDialog::showOrPrint()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index fe000bcbb8..87ad98a4cc 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -529,12 +529,6 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return false;
}
- WalletModel::UnlockContext ctx(requestUnlock());
- if(!ctx.isValid())
- {
- return false;
- }
-
// Short-circuit if we are returning a bumped transaction PSBT to clipboard
if (retval == QMessageBox::Save) {
// "Create Unsigned" clicked
@@ -549,10 +543,15 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
DataStream ssTx{};
ssTx << psbtx;
GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- Q_EMIT message(tr("PSBT copied"), tr("Copied to clipboard", "Fee-bump PSBT saved"), CClientUIInterface::MSG_INFORMATION);
+ Q_EMIT message(tr("PSBT copied"), tr("Fee-bump PSBT copied to clipboard"), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
return true;
}
+ WalletModel::UnlockContext ctx(requestUnlock());
+ if (!ctx.isValid()) {
+ return false;
+ }
+
assert(!m_wallet->privateKeysDisabled() || wallet().hasExternalSigner());
// sign bumped transaction
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c9997ae063..1abaeafb2a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -188,12 +188,12 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
UniValue objTx(UniValue::VOBJ);
TxToUniv(*tx, /*block_hash=*/uint256(), /*entry=*/objTx, /*include_hex=*/true, txundo, verbosity);
- txs.push_back(objTx);
+ txs.push_back(std::move(objTx));
}
break;
}
- result.pushKV("tx", txs);
+ result.pushKV("tx", std::move(txs));
return result;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 73987669a5..2391392bd7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -31,6 +31,7 @@
#include <script/signingprovider.h>
#include <txmempool.h>
#include <univalue.h>
+#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
diff --git a/src/test/fuzz/bip324.cpp b/src/test/fuzz/bip324.cpp
index 37c41f3895..8210e75cee 100644
--- a/src/test/fuzz/bip324.cpp
+++ b/src/test/fuzz/bip324.cpp
@@ -17,7 +17,7 @@ namespace {
void Initialize()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::MAIN);
}
diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp
index 6ea315d4e2..b9a5560ffb 100644
--- a/src/test/fuzz/descriptor_parse.cpp
+++ b/src/test/fuzz/descriptor_parse.cpp
@@ -55,7 +55,7 @@ static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_prov
void initialize_descriptor_parse()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::MAIN);
}
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
index 9e1e318e02..d389a29575 100644
--- a/src/test/fuzz/key.cpp
+++ b/src/test/fuzz/key.cpp
@@ -32,7 +32,7 @@
void initialize_key()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::REGTEST);
}
diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp
index 5f98f2b7f1..aefdefe233 100644
--- a/src/test/fuzz/key_io.cpp
+++ b/src/test/fuzz/key_io.cpp
@@ -14,7 +14,7 @@
void initialize_key_io()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::MAIN);
}
diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp
index b5c95441f8..75baaa2754 100644
--- a/src/test/fuzz/message.cpp
+++ b/src/test/fuzz/message.cpp
@@ -19,7 +19,7 @@
void initialize_message()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::REGTEST);
}
diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp
index 947424c4ac..f10007222c 100644
--- a/src/test/fuzz/miniscript.cpp
+++ b/src/test/fuzz/miniscript.cpp
@@ -1201,7 +1201,7 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
void FuzzInit()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
TEST_DATA.Init();
}
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index 1b7a732260..f6d82c3001 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -25,7 +25,7 @@ std::vector<std::string> g_all_messages;
void initialize_p2p_transport_serialization()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::REGTEST);
g_all_messages = getAllNetMessageTypes();
std::sort(g_all_messages.begin(), g_all_messages.end());
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index 9ae150e553..4695bc611b 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -26,7 +26,7 @@
void initialize_script_sign()
{
- ECC_Start();
+ static ECC_Context ecc_context{};
SelectParams(ChainType::REGTEST);
}
diff --git a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
index 74ef6bfd4e..ae0c8479cb 100644
--- a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
+++ b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
@@ -24,8 +24,7 @@ FUZZ_TARGET(secp256k1_ecdsa_signature_parse_der_lax)
secp256k1_ecdsa_signature sig_der_lax;
const bool parsed_der_lax = ecdsa_signature_parse_der_lax(&sig_der_lax, signature_bytes.data(), signature_bytes.size()) == 1;
if (parsed_der_lax) {
- ECC_Start();
+ ECC_Context ecc_context{};
(void)SigHasLowR(&sig_der_lax);
- ECC_Stop();
}
}
diff --git a/src/test/fuzz/txorphan.cpp b/src/test/fuzz/txorphan.cpp
index a44f47b00d..037c7af77f 100644
--- a/src/test/fuzz/txorphan.cpp
+++ b/src/test/fuzz/txorphan.cpp
@@ -37,13 +37,12 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
SetMockTime(ConsumeTime(fuzzed_data_provider));
TxOrphanage orphanage;
- std::vector<COutPoint> outpoints;
+ std::set<COutPoint> outpoints;
+
// initial outpoints used to construct transactions later
for (uint8_t i = 0; i < 4; i++) {
- outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
+ outpoints.emplace(Txid::FromUint256(uint256{i}), 0);
}
- // if true, allow duplicate input when constructing tx
- const bool duplicate_input = fuzzed_data_provider.ConsumeBool();
CTransactionRef ptx_potential_parent = nullptr;
@@ -53,29 +52,22 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
const CTransactionRef tx = [&] {
CMutableTransaction tx_mut;
const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
- const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
- // pick unique outpoints from outpoints as input
+ const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
+ // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
+ // running any transaction validation logic before adding transactions to the orphanage
for (uint32_t i = 0; i < num_in; i++) {
auto& prevout = PickValue(fuzzed_data_provider, outpoints);
- tx_mut.vin.emplace_back(prevout);
- // pop the picked outpoint if duplicate input is not allowed
- if (!duplicate_input) {
- std::swap(prevout, outpoints.back());
- outpoints.pop_back();
- }
+ // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
+ tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
}
// output amount will not affect txorphanage
for (uint32_t i = 0; i < num_out; i++) {
tx_mut.vout.emplace_back(CAmount{0}, CScript{});
}
- // restore previously popped outpoints
- for (auto& in : tx_mut.vin) {
- outpoints.push_back(in.prevout);
- }
auto new_tx = MakeTransactionRef(tx_mut);
- // add newly constructed transaction to outpoints
+ // add newly constructed outpoints to the coin pool
for (uint32_t i = 0; i < num_out; i++) {
- outpoints.emplace_back(new_tx->GetHash(), i);
+ outpoints.emplace(new_tx->GetHash(), i);
}
return new_tx;
}();
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 0fb5e4eb45..3b890c09db 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -183,6 +183,7 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
m_node.kernel = std::make_unique<kernel::Context>();
+ m_node.ecc_context = std::make_unique<ECC_Context>();
SetupEnvironment();
ValidationCacheSizes validation_cache_sizes{};
@@ -200,6 +201,7 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
BasicTestingSetup::~BasicTestingSetup()
{
+ m_node.ecc_context.reset();
m_node.kernel.reset();
SetMockTime(0s); // Reset mocktime for following tests
LogInstance().DisconnectTestLogger();
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 8ccf9b571c..dbd66e3585 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -6,6 +6,7 @@
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <common/args.h> // IWYU pragma: export
+#include <kernel/context.h>
#include <key.h>
#include <node/caches.h>
#include <node/context.h> // IWYU pragma: export
@@ -15,6 +16,7 @@
#include <util/chaintype.h> // IWYU pragma: export
#include <util/check.h>
#include <util/fs.h>
+#include <util/signalinterrupt.h>
#include <util/string.h>
#include <util/vector.h>
diff --git a/src/util/subprocess.h b/src/util/subprocess.h
index af408b31d4..e76ced687c 100644
--- a/src/util/subprocess.h
+++ b/src/util/subprocess.h
@@ -902,17 +902,9 @@ private:
* Command provided in a single string.
* wait() - Wait for the child to exit.
* retcode() - The return code of the exited child.
- * pid() - PID of the spawned child.
- * poll() - Check the status of the running child.
* send(...) - Send input to the input channel of the child.
* communicate(...) - Get the output/error from the child and close the channels
* from the parent side.
- * input() - Get the input channel/File pointer. Can be used for
- * customizing the way of sending input to child.
- * output() - Get the output channel/File pointer. Usually used
- in case of redirection. See piping examples.
- * error() - Get the error channel/File pointer. Usually used
- in case of redirection.
*/
class Popen
{
@@ -956,14 +948,10 @@ public:
execute_process();
}
- int pid() const noexcept { return child_pid_; }
-
int retcode() const noexcept { return retcode_; }
int wait() noexcept(false);
- int poll() noexcept(false);
-
void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
@@ -1001,15 +989,6 @@ public:
return communicate(nullptr, 0);
}
- FILE* input() { return stream_.input(); }
- FILE* output() { return stream_.output();}
- FILE* error() { return stream_.error(); }
-
- /// Stream close APIs
- void close_input() { stream_.input_.reset(); }
- void close_output() { stream_.output_.reset(); }
- void close_error() { stream_.error_.reset(); }
-
private:
template <typename F, typename... Args>
void init_args(F&& farg, Args&&... args);
@@ -1033,7 +1012,6 @@ private:
std::vector<std::string> vargs_;
std::vector<char*> cargv_;
- bool child_created_ = false;
// Pid of the child process
int child_pid_ = -1;
@@ -1068,7 +1046,7 @@ inline int Popen::wait() noexcept(false)
return 0;
#else
int ret, status;
- std::tie(ret, status) = util::wait_for_child_exit(pid());
+ std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
if (ret == -1) {
if (errno != ECHILD) throw OSError("waitpid failed", errno);
return 0;
@@ -1081,56 +1059,6 @@ inline int Popen::wait() noexcept(false)
#endif
}
-inline int Popen::poll() noexcept(false)
-{
-#ifdef __USING_WINDOWS__
- int ret = WaitForSingleObject(process_handle_, 0);
- if (ret != WAIT_OBJECT_0) return -1;
-
- DWORD dretcode_;
- if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
- throw OSError("GetExitCodeProcess", 0);
-
- retcode_ = (int)dretcode_;
- CloseHandle(process_handle_);
-
- return retcode_;
-#else
- if (!child_created_) return -1; // TODO: ??
-
- int status;
-
- // Returns zero if child is still running
- int ret = waitpid(child_pid_, &status, WNOHANG);
- if (ret == 0) return -1;
-
- if (ret == child_pid_) {
- if (WIFSIGNALED(status)) {
- retcode_ = WTERMSIG(status);
- } else if (WIFEXITED(status)) {
- retcode_ = WEXITSTATUS(status);
- } else {
- retcode_ = 255;
- }
- return retcode_;
- }
-
- if (ret == -1) {
- // From subprocess.py
- // This happens if SIGCHLD is set to be ignored
- // or waiting for child process has otherwise been disabled
- // for our process. This child is dead, we cannot get the
- // status.
- if (errno == ECHILD) retcode_ = 0;
- else throw OSError("waitpid failed", errno);
- } else {
- retcode_ = ret;
- }
-
- return retcode_;
-#endif
-}
-
inline void Popen::execute_process() noexcept(false)
{
#ifdef __USING_WINDOWS__
@@ -1233,8 +1161,6 @@ inline void Popen::execute_process() noexcept(false)
throw OSError("fork failed", errno);
}
- child_created_ = true;
-
if (child_pid_ == 0)
{
// Close descriptors belonging to parent
diff --git a/src/validation.cpp b/src/validation.cpp
index c2b56555a2..609ccec153 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2748,8 +2748,10 @@ bool Chainstate::FlushStateToDisk(
return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
- if (!CoinsTip().Flush())
+ const auto empty_cache{(mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fFlushForPrune};
+ if (empty_cache ? !CoinsTip().Flush() : !CoinsTip().Sync()) {
return FatalError(m_chainman.GetNotifications(), state, _("Failed to write to coin database."));
+ }
m_last_flush = nNow;
full_flush_completed = true;
TRACE5(utxocache, flush,
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 58a5442f4e..0d6c92c9fa 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -405,6 +405,10 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(snapshot['snapshot_blockhash'], dump_output['base_hash'])
assert_equal(snapshot['validated'], False)
+ self.log.info("Check that loading the snapshot again will fail because there is already an active snapshot.")
+ with n2.assert_debug_log(expected_msgs=["[snapshot] can't activate a snapshot-based chainstate more than once"]):
+ assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", n2.loadtxoutset, dump_output['path'])
+
self.connect_nodes(0, 2)
self.wait_until(lambda: n2.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
self.sync_blocks()
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index 268009b0f4..22ae0c307b 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -85,7 +85,7 @@ class InitStressTest(BitcoinTestFramework):
for terminate_line in lines_to_terminate_after:
self.log.info(f"Starting node and will exit after line {terminate_line}")
- with node.wait_for_debug_log([terminate_line]):
+ with node.busy_wait_for_debug_log([terminate_line]):
node.start(extra_args=['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'])
self.log.debug("Terminating node after terminate line was found")
sigterm_node()
diff --git a/test/functional/feature_reindex_readonly.py b/test/functional/feature_reindex_readonly.py
index 25cff87a3b..52c0bb26a6 100755
--- a/test/functional/feature_reindex_readonly.py
+++ b/test/functional/feature_reindex_readonly.py
@@ -75,7 +75,7 @@ class BlockstoreReindexTest(BitcoinTestFramework):
if undo_immutable:
self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
- with self.nodes[0].wait_for_debug_log([b"Reindexing finished"]):
+ with self.nodes[0].assert_debug_log(["Reindexing finished"], timeout=60):
self.start_node(0, extra_args=['-reindex', '-fastprune'])
assert block_count == self.nodes[0].getblockcount()
undo_immutable()
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index e8a568f7ab..d46924f4ce 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -6,6 +6,9 @@
from decimal import Decimal
+from test_framework.mempool_util import (
+ fill_mempool,
+)
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -13,7 +16,6 @@ from test_framework.util import (
assert_fee_amount,
assert_greater_than,
assert_raises_rpc_error,
- fill_mempool,
)
from test_framework.wallet import (
COIN,
@@ -93,7 +95,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- fill_mempool(self, node, self.wallet)
+ fill_mempool(self, node)
current_info = node.getmempoolinfo()
mempoolmin_feerate = current_info["mempoolminfee"]
@@ -183,7 +185,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- fill_mempool(self, node, self.wallet)
+ fill_mempool(self, node)
current_info = node.getmempoolinfo()
mempoolmin_feerate = current_info["mempoolminfee"]
@@ -257,7 +259,7 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
- fill_mempool(self, node, self.wallet)
+ fill_mempool(self, node)
# Deliberately try to create a tx with a fee less than the minimum mempool fee to assert that it does not get added to the mempool
self.log.info('Create a mempool tx that will not pass mempoolminfee')
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index dcb66b2ca1..e83c62915e 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -199,13 +199,13 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert set(mempool1).issubset(set(mempool0))
for tx in chain[:CUSTOM_ANCESTOR_LIMIT]:
assert tx in mempool1
- # TODO: more detailed check of node1's mempool (fees etc.)
- # check transaction unbroadcast info (should be false if in both mempools)
- mempool = self.nodes[0].getrawmempool(True)
- for tx in mempool:
- assert_equal(mempool[tx]['unbroadcast'], False)
-
- # TODO: test ancestor size limits
+ entry0 = self.nodes[0].getmempoolentry(tx)
+ entry1 = self.nodes[1].getmempoolentry(tx)
+ assert not entry0['unbroadcast']
+ assert not entry1['unbroadcast']
+ assert_equal(entry1['fees']['base'], entry0['fees']['base'])
+ assert_equal(entry1['vsize'], entry0['vsize'])
+ assert_equal(entry1['depends'], entry0['depends'])
# Now test descendant chain limits
@@ -251,10 +251,14 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert tx in mempool1
for tx in chain[CUSTOM_DESCENDANT_LIMIT:]:
assert tx not in mempool1
- # TODO: more detailed check of node1's mempool (fees etc.)
-
- # TODO: test descendant size limits
-
+ for tx in mempool1:
+ entry0 = self.nodes[0].getmempoolentry(tx)
+ entry1 = self.nodes[1].getmempoolentry(tx)
+ assert not entry0['unbroadcast']
+ assert not entry1['unbroadcast']
+ assert_equal(entry1['fees']['base'], entry0['fees']['base'])
+ assert_equal(entry1['vsize'], entry0['vsize'])
+ assert_equal(entry1['depends'], entry0['depends'])
# Test reorg handling
# First, the basics:
self.generate(self.nodes[0], 1)
diff --git a/test/functional/p2p_1p1c_network.py b/test/functional/p2p_1p1c_network.py
index e88c826962..ea59248506 100755
--- a/test/functional/p2p_1p1c_network.py
+++ b/test/functional/p2p_1p1c_network.py
@@ -12,6 +12,9 @@ too-low-feerate transactions). The packages should be received and accepted by a
from decimal import Decimal
from math import ceil
+from test_framework.mempool_util import (
+ fill_mempool,
+)
from test_framework.messages import (
msg_tx,
)
@@ -22,7 +25,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
- fill_mempool,
)
from test_framework.wallet import (
MiniWallet,
@@ -45,8 +47,7 @@ class PackageRelayTest(BitcoinTestFramework):
self.supports_cli = False
def raise_network_minfee(self):
- filler_wallet = MiniWallet(self.nodes[0])
- fill_mempool(self, self.nodes[0], filler_wallet)
+ fill_mempool(self, self.nodes[0])
self.log.debug("Wait for the network to sync mempools")
self.sync_mempools()
diff --git a/test/functional/p2p_opportunistic_1p1c.py b/test/functional/p2p_opportunistic_1p1c.py
index e07acd5481..aec6e95fbc 100755
--- a/test/functional/p2p_opportunistic_1p1c.py
+++ b/test/functional/p2p_opportunistic_1p1c.py
@@ -8,6 +8,9 @@ Test opportunistic 1p1c package submission logic.
from decimal import Decimal
import time
+from test_framework.mempool_util import (
+ fill_mempool,
+)
from test_framework.messages import (
CInv,
CTxInWitness,
@@ -24,7 +27,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
- fill_mempool,
)
from test_framework.wallet import (
MiniWallet,
@@ -386,8 +388,7 @@ class PackageRelayTest(BitcoinTestFramework):
self.generate(self.wallet_nonsegwit, 10)
self.generate(self.wallet, 20)
- filler_wallet = MiniWallet(node)
- fill_mempool(self, node, filler_wallet)
+ fill_mempool(self, node)
self.log.info("Check opportunistic 1p1c logic when parent (txid != wtxid) is received before child")
self.test_basic_parent_then_child(self.wallet)
diff --git a/test/functional/p2p_outbound_eviction.py b/test/functional/p2p_outbound_eviction.py
new file mode 100755
index 0000000000..8d89688999
--- /dev/null
+++ b/test/functional/p2p_outbound_eviction.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019-2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+""" Test node outbound peer eviction logic
+
+A subset of our outbound peers are subject to eviction logic if they cannot keep up
+with our vision of the best chain. This criteria applies only to non-protected peers,
+and can be triggered by either not learning about any blocks from an outbound peer after
+a certain deadline, or by them not being able to catch up fast enough (under the same deadline).
+
+This tests the different eviction paths based on the peer's behavior and on whether they are protected
+or not.
+"""
+import time
+
+from test_framework.messages import (
+ from_hex,
+ msg_headers,
+ CBlockHeader,
+)
+from test_framework.p2p import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+
+# Timeouts (in seconds)
+CHAIN_SYNC_TIMEOUT = 20 * 60
+HEADERS_RESPONSE_TIME = 2 * 60
+
+
+class P2POutEvict(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def test_outbound_eviction_unprotected(self):
+ # This tests the eviction logic for **unprotected** outbound peers (that is, PeerManagerImpl::ConsiderEviction)
+ node = self.nodes[0]
+ cur_mock_time = node.mocktime
+
+ # Get our tip header and its parent
+ tip_header = from_hex(CBlockHeader(), node.getblockheader(node.getbestblockhash(), False))
+ prev_header = from_hex(CBlockHeader(), node.getblockheader(f"{tip_header.hashPrevBlock:064x}", False))
+
+ self.log.info("Create an outbound connection and don't send any headers")
+ # Test disconnect due to no block being announced in 22+ minutes (headers are not even exchanged)
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")
+ # Wait for over 20 min to trigger the first eviction timeout. This sets the last call past 2 min in the future.
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+ # Wait for over 2 more min to trigger the disconnection
+ peer.wait_for_getheaders(block_hash=tip_header.hashPrevBlock)
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Test that the peer gets evicted")
+ peer.wait_for_disconnect()
+
+ self.log.info("Create an outbound connection and send header but never catch up")
+ # Mimic a node that just falls behind for long enough
+ # This should also apply for a node doing IBD that does not catch up in time
+ # Connect a peer and make it send us headers ending in our tip's parent
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")
+ peer.send_and_ping(msg_headers([prev_header]))
+
+ # Trigger the timeouts
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+ peer.wait_for_getheaders(block_hash=tip_header.hashPrevBlock)
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Test that the peer gets evicted")
+ peer.wait_for_disconnect()
+
+ self.log.info("Create an outbound connection and keep lagging behind, but not too much")
+ # Test that if the peer never catches up with our current tip, but it does with the
+ # expected work that we set when setting the timer (that is, our tip at the time)
+ # we do not disconnect the peer
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")
+
+ self.log.info("Mine a block so our peer starts lagging")
+ prev_prev_hash = tip_header.hashPrevBlock
+ best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
+ peer.sync_with_ping()
+
+ self.log.info("Keep catching up with the old tip and check that we are not evicted")
+ for i in range(10):
+ # Generate an additional block so the peers is 2 blocks behind
+ prev_header = from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))
+ best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
+ peer.sync_with_ping()
+
+ # Advance time but not enough to evict the peer
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+
+ # Wait until we get out last call (by receiving a getheaders)
+ peer.wait_for_getheaders(block_hash=prev_prev_hash)
+
+ # Send a header with the previous tip (so we go back to 1 block behind)
+ peer.send_and_ping(msg_headers([prev_header]))
+ prev_prev_hash = tip_header.hash
+
+ self.log.info("Create an outbound connection and take some time to catch up, but do it in time")
+ # Check that if the peer manages to catch up within time, the timeouts are removed (and the peer is not disconnected)
+ # We are reusing the peer from the previous case which already sent us a valid (but old) block and whose timer is ticking
+
+ # Send an updated headers message matching our tip
+ peer.send_and_ping(msg_headers([from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))]))
+
+ # Wait for long enough for the timeouts to have triggered and check that we are still connected
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Test that the peer does not get evicted")
+ peer.sync_with_ping()
+
+ node.disconnect_p2ps()
+
+ def test_outbound_eviction_protected(self):
+ # This tests the eviction logic for **protected** outbound peers (that is, PeerManagerImpl::ConsiderEviction)
+ # Outbound connections are flagged as protected as long as they have sent us a connecting block with at least as
+ # much work as our current tip and we have enough empty protected_peers slots.
+ node = self.nodes[0]
+ cur_mock_time = node.mocktime
+ tip_header = from_hex(CBlockHeader(), node.getblockheader(node.getbestblockhash(), False))
+
+ self.log.info("Create an outbound connection to a peer that shares our tip so it gets granted protection")
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")
+ peer.send_and_ping(msg_headers([tip_header]))
+
+ self.log.info("Mine a new block and sync with our peer")
+ self.generateblock(node, output="raw(42)", transactions=[])
+ peer.sync_with_ping()
+
+ self.log.info("Let enough time pass for the timeouts to go off")
+ # Trigger the timeouts and check how we are still connected
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+ peer.wait_for_getheaders(block_hash=tip_header.hashPrevBlock)
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Test that the node does not get evicted")
+ peer.sync_with_ping()
+
+ node.disconnect_p2ps()
+
+ def test_outbound_eviction_mixed(self):
+ # This tests the outbound eviction logic for a mix of protected and unprotected peers.
+ node = self.nodes[0]
+ cur_mock_time = node.mocktime
+
+ self.log.info("Create a mix of protected and unprotected outbound connections to check against eviction")
+
+ # Let's try this logic having multiple peers, some protected and some unprotected
+ # We protect up to 4 peers as long as they have provided a block with the same amount of work as our tip
+ self.log.info("The first 4 peers are protected by sending us a valid block with enough work")
+ tip_header = from_hex(CBlockHeader(), node.getblockheader(node.getbestblockhash(), False))
+ headers_message = msg_headers([tip_header])
+ protected_peers = []
+ for i in range(4):
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="outbound-full-relay")
+ peer.send_and_ping(headers_message)
+ protected_peers.append(peer)
+
+ # We can create 4 additional outbound connections to peers that are unprotected. 2 of them will be well behaved,
+ # whereas the other 2 will misbehave (1 sending no headers, 1 sending old ones)
+ self.log.info("The remaining 4 peers will be mixed between honest (2) and misbehaving peers (2)")
+ prev_header = from_hex(CBlockHeader(), node.getblockheader(f"{tip_header.hashPrevBlock:064x}", False))
+ headers_message = msg_headers([prev_header])
+ honest_unprotected_peers = []
+ for i in range(2):
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=4+i, connection_type="outbound-full-relay")
+ peer.send_and_ping(headers_message)
+ honest_unprotected_peers.append(peer)
+
+ misbehaving_unprotected_peers = []
+ for i in range(2):
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=6+i, connection_type="outbound-full-relay")
+ if i%2==0:
+ peer.send_and_ping(headers_message)
+ misbehaving_unprotected_peers.append(peer)
+
+ self.log.info("Mine a new block and keep the unprotected honest peer on sync, all the rest off-sync")
+ # Mine a block so all peers become outdated
+ target_hash = prev_header.rehash()
+ tip_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
+ tip_header = from_hex(CBlockHeader(), node.getblockheader(tip_hash, False))
+ tip_headers_message = msg_headers([tip_header])
+
+ # Let the timeouts hit and check back
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ for peer in protected_peers + misbehaving_unprotected_peers:
+ peer.sync_with_ping()
+ peer.wait_for_getheaders(block_hash=target_hash)
+ for peer in honest_unprotected_peers:
+ peer.send_and_ping(tip_headers_message)
+ peer.wait_for_getheaders(block_hash=target_hash)
+
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Check how none of the honest nor protected peers was evicted but all the misbehaving unprotected were")
+ for peer in protected_peers + honest_unprotected_peers:
+ peer.sync_with_ping()
+ for peer in misbehaving_unprotected_peers:
+ peer.wait_for_disconnect()
+
+ node.disconnect_p2ps()
+
+ def test_outbound_eviction_blocks_relay_only(self):
+ # The logic for outbound eviction protection only applies to outbound-full-relay peers
+ # This tests that other types of peers (blocks-relay-only for instance) are not granted protection
+ node = self.nodes[0]
+ cur_mock_time = node.mocktime
+ tip_header = from_hex(CBlockHeader(), node.getblockheader(node.getbestblockhash(), False))
+
+ self.log.info("Create an blocks-only outbound connection to a peer that shares our tip. This would usually grant protection")
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only")
+ peer.send_and_ping(msg_headers([tip_header]))
+
+ self.log.info("Mine a new block and sync with our peer")
+ self.generateblock(node, output="raw(42)", transactions=[])
+ peer.sync_with_ping()
+
+ self.log.info("Let enough time pass for the timeouts to go off")
+ # Trigger the timeouts and check how the peer gets evicted, since protection is only given to outbound-full-relay peers
+ cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
+ node.setmocktime(cur_mock_time)
+ peer.sync_with_ping()
+ peer.wait_for_getheaders(block_hash=tip_header.hash)
+ cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
+ node.setmocktime(cur_mock_time)
+ self.log.info("Test that the peer gets evicted")
+ peer.wait_for_disconnect()
+
+ node.disconnect_p2ps()
+
+
+ def run_test(self):
+ self.nodes[0].setmocktime(int(time.time()))
+ self.test_outbound_eviction_unprotected()
+ self.test_outbound_eviction_protected()
+ self.test_outbound_eviction_mixed()
+ self.test_outbound_eviction_blocks_relay_only()
+
+
+if __name__ == '__main__':
+ P2POutEvict().main()
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 7a50f1e605..0af6b1d2c9 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -8,6 +8,9 @@ Test transaction download behavior
from decimal import Decimal
import time
+from test_framework.mempool_util import (
+ fill_mempool,
+)
from test_framework.messages import (
CInv,
MSG_TX,
@@ -24,7 +27,6 @@ from test_framework.p2p import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- fill_mempool,
)
from test_framework.wallet import MiniWallet
@@ -248,7 +250,7 @@ class TxDownloadTest(BitcoinTestFramework):
def test_rejects_filter_reset(self):
self.log.info('Check that rejected tx is not requested again')
node = self.nodes[0]
- fill_mempool(self, node, self.wallet)
+ fill_mempool(self, node)
self.wallet.rescan_utxos()
mempoolminfee = node.getmempoolinfo()['mempoolminfee']
peer = node.add_p2p_connection(TestP2PConn())
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index baf47be7ee..8ac0afdaaa 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -8,6 +8,9 @@ from decimal import Decimal
import random
from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.mempool_util import (
+ fill_mempool,
+)
from test_framework.messages import (
MAX_BIP125_RBF_SEQUENCE,
tx_from_hex,
@@ -18,7 +21,6 @@ from test_framework.util import (
assert_equal,
assert_fee_amount,
assert_raises_rpc_error,
- fill_mempool,
)
from test_framework.wallet import (
DEFAULT_FEE,
@@ -398,7 +400,7 @@ class RPCPackagesTest(BitcoinTestFramework):
])
self.wallet.rescan_utxos()
- fill_mempool(self, node, self.wallet)
+ fill_mempool(self, node)
minrelay = node.getmempoolinfo()["minrelaytxfee"]
parent = self.wallet.create_self_transfer(
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 5b2e3289a9..bcb38b21cd 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -47,7 +47,7 @@ class AddressType(enum.Enum):
b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
-def create_deterministic_address_bcrt1_p2tr_op_true():
+def create_deterministic_address_bcrt1_p2tr_op_true(explicit_internal_key=None):
"""
Generates a deterministic bech32m address (segwit v1 output) that
can be spent with a witness stack of OP_TRUE and the control block
@@ -55,9 +55,10 @@ def create_deterministic_address_bcrt1_p2tr_op_true():
Returns a tuple with the generated address and the internal key.
"""
- internal_key = (1).to_bytes(32, 'big')
+ internal_key = explicit_internal_key or (1).to_bytes(32, 'big')
address = output_key_to_p2tr(taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).output_pubkey)
- assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
+ if explicit_internal_key is None:
+ assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
return (address, internal_key)
diff --git a/test/functional/test_framework/mempool_util.py b/test/functional/test_framework/mempool_util.py
new file mode 100644
index 0000000000..148cc935ed
--- /dev/null
+++ b/test/functional/test_framework/mempool_util.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright (c) 2024 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Helpful routines for mempool testing."""
+from decimal import Decimal
+
+from .blocktools import (
+ COINBASE_MATURITY,
+)
+from .util import (
+ assert_equal,
+ assert_greater_than,
+ create_lots_of_big_transactions,
+ gen_return_txouts,
+)
+from .wallet import (
+ MiniWallet,
+)
+
+
+def fill_mempool(test_framework, node):
+ """Fill mempool until eviction.
+
+ Allows for simpler testing of scenarios with floating mempoolminfee > minrelay
+ Requires -datacarriersize=100000 and
+ -maxmempool=5.
+ It will not ensure mempools become synced as it
+ is based on a single node and assumes -minrelaytxfee
+ is 1 sat/vbyte.
+ To avoid unintentional tx dependencies, the mempool filling txs are created with a
+ tagged ephemeral miniwallet instance.
+ """
+ test_framework.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises")
+ txouts = gen_return_txouts()
+ relayfee = node.getnetworkinfo()['relayfee']
+
+ assert_equal(relayfee, Decimal('0.00001000'))
+
+ tx_batch_size = 1
+ num_of_batches = 75
+ # Generate UTXOs to flood the mempool
+ # 1 to create a tx initially that will be evicted from the mempool later
+ # 75 transactions each with a fee rate higher than the previous one
+ ephemeral_miniwallet = MiniWallet(node, tag_name="fill_mempool_ephemeral_wallet")
+ test_framework.generate(ephemeral_miniwallet, 1 + num_of_batches * tx_batch_size)
+
+ # Mine enough blocks so that the UTXOs are allowed to be spent
+ test_framework.generate(node, COINBASE_MATURITY - 1)
+
+ # Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
+ # change their effective feerate and thus the order in which they are selected for eviction.
+ confirmed_utxos = [ephemeral_miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 1)]
+ assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 1)
+
+ test_framework.log.debug("Create a mempool tx that will be evicted")
+ tx_to_be_evicted_id = ephemeral_miniwallet.send_self_transfer(
+ from_node=node, utxo_to_spend=confirmed_utxos.pop(0), fee_rate=relayfee)["txid"]
+
+ # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
+ # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
+ # by 130 should result in a fee that corresponds to 2x of that fee rate
+ base_fee = relayfee * 130
+
+ test_framework.log.debug("Fill up the mempool with txs with higher fee rate")
+ with node.assert_debug_log(["rolling minimum fee bumped"]):
+ for batch_of_txid in range(num_of_batches):
+ fee = (batch_of_txid + 1) * base_fee
+ utxos = confirmed_utxos[:tx_batch_size]
+ create_lots_of_big_transactions(ephemeral_miniwallet, node, fee, tx_batch_size, txouts, utxos)
+ del confirmed_utxos[:tx_batch_size]
+
+ test_framework.log.debug("The tx should be evicted by now")
+ # The number of transactions created should be greater than the ones present in the mempool
+ assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
+ # Initial tx created should not be present in the mempool anymore as it had a lower fee rate
+ assert tx_to_be_evicted_id not in node.getrawmempool()
+
+ test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
+ assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 67e0be5280..d228bd8991 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -490,7 +490,7 @@ class TestNode():
self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
- def wait_for_debug_log(self, expected_msgs, timeout=60):
+ def busy_wait_for_debug_log(self, expected_msgs, timeout=60):
"""
Block until we see a particular debug log message fragment or until we exceed the timeout.
Return:
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 0de09b6440..c5b69a3954 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -496,65 +496,6 @@ def check_node_connections(*, node, num_in, num_out):
assert_equal(info["connections_in"], num_in)
assert_equal(info["connections_out"], num_out)
-def fill_mempool(test_framework, node, miniwallet):
- """Fill mempool until eviction.
-
- Allows for simpler testing of scenarios with floating mempoolminfee > minrelay
- Requires -datacarriersize=100000 and
- -maxmempool=5.
- It will not ensure mempools become synced as it
- is based on a single node and assumes -minrelaytxfee
- is 1 sat/vbyte.
- To avoid unintentional tx dependencies, it is recommended to use separate miniwallets for
- mempool filling vs transactions in tests.
- """
- test_framework.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises")
- txouts = gen_return_txouts()
- relayfee = node.getnetworkinfo()['relayfee']
-
- assert_equal(relayfee, Decimal('0.00001000'))
-
- tx_batch_size = 1
- num_of_batches = 75
- # Generate UTXOs to flood the mempool
- # 1 to create a tx initially that will be evicted from the mempool later
- # 75 transactions each with a fee rate higher than the previous one
- test_framework.generate(miniwallet, 1 + (num_of_batches * tx_batch_size))
-
- # Mine COINBASE_MATURITY - 1 blocks so that the UTXOs are allowed to be spent
- test_framework.generate(node, 100 - 1)
-
- # Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
- # change their effective feerate and thus the order in which they are selected for eviction.
- confirmed_utxos = [miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 1)]
- assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 1)
-
- test_framework.log.debug("Create a mempool tx that will be evicted")
- tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, utxo_to_spend=confirmed_utxos[0], fee_rate=relayfee)["txid"]
- del confirmed_utxos[0]
-
- # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
- # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
- # by 130 should result in a fee that corresponds to 2x of that fee rate
- base_fee = relayfee * 130
-
- test_framework.log.debug("Fill up the mempool with txs with higher fee rate")
- with node.assert_debug_log(["rolling minimum fee bumped"]):
- for batch_of_txid in range(num_of_batches):
- fee = (batch_of_txid + 1) * base_fee
- utxos = confirmed_utxos[:tx_batch_size]
- create_lots_of_big_transactions(miniwallet, node, fee, tx_batch_size, txouts, utxos)
- del confirmed_utxos[:tx_batch_size]
-
- test_framework.log.debug("The tx should be evicted by now")
- # The number of transactions created should be greater than the ones present in the mempool
- assert_greater_than(tx_batch_size * num_of_batches, len(node.getrawmempool()))
- # Initial tx created should not be present in the mempool anymore as it had a lower fee rate
- assert tx_to_be_evicted_id not in node.getrawmempool()
-
- test_framework.log.debug("Check that mempoolminfee is larger than minrelaytxfee")
- assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
- assert_greater_than(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
# Transaction/Block functions
#############################
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 470ed08ed4..4433cbcc55 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -32,6 +32,7 @@ from test_framework.messages import (
CTxIn,
CTxInWitness,
CTxOut,
+ hash256,
)
from test_framework.script import (
CScript,
@@ -65,7 +66,10 @@ class MiniWalletMode(Enum):
However, if the transactions need to be modified by the user (e.g. prepending
scriptSig for testing opcodes that are activated by a soft-fork), or the txs
should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK
- can be useful. Summary of modes:
+ can be useful. In order to avoid mixing of UTXOs between different MiniWallet
+ instances, a tag name can be passed to the default mode, to create different
+ output scripts. Note that the UTXOs from the pre-generated test chain can
+ only be spent if no tag is passed. Summary of modes:
| output | | tx is | can modify | needs
mode | description | address | standard | scriptSig | signing
@@ -80,22 +84,25 @@ class MiniWalletMode(Enum):
class MiniWallet:
- def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
+ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE, tag_name=None):
self._test_node = test_node
self._utxos = []
self._mode = mode
assert isinstance(mode, MiniWalletMode)
if mode == MiniWalletMode.RAW_OP_TRUE:
+ assert tag_name is None
self._scriptPubKey = bytes(CScript([OP_TRUE]))
elif mode == MiniWalletMode.RAW_P2PK:
# use simple deterministic private key (k=1)
+ assert tag_name is None
self._priv_key = ECKey()
self._priv_key.set((1).to_bytes(32, 'big'), True)
pub_key = self._priv_key.get_pubkey()
self._scriptPubKey = key_to_p2pk_script(pub_key.get_bytes())
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
- self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
+ internal_key = None if tag_name is None else hash256(tag_name.encode())
+ self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true(internal_key)
self._scriptPubKey = address_to_scriptpubkey(self._address)
# When the pre-mined test framework chain is used, it contains coinbase
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 5ccb9d9496..690ab64c83 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -285,6 +285,7 @@ BASE_SCRIPTS = [
'p2p_leak_tx.py --v1transport',
'p2p_leak_tx.py --v2transport',
'p2p_eviction.py',
+ 'p2p_outbound_eviction.py',
'p2p_ibd_stalling.py --v1transport',
'p2p_ibd_stalling.py --v2transport',
'p2p_net_deadlock.py --v1transport',