aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am53
-rw-r--r--src/Makefile.bench.include11
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include18
-rw-r--r--src/Makefile.test_fuzz.include7
-rw-r--r--src/Makefile.test_util.include28
-rw-r--r--src/addrdb.cpp4
-rw-r--r--src/banman.cpp8
-rw-r--r--src/bech32.cpp2
-rw-r--r--src/bench/addrman.cpp8
-rw-r--r--src/bench/base58.cpp6
-rw-r--r--src/bench/bech32.cpp4
-rw-r--r--src/bench/bench.cpp55
-rw-r--r--src/bench/bench.h22
-rw-r--r--src/bench/bench_bitcoin.cpp38
-rw-r--r--src/bench/block_assemble.cpp2
-rw-r--r--src/bench/ccoins_caching.cpp2
-rw-r--r--src/bench/chacha20.cpp6
-rw-r--r--src/bench/chacha_poly_aead.cpp18
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bench/checkqueue.cpp2
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/crypto_hash.cpp32
-rw-r--r--src/bench/descriptors.cpp8
-rw-r--r--src/bench/duplicate_inputs.cpp2
-rw-r--r--src/bench/examples.cpp2
-rw-r--r--src/bench/gcs_filter.cpp10
-rw-r--r--src/bench/hashpadding.cpp4
-rw-r--r--src/bench/lockedpool.cpp2
-rw-r--r--src/bench/logging.cpp10
-rw-r--r--src/bench/mempool_eviction.cpp2
-rw-r--r--src/bench/mempool_stress.cpp4
-rw-r--r--src/bench/merkle_root.cpp2
-rw-r--r--src/bench/nanobench.h2
-rw-r--r--src/bench/peer_eviction.cpp12
-rw-r--r--src/bench/poly1305.cpp6
-rw-r--r--src/bench/prevector.cpp4
-rw-r--r--src/bench/rollingbloom.cpp4
-rw-r--r--src/bench/rpc_blockchain.cpp4
-rw-r--r--src/bench/rpc_mempool.cpp2
-rw-r--r--src/bench/strencodings.cpp2
-rw-r--r--src/bench/util_time.cpp8
-rw-r--r--src/bench/verify_script.cpp4
-rw-r--r--src/bench/wallet_balance.cpp8
-rw-r--r--src/bench/wallet_loading.cpp4
-rw-r--r--src/bitcoin-chainstate.cpp4
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/bitcoin-tx.cpp3
-rw-r--r--src/bitcoin-wallet.cpp19
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/chainparamsseeds.h96
-rw-r--r--src/checkqueue.h3
-rw-r--r--src/common/run_command.cpp64
-rw-r--r--src/common/run_command.h21
-rw-r--r--src/compat/compat.h8
-rw-r--r--src/consensus/consensus.h2
-rw-r--r--src/crypto/chacha20.h2
-rw-r--r--src/crypto/hkdf_sha256_32.h2
-rw-r--r--src/crypto/hmac_sha256.h2
-rw-r--r--src/crypto/hmac_sha512.h2
-rw-r--r--src/crypto/poly1305.h2
-rw-r--r--src/crypto/ripemd160.h2
-rw-r--r--src/crypto/sha1.h2
-rw-r--r--src/crypto/sha256.h2
-rw-r--r--src/crypto/sha256_sse4.cpp2
-rw-r--r--src/crypto/sha3.h2
-rw-r--r--src/crypto/sha512.cpp2
-rw-r--r--src/crypto/sha512.h2
-rw-r--r--src/dbwrapper.cpp2
-rw-r--r--src/dbwrapper.h18
-rw-r--r--src/external_signer.cpp5
-rw-r--r--src/fs.cpp2
-rw-r--r--src/httpserver.cpp6
-rw-r--r--src/i2p.cpp3
-rw-r--r--src/index/base.cpp20
-rw-r--r--src/index/base.h11
-rw-r--r--src/index/blockfilterindex.cpp4
-rw-r--r--src/index/blockfilterindex.h5
-rw-r--r--src/index/coinstatsindex.cpp9
-rw-r--r--src/index/coinstatsindex.h16
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/index/txindex.h4
-rw-r--r--src/init.cpp131
-rw-r--r--src/interfaces/chain.h8
-rw-r--r--src/ipc/interfaces.cpp4
-rw-r--r--src/ipc/process.cpp2
-rw-r--r--src/kernel/chainstatemanager_opts.h14
-rw-r--r--src/kernel/mempool_limits.h9
-rw-r--r--src/kernel/mempool_persist.cpp2
-rw-r--r--src/kernel/mempool_persist.h4
-rw-r--r--src/leveldb/util/env_windows.cc2
-rw-r--r--src/logging.cpp6
-rw-r--r--src/logging.h2
-rw-r--r--src/memusage.h3
-rw-r--r--src/net.cpp38
-rw-r--r--src/net.h10
-rw-r--r--src/net_processing.cpp217
-rw-r--r--src/net_processing.h2
-rw-r--r--src/netbase.cpp22
-rw-r--r--src/netbase.h2
-rw-r--r--src/node/blockstorage.cpp25
-rw-r--r--src/node/blockstorage.h10
-rw-r--r--src/node/caches.cpp2
-rw-r--r--src/node/chainstate.cpp45
-rw-r--r--src/node/chainstatemanager_args.cpp39
-rw-r--r--src/node/chainstatemanager_args.h19
-rw-r--r--src/node/interfaces.cpp19
-rw-r--r--src/node/miner.cpp26
-rw-r--r--src/node/miner.h6
-rw-r--r--src/node/psbt.cpp2
-rw-r--r--src/node/txreconciliation.cpp184
-rw-r--r--src/node/txreconciliation.h90
-rw-r--r--src/node/utxo_snapshot.cpp91
-rw-r--r--src/node/utxo_snapshot.h35
-rw-r--r--src/policy/fees.cpp42
-rw-r--r--src/policy/fees.h11
-rw-r--r--src/policy/rbf.cpp3
-rw-r--r--src/prevector.h2
-rw-r--r--src/protocol.cpp2
-rw-r--r--src/protocol.h8
-rw-r--r--src/psbt.cpp49
-rw-r--r--src/psbt.h30
-rw-r--r--src/qt/bitcoingui.cpp2
-rw-r--r--src/qt/bitcoinstrings.cpp48
-rw-r--r--src/qt/forms/debugwindow.ui4
-rw-r--r--src/qt/forms/intro.ui2
-rw-r--r--src/qt/forms/optionsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/locale/bitcoin_en.ts164
-rw-r--r--src/qt/locale/bitcoin_en.xlf722
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/rpcconsole.cpp8
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/random.cpp2
-rw-r--r--src/rest.cpp44
-rw-r--r--src/rpc/blockchain.cpp294
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/fees.cpp2
-rw-r--r--src/rpc/mempool.cpp10
-rw-r--r--src/rpc/mining.cpp10
-rw-r--r--src/rpc/net.cpp19
-rw-r--r--src/rpc/output_script.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/server.cpp13
-rw-r--r--src/rpc/txoutproof.cpp2
-rw-r--r--src/rpc/util.cpp17
-rw-r--r--src/rpc/util.h28
-rw-r--r--src/script/miniscript.h284
-rw-r--r--src/script/standard.h2
-rw-r--r--src/streams.h9
-rw-r--r--src/support/cleanse.h2
-rw-r--r--src/sync.h49
-rw-r--r--src/test/banman_tests.cpp4
-rw-r--r--src/test/blockchain_tests.cpp4
-rw-r--r--src/test/blockencodings_tests.cpp1
-rw-r--r--src/test/blockmanager_tests.cpp42
-rw-r--r--src/test/coinstatsindex_tests.cpp21
-rw-r--r--src/test/denialofservice_tests.cpp48
-rw-r--r--src/test/fuzz/bitdeque.cpp7
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/mempool_utils.h19
-rw-r--r--src/test/fuzz/policy_estimator.cpp1
-rw-r--r--src/test/fuzz/pow.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp8
-rw-r--r--src/test/fuzz/process_messages.cpp7
-rw-r--r--src/test/fuzz/rbf.cpp2
-rw-r--r--src/test/fuzz/rpc.cpp1
-rw-r--r--src/test/fuzz/tx_pool.cpp7
-rw-r--r--src/test/fuzz/txorphan.cpp2
-rw-r--r--src/test/fuzz/util.cpp39
-rw-r--r--src/test/fuzz/util.h16
-rw-r--r--src/test/fuzz/util/mempool.cpp27
-rw-r--r--src/test/fuzz/util/mempool.h24
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp3
-rw-r--r--src/test/mempool_tests.cpp5
-rw-r--r--src/test/miner_tests.cpp466
-rw-r--r--src/test/miniscript_tests.cpp3
-rw-r--r--src/test/minisketch_tests.cpp2
-rw-r--r--src/test/net_tests.cpp9
-rw-r--r--src/test/netbase_tests.cpp23
-rw-r--r--src/test/policyestimator_tests.cpp1
-rw-r--r--src/test/raii_event_tests.cpp2
-rw-r--r--src/test/rbf_tests.cpp1
-rw-r--r--src/test/sigopcount_tests.cpp1
-rw-r--r--src/test/system_tests.cpp2
-rw-r--r--src/test/txindex_tests.cpp11
-rw-r--r--src/test/txreconciliation_tests.cpp86
-rw-r--r--src/test/util/chainstate.h56
-rw-r--r--src/test/util/net.cpp10
-rw-r--r--src/test/util/net.h9
-rw-r--r--src/test/util/setup_common.cpp69
-rw-r--r--src/test/util/setup_common.h58
-rw-r--r--src/test/util/txmempool.cpp39
-rw-r--r--src/test/util/txmempool.h37
-rw-r--r--src/test/util/validation.h2
-rw-r--r--src/test/util_tests.cpp7
-rw-r--r--src/test/validation_chainstate_tests.cpp11
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp412
-rw-r--r--src/test/validation_flush_tests.cpp4
-rw-r--r--src/txdb.h4
-rw-r--r--src/txmempool.cpp109
-rw-r--r--src/txmempool.h68
-rw-r--r--src/uint256.h11
-rw-r--r--src/univalue/include/univalue.h5
-rw-r--r--src/univalue/lib/univalue.cpp2
-rw-r--r--src/util/error.cpp5
-rw-r--r--src/util/error.h2
-rw-r--r--src/util/sock.cpp32
-rw-r--r--src/util/sock.h12
-rw-r--r--src/util/strencodings.cpp8
-rw-r--r--src/util/strencodings.h11
-rw-r--r--src/util/system.cpp76
-rw-r--r--src/util/system.h10
-rw-r--r--src/util/thread.cpp6
-rw-r--r--src/util/thread.h3
-rw-r--r--src/util/time.cpp16
-rw-r--r--src/util/time.h2
-rw-r--r--src/validation.cpp576
-rw-r--r--src/validation.h92
-rw-r--r--src/wallet/bdb.cpp8
-rw-r--r--src/wallet/interfaces.cpp24
-rw-r--r--src/wallet/rpc/backup.cpp70
-rw-r--r--src/wallet/rpc/coins.cpp12
-rw-r--r--src/wallet/rpc/spend.cpp31
-rw-r--r--src/wallet/rpc/transactions.cpp4
-rw-r--r--src/wallet/rpc/util.cpp16
-rw-r--r--src/wallet/rpc/util.h2
-rw-r--r--src/wallet/scriptpubkeyman.cpp31
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/spend.cpp40
-rw-r--r--src/wallet/test/coinselector_tests.cpp47
-rw-r--r--src/wallet/test/feebumper_tests.cpp2
-rw-r--r--src/wallet/test/fuzz/coinselection.cpp2
-rw-r--r--src/wallet/test/fuzz/parse_iso8601.cpp (renamed from src/test/fuzz/parse_iso8601.cpp)5
-rw-r--r--src/wallet/test/rpc_util_tests.cpp24
-rw-r--r--src/wallet/test/walletload_tests.cpp54
-rw-r--r--src/wallet/wallet.cpp255
-rw-r--r--src/wallet/wallet.h12
-rw-r--r--src/wallet/walletdb.cpp27
-rw-r--r--src/wallet/walletdb.h3
-rw-r--r--src/zmq/zmqabstractnotifier.h2
-rw-r--r--src/zmq/zmqnotificationinterface.cpp14
-rw-r--r--src/zmq/zmqnotificationinterface.h4
-rw-r--r--src/zmq/zmqpublishnotifier.cpp19
-rw-r--r--src/zmq/zmqpublishnotifier.h4
-rw-r--r--src/zmq/zmqrpc.cpp5
248 files changed, 4877 insertions, 2287 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bf26cc9674..6d10f86d57 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,7 @@ noinst_PROGRAMS =
TESTS =
BENCHMARKS =
-BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(LEVELDB_CPPFLAGS)
LIBBITCOIN_NODE=libbitcoin_node.a
LIBBITCOIN_COMMON=libbitcoin_common.a
@@ -133,6 +133,7 @@ BITCOIN_CORE_H = \
clientversion.h \
coins.h \
common/bloom.h \
+ common/run_command.h \
compat/assumptions.h \
compat/byteswap.h \
compat/compat.h \
@@ -197,6 +198,7 @@ BITCOIN_CORE_H = \
node/blockstorage.h \
node/caches.h \
node/chainstate.h \
+ node/chainstatemanager_args.h \
node/coin.h \
node/connection_types.h \
node/context.h \
@@ -208,6 +210,7 @@ BITCOIN_CORE_H = \
node/minisketchwrapper.h \
node/psbt.h \
node/transaction.h \
+ node/txreconciliation.h \
node/utxo_snapshot.h \
node/validation_cache_args.h \
noui.h \
@@ -344,12 +347,8 @@ obj/build.h: FORCE
"$(abs_top_srcdir)"
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
-
-# server: shared between bitcoind and bitcoin-qt
-# Contains code accessing mempool and chain state that is meant to be separated
-# from wallet and gui code (see node/README.md). Shared code should go in
-# libbitcoin_common or libbitcoin_util libraries, instead.
-libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+# node #
+libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_node_a_SOURCES = \
addrdb.cpp \
@@ -383,6 +382,7 @@ libbitcoin_node_a_SOURCES = \
node/blockstorage.cpp \
node/caches.cpp \
node/chainstate.cpp \
+ node/chainstatemanager_args.cpp \
node/coin.cpp \
node/connection_types.cpp \
node/context.cpp \
@@ -395,6 +395,8 @@ libbitcoin_node_a_SOURCES = \
node/minisketchwrapper.cpp \
node/psbt.cpp \
node/transaction.cpp \
+ node/txreconciliation.cpp \
+ node/utxo_snapshot.cpp \
node/validation_cache_args.cpp \
noui.cpp \
policy/fees.cpp \
@@ -437,7 +439,9 @@ endif
if !ENABLE_WALLET
libbitcoin_node_a_SOURCES += dummywallet.cpp
endif
+#
+# zmq #
if ENABLE_ZMQ
libbitcoin_zmq_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -448,11 +452,10 @@ libbitcoin_zmq_a_SOURCES = \
zmq/zmqrpc.cpp \
zmq/zmqutil.cpp
endif
+#
-
-# wallet: shared between bitcoind and bitcoin-qt, but only linked
-# when wallet enabled
-libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BDB_CPPFLAGS) $(SQLITE_CFLAGS)
+# wallet #
+libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(BDB_CPPFLAGS) $(SQLITE_CFLAGS)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
wallet/coincontrol.cpp \
@@ -490,14 +493,17 @@ endif
if USE_BDB
libbitcoin_wallet_a_SOURCES += wallet/bdb.cpp wallet/salvage.cpp
endif
+#
-libbitcoin_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+# wallet tool #
+libbitcoin_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libbitcoin_wallet_tool_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_tool_a_SOURCES = \
wallet/wallettool.cpp \
$(BITCOIN_CORE_H)
+#
-# crypto primitives library
+# crypto #
crypto_libbitcoin_crypto_base_la_CPPFLAGS = $(AM_CPPFLAGS)
# Specify -static in both CXXFLAGS and LDFLAGS so libtool will only build a
@@ -577,8 +583,9 @@ crypto_libbitcoin_crypto_arm_shani_la_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_arm_shani_la_CXXFLAGS += $(ARM_SHANI_CXXFLAGS)
crypto_libbitcoin_crypto_arm_shani_la_CPPFLAGS += -DENABLE_ARM_SHANI
crypto_libbitcoin_crypto_arm_shani_la_SOURCES = crypto/sha256_arm_shani.cpp
+#
-# consensus: shared between all executables that validate any consensus rules.
+# consensus #
libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_consensus_a_SOURCES = \
@@ -614,9 +621,10 @@ libbitcoin_consensus_a_SOURCES = \
util/strencodings.cpp \
util/strencodings.h \
version.h
+#
-# common: shared between bitcoind, and bitcoin-qt and non-server tools
-libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+# common #
+libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
@@ -624,6 +632,7 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \
coins.cpp \
common/bloom.cpp \
+ common/run_command.cpp \
compressor.cpp \
core_read.cpp \
core_write.cpp \
@@ -653,9 +662,10 @@ libbitcoin_common_a_SOURCES = \
script/standard.cpp \
warnings.cpp \
$(BITCOIN_CORE_H)
+#
-# util: shared between all executables.
-libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+# util #
+libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
support/lockedpool.cpp \
@@ -702,8 +712,9 @@ libbitcoin_util_a_SOURCES = \
if USE_LIBEVENT
libbitcoin_util_a_SOURCES += util/url.cpp
endif
+#
-# cli: shared between bitcoin-cli and bitcoin-qt
+# cli #
libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_cli_a_SOURCES = \
@@ -833,7 +844,7 @@ bitcoin_util_LDADD = \
# bitcoin-chainstate binary #
bitcoin_chainstate_SOURCES = bitcoin-chainstate.cpp
-bitcoin_chainstate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+bitcoin_chainstate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
bitcoin_chainstate_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
# $(LIBTOOL_APP_LDFLAGS) deliberately omitted here so that we can test linking
@@ -900,6 +911,7 @@ libbitcoinkernel_la_SOURCES = \
node/blockstorage.cpp \
node/chainstate.cpp \
node/interface_ui.cpp \
+ node/utxo_snapshot.cpp \
policy/feerate.cpp \
policy/fees.cpp \
policy/packages.cpp \
@@ -927,7 +939,6 @@ libbitcoinkernel_la_SOURCES = \
txdb.cpp \
txmempool.cpp \
uint256.cpp \
- util/bytevectorhash.cpp \
util/check.cpp \
util/getuniquepath.cpp \
util/hasher.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index a23d872250..e1e2066877 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -51,8 +51,9 @@ bench_bench_bitcoin_SOURCES = \
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
-bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
+bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
bench_bench_bitcoin_LDADD = \
$(LIBTEST_UTIL) \
$(LIBBITCOIN_NODE) \
@@ -66,7 +67,9 @@ bench_bench_bitcoin_LDADD = \
$(LIBSECP256K1) \
$(LIBUNIVALUE) \
$(EVENT_PTHREADS_LIBS) \
- $(EVENT_LIBS)
+ $(EVENT_LIBS) \
+ $(MINIUPNPC_LIBS) \
+ $(NATPMP_LIBS)
if ENABLE_ZMQ
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -76,11 +79,9 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp
+bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(SQLITE_LIBS)
endif
-bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS)
-bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
-
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index b4acc47aa1..602a118259 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -295,7 +295,7 @@ BITCOIN_QT_RC = qt/res/bitcoin-qt-res.rc
BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS -DQT_USE_QSTRINGBUILDER
qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
- $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS)
+ $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS) $(BOOST_CPPFLAGS)
qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index fa822f2954..89c659d4b9 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -27,7 +27,7 @@ TEST_QT_H = \
qt/test/wallettests.h
qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
- $(QT_INCLUDES) $(QT_TEST_INCLUDES)
+ $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(BOOST_CPPFLAGS)
qt_test_test_bitcoin_qt_SOURCES = \
init/bitcoin-qt.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 8a2386a2b4..571a85e5c9 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -77,6 +77,7 @@ BITCOIN_TESTS =\
test/blockencodings_tests.cpp \
test/blockfilter_index_tests.cpp \
test/blockfilter_tests.cpp \
+ test/blockmanager_tests.cpp \
test/bloom_tests.cpp \
test/bswap_tests.cpp \
test/checkqueue_tests.cpp \
@@ -147,6 +148,7 @@ BITCOIN_TESTS =\
test/transaction_tests.cpp \
test/txindex_tests.cpp \
test/txpackage_tests.cpp \
+ test/txreconciliation_tests.cpp \
test/txrequest_tests.cpp \
test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
@@ -174,7 +176,9 @@ BITCOIN_TESTS += \
wallet/test/availablecoins_tests.cpp \
wallet/test/init_tests.cpp \
wallet/test/ismine_tests.cpp \
- wallet/test/scriptpubkeyman_tests.cpp
+ wallet/test/rpc_util_tests.cpp \
+ wallet/test/scriptpubkeyman_tests.cpp \
+ wallet/test/walletload_tests.cpp
FUZZ_SUITE_LD_COMMON +=\
$(SQLITE_LIBS) \
@@ -185,7 +189,8 @@ BITCOIN_TESTS += wallet/test/db_tests.cpp
endif
FUZZ_WALLET_SRC = \
- wallet/test/fuzz/coinselection.cpp
+ wallet/test/fuzz/coinselection.cpp \
+ wallet/test/fuzz/parse_iso8601.cpp
if USE_SQLITE
FUZZ_WALLET_SRC += \
@@ -202,7 +207,7 @@ BITCOIN_TEST_SUITE += \
endif # ENABLE_WALLET
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
-test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
+test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(BOOST_CPPFLAGS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
@@ -222,7 +227,7 @@ FUZZ_SUITE_LD_COMMON += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_FUZZ_BINARY
-test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_fuzz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) $(RUNTIME_LDFLAGS)
@@ -287,7 +292,6 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/node_eviction.cpp \
test/fuzz/p2p_transport_serialization.cpp \
test/fuzz/parse_hd_keypath.cpp \
- test/fuzz/parse_iso8601.cpp \
test/fuzz/parse_numbers.cpp \
test/fuzz/parse_script.cpp \
test/fuzz/parse_univalue.cpp \
@@ -372,8 +376,8 @@ endif
if TARGET_WINDOWS
else
if ENABLE_BENCH
- @echo "Running bench/bench_bitcoin (one iteration sanity check)..."
- $(BENCH_BINARY) --sanity-check > /dev/null
+ @echo "Running bench/bench_bitcoin (one iteration sanity check, only high priority)..."
+ $(BENCH_BINARY) -sanity-check -priority-level=high > /dev/null
endif
endif
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include
index b43816636f..b35d713d57 100644
--- a/src/Makefile.test_fuzz.include
+++ b/src/Makefile.test_fuzz.include
@@ -10,12 +10,13 @@ EXTRA_LIBRARIES += \
TEST_FUZZ_H = \
test/fuzz/fuzz.h \
test/fuzz/FuzzedDataProvider.h \
- test/fuzz/mempool_utils.h \
- test/fuzz/util.h
+ test/fuzz/util.h \
+ test/fuzz/util/mempool.h
-libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_fuzz_a_SOURCES = \
test/fuzz/fuzz.cpp \
test/fuzz/util.cpp \
+ test/fuzz/util/mempool.cpp \
$(TEST_FUZZ_H)
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 9306bb6fcc..d142572b27 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -5,22 +5,23 @@
LIBTEST_UTIL=libtest_util.a
EXTRA_LIBRARIES += \
- $(LIBTEST_UTIL)
+ $(LIBTEST_UTIL)
TEST_UTIL_H = \
- test/util/blockfilter.h \
- test/util/chainstate.h \
- test/util/logging.h \
- test/util/mining.h \
- test/util/net.h \
- test/util/script.h \
- test/util/setup_common.h \
- test/util/str.h \
- test/util/transaction_utils.h \
- test/util/validation.h \
- test/util/wallet.h
+ test/util/blockfilter.h \
+ test/util/chainstate.h \
+ test/util/logging.h \
+ test/util/mining.h \
+ test/util/net.h \
+ test/util/script.h \
+ test/util/setup_common.h \
+ test/util/str.h \
+ test/util/transaction_utils.h \
+ test/util/txmempool.h \
+ test/util/validation.h \
+ test/util/wallet.h
-libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_util_a_SOURCES = \
test/util/blockfilter.cpp \
@@ -31,6 +32,7 @@ libtest_util_a_SOURCES = \
test/util/setup_common.cpp \
test/util/str.cpp \
test/util/transaction_utils.cpp \
+ test/util/txmempool.cpp \
test/util/validation.cpp \
test/util/wallet.cpp \
$(TEST_UTIL_H)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 31f8eadf98..7106d819b0 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -187,11 +187,11 @@ std::optional<bilingual_str> LoadAddrman(const NetGroupManager& netgroupman, con
auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
const auto path_addr{args.GetDataDirNet() / "peers.dat"};
try {
DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
- LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
+ LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
} catch (const DbNotFoundError&) {
// Addrman can be in an inconsistent state after failure, reset it
addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
diff --git a/src/banman.cpp b/src/banman.cpp
index 508383d9f2..3cd646c148 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -31,12 +31,12 @@ void BanMan::LoadBanlist()
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
- int64_t n_start = GetTimeMillis();
+ const auto start{SteadyClock::now()};
if (m_ban_db.Read(m_banned)) {
SweepBanned(); // sweep out unused entries
LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(),
- GetTimeMillis() - n_start);
+ Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
} else {
LogPrintf("Recreating the banlist database\n");
m_banned = {};
@@ -58,13 +58,13 @@ void BanMan::DumpBanlist()
SetBannedSetDirty(false);
}
- int64_t n_start = GetTimeMillis();
+ const auto start{SteadyClock::now()};
if (!m_ban_db.Write(banmap)) {
SetBannedSetDirty(true);
}
LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(),
- GetTimeMillis() - n_start);
+ Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
}
void BanMan::ClearBanned()
diff --git a/src/bech32.cpp b/src/bech32.cpp
index dce9b2e4cc..8e0025b8f4 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -241,7 +241,7 @@ constexpr std::array<uint32_t, 25> GenerateSyndromeConstants() {
std::array<uint32_t, 25> SYNDROME_CONSTS{};
for (int k = 1; k < 6; ++k) {
for (int shift = 0; shift < 5; ++shift) {
- int16_t b = GF1024_LOG.at(1 << shift);
+ int16_t b = GF1024_LOG.at(size_t{1} << shift);
int16_t c0 = GF1024_EXP.at((997*k + b) % 1023);
int16_t c1 = GF1024_EXP.at((998*k + b) % 1023);
int16_t c2 = GF1024_EXP.at((999*k + b) % 1023);
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index f14d1f89b6..019b133345 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -133,7 +133,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
});
}
-BENCHMARK(AddrManAdd);
-BENCHMARK(AddrManSelect);
-BENCHMARK(AddrManGetAddr);
-BENCHMARK(AddrManAddThenGood);
+BENCHMARK(AddrManAdd, benchmark::PriorityLevel::HIGH);
+BENCHMARK(AddrManSelect, benchmark::PriorityLevel::HIGH);
+BENCHMARK(AddrManGetAddr, benchmark::PriorityLevel::HIGH);
+BENCHMARK(AddrManAddThenGood, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 6f6b4e3bfa..3d08b7201b 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -50,6 +50,6 @@ static void Base58Decode(benchmark::Bench& bench)
}
-BENCHMARK(Base58Encode);
-BENCHMARK(Base58CheckEncode);
-BENCHMARK(Base58Decode);
+BENCHMARK(Base58Encode, benchmark::PriorityLevel::HIGH);
+BENCHMARK(Base58CheckEncode, benchmark::PriorityLevel::HIGH);
+BENCHMARK(Base58Decode, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 0f89a8e2b5..1a166e7081 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -32,5 +32,5 @@ static void Bech32Decode(benchmark::Bench& bench)
}
-BENCHMARK(Bech32Encode);
-BENCHMARK(Bech32Decode);
+BENCHMARK(Bech32Encode, benchmark::PriorityLevel::HIGH);
+BENCHMARK(Bech32Decode, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 26975bb59d..1a3a006286 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -6,6 +6,7 @@
#include <fs.h>
#include <test/util/setup_common.h>
+#include <util/string.h>
#include <chrono>
#include <fstream>
@@ -41,18 +42,42 @@ void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& bench
} // namespace
-benchmark::BenchRunner::BenchmarkMap& benchmark::BenchRunner::benchmarks()
+namespace benchmark {
+
+// map a label to one or multiple priority levels
+std::map<std::string, uint8_t> map_label_priority = {
+ {"high", PriorityLevel::HIGH},
+ {"low", PriorityLevel::LOW},
+ {"all", 0xff}
+};
+
+std::string ListPriorities()
{
- static std::map<std::string, BenchFunction> benchmarks_map;
+ using item_t = std::pair<std::string, uint8_t>;
+ auto sort_by_priority = [](item_t a, item_t b){ return a.second < b.second; };
+ std::set<item_t, decltype(sort_by_priority)> sorted_priorities(map_label_priority.begin(), map_label_priority.end(), sort_by_priority);
+ return Join(sorted_priorities, ',', [](const auto& entry){ return entry.first; });
+}
+
+uint8_t StringToPriority(const std::string& str)
+{
+ auto it = map_label_priority.find(str);
+ if (it == map_label_priority.end()) throw std::runtime_error(strprintf("Unknown priority level %s", str));
+ return it->second;
+}
+
+BenchRunner::BenchmarkMap& BenchRunner::benchmarks()
+{
+ static BenchmarkMap benchmarks_map;
return benchmarks_map;
}
-benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
+BenchRunner::BenchRunner(std::string name, BenchFunction func, PriorityLevel level)
{
- benchmarks().insert(std::make_pair(name, func));
+ benchmarks().insert(std::make_pair(name, std::make_pair(func, level)));
}
-void benchmark::BenchRunner::RunAll(const Args& args)
+void BenchRunner::RunAll(const Args& args)
{
std::regex reFilter(args.regex_filter);
std::smatch baseMatch;
@@ -62,13 +87,19 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
std::vector<ankerl::nanobench::Result> benchmarkResults;
- for (const auto& p : benchmarks()) {
- if (!std::regex_match(p.first, baseMatch, reFilter)) {
+ for (const auto& [name, bench_func] : benchmarks()) {
+ const auto& [func, priority_level] = bench_func;
+
+ if (!(priority_level & args.priority)) {
+ continue;
+ }
+
+ if (!std::regex_match(name, baseMatch, reFilter)) {
continue;
}
if (args.is_list_only) {
- std::cout << p.first << std::endl;
+ std::cout << name << std::endl;
continue;
}
@@ -76,7 +107,7 @@ void benchmark::BenchRunner::RunAll(const Args& args)
if (args.sanity_check) {
bench.epochs(1).epochIterations(1);
}
- bench.name(p.first);
+ bench.name(name);
if (args.min_time > 0ms) {
// convert to nanos before dividing to reduce rounding errors
std::chrono::nanoseconds min_time_ns = args.min_time;
@@ -84,11 +115,11 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
if (args.asymptote.empty()) {
- p.second(bench);
+ func(bench);
} else {
for (auto n : args.asymptote) {
bench.complexityN(n);
- p.second(bench);
+ func(bench);
}
std::cout << bench.complexityBigO() << std::endl;
}
@@ -103,3 +134,5 @@ void benchmark::BenchRunner::RunAll(const Args& args)
"{{/result}}");
GenerateTemplateResults(benchmarkResults, args.output_json, ankerl::nanobench::templates::json());
}
+
+} // namespace benchmark
diff --git a/src/bench/bench.h b/src/bench/bench.h
index 17535e4e81..63e1bf67e2 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -41,6 +41,16 @@ using ankerl::nanobench::Bench;
typedef std::function<void(Bench&)> BenchFunction;
+enum PriorityLevel : uint8_t
+{
+ LOW = 1 << 0,
+ HIGH = 1 << 2,
+};
+
+// List priority labels, comma-separated and sorted by increasing priority
+std::string ListPriorities();
+uint8_t StringToPriority(const std::string& str);
+
struct Args {
bool is_list_only;
bool sanity_check;
@@ -49,22 +59,24 @@ struct Args {
fs::path output_csv;
fs::path output_json;
std::string regex_filter;
+ uint8_t priority;
};
class BenchRunner
{
- typedef std::map<std::string, BenchFunction> BenchmarkMap;
+ // maps from "name" -> (function, priority_level)
+ typedef std::map<std::string, std::pair<BenchFunction, PriorityLevel>> BenchmarkMap;
static BenchmarkMap& benchmarks();
public:
- BenchRunner(std::string name, BenchFunction func);
+ BenchRunner(std::string name, BenchFunction func, PriorityLevel level);
static void RunAll(const Args& args);
};
} // namespace benchmark
-// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
-#define BENCHMARK(n) \
- benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n);
+// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo, priority_level);
+#define BENCHMARK(n, priority_level) \
+ benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n, priority_level);
#endif // BITCOIN_BENCH_BENCH_H
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 1bb4d34db9..1ac8db19fd 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -18,6 +18,8 @@
static const char* DEFAULT_BENCH_FILTER = ".*";
static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
+/** Priority level default value, run "all" priority levels */
+static const std::string DEFAULT_PRIORITY{"all"};
static void SetupBenchArgs(ArgsManager& argsman)
{
@@ -30,6 +32,8 @@ static void SetupBenchArgs(ArgsManager& argsman)
argsman.AddArg("-output-csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output-json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-sanity-check", "Run benchmarks for only one iteration", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-priority-level=<l1,l2,l3>", strprintf("Run benchmarks of one or multiple priority level(s) (%s), default: '%s'",
+ benchmark::ListPriorities(), DEFAULT_PRIORITY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
// parses a comma separated list like "10,20,30,50"
@@ -45,6 +49,14 @@ static std::vector<double> parseAsymptote(const std::string& str) {
return numbers;
}
+static uint8_t parsePriorityLevel(const std::string& str) {
+ uint8_t levels{0};
+ for (const auto& level: SplitString(str, ',')) {
+ levels |= benchmark::StringToPriority(level);
+ }
+ return levels;
+}
+
int main(int argc, char** argv)
{
ArgsManager argsman;
@@ -106,16 +118,22 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
- benchmark::Args args;
- args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
- args.is_list_only = argsman.GetBoolArg("-list", false);
- args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min-time", DEFAULT_MIN_TIME_MS));
- args.output_csv = argsman.GetPathArg("-output-csv");
- args.output_json = argsman.GetPathArg("-output-json");
- args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
- args.sanity_check = argsman.GetBoolArg("-sanity-check", false);
+ try {
+ benchmark::Args args;
+ args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
+ args.is_list_only = argsman.GetBoolArg("-list", false);
+ args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min-time", DEFAULT_MIN_TIME_MS));
+ args.output_csv = argsman.GetPathArg("-output-csv");
+ args.output_json = argsman.GetPathArg("-output-json");
+ args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
+ args.sanity_check = argsman.GetBoolArg("-sanity-check", false);
+ args.priority = parsePriorityLevel(argsman.GetArg("-priority-level", DEFAULT_PRIORITY));
- benchmark::BenchRunner::RunAll(args);
+ benchmark::BenchRunner::RunAll(args);
- return EXIT_SUCCESS;
+ return EXIT_SUCCESS;
+ } catch (const std::exception& e) {
+ tfm::format(std::cerr, "Error: %s\n", e.what());
+ return EXIT_FAILURE;
+ }
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 4ed5397330..09be011fda 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -47,4 +47,4 @@ static void AssembleBlock(benchmark::Bench& bench)
});
}
-BENCHMARK(AssembleBlock);
+BENCHMARK(AssembleBlock, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index 22dfb7aa5b..5d55ed9332 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -51,4 +51,4 @@ static void CCoinsCaching(benchmark::Bench& bench)
ECC_Stop();
}
-BENCHMARK(CCoinsCaching);
+BENCHMARK(CCoinsCaching, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp
index a6f4eec4ca..9584dd58bb 100644
--- a/src/bench/chacha20.cpp
+++ b/src/bench/chacha20.cpp
@@ -39,6 +39,6 @@ static void CHACHA20_1MB(benchmark::Bench& bench)
CHACHA20(bench, BUFFER_SIZE_LARGE);
}
-BENCHMARK(CHACHA20_64BYTES);
-BENCHMARK(CHACHA20_256BYTES);
-BENCHMARK(CHACHA20_1MB);
+BENCHMARK(CHACHA20_64BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_256BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_1MB, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp
index e994279a4d..15b3a4f310 100644
--- a/src/bench/chacha_poly_aead.cpp
+++ b/src/bench/chacha_poly_aead.cpp
@@ -115,12 +115,12 @@ static void HASH_1MB(benchmark::Bench& bench)
HASH(bench, BUFFER_SIZE_LARGE);
}
-BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT);
-BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT);
-BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT);
-BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT);
-BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT);
-BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT);
-BENCHMARK(HASH_64BYTES);
-BENCHMARK(HASH_256BYTES);
-BENCHMARK(HASH_1MB);
+BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT, benchmark::PriorityLevel::HIGH);
+BENCHMARK(HASH_64BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(HASH_256BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(HASH_1MB, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 53aa470042..747279c161 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -50,5 +50,5 @@ static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
});
}
-BENCHMARK(DeserializeBlockTest);
-BENCHMARK(DeserializeAndCheckBlockTest);
+BENCHMARK(DeserializeBlockTest, benchmark::PriorityLevel::HIGH);
+BENCHMARK(DeserializeAndCheckBlockTest, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 602081fb9b..8517c9fee2 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -70,4 +70,4 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
queue.StopWorkerThreads();
ECC_Stop();
}
-BENCHMARK(CCheckQueueSpeedPrevectorJob);
+BENCHMARK(CCheckQueueSpeedPrevectorJob, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 6ada28115e..53d89039a7 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -121,5 +121,5 @@ static void BnBExhaustion(benchmark::Bench& bench)
});
}
-BENCHMARK(CoinSelection);
-BENCHMARK(BnBExhaustion);
+BENCHMARK(CoinSelection, benchmark::PriorityLevel::HIGH);
+BENCHMARK(BnBExhaustion, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index d17ec503e7..162b02f344 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -150,19 +150,19 @@ static void MuHashPrecompute(benchmark::Bench& bench)
});
}
-BENCHMARK(RIPEMD160);
-BENCHMARK(SHA1);
-BENCHMARK(SHA256);
-BENCHMARK(SHA512);
-BENCHMARK(SHA3_256_1M);
-
-BENCHMARK(SHA256_32b);
-BENCHMARK(SipHash_32b);
-BENCHMARK(SHA256D64_1024);
-BENCHMARK(FastRandom_32bit);
-BENCHMARK(FastRandom_1bit);
-
-BENCHMARK(MuHash);
-BENCHMARK(MuHashMul);
-BENCHMARK(MuHashDiv);
-BENCHMARK(MuHashPrecompute);
+BENCHMARK(RIPEMD160, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA1, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA512, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA3_256_1M, benchmark::PriorityLevel::HIGH);
+
+BENCHMARK(SHA256_32b, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SipHash_32b, benchmark::PriorityLevel::HIGH);
+BENCHMARK(SHA256D64_1024, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_32bit, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_1bit, benchmark::PriorityLevel::HIGH);
+
+BENCHMARK(MuHash, benchmark::PriorityLevel::HIGH);
+BENCHMARK(MuHashMul, benchmark::PriorityLevel::HIGH);
+BENCHMARK(MuHashDiv, benchmark::PriorityLevel::HIGH);
+BENCHMARK(MuHashPrecompute, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/descriptors.cpp b/src/bench/descriptors.cpp
index 5c868a8573..972a6ff953 100644
--- a/src/bench/descriptors.cpp
+++ b/src/bench/descriptors.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
#include <key.h>
+#include <pubkey.h>
#include <script/descriptor.h>
#include <script/standard.h>
@@ -12,6 +13,9 @@
static void ExpandDescriptor(benchmark::Bench& bench)
{
+ const ECCVerifyHandle verify_handle;
+ ECC_Start();
+
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};
FlatSigningProvider provider;
@@ -25,6 +29,8 @@ static void ExpandDescriptor(benchmark::Bench& bench)
assert(success);
}
});
+
+ ECC_Stop();
}
-BENCHMARK(ExpandDescriptor);
+BENCHMARK(ExpandDescriptor, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 02a2e689b1..559854ff48 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -62,4 +62,4 @@ static void DuplicateInputs(benchmark::Bench& bench)
});
}
-BENCHMARK(DuplicateInputs);
+BENCHMARK(DuplicateInputs, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp
index 72a9922e94..abef69cc42 100644
--- a/src/bench/examples.cpp
+++ b/src/bench/examples.cpp
@@ -18,4 +18,4 @@ static void Trig(benchmark::Bench& bench)
});
}
-BENCHMARK(Trig);
+BENCHMARK(Trig, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp
index 80babb213b..b795ebff39 100644
--- a/src/bench/gcs_filter.cpp
+++ b/src/bench/gcs_filter.cpp
@@ -81,8 +81,8 @@ static void GCSFilterMatch(benchmark::Bench& bench)
filter.Match(GCSFilter::Element());
});
}
-BENCHMARK(GCSBlockFilterGetHash);
-BENCHMARK(GCSFilterConstruct);
-BENCHMARK(GCSFilterDecode);
-BENCHMARK(GCSFilterDecodeSkipCheck);
-BENCHMARK(GCSFilterMatch);
+BENCHMARK(GCSBlockFilterGetHash, benchmark::PriorityLevel::HIGH);
+BENCHMARK(GCSFilterConstruct, benchmark::PriorityLevel::HIGH);
+BENCHMARK(GCSFilterDecode, benchmark::PriorityLevel::HIGH);
+BENCHMARK(GCSFilterDecodeSkipCheck, benchmark::PriorityLevel::HIGH);
+BENCHMARK(GCSFilterMatch, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/hashpadding.cpp b/src/bench/hashpadding.cpp
index 753c8c2881..ac5aeebe51 100644
--- a/src/bench/hashpadding.cpp
+++ b/src/bench/hashpadding.cpp
@@ -26,7 +26,7 @@ static void PrePadded(benchmark::Bench& bench)
});
}
-BENCHMARK(PrePadded);
+BENCHMARK(PrePadded, benchmark::PriorityLevel::HIGH);
static void RegularPadded(benchmark::Bench& bench)
{
@@ -44,4 +44,4 @@ static void RegularPadded(benchmark::Bench& bench)
});
}
-BENCHMARK(RegularPadded);
+BENCHMARK(RegularPadded, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp
index b6d8824aba..ac8262654c 100644
--- a/src/bench/lockedpool.cpp
+++ b/src/bench/lockedpool.cpp
@@ -39,4 +39,4 @@ static void BenchLockedPool(benchmark::Bench& bench)
addr.clear();
}
-BENCHMARK(BenchLockedPool);
+BENCHMARK(BenchLockedPool, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/logging.cpp b/src/bench/logging.cpp
index d28777df9e..49a9e59893 100644
--- a/src/bench/logging.cpp
+++ b/src/bench/logging.cpp
@@ -41,8 +41,8 @@ static void LoggingNoFile(benchmark::Bench& bench)
});
}
-BENCHMARK(LoggingYoThreadNames);
-BENCHMARK(LoggingNoThreadNames);
-BENCHMARK(LoggingYoCategory);
-BENCHMARK(LoggingNoCategory);
-BENCHMARK(LoggingNoFile);
+BENCHMARK(LoggingYoThreadNames, benchmark::PriorityLevel::HIGH);
+BENCHMARK(LoggingNoThreadNames, benchmark::PriorityLevel::HIGH);
+BENCHMARK(LoggingYoCategory, benchmark::PriorityLevel::HIGH);
+BENCHMARK(LoggingNoCategory, benchmark::PriorityLevel::HIGH);
+BENCHMARK(LoggingNoFile, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index 60d991fab9..878e375a7c 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -132,4 +132,4 @@ static void MempoolEviction(benchmark::Bench& bench)
});
}
-BENCHMARK(MempoolEviction);
+BENCHMARK(MempoolEviction, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index 725a6f8f5b..9f5b28dca7 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -114,5 +114,5 @@ static void MempoolCheck(benchmark::Bench& bench)
});
}
-BENCHMARK(ComplexMemPool);
-BENCHMARK(MempoolCheck);
+BENCHMARK(ComplexMemPool, benchmark::PriorityLevel::HIGH);
+BENCHMARK(MempoolCheck, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/merkle_root.cpp b/src/bench/merkle_root.cpp
index ba6629b9f0..4140d67bc7 100644
--- a/src/bench/merkle_root.cpp
+++ b/src/bench/merkle_root.cpp
@@ -23,4 +23,4 @@ static void MerkleRoot(benchmark::Bench& bench)
});
}
-BENCHMARK(MerkleRoot);
+BENCHMARK(MerkleRoot, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/nanobench.h b/src/bench/nanobench.h
index 27df08fb69..916a7d61ef 100644
--- a/src/bench/nanobench.h
+++ b/src/bench/nanobench.h
@@ -858,7 +858,7 @@ public:
* @brief Retrieves all benchmark results collected by the bench object so far.
*
* Each call to run() generates a Result that is stored within the Bench instance. This is mostly for advanced users who want to
- * see all the nitty gritty detials.
+ * see all the nitty gritty details.
*
* @return All results collected so far.
*/
diff --git a/src/bench/peer_eviction.cpp b/src/bench/peer_eviction.cpp
index f05f5e8f64..c3e3670de7 100644
--- a/src/bench/peer_eviction.cpp
+++ b/src/bench/peer_eviction.cpp
@@ -141,15 +141,15 @@ static void EvictionProtection3Networks250Candidates(benchmark::Bench& bench)
// - 250 candidates is the number of peers reported by operators of busy nodes
// No disadvantaged networks, with 250 eviction candidates.
-BENCHMARK(EvictionProtection0Networks250Candidates);
+BENCHMARK(EvictionProtection0Networks250Candidates, benchmark::PriorityLevel::HIGH);
// 1 disadvantaged network (Tor) with 250 eviction candidates.
-BENCHMARK(EvictionProtection1Networks250Candidates);
+BENCHMARK(EvictionProtection1Networks250Candidates, benchmark::PriorityLevel::HIGH);
// 2 disadvantaged networks (I2P, Tor) with 250 eviction candidates.
-BENCHMARK(EvictionProtection2Networks250Candidates);
+BENCHMARK(EvictionProtection2Networks250Candidates, benchmark::PriorityLevel::HIGH);
// 3 disadvantaged networks (I2P/localhost/Tor) with 50/100/250 eviction candidates.
-BENCHMARK(EvictionProtection3Networks050Candidates);
-BENCHMARK(EvictionProtection3Networks100Candidates);
-BENCHMARK(EvictionProtection3Networks250Candidates);
+BENCHMARK(EvictionProtection3Networks050Candidates, benchmark::PriorityLevel::HIGH);
+BENCHMARK(EvictionProtection3Networks100Candidates, benchmark::PriorityLevel::HIGH);
+BENCHMARK(EvictionProtection3Networks250Candidates, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp
index cdef97c0ea..ad5a72ffde 100644
--- a/src/bench/poly1305.cpp
+++ b/src/bench/poly1305.cpp
@@ -36,6 +36,6 @@ static void POLY1305_1MB(benchmark::Bench& bench)
POLY1305(bench, BUFFER_SIZE_LARGE);
}
-BENCHMARK(POLY1305_64BYTES);
-BENCHMARK(POLY1305_256BYTES);
-BENCHMARK(POLY1305_1MB);
+BENCHMARK(POLY1305_64BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(POLY1305_256BYTES, benchmark::PriorityLevel::HIGH);
+BENCHMARK(POLY1305_1MB, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index b3688bab1b..9e2e7d11c4 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -85,12 +85,12 @@ static void PrevectorDeserialize(benchmark::Bench& bench)
{ \
Prevector##name<nontrivial_t>(bench); \
} \
- BENCHMARK(Prevector##name##Nontrivial); \
+ BENCHMARK(Prevector##name##Nontrivial, benchmark::PriorityLevel::HIGH); \
static void Prevector##name##Trivial(benchmark::Bench& bench) \
{ \
Prevector##name<trivial_t>(bench); \
} \
- BENCHMARK(Prevector##name##Trivial);
+ BENCHMARK(Prevector##name##Trivial, benchmark::PriorityLevel::HIGH);
PREVECTOR_TEST(Clear)
PREVECTOR_TEST(Destructor)
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 8f05e3bad0..865d99f9e8 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -32,5 +32,5 @@ static void RollingBloomReset(benchmark::Bench& bench)
});
}
-BENCHMARK(RollingBloom);
-BENCHMARK(RollingBloomReset);
+BENCHMARK(RollingBloom, benchmark::PriorityLevel::HIGH);
+BENCHMARK(RollingBloomReset, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index e6fc8d21f4..5a178f308a 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -45,7 +45,7 @@ static void BlockToJsonVerbose(benchmark::Bench& bench)
});
}
-BENCHMARK(BlockToJsonVerbose);
+BENCHMARK(BlockToJsonVerbose, benchmark::PriorityLevel::HIGH);
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
{
@@ -57,4 +57,4 @@ static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
});
}
-BENCHMARK(BlockToJsonVerboseWrite);
+BENCHMARK(BlockToJsonVerboseWrite, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp
index 0e6fdae3d7..4fdc31ae05 100644
--- a/src/bench/rpc_mempool.cpp
+++ b/src/bench/rpc_mempool.cpp
@@ -40,4 +40,4 @@ static void RpcMempool(benchmark::Bench& bench)
});
}
-BENCHMARK(RpcMempool);
+BENCHMARK(RpcMempool, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/strencodings.cpp b/src/bench/strencodings.cpp
index 69b3a83cbf..16d14278a7 100644
--- a/src/bench/strencodings.cpp
+++ b/src/bench/strencodings.cpp
@@ -15,4 +15,4 @@ static void HexStrBench(benchmark::Bench& bench)
});
}
-BENCHMARK(HexStrBench);
+BENCHMARK(HexStrBench, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp
index afc733482e..8256b5f7ba 100644
--- a/src/bench/util_time.cpp
+++ b/src/bench/util_time.cpp
@@ -36,7 +36,7 @@ static void BenchTimeMillisSys(benchmark::Bench& bench)
});
}
-BENCHMARK(BenchTimeDeprecated);
-BENCHMARK(BenchTimeMillis);
-BENCHMARK(BenchTimeMillisSys);
-BENCHMARK(BenchTimeMock);
+BENCHMARK(BenchTimeDeprecated, benchmark::PriorityLevel::HIGH);
+BENCHMARK(BenchTimeMillis, benchmark::PriorityLevel::HIGH);
+BENCHMARK(BenchTimeMillisSys, benchmark::PriorityLevel::HIGH);
+BENCHMARK(BenchTimeMock, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 8e4708f260..f0e9db8ba1 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -96,5 +96,5 @@ static void VerifyNestedIfScript(benchmark::Bench& bench)
});
}
-BENCHMARK(VerifyScriptBench);
-BENCHMARK(VerifyNestedIfScript);
+BENCHMARK(VerifyScriptBench, benchmark::PriorityLevel::HIGH);
+BENCHMARK(VerifyNestedIfScript, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index a5dd2f3bb1..22d99c0e29 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -57,7 +57,7 @@ static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /
static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/false, /*add_mine=*/true); }
static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/false, /*add_mine=*/false); }
-BENCHMARK(WalletBalanceDirty);
-BENCHMARK(WalletBalanceClean);
-BENCHMARK(WalletBalanceMine);
-BENCHMARK(WalletBalanceWatch);
+BENCHMARK(WalletBalanceDirty, benchmark::PriorityLevel::HIGH);
+BENCHMARK(WalletBalanceClean, benchmark::PriorityLevel::HIGH);
+BENCHMARK(WalletBalanceMine, benchmark::PriorityLevel::HIGH);
+BENCHMARK(WalletBalanceWatch, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/wallet_loading.cpp b/src/bench/wallet_loading.cpp
index 27e4dd015d..8bfaf3044b 100644
--- a/src/bench/wallet_loading.cpp
+++ b/src/bench/wallet_loading.cpp
@@ -118,10 +118,10 @@ static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet)
#ifdef USE_BDB
static void WalletLoadingLegacy(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/true); }
-BENCHMARK(WalletLoadingLegacy);
+BENCHMARK(WalletLoadingLegacy, benchmark::PriorityLevel::HIGH);
#endif
#ifdef USE_SQLITE
static void WalletLoadingDescriptors(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/false); }
-BENCHMARK(WalletLoadingDescriptors);
+BENCHMARK(WalletLoadingDescriptors, benchmark::PriorityLevel::HIGH);
#endif
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index f3bd543de8..d972b71a65 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -104,7 +104,7 @@ int main(int argc, char* argv[])
}
}
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
+ for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) {
std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
@@ -253,7 +253,7 @@ epilogue:
GetMainSignals().FlushBackgroundCallbacks();
{
LOCK(cs_main);
- for (CChainState* chainstate : chainman.GetAll()) {
+ for (Chainstate* chainstate : chainman.GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index e64e2202ba..6d77385584 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -642,7 +642,7 @@ public:
" send Time since last message sent to the peer, in seconds\n"
" recv Time since last message received from the peer, in seconds\n"
" txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
- " \"*\" - the peer requested we not relay transactions to it (relaytxes is false)\n"
+ " \"*\" - whether we relay transactions to this peer (relaytxes is false)\n"
" blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
" hb High-bandwidth BIP152 compact block relay\n"
" \".\" (to) - we selected the peer as a high-bandwidth peer\n"
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index c2bb89b8cf..010cac5920 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -15,7 +15,6 @@
#include <key_io.h>
#include <fs.h>
#include <policy/policy.h>
-#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/sign.h>
@@ -28,9 +27,9 @@
#include <util/system.h>
#include <util/translation.h>
+#include <cstdio>
#include <functional>
#include <memory>
-#include <stdio.h>
static bool fCreateBlank;
static std::map<std::string,UniValue> registers;
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index a7d49452b0..d556300ee2 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -50,15 +50,16 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddCommand("createfromdump", "Create new wallet file from dumped records");
}
-static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
+static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[])
{
SetupWalletToolArgs(args);
std::string error_message;
if (!args.ParseParameters(argc, argv, error_message)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message);
- return false;
+ return EXIT_FAILURE;
}
- if (argc < 2 || HelpRequested(args) || args.IsArgSet("-version")) {
+ const bool missing_args{argc < 2};
+ if (missing_args || HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n";
if (args.IsArgSet("-version")) {
@@ -73,7 +74,11 @@ static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
strUsage += "\n" + args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
- return false;
+ if (missing_args) {
+ tfm::format(std::cerr, "Error: too few parameters\n");
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
}
// check for printtoconsole, allow -debug
@@ -81,12 +86,12 @@ static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
if (!CheckDataDirOption()) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""));
- return false;
+ return EXIT_FAILURE;
}
// Check for chain settings (Params() calls are only valid after this clause)
SelectParams(args.GetChainName());
- return true;
+ return std::nullopt;
}
MAIN_FUNCTION
@@ -106,7 +111,7 @@ MAIN_FUNCTION
SetupEnvironment();
RandomInit();
try {
- if (!WalletAppInit(args, argc, argv)) return EXIT_FAILURE;
+ if (const auto maybe_exit{WalletAppInit(args, argc, argv)}) return *maybe_exit;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "WalletAppInit()");
return EXIT_FAILURE;
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 85ba88f6ab..9f81640ddb 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -216,7 +216,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
if (token) { // Success
exit(EXIT_SUCCESS);
} else { // fRet = false or token read error (premature exit).
- tfm::format(std::cerr, "Error during initializaton - check debug.log for details\n");
+ tfm::format(std::cerr, "Error during initialization - check debug.log for details\n");
exit(EXIT_FAILURE);
}
}
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 5b5c76d51d..f065647d49 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -861,17 +861,91 @@ static const uint8_t chainparams_seed_main[] = {
};
static const uint8_t chainparams_seed_test[] = {
- 0x04,0x20,0xdf,0x55,0xaa,0x83,0xd5,0xc5,0xb8,0xe7,0x75,0x78,0xd4,0x29,0x51,0x4b,0x26,0x1c,0x23,0xdf,0x28,0x4d,0x29,0x85,0x07,0xb5,0xe2,0x29,0x69,0x3e,0x25,0xbb,0x61,0xcf,0x47,0x9d,
- 0x04,0x20,0x0a,0xdd,0xa2,0x48,0xb5,0x56,0xa3,0x1f,0xca,0x3c,0x4c,0x9e,0xca,0x6e,0xb3,0xd5,0x5e,0x68,0xf6,0x28,0x31,0x57,0x24,0xfb,0x9d,0x2b,0x55,0x4f,0xd7,0x90,0x62,0xd3,0x47,0x9d,
- 0x04,0x20,0x2d,0x04,0xa1,0x4a,0xd4,0x7c,0x7b,0x16,0x2e,0xb7,0xd2,0xa1,0x08,0xc5,0xd2,0xbd,0x53,0x87,0x34,0xdc,0x38,0x26,0xca,0x56,0xf2,0xac,0xc5,0x62,0x70,0x72,0x3f,0x63,0x47,0x9d,
- 0x04,0x20,0x30,0x57,0x85,0xe0,0x02,0x4a,0xd1,0x31,0xeb,0x16,0x1b,0x1d,0xa8,0x43,0x0b,0xb4,0xc6,0xac,0x7d,0x46,0x24,0x0b,0x55,0x9d,0x16,0xe6,0x46,0x03,0x72,0xfe,0xd4,0xef,0x47,0x9d,
- 0x04,0x20,0x36,0x6c,0xf1,0xd2,0xbb,0xda,0xff,0x8c,0x93,0x61,0x10,0xf2,0x9d,0xa1,0xa4,0x0a,0x30,0x9b,0x0c,0x69,0x6d,0xaa,0xd4,0x9c,0xfd,0xb5,0x5b,0x5e,0x30,0x9f,0xf3,0x13,0x47,0x9d,
- 0x04,0x20,0x3e,0xe2,0xf3,0xe5,0xc5,0xbe,0x61,0xdd,0x4c,0x3e,0xdb,0x0d,0xd2,0xf9,0x42,0xe3,0x31,0xb2,0xa8,0x51,0x31,0xf6,0xce,0xc2,0x38,0x20,0x27,0x39,0x73,0x68,0x5a,0x42,0x47,0x9d,
- 0x04,0x20,0x51,0x79,0x05,0x9c,0x8a,0xdf,0x03,0xb5,0x1b,0x17,0xc3,0x86,0xb6,0x54,0xcc,0xe0,0x6e,0x58,0xa6,0x41,0x4c,0xcc,0x0c,0x60,0x08,0xa6,0x0f,0x1d,0x11,0xd8,0x29,0xa6,0x47,0x9d,
+ 0x04,0x20,0xd7,0x13,0xfe,0x00,0xf0,0xf1,0x07,0xcb,0x30,0xb1,0x31,0xc7,0x68,0xbb,0x05,0xca,0x18,0xb5,0x1d,0xb1,0x0f,0xf2,0x7e,0x66,0xdb,0xaa,0xf4,0x05,0xaf,0x37,0x0a,0x62,0x47,0x9d,
+ 0x04,0x20,0xd1,0x71,0xfe,0x4c,0x9e,0xe9,0x99,0xb1,0x6c,0xd5,0xdd,0x3a,0xc3,0xd8,0x74,0x1d,0x42,0x32,0x9f,0xca,0xe9,0x47,0x9e,0x18,0x74,0x43,0x22,0xb9,0xa5,0x31,0xb9,0x66,0x47,0x9d,
+ 0x04,0x20,0xd2,0xe4,0xd1,0x40,0x65,0x5d,0x95,0xaf,0xe8,0x67,0xc0,0xe3,0x72,0xfd,0x0a,0x2e,0x35,0xb7,0xbd,0xac,0x67,0x78,0x36,0xe1,0xb8,0xba,0x30,0xcf,0x2e,0x05,0xdd,0x9d,0x47,0x9d,
+ 0x04,0x20,0xde,0xdd,0xe8,0x01,0x03,0x98,0x3f,0x2d,0x3d,0x8b,0x99,0x27,0xea,0xe1,0xa3,0xde,0xff,0x50,0x30,0xc2,0x41,0x4e,0x7e,0x72,0x24,0x3d,0xad,0x78,0x3c,0xab,0x12,0x67,0x47,0x9d,
+ 0x04,0x20,0xdb,0x9d,0xe0,0xc8,0x5d,0x65,0x4b,0xe0,0x7b,0x0f,0x63,0x80,0x85,0x21,0x73,0x07,0x94,0x0f,0xb0,0x23,0x38,0xec,0x78,0xb6,0x07,0xa1,0xd2,0xb0,0x82,0xf8,0x20,0xea,0x47,0x9d,
+ 0x04,0x20,0xdb,0xc9,0xb2,0xe1,0x4b,0x43,0xf1,0xc9,0x3d,0xea,0x51,0x9a,0x71,0x6b,0x4a,0x15,0xe7,0x3e,0xfe,0x8b,0x24,0x7a,0xaa,0xff,0x53,0xf7,0xfc,0x10,0xc6,0xcc,0x14,0x88,0x47,0x9d,
+ 0x04,0x20,0xe5,0x38,0xc1,0x67,0x52,0x2d,0x6f,0x2f,0xbe,0xc2,0xb3,0x45,0x2b,0x19,0x8c,0x10,0xf3,0x09,0xe5,0x37,0xfe,0x66,0x57,0x6f,0x7a,0x4d,0x78,0xf8,0x50,0x41,0xd6,0x90,0x47,0x9d,
+ 0x04,0x20,0xed,0x76,0x8d,0x28,0xb0,0x44,0x2c,0xff,0xf4,0xd9,0x6a,0x5c,0xf9,0xca,0x5f,0x81,0xa2,0x23,0x48,0x82,0x24,0x2e,0xfc,0xbe,0x0a,0x1e,0xf9,0x02,0x43,0x31,0x37,0xf5,0x47,0x9d,
+ 0x04,0x20,0xee,0x57,0x1b,0xab,0xd0,0xd7,0x27,0xca,0xe7,0xaa,0xe2,0x09,0xf3,0x4e,0x80,0x90,0xd2,0x0a,0x9d,0xc4,0x59,0xd0,0xdc,0xa1,0x97,0x09,0x4e,0x2f,0xfe,0x9f,0xa8,0x0a,0x47,0x9d,
+ 0x04,0x20,0xf7,0xe5,0xb0,0x17,0xdc,0x21,0xed,0xfb,0xb6,0xf5,0xe6,0xb4,0x0b,0xb3,0xf3,0x6c,0xb8,0x3f,0x2a,0x93,0x89,0x66,0x2f,0x05,0x30,0xdf,0xfa,0xdf,0xf2,0x1b,0x52,0x78,0x47,0x9d,
+ 0x04,0x20,0xf0,0x38,0xe6,0xe3,0xbb,0x4d,0x17,0x56,0xd0,0xae,0xf6,0xf4,0xa9,0x99,0x96,0xa9,0xe8,0xfc,0xfb,0x76,0x08,0xed,0x7f,0xb7,0xf0,0xa0,0xd3,0x8d,0xea,0x18,0x87,0x8c,0x47,0x9d,
+ 0x04,0x20,0xf1,0x67,0x83,0x6d,0xa7,0x17,0xa6,0xdd,0x1f,0x19,0x5c,0xbd,0x92,0x8e,0x45,0x0c,0x5c,0x2c,0x9d,0x57,0x98,0xe1,0x46,0x40,0x3d,0x87,0x30,0x02,0x9f,0x1d,0x88,0xde,0x47,0x9d,
+ 0x04,0x20,0xff,0x7b,0xca,0x5b,0xa3,0x4c,0x68,0x7d,0xf1,0x3d,0x18,0xef,0xb4,0x3e,0x39,0x34,0x0d,0x9c,0x3c,0x49,0x9b,0xe7,0x67,0x2c,0x78,0x61,0x42,0x67,0x62,0xd0,0x15,0xb5,0x47,0x9d,
+ 0x04,0x20,0xff,0xbc,0xb7,0x65,0x60,0x0d,0xc1,0x04,0xbc,0x30,0x95,0x67,0xe9,0x37,0x62,0xf7,0x5b,0xe3,0x2a,0xd6,0x3b,0xaf,0x8a,0x98,0xc9,0xfb,0x4b,0x66,0xb2,0xdc,0x13,0xc0,0x47,0x9d,
+ 0x04,0x20,0xf8,0x57,0x5e,0x80,0x2d,0xc4,0xc6,0x5e,0x7f,0x15,0x09,0xee,0xcc,0x29,0xf1,0x65,0x53,0x1c,0x41,0xc1,0xf7,0x51,0xa4,0xb0,0x76,0x86,0xe3,0x10,0x24,0x4d,0xd2,0x11,0x47,0x9d,
+ 0x04,0x20,0xfd,0x74,0x90,0x71,0x06,0xbf,0x2b,0xdc,0x69,0x31,0xf2,0x63,0x1f,0x18,0x17,0x71,0xd2,0x75,0xe4,0xf5,0x35,0x89,0xc0,0x55,0x71,0x05,0xc4,0x77,0x64,0xbf,0x20,0x36,0x47,0x9d,
+ 0x04,0x20,0xfe,0x4c,0x16,0x4f,0x37,0x75,0xb4,0xe2,0x54,0x7d,0x00,0x21,0x39,0x3a,0x2c,0xb7,0xe8,0x9a,0xf3,0x2f,0x8a,0xf6,0xe4,0x6b,0x7d,0xea,0x18,0xc3,0xc3,0x86,0x57,0xc5,0x47,0x9d,
+ 0x04,0x20,0x00,0xe5,0x30,0x05,0x39,0xf9,0x05,0xac,0x6d,0x33,0xe5,0xb4,0x3a,0xd1,0x8c,0x75,0xb3,0x5f,0x9b,0xcc,0xd1,0x76,0xa0,0x24,0x84,0x49,0x4b,0x40,0x67,0xee,0x4d,0x93,0x47,0x9d,
+ 0x04,0x20,0x01,0x25,0x8f,0x4c,0xb4,0x28,0x06,0xaa,0x4f,0xc5,0x5d,0x34,0x19,0x40,0xcd,0xb6,0xb9,0xad,0x52,0x3a,0xc3,0x52,0x05,0x9a,0x97,0x5e,0x69,0x9a,0x2a,0x66,0xde,0x48,0x47,0x9d,
+ 0x04,0x20,0x02,0xee,0xed,0xe8,0x3d,0x20,0xd1,0xb0,0xb7,0x44,0xd6,0xb9,0x08,0x9b,0x13,0x35,0xee,0xf4,0x0b,0x8a,0x6e,0x10,0xfa,0xbc,0x75,0x14,0xcb,0x28,0xd4,0x40,0x44,0x3a,0x47,0x9d,
+ 0x04,0x20,0x03,0x88,0x08,0xcf,0x7c,0xa3,0x1c,0xe6,0xd4,0x7a,0x50,0x61,0x1d,0x84,0x5c,0x95,0x89,0x4f,0x03,0x8c,0x91,0xf4,0x67,0xf2,0x4a,0xc4,0x2a,0x44,0x6e,0x47,0xc8,0xae,0x47,0x9d,
+ 0x04,0x20,0x03,0xe3,0x9a,0xa7,0xe7,0x30,0xa7,0x21,0x93,0x8c,0x52,0x76,0x4b,0x43,0x7d,0x35,0x70,0xa9,0x3d,0x35,0x54,0x11,0x83,0x1b,0xe1,0x7b,0x6d,0xd9,0xaa,0xb2,0x26,0x9b,0x47,0x9d,
+ 0x04,0x20,0x05,0x9e,0xaf,0x67,0x77,0x31,0xef,0xe7,0x65,0xd4,0x3b,0x86,0x2c,0x0c,0x10,0x9c,0x7d,0x4c,0xe4,0x2d,0xb7,0x05,0x12,0x51,0x17,0xfb,0x15,0x47,0xdd,0xd7,0x5b,0x0e,0x47,0x9d,
+ 0x04,0x20,0x06,0x30,0xd8,0x03,0x34,0x16,0x0f,0xa4,0x8f,0xb1,0x21,0xc4,0x53,0x19,0x87,0xa3,0x60,0xf1,0xdb,0x2b,0x09,0xf2,0x18,0x1b,0x1e,0x5a,0x78,0x29,0xe7,0x4e,0x12,0x7e,0x47,0x9d,
+ 0x04,0x20,0x0e,0x90,0xa0,0x77,0x60,0x80,0x95,0x5e,0x1a,0x5f,0xb3,0x1c,0x6d,0x87,0x11,0x20,0x35,0x52,0xe5,0x9d,0x7f,0xf0,0x4d,0xec,0x68,0x30,0xa4,0xca,0xd5,0xaf,0x0e,0x20,0x47,0x9d,
+ 0x04,0x20,0x09,0x3f,0x9b,0xec,0xe7,0xf0,0xd6,0x03,0xba,0x2b,0xac,0xa3,0x13,0x41,0x9c,0x70,0x0e,0x58,0x9a,0x0c,0xd4,0xc3,0x66,0x84,0x7c,0xa7,0x76,0xd9,0xd3,0xa5,0xff,0xba,0x47,0x9d,
+ 0x04,0x20,0x0a,0xe8,0xaf,0x6a,0xc9,0xd7,0x03,0x2a,0x8e,0xc7,0xe7,0xd9,0x47,0x77,0x87,0x3d,0xa4,0x09,0xa8,0xb3,0x44,0x2e,0x4a,0xcf,0xaa,0x08,0x3e,0x79,0x4d,0x2e,0x25,0xfd,0x47,0x9d,
+ 0x04,0x20,0x0d,0x02,0xcf,0x15,0x0e,0x79,0x72,0xab,0xc2,0x25,0xbf,0xab,0x07,0xa1,0xc4,0xe8,0x0e,0xb3,0xe2,0x81,0xcf,0x7e,0xe3,0x4a,0x10,0xc7,0x0e,0x0e,0xbd,0xad,0x32,0x20,0x47,0x9d,
+ 0x04,0x20,0x1e,0xc5,0x90,0x07,0x29,0x4f,0x7e,0xb3,0x46,0x51,0xe9,0x81,0x65,0x24,0x7f,0xae,0xcd,0x96,0xc6,0x00,0xfe,0x70,0x8c,0xc1,0x8a,0xe2,0xe3,0x80,0x48,0x40,0xab,0x10,0x47,0x9d,
+ 0x04,0x20,0x18,0xd3,0xc1,0x52,0xa1,0xde,0xaa,0x4e,0x88,0xf3,0x7f,0x92,0x09,0xcc,0x43,0x6b,0x59,0x76,0x3d,0xce,0x2a,0x66,0x7c,0xa3,0xf3,0x39,0xaf,0x73,0xcf,0xf8,0x83,0x89,0x47,0x9d,
+ 0x04,0x20,0x1c,0x0e,0x75,0xca,0x45,0xb3,0x0f,0xb3,0x7b,0x27,0xb5,0xde,0x22,0x86,0xde,0xe5,0xf3,0xd8,0x95,0x0a,0x11,0x96,0x86,0x84,0xee,0xbd,0x7a,0x04,0xd7,0x90,0xca,0x04,0x47,0x9d,
+ 0x04,0x20,0x1c,0x66,0x71,0x60,0x3c,0xbf,0x22,0x32,0x91,0x56,0xe9,0xbf,0x74,0xb8,0xd7,0x47,0xc1,0x07,0x2e,0x88,0x59,0xa8,0xb0,0x9a,0xd5,0x93,0x09,0xb6,0xdb,0x6e,0x40,0x6a,0x47,0x9d,
+ 0x04,0x20,0x1d,0x83,0xcf,0x89,0x90,0x06,0xa6,0x97,0xb2,0xa9,0x01,0x01,0x1f,0x98,0x62,0x04,0x65,0xa5,0x93,0x3e,0x6a,0x08,0x53,0xa3,0x90,0x2e,0xb5,0x02,0x1e,0x78,0x98,0x3d,0x47,0x9d,
+ 0x04,0x20,0x27,0xe6,0xa8,0x97,0xbc,0x69,0xb7,0x0e,0xd4,0x4d,0xe9,0x9b,0xff,0xe6,0xc9,0xb3,0x3f,0xc5,0xa8,0xa0,0xaf,0x19,0x61,0xd2,0xfb,0x7d,0x5c,0xdf,0x62,0xb0,0x36,0xe6,0x47,0x9d,
+ 0x04,0x20,0x24,0xe9,0x86,0x63,0x9f,0x96,0xe1,0x42,0x3e,0xa3,0x03,0x9d,0xfd,0x23,0xa4,0xeb,0x05,0xfa,0x3a,0xb1,0xcd,0x3a,0xce,0x24,0xbd,0x87,0x99,0x65,0xd9,0x19,0x75,0xf1,0x47,0x9d,
+ 0x04,0x20,0x28,0x50,0xc2,0x49,0xb8,0x3e,0x68,0x10,0xe6,0x02,0xdd,0x01,0x42,0xe5,0x41,0xc1,0x58,0xd5,0x5e,0xb8,0x7b,0xae,0x0a,0x90,0x14,0x0f,0xe3,0x97,0xe4,0xfa,0x2b,0xaf,0x47,0x9d,
+ 0x04,0x20,0x2e,0x43,0x1b,0x30,0xd6,0x62,0x9d,0xf8,0x50,0x8b,0x89,0xcb,0x49,0x2a,0x7b,0x42,0x40,0x10,0x0b,0xfe,0xcd,0xfe,0x6e,0xe8,0x65,0x89,0x2a,0xc3,0x8d,0x49,0xf2,0x2a,0x47,0x9d,
+ 0x04,0x20,0x36,0x3c,0xd4,0x1f,0x8f,0x63,0xfa,0x49,0x62,0xb5,0x69,0xd6,0x9f,0x42,0xaa,0xfe,0x54,0x14,0xd1,0xd2,0xb2,0xae,0x52,0xe1,0x08,0x7e,0xc6,0x15,0x56,0x45,0xbd,0xb3,0x47,0x9d,
+ 0x04,0x20,0x38,0x3d,0xac,0xa2,0x19,0x80,0xdc,0x61,0xff,0xb9,0x37,0xd6,0x53,0xf0,0xb0,0x89,0xb6,0x14,0x33,0x62,0x7c,0x33,0x8a,0x3d,0xdc,0xdd,0xba,0xfb,0x70,0xa4,0x6f,0x9b,0x47,0x9d,
+ 0x04,0x20,0x38,0x04,0x94,0x99,0x3a,0x60,0x61,0x08,0xcc,0xf9,0x43,0x18,0x8f,0x01,0xb5,0x43,0x7e,0x35,0xa5,0x27,0x2a,0xf6,0x85,0x78,0x81,0x36,0xed,0xb6,0xb0,0xd2,0xac,0x77,0x47,0x9d,
+ 0x04,0x20,0x38,0x54,0xfe,0xde,0xcf,0x83,0xb9,0x06,0x7f,0xaa,0x79,0x96,0x58,0x89,0x78,0x45,0x29,0x51,0x9a,0xbf,0x64,0xd1,0x02,0xe2,0x5f,0x74,0x15,0x2e,0x0d,0x8f,0x81,0x9e,0x47,0x9d,
+ 0x04,0x20,0x3c,0x0c,0xec,0x7b,0x47,0x74,0x12,0xcc,0xef,0xe1,0x88,0xcb,0x57,0x11,0xd5,0xd8,0x91,0x7e,0x95,0x17,0x0c,0x12,0xe2,0x46,0x7c,0xc0,0xe3,0xe3,0x92,0x26,0xeb,0x85,0x47,0x9d,
+ 0x04,0x20,0x3d,0x42,0xc6,0x66,0xb0,0x8b,0xcc,0xf9,0x6a,0xfd,0xa7,0x10,0xfe,0x2a,0x45,0xd9,0x3a,0xcd,0x15,0xa4,0x00,0xbf,0xde,0x1a,0x6d,0x3a,0x5b,0xa8,0xc8,0x95,0x6a,0x3c,0x47,0x9d,
+ 0x04,0x20,0x3d,0xb7,0x5c,0xbf,0x7a,0xb7,0x0f,0xe3,0x21,0xeb,0xa1,0x3c,0x8b,0xf3,0x9d,0x72,0x3d,0x69,0xfc,0xcc,0x22,0xa5,0x1c,0xb4,0x3f,0x98,0xcb,0x63,0xa8,0xc8,0x9f,0xcc,0x47,0x9d,
+ 0x04,0x20,0x40,0xc7,0x1f,0x78,0x96,0x51,0xc8,0xd4,0x54,0x0f,0x32,0x00,0xda,0x0d,0x2a,0xb4,0x04,0xff,0x0e,0xf3,0x94,0x8d,0xf4,0x5e,0x23,0x74,0xfa,0x0c,0xfa,0x87,0xfb,0x3b,0x47,0x9d,
+ 0x04,0x20,0x43,0x30,0x0d,0xf9,0x04,0xba,0x10,0x88,0x45,0x56,0xa5,0xee,0x22,0x1d,0xb5,0xe2,0x8d,0xd9,0x1e,0x68,0x8d,0x87,0xaa,0x9b,0x82,0x6a,0x71,0xe5,0x95,0x2b,0x79,0xf6,0x47,0x9d,
+ 0x04,0x20,0x45,0x0e,0x7a,0x38,0x4d,0x97,0xc8,0xd9,0xc7,0x2b,0xbc,0xae,0xa9,0xe3,0x3a,0x40,0x65,0xf3,0xc0,0xc8,0x31,0xab,0xe9,0x02,0x99,0x82,0xb2,0xa0,0x79,0x60,0x92,0xed,0x47,0x9d,
+ 0x04,0x20,0x4a,0x3b,0x66,0x48,0xee,0x55,0x05,0xed,0x91,0x14,0x5e,0x57,0x64,0xeb,0x23,0xab,0x56,0x83,0x56,0x36,0x2d,0x6a,0x2f,0xf8,0x28,0x11,0x37,0x3a,0x73,0x76,0x2e,0x6a,0x47,0x9d,
+ 0x04,0x20,0x4a,0x4b,0x4c,0x27,0xea,0x89,0xb2,0xa6,0x3e,0xf5,0x6e,0xc3,0xa4,0xe8,0xf6,0x5e,0x54,0xcc,0x93,0x64,0xce,0x36,0x82,0xd9,0x5f,0x1b,0x65,0x6b,0x02,0xc6,0x66,0x60,0x47,0x9d,
+ 0x04,0x20,0x4b,0x74,0xfe,0x48,0xc5,0x79,0xd5,0x77,0xba,0xdf,0x52,0x63,0x40,0xc4,0x30,0x04,0xff,0xf5,0x29,0x01,0xcc,0x15,0x5a,0x58,0xf7,0xc0,0x16,0x28,0x83,0x36,0x60,0xa0,0x47,0x9d,
+ 0x04,0x20,0x4c,0x6a,0x4c,0x67,0x94,0x97,0xa4,0xe3,0x78,0x9e,0x16,0x65,0x58,0xf5,0x95,0xa9,0xcd,0x94,0x9a,0xbc,0x40,0xc2,0x4e,0x20,0x71,0x72,0xd9,0x02,0x50,0x75,0x9f,0x41,0x47,0x9d,
+ 0x04,0x20,0x4c,0x85,0xc2,0xc6,0xb5,0x6a,0xf4,0x3d,0x84,0xf9,0xc3,0x8b,0x21,0x7f,0x57,0x7e,0x66,0x66,0x6b,0x6d,0x5f,0x0a,0xd0,0xf0,0x76,0x56,0x65,0x78,0xf8,0xa7,0x42,0x75,0x47,0x9d,
+ 0x04,0x20,0x52,0xf0,0xcb,0x8e,0xa9,0x27,0xd4,0x21,0x57,0x73,0xcb,0x40,0x17,0x6e,0x83,0x73,0xe4,0x59,0xa9,0x89,0x23,0x14,0x14,0x6a,0x91,0x85,0xd2,0xce,0x1c,0x85,0x05,0x06,0x47,0x9d,
+ 0x04,0x20,0x55,0x93,0x75,0x82,0x96,0x4c,0xc8,0x24,0x96,0x48,0xb3,0x9e,0x15,0xf5,0xf5,0x6f,0x5b,0xec,0xf6,0x39,0xff,0xa8,0xaf,0x3a,0x4c,0x5d,0x22,0x3d,0xd3,0x14,0xc3,0x05,0x47,0x9d,
+ 0x04,0x20,0x58,0xbf,0x47,0xe7,0x52,0x8d,0xb5,0x83,0xbc,0x55,0xb1,0xb8,0x02,0xe3,0xdc,0xc2,0xc4,0xe7,0x79,0x85,0xb2,0xe7,0x38,0x27,0xae,0xad,0x0c,0xb8,0x34,0xed,0x71,0xde,0x47,0x9d,
+ 0x04,0x20,0x59,0x76,0xc7,0xdc,0xb8,0x4d,0x1d,0x51,0x40,0x75,0x43,0x57,0xdd,0x3e,0xa3,0x63,0x06,0x0f,0x5e,0x18,0x29,0x10,0x08,0xac,0xa0,0x60,0xd2,0xf3,0x63,0x6e,0xa6,0x58,0x47,0x9d,
0x04,0x20,0x60,0xbe,0xae,0x7d,0xa3,0x4d,0x6a,0x71,0x1a,0x5d,0xe5,0x98,0x9c,0xde,0xa0,0x99,0x39,0x19,0xd3,0x01,0x0a,0x5d,0x1c,0x21,0x43,0x94,0x92,0x71,0x5d,0x77,0xd7,0xdf,0x47,0x9d,
- 0x04,0x20,0x64,0x4e,0x86,0xa1,0x02,0xa1,0x8a,0xef,0xb0,0xd1,0xb5,0x77,0x69,0xb9,0x6a,0xdc,0xdf,0x35,0x8a,0xda,0xa4,0x3e,0x83,0xfa,0x50,0xe6,0xca,0x0e,0x2b,0x99,0x0a,0x17,0x47,0x9d,
- 0x04,0x20,0xa2,0x28,0x3c,0x5a,0x5b,0x82,0x32,0x66,0x11,0xe5,0x71,0xff,0x6b,0x25,0x92,0x75,0xdd,0x7a,0x4f,0x90,0x8b,0x1d,0x34,0xa4,0xf1,0x6e,0xb9,0xfb,0xb5,0x2e,0x7c,0x7f,0x47,0x9d,
- 0x04,0x20,0xc8,0xb5,0x6a,0xba,0x02,0x26,0x45,0x12,0xfb,0x93,0x8a,0x51,0xe4,0xb0,0xf3,0x94,0xb7,0xc0,0x74,0x72,0xeb,0x67,0x91,0x9e,0x04,0x36,0x6a,0x4b,0xef,0x0d,0x88,0xfe,0x47,0x9d,
- 0x04,0x20,0xc8,0xfa,0xcd,0x8c,0xc3,0x6f,0x3c,0xd0,0x27,0x7e,0x7d,0xeb,0x51,0x01,0x65,0xb6,0x9e,0x02,0x09,0x64,0xf4,0x87,0x78,0x7b,0x8f,0x9d,0xaf,0x3b,0xa5,0xcc,0x56,0x2c,0x47,0x9d,
+ 0x04,0x20,0x62,0x42,0x6d,0x98,0xc4,0xa1,0x16,0xc1,0x7d,0x17,0x9e,0x37,0x94,0x88,0x58,0x76,0xa9,0x13,0x6a,0x98,0x39,0x22,0x58,0x13,0xf8,0x9a,0x99,0xa2,0x25,0x56,0x59,0x05,0x47,0x9d,
+ 0x04,0x20,0x6a,0xb1,0x07,0x66,0xbe,0x50,0xec,0x8e,0x6b,0x72,0xfc,0xb4,0xc2,0x67,0x26,0xa1,0x7a,0x10,0x15,0xd3,0xd0,0x36,0x7a,0xf0,0x83,0xd0,0xc3,0x59,0x85,0x68,0x90,0xd8,0x47,0x9d,
+ 0x04,0x20,0x6d,0x6b,0x07,0x72,0xf7,0x45,0x8c,0x1d,0xe3,0x5c,0xf2,0x58,0x20,0xd2,0x95,0x25,0x22,0x51,0xc3,0x59,0x96,0xc1,0xdc,0x7e,0xa4,0x82,0x89,0x0a,0xf5,0x25,0x38,0x1a,0x47,0x9d,
+ 0x04,0x20,0x77,0xaa,0x57,0x56,0x17,0xce,0xdd,0x79,0x64,0xaf,0x78,0xb2,0xf4,0x91,0x36,0x75,0x1c,0x1c,0xcd,0x31,0x35,0x4a,0xe4,0x8b,0x64,0x65,0x4e,0x06,0xec,0x2a,0xc4,0x95,0x47,0x9d,
+ 0x04,0x20,0x70,0x11,0x6d,0x2d,0xa7,0xc9,0x9f,0xd8,0xe3,0xe8,0xae,0x5a,0x4a,0x50,0xac,0x3f,0xd6,0x57,0xad,0xd0,0xa5,0x35,0x14,0xd7,0xb1,0x76,0x58,0xb0,0x90,0xa4,0x1c,0x9b,0x47,0x9d,
+ 0x04,0x20,0x72,0xdb,0xfc,0x04,0x48,0xee,0xec,0xae,0x1b,0xad,0xe6,0x9a,0x87,0xe1,0x17,0xae,0x13,0x70,0xae,0x83,0x90,0x62,0x59,0x30,0xed,0x44,0x43,0xe9,0xab,0xee,0xaa,0xd0,0x47,0x9d,
+ 0x04,0x20,0x74,0xd8,0x56,0x9c,0x5e,0xb4,0x62,0x12,0xea,0x6f,0x1e,0x3d,0x95,0x22,0x50,0x16,0xe3,0xba,0x45,0xb2,0xa3,0xad,0x26,0xb0,0x13,0x74,0xdd,0xec,0x22,0xe0,0x70,0x9a,0x47,0x9d,
+ 0x04,0x20,0x75,0x45,0xe9,0x01,0x21,0x81,0x44,0x1b,0x57,0x76,0x8c,0x3f,0x85,0xa9,0x74,0xf4,0x3c,0xf8,0xd5,0x12,0xcf,0x13,0x1e,0x2f,0x18,0x13,0x78,0xa3,0x51,0x59,0x49,0x49,0x47,0x9d,
+ 0x04,0x20,0x7b,0x3b,0xf1,0xa0,0x78,0xc9,0xba,0xb4,0xd2,0xad,0xed,0x0b,0x1e,0xd2,0x8d,0xd2,0x08,0xd1,0x51,0x5b,0x01,0x74,0x6c,0xc0,0x5d,0xcd,0x4c,0x53,0xf3,0xba,0x16,0xe3,0x47,0x9d,
+ 0x04,0x20,0x7b,0x11,0x26,0x1d,0xf6,0xa1,0xb7,0xc3,0x36,0x19,0x0d,0x27,0x10,0x36,0xb7,0xd5,0x93,0x60,0xc8,0xf8,0x82,0x79,0x46,0xe8,0x78,0xce,0x29,0x50,0x2b,0x44,0x3f,0x7a,0x47,0x9d,
+ 0x04,0x20,0x7b,0x96,0x08,0xa2,0x54,0x5c,0xd0,0x94,0x9c,0x0a,0xa7,0xb3,0xea,0x7d,0x38,0x0b,0x8e,0xee,0x91,0xe3,0x91,0xb9,0xc9,0xcc,0x81,0x28,0x2e,0xc3,0x02,0x9a,0xcf,0x65,0x47,0x9d,
+ 0x04,0x20,0x81,0x34,0x90,0x93,0x9c,0xf9,0xc9,0xe7,0x81,0xa5,0xab,0xf1,0x03,0xe1,0xb3,0x2f,0xf7,0xcf,0x19,0x94,0x2f,0x7a,0x1b,0xc5,0x83,0xc9,0x8c,0xc3,0x5b,0x09,0x96,0x14,0x47,0x9d,
+ 0x04,0x20,0x86,0x7d,0x81,0xf5,0x72,0x30,0xc0,0x91,0x4c,0x8e,0x04,0x48,0x47,0xce,0xcd,0x43,0xef,0x22,0x67,0x17,0xea,0x95,0x2f,0x51,0x4f,0x57,0x67,0x58,0x21,0xa5,0x2e,0x68,0x47,0x9d,
+ 0x04,0x20,0x8b,0xfc,0xf6,0xec,0x3b,0x71,0x25,0x88,0xe8,0xc8,0x72,0xc3,0x89,0x05,0x27,0x81,0x28,0x6b,0xb6,0x72,0xa7,0xc5,0x16,0xaa,0x67,0xe6,0x37,0xb7,0x2b,0x99,0xa8,0x97,0x47,0x9d,
+ 0x04,0x20,0xa2,0x46,0x23,0xd2,0xdb,0x3e,0x7a,0x65,0x05,0xd4,0x45,0x78,0xff,0x3e,0xc1,0x28,0xdb,0xfa,0xf1,0x5e,0x32,0xe9,0x6a,0x8f,0x96,0x45,0x15,0x01,0x7f,0x1f,0x75,0xbf,0x47,0x9d,
+ 0x04,0x20,0xa8,0xa6,0xbb,0x02,0x06,0x65,0x3b,0xb2,0x52,0x79,0xd1,0xc9,0x6a,0x01,0x42,0x43,0xca,0x69,0xe1,0x69,0x90,0xfe,0x2c,0xd2,0x7c,0x18,0xfd,0xab,0xc7,0x71,0x7f,0x8b,0x47,0x9d,
+ 0x04,0x20,0xa9,0x7b,0x48,0xf7,0x73,0xcb,0x07,0x37,0x4e,0xaa,0x68,0xc4,0x67,0x08,0xea,0x5b,0x68,0x0a,0xfe,0x14,0x82,0x6a,0xa1,0x79,0x82,0x9c,0x0b,0x52,0x17,0xd6,0x6b,0x1b,0x47,0x9d,
+ 0x04,0x20,0xb3,0x6e,0x64,0x99,0x26,0x7a,0xeb,0xd4,0x53,0x8c,0x94,0xb1,0x4a,0x37,0x02,0xaa,0xa9,0xc8,0x36,0x11,0xec,0xf9,0x0f,0x08,0xa3,0x9f,0x3e,0xaf,0xd2,0xd1,0x2a,0x0b,0x47,0x9d,
+ 0x04,0x20,0xb3,0xd4,0x15,0x0e,0x3f,0x12,0xed,0xfc,0xab,0x69,0x55,0x91,0x45,0xcf,0xc1,0x2f,0x35,0xa4,0xf7,0x94,0xff,0xd8,0xbe,0x44,0xc8,0x36,0xaf,0x0f,0xa0,0x4e,0x36,0x22,0x47,0x9d,
+ 0x04,0x20,0xb6,0x5e,0x39,0xeb,0x93,0x18,0x22,0xad,0x78,0x94,0x34,0x48,0x31,0x8d,0xd1,0x42,0xb7,0x38,0x58,0x49,0xea,0x00,0xb9,0xcf,0x58,0x43,0x38,0x22,0x22,0x88,0x87,0xde,0x47,0x9d,
+ 0x04,0x20,0xb9,0x80,0xf6,0xa1,0x4a,0x0a,0xc6,0xe3,0x19,0x27,0xe9,0x2d,0x75,0x60,0x14,0x3d,0x2c,0xda,0x97,0x77,0x54,0x9f,0x78,0xb8,0x9b,0x72,0x6a,0x58,0xab,0x8b,0x95,0x95,0x47,0x9d,
+ 0x04,0x20,0xba,0xab,0x91,0x9c,0x48,0x8b,0x9b,0x34,0x5c,0x30,0xf3,0xd2,0x78,0xd3,0xbf,0x09,0x79,0x23,0x4e,0x18,0x5d,0x5e,0x75,0x6b,0xa2,0x91,0x63,0x2c,0x75,0xfd,0x06,0x36,0x47,0x9d,
+ 0x04,0x20,0xbb,0x50,0xf0,0x50,0x8d,0xb7,0x6c,0xd4,0x87,0x59,0x0d,0xfd,0x5f,0x51,0x86,0xa8,0x43,0xb3,0x7a,0xe7,0x2f,0x54,0x97,0x7d,0xed,0xc3,0x7b,0x31,0xe6,0xb9,0x05,0xe7,0x47,0x9d,
+ 0x04,0x20,0xc0,0xc1,0xf5,0x59,0xe8,0x46,0xf6,0x9d,0x41,0xf0,0xde,0x8b,0x32,0xf9,0x6f,0xd6,0xb2,0xd5,0x36,0x56,0x43,0xd3,0x60,0x08,0x55,0x94,0x13,0xf5,0xef,0x7f,0x4d,0x4a,0x47,0x9d,
+ 0x04,0x20,0xce,0xd3,0xd2,0xba,0x56,0xa1,0xde,0xc9,0xc4,0xdb,0x50,0x7b,0xe9,0x4d,0x59,0x65,0x1c,0x02,0x4a,0xa3,0xea,0x73,0xb2,0x66,0x70,0xe7,0x85,0xc1,0x20,0x60,0x79,0x96,0x47,0x9d,
+ 0x04,0x20,0xc8,0x88,0xfe,0x71,0x5f,0xa3,0x6c,0x96,0x6a,0xd7,0x9e,0x38,0x84,0x9f,0x44,0xe1,0x6b,0xdc,0x98,0x31,0xad,0x96,0x29,0xe7,0x00,0x83,0x63,0x03,0xae,0x69,0x2e,0x63,0x47,0x9d,
+ 0x04,0x20,0xcb,0x2a,0x8c,0xe7,0xe5,0x1f,0x4e,0x3a,0x13,0xd6,0x9e,0xd7,0x68,0x51,0x83,0xf4,0x2d,0x3e,0x21,0x38,0x63,0x18,0xe9,0x97,0x27,0xff,0x45,0x48,0xc3,0x6c,0xca,0x59,0x47,0x9d,
+ 0x04,0x20,0xcb,0x80,0x0d,0xdf,0xf0,0xa6,0x28,0x84,0x98,0xc8,0x47,0x72,0xbe,0x53,0x04,0x43,0x9c,0x3a,0x0e,0x46,0x9c,0x75,0x4c,0x57,0x50,0x98,0xe5,0x25,0x80,0x5d,0xa2,0x91,0x47,0x9d,
+ 0x04,0x20,0xcb,0xaa,0x0a,0x5d,0x6e,0x8a,0xfa,0x49,0x5a,0x8c,0x0c,0x9d,0x7a,0xe9,0x9d,0xc6,0xe4,0xcd,0xc1,0x6a,0xff,0xbb,0xf1,0x89,0xf1,0xd5,0x16,0x3f,0xdc,0xbb,0xe9,0x1b,0x47,0x9d,
};
#endif // BITCOIN_CHAINPARAMSSEEDS_H
diff --git a/src/checkqueue.h b/src/checkqueue.h
index bead6f0c6f..c4a64444e9 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -199,11 +199,12 @@ public:
WITH_LOCK(m_mutex, m_request_stop = false);
}
+ bool HasThreads() const { return !m_worker_threads.empty(); }
+
~CCheckQueue()
{
assert(m_worker_threads.empty());
}
-
};
/**
diff --git a/src/common/run_command.cpp b/src/common/run_command.cpp
new file mode 100644
index 0000000000..6ad9f75b5d
--- /dev/null
+++ b/src/common/run_command.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <common/run_command.h>
+
+#include <tinyformat.h>
+#include <univalue.h>
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+#if defined(__GNUC__)
+// Boost 1.78 requires the following workaround.
+// See: https://github.com/boostorg/process/issues/235
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnarrowing"
+#endif
+#include <boost/process.hpp>
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+#endif // ENABLE_EXTERNAL_SIGNER
+
+UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
+{
+#ifdef ENABLE_EXTERNAL_SIGNER
+ namespace bp = boost::process;
+
+ UniValue result_json;
+ bp::opstream stdin_stream;
+ bp::ipstream stdout_stream;
+ bp::ipstream stderr_stream;
+
+ if (str_command.empty()) return UniValue::VNULL;
+
+ bp::child c(
+ str_command,
+ bp::std_out > stdout_stream,
+ bp::std_err > stderr_stream,
+ bp::std_in < stdin_stream
+ );
+ if (!str_std_in.empty()) {
+ stdin_stream << str_std_in << std::endl;
+ }
+ stdin_stream.pipe().close();
+
+ std::string result;
+ std::string error;
+ std::getline(stdout_stream, result);
+ std::getline(stderr_stream, error);
+
+ c.wait();
+ const int n_error = c.exit_code();
+ if (n_error) throw std::runtime_error(strprintf("RunCommandParseJSON error: process(%s) returned %d: %s\n", str_command, n_error, error));
+ if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
+
+ return result_json;
+#else
+ throw std::runtime_error("Compiled without external signing support (required for external signing).");
+#endif // ENABLE_EXTERNAL_SIGNER
+}
diff --git a/src/common/run_command.h b/src/common/run_command.h
new file mode 100644
index 0000000000..2fbdc071ee
--- /dev/null
+++ b/src/common/run_command.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_COMMON_RUN_COMMAND_H
+#define BITCOIN_COMMON_RUN_COMMAND_H
+
+#include <string>
+
+class UniValue;
+
+/**
+ * Execute a command which returns JSON, and parse the result.
+ *
+ * @param str_command The command to execute, including any arguments
+ * @param str_std_in string to pass to stdin
+ * @return parsed JSON
+ */
+UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
+
+#endif // BITCOIN_COMMON_RUN_COMMAND_H
diff --git a/src/compat/compat.h b/src/compat/compat.h
index a8e5552c0a..cc37797577 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -109,14 +109,6 @@ typedef char* sockopt_arg_type;
#define USE_POLL
#endif
-bool static inline IsSelectableSocket(const SOCKET& s) {
-#if defined(USE_POLL) || defined(WIN32)
- return true;
-#else
- return (s < FD_SETSIZE);
-#endif
-}
-
// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h
index b2a31e3ba4..10bdbf31a8 100644
--- a/src/consensus/consensus.h
+++ b/src/consensus/consensus.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_CONSENSUS_CONSENSUS_H
#define BITCOIN_CONSENSUS_CONSENSUS_H
-#include <stdlib.h>
+#include <cstdlib>
#include <stdint.h>
/** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
index 69fbbe9fa5..de16a77878 100644
--- a/src/crypto/chacha20.h
+++ b/src/crypto/chacha20.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_CHACHA20_H
#define BITCOIN_CRYPTO_CHACHA20_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
https://cr.yp.to/chacha/chacha-20080128.pdf */
diff --git a/src/crypto/hkdf_sha256_32.h b/src/crypto/hkdf_sha256_32.h
index fa1e42aec1..878b03a37f 100644
--- a/src/crypto/hkdf_sha256_32.h
+++ b/src/crypto/hkdf_sha256_32.h
@@ -7,8 +7,8 @@
#include <crypto/hmac_sha256.h>
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A rfc5869 HKDF implementation with HMAC_SHA256 and fixed key output length of 32 bytes (L=32) */
class CHKDF_HMAC_SHA256_L32
diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h
index d31fda1dd1..9c25edd7c1 100644
--- a/src/crypto/hmac_sha256.h
+++ b/src/crypto/hmac_sha256.h
@@ -7,8 +7,8 @@
#include <crypto/sha256.h>
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A hasher class for HMAC-SHA-256. */
class CHMAC_SHA256
diff --git a/src/crypto/hmac_sha512.h b/src/crypto/hmac_sha512.h
index 1ea9a3671e..6acce8992e 100644
--- a/src/crypto/hmac_sha512.h
+++ b/src/crypto/hmac_sha512.h
@@ -7,8 +7,8 @@
#include <crypto/sha512.h>
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A hasher class for HMAC-SHA-512. */
class CHMAC_SHA512
diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h
index 1598b013b9..c80faada7e 100644
--- a/src/crypto/poly1305.h
+++ b/src/crypto/poly1305.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_POLY1305_H
#define BITCOIN_CRYPTO_POLY1305_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
#define POLY1305_KEYLEN 32
#define POLY1305_TAGLEN 16
diff --git a/src/crypto/ripemd160.h b/src/crypto/ripemd160.h
index 38ea375c1f..f1d89b8407 100644
--- a/src/crypto/ripemd160.h
+++ b/src/crypto/ripemd160.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_RIPEMD160_H
#define BITCOIN_CRYPTO_RIPEMD160_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A hasher class for RIPEMD-160. */
class CRIPEMD160
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
index 8b4568ee12..6ef0187efd 100644
--- a/src/crypto/sha1.h
+++ b/src/crypto/sha1.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_SHA1_H
#define BITCOIN_CRYPTO_SHA1_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A hasher class for SHA1. */
class CSHA1
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index 028ee14345..24bd1f2e7e 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_SHA256_H
#define BITCOIN_CRYPTO_SHA256_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
#include <string>
/** A hasher class for SHA-256. */
diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp
index f1a7fefea3..bc69703607 100644
--- a/src/crypto/sha256_sse4.cpp
+++ b/src/crypto/sha256_sse4.cpp
@@ -5,8 +5,8 @@
// This is a translation to GCC extended asm syntax from YASM code by Intel
// (available at the bottom of this file).
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
#if defined(__x86_64__) || defined(__amd64__)
diff --git a/src/crypto/sha3.h b/src/crypto/sha3.h
index 88d8c1204d..78608eae76 100644
--- a/src/crypto/sha3.h
+++ b/src/crypto/sha3.h
@@ -7,8 +7,8 @@
#include <span.h>
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
//! The Keccak-f[1600] transform.
void KeccakF(uint64_t (&st)[25]);
diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp
index 85a7bbcb53..59b79609dd 100644
--- a/src/crypto/sha512.cpp
+++ b/src/crypto/sha512.cpp
@@ -30,7 +30,7 @@ void inline Round(uint64_t a, uint64_t b, uint64_t c, uint64_t& d, uint64_t e, u
h = t1 + t2;
}
-/** Initialize SHA-256 state. */
+/** Initialize SHA-512 state. */
void inline Initialize(uint64_t* s)
{
s[0] = 0x6a09e667f3bcc908ull;
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
index 21ca930c75..7356dff6d9 100644
--- a/src/crypto/sha512.h
+++ b/src/crypto/sha512.h
@@ -5,8 +5,8 @@
#ifndef BITCOIN_CRYPTO_SHA512_H
#define BITCOIN_CRYPTO_SHA512_H
+#include <cstdlib>
#include <stdint.h>
-#include <stdlib.h>
/** A hasher class for SHA-512. */
class CSHA512
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 4dbc839941..7f45e35aef 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -128,7 +128,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
}
CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
- : m_name{fs::PathToString(path.stem())}
+ : m_name{fs::PathToString(path.stem())}, m_path{path}, m_is_memory{fMemory}
{
penv = nullptr;
readoptions.verify_checksums = true;
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 665eaa0e98..1052da01d5 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -39,6 +39,10 @@ public:
class CDBWrapper;
+namespace dbwrapper {
+ using leveldb::DestroyDB;
+}
+
/** These should be considered an implementation detail of the specific database.
*/
namespace dbwrapper_private {
@@ -219,6 +223,12 @@ private:
std::vector<unsigned char> CreateObfuscateKey() const;
+ //! path to filesystem storage
+ const fs::path m_path;
+
+ //! whether or not the database resides in memory
+ bool m_is_memory;
+
public:
/**
* @param[in] path Location in the filesystem where leveldb data will be stored.
@@ -268,6 +278,14 @@ public:
return WriteBatch(batch, fSync);
}
+ //! @returns filesystem path to the on-disk data.
+ std::optional<fs::path> StoragePath() {
+ if (m_is_memory) {
+ return {};
+ }
+ return m_path;
+ }
+
template <typename K>
bool Exists(const K& key) const
{
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index 094314e878..f255834830 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -3,10 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <common/run_command.h>
#include <core_io.h>
#include <psbt.h>
#include <util/strencodings.h>
-#include <util/system.h>
#include <external_signer.h>
#include <algorithm>
@@ -81,6 +81,9 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
for (const auto& entry : input.hd_keypaths) {
if (parsed_m_fingerprint == MakeUCharSpan(entry.second.fingerprint)) return true;
}
+ for (const auto& entry : input.m_tap_bip32_paths) {
+ if (parsed_m_fingerprint == MakeUCharSpan(entry.second.second.fingerprint)) return true;
+ }
return false;
};
diff --git a/src/fs.cpp b/src/fs.cpp
index 74b167e313..07cce269ed 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -126,7 +126,7 @@ bool FileLock::TryLock()
if (hFile == INVALID_HANDLE_VALUE) {
return false;
}
- _OVERLAPPED overlapped = {0};
+ _OVERLAPPED overlapped = {};
if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, std::numeric_limits<DWORD>::max(), std::numeric_limits<DWORD>::max(), &overlapped)) {
reason = GetErrorReason();
return false;
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index e68436cc2c..1a19555f76 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -21,11 +21,11 @@
#include <util/threadnames.h>
#include <util/translation.h>
+#include <cstdio>
+#include <cstdlib>
#include <deque>
#include <memory>
#include <optional>
-#include <stdio.h>
-#include <stdlib.h>
#include <string>
#include <sys/types.h>
@@ -320,7 +320,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
// Bind addresses
for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
- LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second);
+ LogPrintf("Binding RPC on address %s port %i\n", i->first, i->second);
evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
if (bind_handle) {
CNetAddr addr;
diff --git a/src/i2p.cpp b/src/i2p.cpp
index f7d480988b..28be8009dc 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -325,6 +325,7 @@ void Session::DestGenerate(const Sock& sock)
// https://geti2p.net/spec/common-structures#key-certificates
// "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
// Use "7" because i2pd <2.24.0 does not recognize the textual form.
+ // If SIGNATURE_TYPE is not specified, then the default one is DSA_SHA1.
const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
@@ -378,7 +379,7 @@ void Session::CreateIfNotCreatedAlready()
// in the reply in DESTINATION=.
const Reply& reply = SendRequestAndGetReply(
*sock,
- strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT", session_id));
+ strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7", session_id));
m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
} else {
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 1ebe89ef7c..3eea09b17d 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -18,6 +18,9 @@
#include <validation.h> // For g_chainman
#include <warnings.h>
+#include <string>
+#include <utility>
+
using node::ReadBlockFromDisk;
constexpr uint8_t DB_BEST_BLOCK{'B'};
@@ -62,8 +65,8 @@ void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator
batch.Write(DB_BEST_BLOCK, locator);
}
-BaseIndex::BaseIndex(std::unique_ptr<interfaces::Chain> chain)
- : m_chain{std::move(chain)} {}
+BaseIndex::BaseIndex(std::unique_ptr<interfaces::Chain> chain, std::string name)
+ : m_chain{std::move(chain)}, m_name{std::move(name)} {}
BaseIndex::~BaseIndex()
{
@@ -295,6 +298,10 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
}
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block.get());
if (CustomAppend(block_info)) {
+ // Setting the best block index is intentionally the last step of this
+ // function, so BlockUntilSyncedToCurrentChain callers waiting for the
+ // best block index to be updated can rely on the block being fully
+ // processed, and the index object being safe to delete.
SetBestBlockIndex(pindex);
} else {
FatalError("%s: Failed to write block %s to index",
@@ -411,10 +418,17 @@ IndexSummary BaseIndex::GetSummary() const
void BaseIndex::SetBestBlockIndex(const CBlockIndex* block) {
assert(!node::fPruneMode || AllowPrune());
- m_best_block_index = block;
if (AllowPrune() && block) {
node::PruneLockInfo prune_lock;
prune_lock.height_first = block->nHeight;
WITH_LOCK(::cs_main, m_chainstate->m_blockman.UpdatePruneLock(GetName(), prune_lock));
}
+
+ // Intentionally set m_best_block_index as the last step in this function,
+ // after updating prune locks above, and after making any other references
+ // to *this, so the BlockUntilSyncedToCurrentChain function (which checks
+ // m_best_block_index as an optimization) can be used to wait for the last
+ // BlockConnected notification and safely assume that prune locks are
+ // updated and that the index object is safe to delete.
+ m_best_block_index = block;
}
diff --git a/src/index/base.h b/src/index/base.h
index 5a484377e7..349178a535 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -10,9 +10,11 @@
#include <threadinterrupt.h>
#include <validationinterface.h>
+#include <string>
+
class CBlock;
class CBlockIndex;
-class CChainState;
+class Chainstate;
namespace interfaces {
class Chain;
} // namespace interfaces
@@ -94,7 +96,8 @@ private:
protected:
std::unique_ptr<interfaces::Chain> m_chain;
- CChainState* m_chainstate{nullptr};
+ Chainstate* m_chainstate{nullptr};
+ const std::string m_name;
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override;
@@ -117,13 +120,13 @@ protected:
virtual DB& GetDB() const = 0;
/// Get the name of the index for display in logs.
- virtual const char* GetName() const = 0;
+ const std::string& GetName() const LIFETIMEBOUND { return m_name; }
/// Update the internal best block index as well as the prune lock.
void SetBestBlockIndex(const CBlockIndex* block);
public:
- BaseIndex(std::unique_ptr<interfaces::Chain> chain);
+ BaseIndex(std::unique_ptr<interfaces::Chain> chain, std::string name);
/// Destructor interrupts sync thread if running and blocks until it exits.
virtual ~BaseIndex();
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index f4837f3456..292e11c874 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -97,7 +97,8 @@ static std::map<BlockFilterType, BlockFilterIndex> g_filter_indexes;
BlockFilterIndex::BlockFilterIndex(std::unique_ptr<interfaces::Chain> chain, BlockFilterType filter_type,
size_t n_cache_size, bool f_memory, bool f_wipe)
- : BaseIndex(std::move(chain)), m_filter_type(filter_type)
+ : BaseIndex(std::move(chain), BlockFilterTypeName(filter_type) + " block filter index")
+ , m_filter_type(filter_type)
{
const std::string& filter_name = BlockFilterTypeName(filter_type);
if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
@@ -105,7 +106,6 @@ BlockFilterIndex::BlockFilterIndex(std::unique_ptr<interfaces::Chain> chain, Blo
fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / fs::u8path(filter_name);
fs::create_directories(path);
- m_name = filter_name + " block filter index";
m_db = std::make_unique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
}
diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h
index a31f7e460e..9e69388dc8 100644
--- a/src/index/blockfilterindex.h
+++ b/src/index/blockfilterindex.h
@@ -12,6 +12,8 @@
#include <index/base.h>
#include <util/hasher.h>
+static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
+
/** Interval between compact filter checkpoints. See BIP 157. */
static constexpr int CFCHECKPT_INTERVAL = 1000;
@@ -26,7 +28,6 @@ class BlockFilterIndex final : public BaseIndex
{
private:
BlockFilterType m_filter_type;
- std::string m_name;
std::unique_ptr<BaseIndex::DB> m_db;
FlatFilePos m_next_filter_pos;
@@ -52,8 +53,6 @@ protected:
BaseIndex::DB& GetDB() const LIFETIMEBOUND override { return *m_db; }
- const char* GetName() const LIFETIMEBOUND override { return m_name.c_str(); }
-
public:
/** Constructs the index, which becomes available to be queried. */
explicit BlockFilterIndex(std::unique_ptr<interfaces::Chain> chain, BlockFilterType filter_type,
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index b9029e946a..d3559b1b75 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -6,6 +6,7 @@
#include <coins.h>
#include <crypto/muhash.h>
#include <index/coinstatsindex.h>
+#include <kernel/coinstats.h>
#include <node/blockstorage.h>
#include <serialize.h>
#include <txdb.h>
@@ -104,7 +105,7 @@ struct DBHashKey {
std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
- : BaseIndex(std::move(chain))
+ : BaseIndex(std::move(chain), "coinstatsindex")
{
fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
fs::create_directories(path);
@@ -322,13 +323,13 @@ static bool LookUpOne(const CDBWrapper& db, const interfaces::BlockKey& block, D
return db.Read(DBHashKey(block.hash), result);
}
-std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const
+std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex& block_index) const
{
- CCoinsStats stats{Assert(block_index)->nHeight, block_index->GetBlockHash()};
+ CCoinsStats stats{block_index.nHeight, block_index.GetBlockHash()};
stats.index_used = true;
DBVal entry;
- if (!LookUpOne(*m_db, {block_index->GetBlockHash(), block_index->nHeight}, entry)) {
+ if (!LookUpOne(*m_db, {block_index.GetBlockHash(), block_index.nHeight}, entry)) {
return std::nullopt;
}
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index c4af223388..aa0d7f9fd5 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -5,11 +5,16 @@
#ifndef BITCOIN_INDEX_COINSTATSINDEX_H
#define BITCOIN_INDEX_COINSTATSINDEX_H
-#include <chain.h>
#include <crypto/muhash.h>
-#include <flatfile.h>
#include <index/base.h>
-#include <kernel/coinstats.h>
+
+class CBlockIndex;
+class CDBBatch;
+namespace kernel {
+struct CCoinsStats;
+}
+
+static constexpr bool DEFAULT_COINSTATSINDEX{false};
/**
* CoinStatsIndex maintains statistics on the UTXO set.
@@ -17,7 +22,6 @@
class CoinStatsIndex final : public BaseIndex
{
private:
- std::string m_name;
std::unique_ptr<BaseIndex::DB> m_db;
MuHash3072 m_muhash;
@@ -49,14 +53,12 @@ protected:
BaseIndex::DB& GetDB() const override { return *m_db; }
- const char* GetName() const override { return "coinstatsindex"; }
-
public:
// Constructs the index, which becomes available to be queried.
explicit CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
// Look up stats for a specific block using CBlockIndex
- std::optional<kernel::CCoinsStats> LookUpStats(const CBlockIndex* block_index) const;
+ std::optional<kernel::CCoinsStats> LookUpStats(const CBlockIndex& block_index) const;
};
/// The global UTXO set hash object.
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index b719aface8..a4fe1b611e 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -49,7 +49,7 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
}
TxIndex::TxIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
- : BaseIndex(std::move(chain)), m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
+ : BaseIndex(std::move(chain), "txindex"), m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
{}
TxIndex::~TxIndex() = default;
diff --git a/src/index/txindex.h b/src/index/txindex.h
index be240c4582..4cea35045d 100644
--- a/src/index/txindex.h
+++ b/src/index/txindex.h
@@ -7,6 +7,8 @@
#include <index/base.h>
+static constexpr bool DEFAULT_TXINDEX{false};
+
/**
* TxIndex is used to look up transactions included in the blockchain by hash.
* The index is written to a LevelDB database and records the filesystem
@@ -27,8 +29,6 @@ protected:
BaseIndex::DB& GetDB() const override;
- const char* GetName() const override { return "txindex"; }
-
public:
/// Constructs the index, which becomes available to be queried.
explicit TxIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
diff --git a/src/init.cpp b/src/init.cpp
index 0ca94974bb..24659de3df 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -40,11 +40,13 @@
#include <node/blockstorage.h>
#include <node/caches.h>
#include <node/chainstate.h>
+#include <node/chainstatemanager_args.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <node/mempool_args.h>
#include <node/mempool_persist_args.h>
#include <node/miner.h>
+#include <node/txreconciliation.h>
#include <node/validation_cache_args.h>
#include <policy/feerate.h>
#include <policy/fees.h>
@@ -263,7 +265,7 @@ void Shutdown(NodeContext& node)
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (node.chainman) {
LOCK(cs_main);
- for (CChainState* chainstate : node.chainman->GetAll()) {
+ for (Chainstate* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
}
@@ -294,7 +296,7 @@ void Shutdown(NodeContext& node)
if (node.chainman) {
LOCK(cs_main);
- for (CChainState* chainstate : node.chainman->GetAll()) {
+ for (Chainstate* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
@@ -476,7 +478,6 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
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("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, 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);
@@ -485,6 +486,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), 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);
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
@@ -553,7 +555,10 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_BYTES >> 20), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-maxtipage=<n>",
+ strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)",
+ Ticks<std::chrono::seconds>(DEFAULT_MAX_TIP_AGE)),
+ ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-printpriority", strprintf("Log transaction fee rate in " + CURRENCY_UNIT + "/kvB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
@@ -566,6 +571,8 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", DEFAULT_MEMPOOL_FULL_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
+ OptionsCategory::NODE_RELAY);
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
@@ -927,21 +934,6 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
init::SetLoggingCategories(args);
init::SetLoggingLevel(args);
- fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
- fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
-
- hashAssumeValid = uint256S(args.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
-
- if (args.IsArgSet("-minimumchainwork")) {
- const std::string minChainWorkStr = args.GetArg("-minimumchainwork", "");
- if (!IsHexNumber(minChainWorkStr)) {
- return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr));
- }
- nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
- } else {
- nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
- }
-
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg = args.GetIntArg("-prune", 0);
if (nPruneArg < 0) {
@@ -992,8 +984,6 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError(Untranslated("Unknown rpcserialversion requested."));
- nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
-
if (args.GetBoolArg("-reindex-chainstate", false)) {
// indexes that must be deactivated to prevent index corruption, see #24630
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
@@ -1041,6 +1031,16 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
}
#endif // USE_SYSCALL_SANDBOX
+ // Also report errors from parsing before daemonization
+ {
+ ChainstateManager::Options chainman_opts_dummy{
+ .chainparams = chainparams,
+ };
+ if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) {
+ return InitError(*error);
+ }
+ }
+
return true;
}
@@ -1143,7 +1143,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Script verification uses %d additional threads\n", script_threads);
if (script_threads >= 1) {
- g_parallel_script_checks = true;
StartScriptCheckWorkerThreads(script_threads);
}
@@ -1254,6 +1253,51 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// as they would never get updated.
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(args));
+ // Check port numbers
+ for (const std::string port_option : {
+ "-port",
+ "-rpcport",
+ }) {
+ if (args.IsArgSet(port_option)) {
+ const std::string port = args.GetArg(port_option, "");
+ uint16_t n;
+ if (!ParseUInt16(port, &n) || n == 0) {
+ return InitError(InvalidPortErrMsg(port_option, port));
+ }
+ }
+ }
+
+ for (const std::string port_option : {
+ "-i2psam",
+ "-onion",
+ "-proxy",
+ "-rpcbind",
+ "-torcontrol",
+ "-whitebind",
+ "-zmqpubhashblock",
+ "-zmqpubhashtx",
+ "-zmqpubrawblock",
+ "-zmqpubrawtx",
+ "-zmqpubsequence",
+ }) {
+ for (const std::string& socket_addr : args.GetArgs(port_option)) {
+ std::string host_out;
+ uint16_t port_out{0};
+ if (!SplitHostPort(socket_addr, port_out, host_out)) {
+ return InitError(InvalidPortErrMsg(port_option, socket_addr));
+ }
+ }
+ }
+
+ for (const std::string& socket_addr : args.GetArgs("-bind")) {
+ std::string host_out;
+ uint16_t port_out{0};
+ std::string bind_socket_addr = socket_addr.substr(0, socket_addr.rfind('='));
+ if (!SplitHostPort(bind_socket_addr, port_out, host_out)) {
+ return InitError(InvalidPortErrMsg("-bind", socket_addr));
+ }
+ }
+
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
for (const std::string& cmt : args.GetArgs("-uacomment")) {
@@ -1284,6 +1328,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
if (!args.IsArgSet("-cjdnsreachable")) {
+ if (args.IsArgSet("-onlynet") && IsReachable(NET_CJDNS)) {
+ return InitError(
+ _("Outbound connections restricted to CJDNS (-onlynet=cjdns) but "
+ "-cjdnsreachable is not provided"));
+ }
SetReachable(NET_CJDNS, false);
}
// Now IsReachable(NET_CJDNS) is true if:
@@ -1324,6 +1373,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
onion_proxy = addrProxy;
}
+ const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && IsReachable(NET_ONION)};
+
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
// An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none)
@@ -1331,6 +1382,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
onion_proxy = Proxy{};
+ if (onlynet_used_with_onion) {
+ return InitError(
+ _("Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
+ "reaching the Tor network is explicitly forbidden: -onion=0"));
+ }
} else {
CService addr;
if (!Lookup(onionArg, addr, 9050, fNameLookup) || !addr.IsValid()) {
@@ -1343,11 +1399,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (onion_proxy.IsValid()) {
SetProxy(NET_ONION, onion_proxy);
} else {
- if (args.IsArgSet("-onlynet") && IsReachable(NET_ONION)) {
+ // If -listenonion is set, then we will (try to) connect to the Tor control port
+ // later from the torcontrol thread and may retrieve the onion proxy from there.
+ const bool listenonion_disabled{!args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)};
+ if (onlynet_used_with_onion && listenonion_disabled) {
return InitError(
_("Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
- "reaching the Tor network is not provided (no -proxy= and no -onion= given) or "
- "it is explicitly forbidden (-onion=0)"));
+ "reaching the Tor network is not provided: none of -proxy, -onion or "
+ "-listenonion is given"));
}
SetReachable(NET_ONION, false);
}
@@ -1372,6 +1431,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
fReindex = args.GetBoolArg("-reindex", false);
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
+ ChainstateManager::Options chainman_opts{
+ .chainparams = chainparams,
+ .adjusted_time_callback = GetAdjustedTime,
+ };
+ Assert(!ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
// cache size calculations
CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size());
@@ -1408,10 +1472,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
- const ChainstateManager::Options chainman_opts{
- .chainparams = chainparams,
- .adjusted_time_callback = GetAdjustedTime,
- };
node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
ChainstateManager& chainman = *node.chainman;
@@ -1430,7 +1490,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
};
uiInterface.InitMessage(_("Loading block index…").translated);
- const int64_t load_block_index_start_time = GetTimeMillis();
+ const auto load_block_index_start_time{SteadyClock::now()};
auto catch_exceptions = [](auto&& f) {
try {
return f();
@@ -1449,7 +1509,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
std::tie(status, error) = catch_exceptions([&]{ return VerifyLoadedChainstate(chainman, options);});
if (status == node::ChainstateLoadStatus::SUCCESS) {
fLoaded = true;
- LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
+ LogPrintf(" block index %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - load_block_index_start_time));
}
}
@@ -1532,7 +1592,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (fPruneMode) {
if (!fReindex) {
LOCK(cs_main);
- for (CChainState* chainstate : chainman.GetAll()) {
+ for (Chainstate* chainstate : chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore…").translated);
chainstate->PruneAndFlush();
}
@@ -1647,7 +1707,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
const auto BadPortWarning = [](const char* prefix, uint16_t port) {
return strprintf(_("%s request to listen on port %u. This port is considered \"bad\" and "
- "thus it is unlikely that any Bitcoin Core peers connect to it. See "
+ "thus it is unlikely that any peer will connect to it. See "
"doc/p2p-bad-ports.md for details and a full list."),
prefix,
port);
@@ -1746,6 +1806,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
SetProxy(NET_I2P, Proxy{addr});
} else {
+ if (args.IsArgSet("-onlynet") && IsReachable(NET_I2P)) {
+ return InitError(
+ _("Outbound connections restricted to i2p (-onlynet=i2p) but "
+ "-i2psam is not provided"));
+ }
SetReachable(NET_I2P, false);
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 5fc0e540a9..7a3d88b18f 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
+#include <blockfilter.h>
#include <primitives/transaction.h> // For CTransactionRef
#include <util/settings.h> // For util::SettingsValue
@@ -143,6 +144,13 @@ public:
//! or one of its ancestors.
virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
+ //! Returns whether a block filter index is available.
+ virtual bool hasBlockFilterIndex(BlockFilterType filter_type) = 0;
+
+ //! Returns whether any of the elements match the block via a BIP 157 block filter
+ //! or std::nullopt if the block filter for this block couldn't be found.
+ virtual std::optional<bool> blockFilterMatchesAny(BlockFilterType filter_type, const uint256& block_hash, const GCSFilter::ElementSet& filter_set) = 0;
+
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0;
diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp
index 580590fde9..ee0d4123ce 100644
--- a/src/ipc/interfaces.cpp
+++ b/src/ipc/interfaces.cpp
@@ -12,11 +12,11 @@
#include <tinyformat.h>
#include <util/system.h>
+#include <cstdio>
+#include <cstdlib>
#include <functional>
#include <memory>
#include <stdexcept>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <string>
#include <unistd.h>
diff --git a/src/ipc/process.cpp b/src/ipc/process.cpp
index 9036b80c45..9474ad2c4a 100644
--- a/src/ipc/process.cpp
+++ b/src/ipc/process.cpp
@@ -10,10 +10,10 @@
#include <util/strencodings.h>
#include <cstdint>
+#include <cstdlib>
#include <exception>
#include <iostream>
#include <stdexcept>
-#include <stdlib.h>
#include <string.h>
#include <system_error>
#include <unistd.h>
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
index 520d0e8e75..020ae24c11 100644
--- a/src/kernel/chainstatemanager_opts.h
+++ b/src/kernel/chainstatemanager_opts.h
@@ -5,13 +5,19 @@
#ifndef BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
#define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
+#include <arith_uint256.h>
+#include <uint256.h>
#include <util/time.h>
#include <cstdint>
#include <functional>
+#include <optional>
class CChainParams;
+static constexpr bool DEFAULT_CHECKPOINTS_ENABLED{true};
+static constexpr auto DEFAULT_MAX_TIP_AGE{24h};
+
namespace kernel {
/**
@@ -22,6 +28,14 @@ namespace kernel {
struct ChainstateManagerOpts {
const CChainParams& chainparams;
const std::function<NodeClock::time_point()> adjusted_time_callback{nullptr};
+ std::optional<bool> check_block_index{};
+ bool checkpoints_enabled{DEFAULT_CHECKPOINTS_ENABLED};
+ //! If set, it will override the minimum work we will assume exists on some valid chain.
+ std::optional<arith_uint256> minimum_chain_work;
+ //! If set, it will override the block hash whose ancestors we will assume to have valid scripts without checking them.
+ std::optional<uint256> assumed_valid_block;
+ //! If the tip is older than this, the node is considered to be in initial block download.
+ std::chrono::seconds max_tip_age{DEFAULT_MAX_TIP_AGE};
};
} // namespace kernel
diff --git a/src/kernel/mempool_limits.h b/src/kernel/mempool_limits.h
index e192e7e6cd..8d4495c3cb 100644
--- a/src/kernel/mempool_limits.h
+++ b/src/kernel/mempool_limits.h
@@ -24,6 +24,15 @@ struct MemPoolLimits {
int64_t descendant_count{DEFAULT_DESCENDANT_LIMIT};
//! The maximum allowed size in virtual bytes of an entry and its descendants within a package.
int64_t descendant_size_vbytes{DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * 1'000};
+
+ /**
+ * @return MemPoolLimits with all the limits set to the maximum
+ */
+ static constexpr MemPoolLimits NoLimits()
+ {
+ int64_t no_limit{std::numeric_limits<int64_t>::max()};
+ return {no_limit, no_limit, no_limit, no_limit};
+ }
};
} // namespace kernel
diff --git a/src/kernel/mempool_persist.cpp b/src/kernel/mempool_persist.cpp
index 1a1cf2bbdc..a14b2e6163 100644
--- a/src/kernel/mempool_persist.cpp
+++ b/src/kernel/mempool_persist.cpp
@@ -37,7 +37,7 @@ namespace kernel {
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, CChainState& active_chainstate, FopenFn mockable_fopen_function)
+bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, FopenFn mockable_fopen_function)
{
if (load_path.empty()) return false;
diff --git a/src/kernel/mempool_persist.h b/src/kernel/mempool_persist.h
index 9a15ec6dca..ca4917e38b 100644
--- a/src/kernel/mempool_persist.h
+++ b/src/kernel/mempool_persist.h
@@ -7,7 +7,7 @@
#include <fs.h>
-class CChainState;
+class Chainstate;
class CTxMemPool;
namespace kernel {
@@ -19,7 +19,7 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path,
/** Load the mempool from disk. */
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,
- CChainState& active_chainstate,
+ Chainstate& active_chainstate,
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
} // namespace kernel
diff --git a/src/leveldb/util/env_windows.cc b/src/leveldb/util/env_windows.cc
index 4dcba222a1..aafcdcc3be 100644
--- a/src/leveldb/util/env_windows.cc
+++ b/src/leveldb/util/env_windows.cc
@@ -194,7 +194,7 @@ class WindowsRandomAccessFile : public RandomAccessFile {
Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const override {
DWORD bytes_read = 0;
- OVERLAPPED overlapped = {0};
+ OVERLAPPED overlapped = {};
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
overlapped.Offset = static_cast<DWORD>(offset);
diff --git a/src/logging.cpp b/src/logging.cpp
index a3f1d39be5..ed0c2a56a5 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -180,6 +180,8 @@ const CLogCategoryDesc LogCategories[] =
#endif
{BCLog::UTIL, "util"},
{BCLog::BLOCKSTORE, "blockstorage"},
+ {BCLog::TXRECONCILIATION, "txreconciliation"},
+ {BCLog::SCAN, "scan"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
@@ -280,6 +282,10 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
return "util";
case BCLog::LogFlags::BLOCKSTORE:
return "blockstorage";
+ case BCLog::LogFlags::TXRECONCILIATION:
+ return "txreconciliation";
+ case BCLog::LogFlags::SCAN:
+ return "scan";
case BCLog::LogFlags::ALL:
return "all";
}
diff --git a/src/logging.h b/src/logging.h
index fe91ee43a5..14a0f08f8d 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -66,6 +66,8 @@ namespace BCLog {
#endif
UTIL = (1 << 25),
BLOCKSTORE = (1 << 26),
+ TXRECONCILIATION = (1 << 27),
+ SCAN = (1 << 28),
ALL = ~(uint32_t)0,
};
enum class Level {
diff --git a/src/memusage.h b/src/memusage.h
index a6e894129a..fd85a7956c 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -8,9 +8,8 @@
#include <indirectmap.h>
#include <prevector.h>
-#include <stdlib.h>
-
#include <cassert>
+#include <cstdlib>
#include <map>
#include <memory>
#include <set>
diff --git a/src/net.cpp b/src/net.cpp
index d1df393ad3..3c28b9eddf 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -91,13 +91,12 @@ static constexpr auto FEELER_SLEEP_WINDOW{1s};
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
- BF_EXPLICIT = (1U << 0),
- BF_REPORT_ERROR = (1U << 1),
+ BF_REPORT_ERROR = (1U << 0),
/**
* Do not call AddLocal() for our special addresses, e.g., for incoming
* Tor connections, to prevent gossiping them over the network.
*/
- BF_DONT_ADVERTISE = (1U << 2),
+ BF_DONT_ADVERTISE = (1U << 1),
};
// The set of sockets cannot be modified while waiting
@@ -971,8 +970,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
return;
}
- if (!IsSelectableSocket(sock->Get()))
- {
+ if (!sock->IsSelectable()) {
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
return;
}
@@ -1500,12 +1498,12 @@ void CConnman::ThreadDNSAddressSeed()
void CConnman::DumpAddresses()
{
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
DumpPeerAddresses(::gArgs, addrman);
LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n",
- addrman.size(), GetTimeMillis() - nStart);
+ addrman.size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
}
void CConnman::ProcessAddrFetch()
@@ -1648,6 +1646,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (add_fixed_seeds_now) {
std::vector<CAddress> seed_addrs{ConvertSeeds(Params().FixedSeeds())};
+ // We will not make outgoing connections to peers that are unreachable
+ // (e.g. because of -onlynet configuration).
+ // Therefore, we do not add them to addrman in the first place.
+ // Note that if you change -onlynet setting from one network to another,
+ // peers.dat will contain only peers of unreachable networks and
+ // manual intervention will be needed (either delete peers.dat after
+ // configuration change or manually add some reachable peer using addnode),
+ // see <https://github.com/bitcoin/bitcoin/issues/26035> for details.
seed_addrs.erase(std::remove_if(seed_addrs.begin(), seed_addrs.end(),
[](const CAddress& addr) { return !IsReachable(addr); }),
seed_addrs.end());
@@ -1983,8 +1989,12 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
}
}
+Mutex NetEventsInterface::g_msgproc_mutex;
+
void CConnman::ThreadMessageHandler()
{
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER);
while (!flagInterruptMsgProc)
{
@@ -2006,10 +2016,7 @@ void CConnman::ThreadMessageHandler()
if (flagInterruptMsgProc)
return;
// Send messages
- {
- LOCK(pnode->cs_sendProcessing);
- m_msgproc->SendMessages(pnode);
- }
+ m_msgproc->SendMessages(pnode);
if (flagInterruptMsgProc)
return;
@@ -2222,9 +2229,6 @@ bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlag
{
const CService addr{MaybeFlipIPv6toCJDNS(addr_)};
- if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) {
- return false;
- }
bilingual_str strError;
if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && m_client_interface) {
@@ -2244,13 +2248,13 @@ bool CConnman::InitBinds(const Options& options)
{
bool fBound = false;
for (const auto& addrBind : options.vBinds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::None);
+ fBound |= Bind(addrBind, BF_REPORT_ERROR, NetPermissionFlags::None);
}
for (const auto& addrBind : options.vWhiteBinds) {
- fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
+ fBound |= Bind(addrBind.m_service, BF_REPORT_ERROR, addrBind.m_flags);
}
for (const auto& addr_bind : options.onion_binds) {
- fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::None);
+ fBound |= Bind(addr_bind, BF_DONT_ADVERTISE, NetPermissionFlags::None);
}
if (options.bind_on_any) {
struct in_addr inaddr_any;
diff --git a/src/net.h b/src/net.h
index 66a228b3ec..11bfc4c9fb 100644
--- a/src/net.h
+++ b/src/net.h
@@ -164,6 +164,7 @@ bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
CService GetLocalAddress(const CNetAddr& addrPeer);
+CService MaybeFlipIPv6toCJDNS(const CService& service);
extern bool fDiscover;
@@ -377,8 +378,6 @@ public:
std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg);
size_t nProcessQueueSize GUARDED_BY(cs_vProcessMsg){0};
- RecursiveMutex cs_sendProcessing;
-
uint64_t nRecvBytes GUARDED_BY(cs_vRecv){0};
std::atomic<std::chrono::seconds> m_last_send{0s};
@@ -629,6 +628,9 @@ private:
class NetEventsInterface
{
public:
+ /** Mutex for anything that is only accessed via the msg processing thread */
+ static Mutex g_msgproc_mutex;
+
/** Initialize a peer (setup state, queue any initial messages) */
virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0;
@@ -642,7 +644,7 @@ public:
* @param[in] interrupt Interrupt condition for processing threads
* @return True if there is more work to be done
*/
- virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
/**
* Send queued protocol messages to a given node.
@@ -650,7 +652,7 @@ public:
* @param[in] pnode The node which we are sending messages to.
* @return True if there is more work to be done
*/
- virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0;
+ virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
protected:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 74700580ad..363f2fde71 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -20,6 +20,7 @@
#include <netbase.h>
#include <netmessagemaker.h>
#include <node/blockstorage.h>
+#include <node/txreconciliation.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -264,19 +265,14 @@ struct Peer {
/** The feerate in the most recent BIP133 `feefilter` message sent to the peer.
* It is *not* a p2p protocol violation for the peer to send us
* transactions with a lower fee rate than this. See BIP133. */
- CAmount m_fee_filter_sent{0};
+ CAmount m_fee_filter_sent GUARDED_BY(NetEventsInterface::g_msgproc_mutex){0};
/** Timestamp after which we will send the next BIP133 `feefilter` message
* to the peer. */
- std::chrono::microseconds m_next_send_feefilter{0};
+ std::chrono::microseconds m_next_send_feefilter GUARDED_BY(NetEventsInterface::g_msgproc_mutex){0};
struct TxRelay {
mutable RecursiveMutex m_bloom_filter_mutex;
- /** Whether the peer wishes to receive transaction announcements.
- *
- * This is initially set based on the fRelay flag in the received
- * `version` message. If initially set to false, it can only be flipped
- * to true if we have offered the peer NODE_BLOOM services and it sends
- * us a `filterload` or `filterclear` message. See BIP37. */
+ /** Whether we relay transactions to this peer. */
bool m_relay_txs GUARDED_BY(m_bloom_filter_mutex){false};
/** A bloom filter for which transactions to announce to the peer. See BIP37. */
std::unique_ptr<CBloomFilter> m_bloom_filter PT_GUARDED_BY(m_bloom_filter_mutex) GUARDED_BY(m_bloom_filter_mutex){nullptr};
@@ -298,7 +294,7 @@ struct Peer {
std::atomic<std::chrono::seconds> m_last_mempool_req{0s};
/** The next time after which we will send an `inv` message containing
* transaction announcements to this peer. */
- std::chrono::microseconds m_next_inv_send_time{0};
+ std::chrono::microseconds m_next_inv_send_time GUARDED_BY(NetEventsInterface::g_msgproc_mutex){0};
/** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
std::atomic<CAmount> m_fee_filter_received{0};
@@ -319,7 +315,7 @@ struct Peer {
};
/** A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
- std::vector<CAddress> m_addrs_to_send;
+ std::vector<CAddress> m_addrs_to_send GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
/** Probabilistic filter to track recent addr messages relayed with this
* peer. Used to avoid relaying redundant addresses to this peer.
*
@@ -329,7 +325,7 @@ struct Peer {
*
* Presence of this filter must correlate with m_addr_relay_enabled.
**/
- std::unique_ptr<CRollingBloomFilter> m_addr_known;
+ std::unique_ptr<CRollingBloomFilter> m_addr_known GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
/** Whether we are participating in address relay with this connection.
*
* We set this bool to true for outbound peers (other than
@@ -346,7 +342,7 @@ struct Peer {
* initialized.*/
std::atomic_bool m_addr_relay_enabled{false};
/** Whether a getaddr request to this peer is outstanding. */
- bool m_getaddr_sent{false};
+ bool m_getaddr_sent GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
/** Guards address sending timers. */
mutable Mutex m_addr_send_times_mutex;
/** Time point to send the next ADDR message to this peer. */
@@ -357,12 +353,12 @@ struct Peer {
* messages, indicating a preference to receive ADDRv2 instead of ADDR ones. */
std::atomic_bool m_wants_addrv2{false};
/** Whether this peer has already sent us a getaddr message. */
- bool m_getaddr_recvd{false};
+ bool m_getaddr_recvd GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
/** Number of addresses that can be processed from this peer. Start at 1 to
* permit self-announcement. */
- double m_addr_token_bucket{1.0};
+ double m_addr_token_bucket GUARDED_BY(NetEventsInterface::g_msgproc_mutex){1.0};
/** When m_addr_token_bucket was last updated */
- std::chrono::microseconds m_addr_token_timestamp{GetTime<std::chrono::microseconds>()};
+ std::chrono::microseconds m_addr_token_timestamp GUARDED_BY(NetEventsInterface::g_msgproc_mutex){GetTime<std::chrono::microseconds>()};
/** Total number of addresses that were dropped due to rate limiting. */
std::atomic<uint64_t> m_addr_rate_limited{0};
/** Total number of addresses that were processed (excludes rate-limited ones). */
@@ -372,7 +368,7 @@ struct Peer {
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
/** Whether we've sent this peer a getheaders in response to an inv prior to initial-headers-sync completing */
- bool m_inv_triggered_getheaders_before_sync{false};
+ bool m_inv_triggered_getheaders_before_sync GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
/** Protects m_getdata_requests **/
Mutex m_getdata_requests_mutex;
@@ -380,7 +376,7 @@ struct Peer {
std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
/** Time of the last getheaders message to this peer */
- NodeClock::time_point m_last_getheaders_timestamp{};
+ NodeClock::time_point m_last_getheaders_timestamp GUARDED_BY(NetEventsInterface::g_msgproc_mutex){};
/** Protects m_headers_sync **/
Mutex m_headers_sync_mutex;
@@ -515,9 +511,9 @@ public:
void InitializeNode(CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
- bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
+ bool SendMessages(CNode* pto) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex);
/** Implement PeerManager */
void StartScheduledTasks(CScheduler& scheduler) override;
@@ -532,12 +528,12 @@ public:
void UnitTestMisbehaving(NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), howmuch, ""); };
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
private:
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_msgproc_mutex);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(std::chrono::seconds now) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -601,7 +597,7 @@ private:
void ProcessHeadersMessage(CNode& pfrom, Peer& peer,
std::vector<CBlockHeader>&& headers,
bool via_compact_block)
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
/** Various helpers for headers processing, invoked by ProcessHeadersMessage() */
/** Return true if headers are continuous and have valid proof-of-work (DoS points assigned on failure) */
bool CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, Peer& peer);
@@ -610,7 +606,7 @@ private:
/** Deal with state tracking and headers sync for peers that send the
* occasional non-connecting header (this can happen due to BIP 130 headers
* announcements for blocks interacting with the 2hr (MAX_FUTURE_BLOCK_TIME) rule). */
- void HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer, const std::vector<CBlockHeader>& headers);
+ void HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer, const std::vector<CBlockHeader>& headers) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
/** Return true if the headers connect to each other, false otherwise */
bool CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const;
/** Try to continue a low-work headers sync that has already begun.
@@ -633,7 +629,7 @@ private:
*/
bool IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom,
std::vector<CBlockHeader>& headers)
- EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex, !m_headers_presync_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
/** Check work on a headers chain to be processed, and if insufficient,
* initiate our anti-DoS headers sync mechanism.
*
@@ -649,7 +645,7 @@ private:
bool TryLowWorkHeadersSync(Peer& peer, CNode& pfrom,
const CBlockIndex* chain_start_header,
std::vector<CBlockHeader>& headers)
- EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex, !m_headers_presync_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
/** Return true if the given header is an ancestor of
* m_chainman.m_best_header or our current tip */
@@ -659,7 +655,7 @@ private:
* We don't issue a getheaders message if we have a recent one outstanding.
* This returns true if a getheaders is actually sent, and false otherwise.
*/
- bool MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer);
+ bool MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
/** Potentially fetch blocks from this peer upon receipt of a new headers tip */
void HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex* pindexLast);
/** Update peer state based on received headers message */
@@ -683,10 +679,10 @@ private:
void MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now);
/** Send `addr` messages on a regular schedule. */
- void MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time);
+ void MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
/** Send a single `sendheaders` message, after we have completed headers sync with a peer. */
- void MaybeSendSendHeaders(CNode& node, Peer& peer);
+ void MaybeSendSendHeaders(CNode& node, Peer& peer) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
/** Relay (gossip) an address to a few randomly chosen nodes.
*
@@ -695,10 +691,10 @@ private:
* @param[in] fReachable Whether the address' network is reachable. We relay unreachable
* addresses less.
*/
- void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
/** Send `feefilter` message. */
- void MaybeSendFeefilter(CNode& node, Peer& peer, std::chrono::microseconds current_time);
+ void MaybeSendFeefilter(CNode& node, Peer& peer, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
const CChainParams& m_chainparams;
CConnman& m_connman;
@@ -708,6 +704,7 @@ private:
ChainstateManager& m_chainman;
CTxMemPool& m_mempool;
TxRequestTracker m_txrequest GUARDED_BY(::cs_main);
+ std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
/** The height of the best chain */
std::atomic<int> m_best_height{-1};
@@ -751,7 +748,7 @@ private:
int nSyncStarted GUARDED_BY(cs_main) = 0;
/** Hash of the last block we received via INV */
- uint256 m_last_block_inv_triggering_headers_sync{};
+ uint256 m_last_block_inv_triggering_headers_sync GUARDED_BY(g_msgproc_mutex){};
/**
* Sources of received blocks, saved to be able punish them when processing
@@ -863,7 +860,7 @@ private:
std::atomic_bool m_headers_presync_should_signal{false};
/** Height of the highest block announced using BIP 152 high-bandwidth mode. */
- int m_highest_fast_announce{0};
+ int m_highest_fast_announce GUARDED_BY(::cs_main){0};
/** Have we requested this block from a peer */
bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -1010,7 +1007,10 @@ private:
* @return True if address relay is enabled with peer
* False if address relay is disallowed
*/
- bool SetupAddressRelay(const CNode& node, Peer& peer);
+ 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);
};
const CNodeState* PeerManagerImpl::State(NodeId pnode) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1036,13 +1036,13 @@ static bool IsAddrCompatible(const Peer& peer, const CAddress& addr)
return peer.m_wants_addrv2 || addr.IsAddrV1Compatible();
}
-static void AddAddressKnown(Peer& peer, const CAddress& addr)
+void PeerManagerImpl::AddAddressKnown(Peer& peer, const CAddress& addr)
{
assert(peer.m_addr_known);
peer.m_addr_known->insert(addr.GetKey());
}
-static void PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand)
+void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand)
{
// Known checking here is only to save space from duplicates.
// Before sending, we'll filter it again for known addresses that were
@@ -1291,7 +1291,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(const Peer& peer, unsigned int co
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(peer.m_id);
- if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < m_chainman.MinimumChainWork()) {
// This peer has nothing interesting.
return;
}
@@ -1380,7 +1380,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
CService addr_you = addr.IsRoutable() && !IsProxy(addr) && addr.IsAddrV1Compatible() ? addr : CService();
uint64_t your_services{addr.nServices};
- const bool tx_relay = !m_ignore_incoming_txs && !pnode.IsBlockOnlyConn() && !pnode.IsFeelerConn();
+ const bool tx_relay{!RejectIncomingTxs(pnode)};
m_connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, my_services, nTime,
your_services, addr_you, // Together the pre-version-31402 serialization of CAddress "addrYou" (without nTime)
my_services, CService(), // Together the pre-version-31402 serialization of CAddress "addrMe" (without nTime)
@@ -1494,6 +1494,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
+ if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
m_num_preferred_download_peers -= state->fPreferredDownload;
m_peers_downloading_from -= (state->nBlocksInFlight != 0);
assert(m_peers_downloading_from >= 0);
@@ -1778,6 +1779,11 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
m_mempool(pool),
m_ignore_incoming_txs(ignore_incoming_txs)
{
+ // While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation.
+ // This argument can go away after Erlay support is complete.
+ if (gArgs.GetBoolArg("-txreconciliation", DEFAULT_TXRECONCILIATION_ENABLE)) {
+ m_txreconciliation = std::make_unique<TxReconciliationTracker>(TXRECONCILIATION_VERSION);
+ }
}
void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
@@ -2386,7 +2392,7 @@ arith_uint256 PeerManagerImpl::GetAntiDoSWorkThreshold()
// near our tip.
near_chaintip_work = tip->nChainWork - std::min<arith_uint256>(144*GetBlockProof(*tip), tip->nChainWork);
}
- return std::max(near_chaintip_work, arith_uint256(nMinimumChainWork));
+ return std::max(near_chaintip_work, m_chainman.MinimumChainWork());
}
/**
@@ -2559,14 +2565,22 @@ bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlo
// Now a HeadersSyncState object for tracking this synchronization is created,
// process the headers using it as normal.
- return IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers);
+ if (!IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers)) {
+ // Something went wrong, reset the headers sync.
+ peer.m_headers_sync.reset(nullptr);
+ LOCK(m_headers_presync_mutex);
+ m_headers_presync_stats.erase(peer.m_id);
+ }
} else {
LogPrint(BCLog::NET, "Ignoring low-work chain (height=%u) from peer=%d\n", chain_start_header->nHeight + headers.size(), pfrom.GetId());
- // Since this is a low-work headers chain, no further processing is required.
- headers = {};
- return true;
}
+
+ // The peer has not yet given us a chain that meets our work threshold,
+ // so we want to prevent further processing of the headers in any case.
+ headers = {};
+ return true;
}
+
return false;
}
@@ -2696,14 +2710,14 @@ void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !may_have_more_headers) {
// If the peer has no more headers to give us, then we know we have
// their tip.
- if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < m_chainman.MinimumChainWork()) {
// This peer has too little work on their headers chain to help
// us sync -- disconnect if it is an outbound disconnection
// candidate.
- // Note: We compare their tip to nMinimumChainWork (rather than
+ // Note: We compare their tip to the minumum chain work (rather than
// m_chainman.ActiveChain().Tip()) because we won't start block download
// until we have a headers chain that has at least
- // nMinimumChainWork, even if a peer has a chain past our tip,
+ // the minimum chain work, even if a peer has a chain past our tip,
// as an anti-DoS measure.
if (pfrom.IsOutboundOrBlockRelayConn()) {
LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
@@ -2843,7 +2857,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
// If we don't have the last header, then this peer will have given us
// something new (if these headers are valid).
- bool received_new_header{last_received_header != nullptr};
+ bool received_new_header{last_received_header == nullptr};
// Now process all the headers.
BlockValidationState state;
@@ -3135,6 +3149,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const std::chrono::microseconds time_received,
const std::atomic<bool>& interruptMsgProc)
{
+ AssertLockHeld(g_msgproc_mutex);
+
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
PeerRef peer = GetPeerRef(pfrom.GetId());
@@ -3236,8 +3252,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
}
- m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
-
pfrom.m_has_all_wanted_services = HasAllDesirableServiceFlags(nServices);
peer->m_their_services = nServices;
pfrom.SetAddrLocal(addrMe);
@@ -3247,10 +3261,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
peer->m_starting_height = starting_height;
- // We only initialize the m_tx_relay data structure if:
- // - this isn't an outbound block-relay-only connection; and
- // - fRelay=true or we're offering NODE_BLOOM to this peer
- // (NODE_BLOOM means that the peer may turn on tx relay later)
+ // We only initialize the Peer::TxRelay m_relay_txs data structure if:
+ // - this isn't an outbound block-relay-only connection, and
+ // - fRelay=true (the peer wishes to receive transaction announcements)
+ // or we're offering NODE_BLOOM to this peer. NODE_BLOOM means that
+ // the peer may turn on transaction relay later.
if (!pfrom.IsBlockOnlyConn() &&
(fRelay || (peer->m_our_services & NODE_BLOOM))) {
auto* const tx_relay = peer->SetTxRelay();
@@ -3261,6 +3276,25 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (fRelay) pfrom.m_relays_txs = true;
}
+ if (greatest_common_version >= WTXID_RELAY_VERSION && m_txreconciliation) {
+ // Per BIP-330, we announce txreconciliation support if:
+ // - protocol version per the VERSION message supports WTXID_RELAY;
+ // - we intended to exchange transactions over this connection while establishing it
+ // and the peer indicated support for transaction relay in the VERSION message;
+ // - we are not in -blocksonly mode.
+ if (pfrom.m_relays_txs && !m_ignore_incoming_txs) {
+ const uint64_t recon_salt = m_txreconciliation->PreRegisterPeer(pfrom.GetId());
+ // We suggest our txreconciliation role (initiator/responder) based on
+ // the connection direction.
+ m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDTXRCNCL,
+ !pfrom.IsInboundConn(),
+ pfrom.IsInboundConn(),
+ TXRECONCILIATION_VERSION, recon_salt));
+ }
+ }
+
+ m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
+
// Potentially mark this peer as a preferred download peer.
{
LOCK(cs_main);
@@ -3388,6 +3422,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// they may wish to request compact blocks from us
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION));
}
+
+ if (m_txreconciliation) {
+ if (!peer->m_wtxid_relay || !m_txreconciliation->IsPeerRegistered(pfrom.GetId())) {
+ // We could have optimistically pre-registered/registered the peer. In that case,
+ // we should forget about the reconciliation state here if this wasn't followed
+ // by WTXIDRELAY (since WTXIDRELAY can't be announced later).
+ m_txreconciliation->ForgetPeer(pfrom.GetId());
+ }
+ }
+
pfrom.fSuccessfullyConnected = true;
return;
}
@@ -3451,6 +3495,58 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ // Received from a peer demonstrating readiness to announce transactions via reconciliations.
+ // This feature negotiation must happen between VERSION and VERACK to avoid relay problems
+ // from switching announcement protocols after the connection is up.
+ if (msg_type == NetMsgType::SENDTXRCNCL) {
+ if (!m_txreconciliation) {
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "sendtxrcncl from peer=%d ignored, as our node does not have txreconciliation enabled\n", pfrom.GetId());
+ return;
+ }
+
+ if (pfrom.fSuccessfullyConnected) {
+ // Disconnect peers that send a SENDTXRCNCL message after VERACK.
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "sendtxrcncl received after verack from peer=%d; disconnecting\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
+
+ if (!peer->GetTxRelay()) {
+ // Disconnect peers that send a SENDTXRCNCL message even though we indicated we don't
+ // support transaction relay.
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "sendtxrcncl received from peer=%d to which we indicated no tx relay; disconnecting\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
+
+ bool is_peer_initiator, is_peer_responder;
+ uint32_t peer_txreconcl_version;
+ uint64_t remote_salt;
+ vRecv >> is_peer_initiator >> is_peer_responder >> peer_txreconcl_version >> remote_salt;
+
+ if (m_txreconciliation->IsPeerRegistered(pfrom.GetId())) {
+ // A peer is already registered, meaning we already received SENDTXRCNCL from them.
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "txreconciliation protocol violation from peer=%d (sendtxrcncl received from already registered peer); disconnecting\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
+
+ const ReconciliationRegisterResult result = m_txreconciliation->RegisterPeer(pfrom.GetId(), pfrom.IsInboundConn(),
+ is_peer_initiator, is_peer_responder,
+ peer_txreconcl_version,
+ remote_salt);
+
+ // If it's a protocol violation, disconnect.
+ // If the peer was not found (but something unexpected happened) or it was registered,
+ // nothing to be done.
+ if (result == ReconciliationRegisterResult::PROTOCOL_VIOLATION) {
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "txreconciliation protocol violation from peer=%d; disconnecting\n", pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
+ return;
+ }
+
if (!pfrom.fSuccessfullyConnected) {
LogPrint(BCLog::NET, "Unsupported message \"%s\" prior to verack from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
return;
@@ -3805,12 +3901,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Note that if we were to be on a chain that forks from the checkpointed
// chain, then serving those headers to a peer that has seen the
// checkpointed chain would cause that peer to disconnect us. Requiring
- // that our chainwork exceed nMinimumChainWork is a protection against
+ // that our chainwork exceed the mimimum chain work is a protection against
// being fed a bogus chain when we started up for the first time and
// getting partitioned off the honest network for serving that chain to
// others.
if (m_chainman.ActiveTip() == nullptr ||
- (m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && !pfrom.HasPermission(NetPermissionFlags::Download))) {
+ (m_chainman.ActiveTip()->nChainWork < m_chainman.MinimumChainWork() && !pfrom.HasPermission(NetPermissionFlags::Download))) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work; sending empty response\n", pfrom.GetId());
// Just respond with an empty headers message, to tell the peer to
// go away but not treat us as unresponsive.
@@ -4274,7 +4370,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// (eg disk space). Because we only try to reconstruct blocks when
// we're close to caught up (via the CanDirectFetch() requirement
// above, combined with the behavior of not requesting blocks until
- // we have a chain with at least nMinimumChainWork), and we ignore
+ // we have a chain with at least the minimum chain work), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true);
@@ -4748,6 +4844,8 @@ bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
{
+ AssertLockHeld(g_msgproc_mutex);
+
bool fMoreWork = false;
PeerRef peer = GetPeerRef(pfrom->GetId());
@@ -5099,7 +5197,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
// Remove addr records that the peer already knows about, and add new
// addrs to the m_addr_known filter on the same pass.
- auto addr_already_known = [&peer](const CAddress& addr) {
+ auto addr_already_known = [&peer](const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) {
bool ret = peer.m_addr_known->contains(addr.GetKey());
if (!ret) peer.m_addr_known->insert(addr.GetKey());
return ret;
@@ -5138,7 +5236,7 @@ void PeerManagerImpl::MaybeSendSendHeaders(CNode& node, Peer& peer)
LOCK(cs_main);
CNodeState &state = *State(node.GetId());
if (state.pindexBestKnownBlock != nullptr &&
- state.pindexBestKnownBlock->nChainWork > nMinimumChainWork) {
+ state.pindexBestKnownBlock->nChainWork > m_chainman.MinimumChainWork()) {
// Tell our peer we prefer to receive headers rather than inv's
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
@@ -5217,6 +5315,7 @@ bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const
{
// block-relay-only peers may never send txs to us
if (peer.IsBlockOnlyConn()) return true;
+ if (peer.IsFeelerConn()) return true;
// In -blocksonly mode, peers need the 'relay' permission to send txs to us
if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true;
return false;
@@ -5240,6 +5339,8 @@ bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer)
bool PeerManagerImpl::SendMessages(CNode* pto)
{
+ AssertLockHeld(g_msgproc_mutex);
+
PeerRef peer = GetPeerRef(pto->GetId());
if (!peer) return false;
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
diff --git a/src/net_processing.h b/src/net_processing.h
index 0a882b1e53..0d0842fa8e 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -84,7 +84,7 @@ public:
/** Process a single message from a peer. Public for fuzz testing */
virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
- const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0;
+ const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
/** This function is used for testing the stale tip eviction logic, see denialofservice_tests.cpp */
virtual void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) = 0;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index a5f7bda875..8169b40ea6 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -304,8 +304,7 @@ enum class IntrRecvError {
* read.
*
* @see This function can be interrupted by calling InterruptSocks5(bool).
- * Sockets can be made non-blocking with SetSocketNonBlocking(const
- * SOCKET&).
+ * Sockets can be made non-blocking with Sock::SetNonBlocking().
*/
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
{
@@ -503,7 +502,7 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
- if (!IsSelectableSocket(sock->Get())) {
+ if (!sock->IsSelectable()) {
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
return nullptr;
}
@@ -525,7 +524,7 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
}
// Set the non-blocking option on the socket.
- if (!SetSocketNonBlocking(sock->Get())) {
+ if (!sock->SetNonBlocking()) {
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
return nullptr;
}
@@ -717,21 +716,6 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
return false;
}
-bool SetSocketNonBlocking(const SOCKET& hSocket)
-{
-#ifdef WIN32
- u_long nOne = 1;
- if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
-#else
- int fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
-#endif
- return false;
- }
-
- return true;
-}
-
void InterruptSocks5(bool interrupt)
{
interruptSocks5Recv = interrupt;
diff --git a/src/netbase.h b/src/netbase.h
index fadc8b418e..f7816f5d1d 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -221,8 +221,6 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
*/
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
-/** Enable non-blocking mode for a socket */
-bool SetSocketNonBlocking(const SOCKET& hSocket);
void InterruptSocks5(bool interrupt);
/**
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 42b6b017fe..04d46f4361 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -524,6 +524,16 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize)
void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
{
LOCK(cs_LastBlockFile);
+
+ if (m_blockfile_info.size() < 1) {
+ // Return if we haven't loaded any blockfiles yet. This happens during
+ // chainstate init, when we call ChainstateManager::MaybeRebalanceCaches() (which
+ // then calls FlushStateToDisk()), resulting in a call to this function before we
+ // have populated `m_blockfile_info` via LoadBlockIndexDB().
+ return;
+ }
+ assert(static_cast<int>(m_blockfile_info.size()) > m_last_blockfile);
+
FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize);
if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
@@ -789,19 +799,24 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
return true;
}
-/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp)
{
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
FlatFilePos blockPos;
- if (dbp != nullptr) {
+ const auto position_known {dbp != nullptr};
+ if (position_known) {
blockPos = *dbp;
+ } else {
+ // when known, blockPos.nPos points at the offset of the block data in the blk file. that already accounts for
+ // the serialization header present in the file (the 4 magic message start bytes + the 4 length bytes = 8 bytes = BLOCK_SERIALIZATION_HEADER_SIZE).
+ // we add BLOCK_SERIALIZATION_HEADER_SIZE only for new blocks since they will have the serialization header added when written to disk.
+ nBlockSize += static_cast<unsigned int>(BLOCK_SERIALIZATION_HEADER_SIZE);
}
- if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) {
+ if (!FindBlockPos(blockPos, nBlockSize, nHeight, active_chain, block.GetBlockTime(), position_known)) {
error("%s: FindBlockPos failed", __func__);
return FlatFilePos();
}
- if (dbp == nullptr) {
+ if (!position_known) {
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
AbortNode("Failed to write block");
return FlatFilePos();
@@ -882,7 +897,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
// We can't hold cs_main during ActivateBestChain even though we're accessing
// the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
// the relevant pointers before the ABC call.
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
+ for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) {
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 9b76371aae..29501c1959 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -26,7 +26,7 @@ class CBlockFileInfo;
class CBlockUndo;
class CChain;
class CChainParams;
-class CChainState;
+class Chainstate;
class ChainstateManager;
struct CCheckpointData;
struct FlatFilePos;
@@ -44,6 +44,9 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
/** The maximum size of a blk?????.dat file (since 0.8) */
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
+/** Size of header written by WriteBlockToDisk before a serialized CBlock */
+static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int);
+
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
/** Pruning-related variables and constants */
@@ -75,12 +78,12 @@ struct PruneLockInfo {
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
* to determine where the most-work tip is.
*
- * This data is used mostly in `CChainState` - information about, e.g.,
+ * This data is used mostly in `Chainstate` - information about, e.g.,
* candidate tips is not maintained here.
*/
class BlockManager
{
- friend CChainState;
+ friend Chainstate;
friend ChainstateManager;
private:
@@ -171,6 +174,7 @@ public:
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ /** Store block on disk. If dbp is not nullptr, then it provides the known position of the block within a block file on disk. */
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);
/** Calculate the amount of disk space the block & undo files currently use */
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index f168332ee6..a39ad7aeb6 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -4,9 +4,9 @@
#include <node/caches.h>
+#include <index/txindex.h>
#include <txdb.h>
#include <util/system.h>
-#include <validation.h>
namespace node {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index c4dd9ba6c5..60acb614b4 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -4,9 +4,11 @@
#include <node/chainstate.h>
+#include <arith_uint256.h>
#include <chain.h>
#include <coins.h>
#include <consensus/params.h>
+#include <logging.h>
#include <node/blockstorage.h>
#include <node/caches.h>
#include <sync.h>
@@ -21,6 +23,7 @@
#include <algorithm>
#include <atomic>
#include <cassert>
+#include <limits>
#include <memory>
#include <vector>
@@ -28,17 +31,17 @@ namespace node {
ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes,
const ChainstateLoadOptions& options)
{
- auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull();
};
- if (!hashAssumeValid.IsNull()) {
- LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());
+ if (!chainman.AssumedValidBlock().IsNull()) {
+ LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex());
} else {
LogPrintf("Validating signatures for all blocks.\n");
}
- LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
- if (nMinimumChainWork < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) {
+ LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex());
+ if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex());
}
if (nPruneTarget == std::numeric_limits<uint64_t>::max()) {
@@ -48,10 +51,15 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
}
LOCK(cs_main);
- chainman.InitializeChainstate(options.mempool);
chainman.m_total_coinstip_cache = cache_sizes.coins;
chainman.m_total_coinsdb_cache = cache_sizes.coins_db;
+ // Load the fully validated chainstate.
+ chainman.InitializeChainstate(options.mempool);
+
+ // Load a chain created from a UTXO snapshot, if any exist.
+ chainman.DetectSnapshotChainstate(options.mempool);
+
auto& pblocktree{chainman.m_blockman.m_block_tree_db};
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
@@ -98,12 +106,20 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
}
+ // Conservative value which is arbitrarily chosen, as it will ultimately be changed
+ // by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure
+ // that the sum of the two caches (40%) does not exceed the allowable amount
+ // during this temporary initialization state.
+ double init_cache_fraction = 0.2;
+
// At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()!
- for (CChainState* chainstate : chainman.GetAll()) {
+ for (Chainstate* chainstate : chainman.GetAll()) {
+ LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
+
chainstate->InitCoinsDB(
- /*cache_size_bytes=*/cache_sizes.coins_db,
+ /*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
/*in_memory=*/options.coins_db_in_memory,
/*should_wipe=*/options.reindex || options.reindex_chainstate);
@@ -125,7 +141,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
}
// The on-disk coinsdb is now in a good state, create the cache
- chainstate->InitCoinsCache(cache_sizes.coins);
+ chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
assert(chainstate->CanFlushToDisk());
if (!is_coinsview_empty(chainstate)) {
@@ -140,24 +156,29 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
if (!options.reindex) {
auto chainstates{chainman.GetAll()};
if (std::any_of(chainstates.begin(), chainstates.end(),
- [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
+ [](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
chainman.GetConsensus().SegwitHeight)};
};
}
+ // Now that chainstates are loaded and we're able to flush to
+ // disk, rebalance the coins caches to desired levels based
+ // on the condition of each chainstate.
+ chainman.MaybeRebalanceCaches();
+
return {ChainstateLoadStatus::SUCCESS, {}};
}
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options)
{
- auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull();
};
LOCK(cs_main);
- for (CChainState* chainstate : chainman.GetAll()) {
+ for (Chainstate* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
const CBlockIndex* tip = chainstate->m_chain.Tip();
if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
new file mode 100644
index 0000000000..b0d929626b
--- /dev/null
+++ b/src/node/chainstatemanager_args.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/chainstatemanager_args.h>
+
+#include <arith_uint256.h>
+#include <tinyformat.h>
+#include <uint256.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <validation.h>
+
+#include <chrono>
+#include <optional>
+#include <string>
+
+namespace node {
+std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts)
+{
+ if (auto value{args.GetBoolArg("-checkblockindex")}) opts.check_block_index = *value;
+
+ if (auto value{args.GetBoolArg("-checkpoints")}) opts.checkpoints_enabled = *value;
+
+ if (auto value{args.GetArg("-minimumchainwork")}) {
+ if (!IsHexNumber(*value)) {
+ return strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), *value);
+ }
+ opts.minimum_chain_work = UintToArith256(uint256S(*value));
+ }
+
+ if (auto value{args.GetArg("-assumevalid")}) opts.assumed_valid_block = uint256S(*value);
+
+ if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value};
+
+ return std::nullopt;
+}
+} // namespace node
diff --git a/src/node/chainstatemanager_args.h b/src/node/chainstatemanager_args.h
new file mode 100644
index 0000000000..6c46b998f2
--- /dev/null
+++ b/src/node/chainstatemanager_args.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NODE_CHAINSTATEMANAGER_ARGS_H
+#define BITCOIN_NODE_CHAINSTATEMANAGER_ARGS_H
+
+#include <validation.h>
+
+#include <optional>
+
+class ArgsManager;
+struct bilingual_str;
+
+namespace node {
+std::optional<bilingual_str> ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts);
+} // namespace node
+
+#endif // BITCOIN_NODE_CHAINSTATEMANAGER_ARGS_H
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index aa7ddec770..979c625463 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -4,10 +4,12 @@
#include <addrdb.h>
#include <banman.h>
+#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <deploymentstatus.h>
#include <external_signer.h>
+#include <index/blockfilterindex.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
@@ -536,6 +538,20 @@ public:
}
return std::nullopt;
}
+ bool hasBlockFilterIndex(BlockFilterType filter_type) override
+ {
+ return GetBlockFilterIndex(filter_type) != nullptr;
+ }
+ std::optional<bool> blockFilterMatchesAny(BlockFilterType filter_type, const uint256& block_hash, const GCSFilter::ElementSet& filter_set) override
+ {
+ const BlockFilterIndex* block_filter_index{GetBlockFilterIndex(filter_type)};
+ if (!block_filter_index) return std::nullopt;
+
+ BlockFilter filter;
+ const CBlockIndex* index{WITH_LOCK(::cs_main, return chainman().m_blockman.LookupBlockIndex(block_hash))};
+ if (index == nullptr || !block_filter_index->LookupFilter(index, filter)) return std::nullopt;
+ return filter.GetFilter().MatchAny(filter_set);
+ }
bool findBlock(const uint256& hash, const FoundBlock& block) override
{
WAIT_LOCK(cs_main, lock);
@@ -660,8 +676,7 @@ public:
std::string unused_error_string;
LOCK(m_node.mempool->cs);
return m_node.mempool->CalculateMemPoolAncestors(
- entry, ancestors, limits.ancestor_count, limits.ancestor_size_vbytes,
- limits.descendant_count, limits.descendant_size_vbytes, unused_error_string);
+ entry, ancestors, limits, unused_error_string);
}
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
{
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index f04742deeb..e11ec5b0f1 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -62,7 +62,7 @@ BlockAssembler::Options::Options()
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool* mempool, const Options& options)
+BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options)
: chainparams{chainstate.m_chainman.GetParams()},
m_mempool(mempool),
m_chainstate(chainstate)
@@ -87,7 +87,7 @@ static BlockAssembler::Options DefaultOptions()
return options;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool* mempool)
+BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool)
: BlockAssembler(chainstate, mempool, DefaultOptions()) {}
void BlockAssembler::resetBlock()
@@ -105,7 +105,7 @@ void BlockAssembler::resetBlock()
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
- int64_t nTimeStart = GetTimeMicros();
+ const auto time_start{SteadyClock::now()};
resetBlock();
@@ -143,7 +143,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
addPackageTxs(*m_mempool, nPackagesSelected, nDescendantsUpdated);
}
- int64_t nTime1 = GetTimeMicros();
+ const auto time_1{SteadyClock::now()};
m_last_block_num_txs = nBlockTx;
m_last_block_weight = nBlockWeight;
@@ -173,9 +173,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, GetAdjustedTime, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
- int64_t nTime2 = GetTimeMicros();
+ const auto time_2{SteadyClock::now()};
- LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", 0.001 * (nTime1 - nTimeStart), nPackagesSelected, nDescendantsUpdated, 0.001 * (nTime2 - nTime1), 0.001 * (nTime2 - nTimeStart));
+ LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n",
+ Ticks<MillisecondsDouble>(time_1 - time_start), nPackagesSelected, nDescendantsUpdated,
+ Ticks<MillisecondsDouble>(time_2 - time_1),
+ Ticks<MillisecondsDouble>(time_2 - time_start));
return std::move(pblocktemplate);
}
@@ -257,13 +260,9 @@ static int UpdatePackagesForAdded(const CTxMemPool& mempool,
modtxiter mit = mapModifiedTx.find(desc);
if (mit == mapModifiedTx.end()) {
CTxMemPoolModifiedEntry modEntry(desc);
- modEntry.nSizeWithAncestors -= it->GetTxSize();
- modEntry.nModFeesWithAncestors -= it->GetModifiedFee();
- modEntry.nSigOpCostWithAncestors -= it->GetSigOpCost();
- mapModifiedTx.insert(modEntry);
- } else {
- mapModifiedTx.modify(mit, update_for_parent_inclusion(it));
+ mit = mapModifiedTx.insert(modEntry).first;
}
+ mapModifiedTx.modify(mit, update_for_parent_inclusion(it));
}
}
return nDescendantsUpdated;
@@ -396,9 +395,8 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
}
CTxMemPool::setEntries ancestors;
- uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
- mempool.CalculateMemPoolAncestors(*iter, ancestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
+ mempool.CalculateMemPoolAncestors(*iter, ancestors, CTxMemPool::Limits::NoLimits(), dummy, false);
onlyUnconfirmed(ancestors);
ancestors.insert(iter);
diff --git a/src/node/miner.h b/src/node/miner.h
index 26454df3df..7269ce1186 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -148,7 +148,7 @@ private:
const CChainParams& chainparams;
const CTxMemPool* const m_mempool;
- CChainState& m_chainstate;
+ Chainstate& m_chainstate;
public:
struct Options {
@@ -157,8 +157,8 @@ public:
CFeeRate blockMinFeeRate;
};
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool* mempool);
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool* mempool, const Options& options);
+ explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool);
+ explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index 57162cd679..ca3fc0955d 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -59,7 +59,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
}
// Check if it is final
- if (!utxo.IsNull() && !PSBTInputSigned(input)) {
+ if (!PSBTInputSignedAndVerified(psbtx, i, &txdata)) {
input_analysis.is_final = false;
// Figure out what is missing
diff --git a/src/node/txreconciliation.cpp b/src/node/txreconciliation.cpp
new file mode 100644
index 0000000000..974358fcda
--- /dev/null
+++ b/src/node/txreconciliation.cpp
@@ -0,0 +1,184 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/txreconciliation.h>
+
+#include <util/check.h>
+#include <util/system.h>
+
+#include <unordered_map>
+#include <variant>
+
+
+namespace {
+
+/** Static salt component used to compute short txids for sketch construction, see BIP-330. */
+const std::string RECON_STATIC_SALT = "Tx Relay Salting";
+const HashWriter RECON_SALT_HASHER = TaggedHash(RECON_STATIC_SALT);
+
+/**
+ * Salt (specified by BIP-330) constructed from contributions from both peers. It is used
+ * to compute transaction short IDs, which are then used to construct a sketch representing a set
+ * of transactions we want to announce to the peer.
+ */
+uint256 ComputeSalt(uint64_t salt1, uint64_t salt2)
+{
+ // According to BIP-330, salts should be combined in ascending order.
+ return (HashWriter(RECON_SALT_HASHER) << std::min(salt1, salt2) << std::max(salt1, salt2)).GetSHA256();
+}
+
+/**
+ * Keeps track of txreconciliation-related per-peer state.
+ */
+class TxReconciliationState
+{
+public:
+ /**
+ * TODO: This field is public to ignore -Wunused-private-field. Make private once used in
+ * the following commits.
+ *
+ * Reconciliation protocol assumes using one role consistently: either a reconciliation
+ * initiator (requesting sketches), or responder (sending sketches). This defines our role.
+ *
+ */
+ bool m_we_initiate;
+
+ /**
+ * TODO: These fields are public to ignore -Wunused-private-field. Make private once used in
+ * the following commits.
+ *
+ * These values are used to salt short IDs, which is necessary for transaction reconciliations.
+ */
+ uint64_t m_k0, m_k1;
+
+ TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {}
+};
+
+} // namespace
+
+/** Actual implementation for TxReconciliationTracker's data structure. */
+class TxReconciliationTracker::Impl
+{
+private:
+ mutable Mutex m_txreconciliation_mutex;
+
+ // Local protocol version
+ uint32_t m_recon_version;
+
+ /**
+ * Keeps track of txreconciliation states of eligible peers.
+ * For pre-registered peers, the locally generated salt is stored.
+ * For registered peers, the locally generated salt is forgotten, and the state (including
+ * "full" salt) is stored instead.
+ */
+ std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);
+
+public:
+ explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {}
+
+ uint64_t PreRegisterPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
+ {
+ AssertLockNotHeld(m_txreconciliation_mutex);
+ LOCK(m_txreconciliation_mutex);
+ // We do not support txreconciliation salt/version updates.
+ assert(m_states.find(peer_id) == m_states.end());
+
+ LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id);
+ const uint64_t local_salt{GetRand(UINT64_MAX)};
+
+ // We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's
+ // safe to assume we don't have this record yet.
+ Assert(m_states.emplace(peer_id, local_salt).second);
+ return local_salt;
+ }
+
+ ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound, bool is_peer_recon_initiator,
+ bool is_peer_recon_responder, uint32_t peer_recon_version,
+ uint64_t remote_salt) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
+ {
+ AssertLockNotHeld(m_txreconciliation_mutex);
+ LOCK(m_txreconciliation_mutex);
+ auto recon_state = m_states.find(peer_id);
+
+ // A peer should be in the pre-registered state to proceed here.
+ if (recon_state == m_states.end()) return NOT_FOUND;
+ uint64_t* local_salt = std::get_if<uint64_t>(&recon_state->second);
+ // A peer is already registered. This should be checked by the caller.
+ Assume(local_salt);
+
+ // If the peer supports the version which is lower than ours, we downgrade to the version
+ // it supports. For now, this only guarantees that nodes with future reconciliation
+ // versions have the choice of reconciling with this current version. However, they also
+ // have the choice to refuse supporting reconciliations if the common version is not
+ // satisfactory (e.g. too low).
+ const uint32_t recon_version{std::min(peer_recon_version, m_recon_version)};
+ // v1 is the lowest version, so suggesting something below must be a protocol violation.
+ if (recon_version < 1) return PROTOCOL_VIOLATION;
+
+ // Must match SENDTXRCNCL logic.
+ const bool they_initiate = is_peer_recon_initiator && is_peer_inbound;
+ const bool we_initiate = !is_peer_inbound && is_peer_recon_responder;
+
+ // If we ever announce support for both requesting and responding, this will need
+ // tie-breaking. For now, this is mutually exclusive because both are based on the
+ // inbound flag.
+ assert(!(they_initiate && we_initiate));
+
+ // The peer set both flags to false, we treat it as a protocol violation.
+ if (!(they_initiate || we_initiate)) return PROTOCOL_VIOLATION;
+
+ LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Register peer=%d with the following params: " /* Continued */
+ "we_initiate=%i, they_initiate=%i.\n",
+ peer_id, we_initiate, they_initiate);
+
+ const uint256 full_salt{ComputeSalt(*local_salt, remote_salt)};
+ recon_state->second = TxReconciliationState(we_initiate, full_salt.GetUint64(0), full_salt.GetUint64(1));
+ return SUCCESS;
+ }
+
+ void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
+ {
+ AssertLockNotHeld(m_txreconciliation_mutex);
+ LOCK(m_txreconciliation_mutex);
+ if (m_states.erase(peer_id)) {
+ LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Forget txreconciliation state of peer=%d\n", peer_id);
+ }
+ }
+
+ bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
+ {
+ AssertLockNotHeld(m_txreconciliation_mutex);
+ LOCK(m_txreconciliation_mutex);
+ auto recon_state = m_states.find(peer_id);
+ return (recon_state != m_states.end() &&
+ std::holds_alternative<TxReconciliationState>(recon_state->second));
+ }
+};
+
+TxReconciliationTracker::TxReconciliationTracker(uint32_t recon_version) : m_impl{std::make_unique<TxReconciliationTracker::Impl>(recon_version)} {}
+
+TxReconciliationTracker::~TxReconciliationTracker() = default;
+
+uint64_t TxReconciliationTracker::PreRegisterPeer(NodeId peer_id)
+{
+ return m_impl->PreRegisterPeer(peer_id);
+}
+
+ReconciliationRegisterResult TxReconciliationTracker::RegisterPeer(NodeId peer_id, bool is_peer_inbound,
+ bool is_peer_recon_initiator, bool is_peer_recon_responder,
+ uint32_t peer_recon_version, uint64_t remote_salt)
+{
+ return m_impl->RegisterPeer(peer_id, is_peer_inbound, is_peer_recon_initiator, is_peer_recon_responder,
+ peer_recon_version, remote_salt);
+}
+
+void TxReconciliationTracker::ForgetPeer(NodeId peer_id)
+{
+ m_impl->ForgetPeer(peer_id);
+}
+
+bool TxReconciliationTracker::IsPeerRegistered(NodeId peer_id) const
+{
+ return m_impl->IsPeerRegistered(peer_id);
+}
diff --git a/src/node/txreconciliation.h b/src/node/txreconciliation.h
new file mode 100644
index 0000000000..a4f0870914
--- /dev/null
+++ b/src/node/txreconciliation.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NODE_TXRECONCILIATION_H
+#define BITCOIN_NODE_TXRECONCILIATION_H
+
+#include <net.h>
+#include <sync.h>
+
+#include <memory>
+#include <tuple>
+
+/** Whether transaction reconciliation protocol should be enabled by default. */
+static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
+/** Supported transaction reconciliation protocol version */
+static constexpr uint32_t TXRECONCILIATION_VERSION{1};
+
+enum ReconciliationRegisterResult {
+ NOT_FOUND = 0,
+ SUCCESS = 1,
+ PROTOCOL_VIOLATION = 2,
+};
+
+/**
+ * Transaction reconciliation is a way for nodes to efficiently announce transactions.
+ * This object keeps track of all txreconciliation-related communications with the peers.
+ * The high-level protocol is:
+ * 0. Txreconciliation protocol handshake.
+ * 1. Once we receive a new transaction, add it to the set instead of announcing immediately.
+ * 2. At regular intervals, a txreconciliation initiator requests a sketch from a peer, where a
+ * sketch is a compressed representation of short form IDs of the transactions in their set.
+ * 3. Once the initiator received a sketch from the peer, the initiator computes a local sketch,
+ * and combines the two sketches to attempt finding the difference in *sets*.
+ * 4a. If the difference was not larger than estimated, see SUCCESS below.
+ * 4b. If the difference was larger than estimated, initial txreconciliation fails. The initiator
+ * requests a larger sketch via an extension round (allowed only once).
+ * - If extension succeeds (a larger sketch is sufficient), see SUCCESS below.
+ * - If extension fails (a larger sketch is insufficient), see FAILURE below.
+ *
+ * SUCCESS. The initiator knows full symmetrical difference and can request what the initiator is
+ * missing and announce to the peer what the peer is missing.
+ *
+ * FAILURE. The initiator notifies the peer about the failure and announces all transactions from
+ * the corresponding set. Once the peer received the failure notification, the peer
+ * announces all transactions from their set.
+
+ * This is a modification of the Erlay protocol (https://arxiv.org/abs/1905.10518) with two
+ * changes (sketch extensions instead of bisections, and an extra INV exchange round), both
+ * are motivated in BIP-330.
+ */
+class TxReconciliationTracker
+{
+private:
+ class Impl;
+ const std::unique_ptr<Impl> m_impl;
+
+public:
+ explicit TxReconciliationTracker(uint32_t recon_version);
+ ~TxReconciliationTracker();
+
+ /**
+ * Step 0. Generates initial part of the state (salt) required to reconcile txs with the peer.
+ * The salt is used for short ID computation required for txreconciliation.
+ * The function returns the salt.
+ * A peer can't participate in future txreconciliations without this call.
+ * This function must be called only once per peer.
+ */
+ uint64_t PreRegisterPeer(NodeId peer_id);
+
+ /**
+ * Step 0. Once the peer agreed to reconcile txs with us, generate the state required to track
+ * ongoing reconciliations. Must be called only after pre-registering the peer and only once.
+ */
+ ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound, bool is_peer_recon_initiator,
+ bool is_peer_recon_responder, uint32_t peer_recon_version, uint64_t remote_salt);
+
+ /**
+ * Attempts to forget txreconciliation-related state of the peer (if we previously stored any).
+ * After this, we won't be able to reconcile transactions with the peer.
+ */
+ void ForgetPeer(NodeId peer_id);
+
+ /**
+ * Check if a peer is registered to reconcile transactions with us.
+ */
+ bool IsPeerRegistered(NodeId peer_id) const;
+};
+
+#endif // BITCOIN_NODE_TXRECONCILIATION_H
diff --git a/src/node/utxo_snapshot.cpp b/src/node/utxo_snapshot.cpp
new file mode 100644
index 0000000000..bab1b75211
--- /dev/null
+++ b/src/node/utxo_snapshot.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/utxo_snapshot.h>
+
+#include <fs.h>
+#include <logging.h>
+#include <streams.h>
+#include <uint256.h>
+#include <util/system.h>
+#include <validation.h>
+
+#include <cstdio>
+#include <optional>
+
+namespace node {
+
+bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
+{
+ AssertLockHeld(::cs_main);
+ assert(snapshot_chainstate.m_from_snapshot_blockhash);
+
+ const std::optional<fs::path> chaindir = snapshot_chainstate.CoinsDB().StoragePath();
+ assert(chaindir); // Sanity check that chainstate isn't in-memory.
+ const fs::path write_to = *chaindir / node::SNAPSHOT_BLOCKHASH_FILENAME;
+
+ FILE* file{fsbridge::fopen(write_to, "wb")};
+ AutoFile afile{file};
+ if (afile.IsNull()) {
+ LogPrintf("[snapshot] failed to open base blockhash file for writing: %s\n",
+ fs::PathToString(write_to));
+ return false;
+ }
+ afile << *snapshot_chainstate.m_from_snapshot_blockhash;
+
+ if (afile.fclose() != 0) {
+ LogPrintf("[snapshot] failed to close base blockhash file %s after writing\n",
+ fs::PathToString(write_to));
+ return false;
+ }
+ return true;
+}
+
+std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
+{
+ if (!fs::exists(chaindir)) {
+ LogPrintf("[snapshot] cannot read base blockhash: no chainstate dir " /* Continued */
+ "exists at path %s\n", fs::PathToString(chaindir));
+ return std::nullopt;
+ }
+ const fs::path read_from = chaindir / node::SNAPSHOT_BLOCKHASH_FILENAME;
+ const std::string read_from_str = fs::PathToString(read_from);
+
+ if (!fs::exists(read_from)) {
+ LogPrintf("[snapshot] snapshot chainstate dir is malformed! no base blockhash file " /* Continued */
+ "exists at path %s. Try deleting %s and calling loadtxoutset again?\n",
+ fs::PathToString(chaindir), read_from_str);
+ return std::nullopt;
+ }
+
+ uint256 base_blockhash;
+ FILE* file{fsbridge::fopen(read_from, "rb")};
+ AutoFile afile{file};
+ if (afile.IsNull()) {
+ LogPrintf("[snapshot] failed to open base blockhash file for reading: %s\n",
+ read_from_str);
+ return std::nullopt;
+ }
+ afile >> base_blockhash;
+
+ if (std::fgetc(afile.Get()) != EOF) {
+ LogPrintf("[snapshot] warning: unexpected trailing data in %s\n", read_from_str);
+ } else if (std::ferror(afile.Get())) {
+ LogPrintf("[snapshot] warning: i/o error reading %s\n", read_from_str);
+ }
+ return base_blockhash;
+}
+
+std::optional<fs::path> FindSnapshotChainstateDir()
+{
+ fs::path possible_dir =
+ gArgs.GetDataDirNet() / fs::u8path(strprintf("chainstate%s", SNAPSHOT_CHAINSTATE_SUFFIX));
+
+ if (fs::exists(possible_dir)) {
+ return possible_dir;
+ }
+ return std::nullopt;
+}
+
+} // namespace node
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
index 401d4baaeb..c94521792f 100644
--- a/src/node/utxo_snapshot.h
+++ b/src/node/utxo_snapshot.h
@@ -6,12 +6,18 @@
#ifndef BITCOIN_NODE_UTXO_SNAPSHOT_H
#define BITCOIN_NODE_UTXO_SNAPSHOT_H
+#include <fs.h>
#include <uint256.h>
#include <serialize.h>
+#include <validation.h>
+
+#include <optional>
+
+extern RecursiveMutex cs_main;
namespace node {
//! Metadata describing a serialized version of a UTXO set from which an
-//! assumeutxo CChainState can be constructed.
+//! assumeutxo Chainstate can be constructed.
class SnapshotMetadata
{
public:
@@ -33,6 +39,33 @@ public:
SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); }
};
+
+//! The file in the snapshot chainstate dir which stores the base blockhash. This is
+//! needed to reconstruct snapshot chainstates on init.
+//!
+//! Because we only allow loading a single snapshot at a time, there will only be one
+//! chainstate directory with this filename present within it.
+const fs::path SNAPSHOT_BLOCKHASH_FILENAME{"base_blockhash"};
+
+//! Write out the blockhash of the snapshot base block that was used to construct
+//! this chainstate. This value is read in during subsequent initializations and
+//! used to reconstruct snapshot-based chainstates.
+bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+//! Read the blockhash of the snapshot base block that was used to construct the
+//! chainstate.
+std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+//! Suffix appended to the chainstate (leveldb) dir when created based upon
+//! a snapshot.
+constexpr std::string_view SNAPSHOT_CHAINSTATE_SUFFIX = "_snapshot";
+
+
+//! Return a path to the snapshot-based chainstate dir, if one exists.
+std::optional<fs::path> FindSnapshotChainstateDir();
+
} // namespace node
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 2b940be07e..ab5599a1b4 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -997,8 +997,9 @@ bool CBlockPolicyEstimator::Read(AutoFile& filein)
return true;
}
-void CBlockPolicyEstimator::FlushUnconfirmed() {
- int64_t startclear = GetTimeMicros();
+void CBlockPolicyEstimator::FlushUnconfirmed()
+{
+ const auto startclear{SteadyClock::now()};
LOCK(m_cs_fee_estimator);
size_t num_entries = mapMemPoolTxs.size();
// Remove every entry in mapMemPoolTxs
@@ -1006,24 +1007,41 @@ void CBlockPolicyEstimator::FlushUnconfirmed() {
auto mi = mapMemPoolTxs.begin();
_removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs
}
- int64_t endclear = GetTimeMicros();
- LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001);
+ const auto endclear{SteadyClock::now()};
+ LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear));
}
-FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
+static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
+ double max_filter_fee_rate,
+ double fee_filter_spacing)
{
- CAmount minFeeLimit = std::max(CAmount(1), minIncrementalFee.GetFeePerK() / 2);
- feeset.insert(0);
- for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FILTER_FEERATE; bucketBoundary *= FEE_FILTER_SPACING) {
- feeset.insert(bucketBoundary);
+ std::set<double> fee_set;
+
+ const CAmount min_fee_limit{std::max(CAmount(1), min_incremental_fee.GetFeePerK() / 2)};
+ fee_set.insert(0);
+ for (double bucket_boundary = min_fee_limit;
+ bucket_boundary <= max_filter_fee_rate;
+ bucket_boundary *= fee_filter_spacing) {
+
+ fee_set.insert(bucket_boundary);
}
+
+ return fee_set;
+}
+
+FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
+ : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)}
+{
}
CAmount FeeFilterRounder::round(CAmount currentMinFee)
{
- std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
- if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
- it--;
+ AssertLockNotHeld(m_insecure_rand_mutex);
+ std::set<double>::iterator it = m_fee_set.lower_bound(currentMinFee);
+ if (it == m_fee_set.end() ||
+ (it != m_fee_set.begin() &&
+ WITH_LOCK(m_insecure_rand_mutex, return insecure_rand.rand32()) % 3 != 0)) {
+ --it;
}
return static_cast<CAmount>(*it);
}
diff --git a/src/policy/fees.h b/src/policy/fees.h
index e4628bf853..204c4f2118 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -299,14 +299,15 @@ private:
public:
/** Create new FeeFilterRounder */
- explicit FeeFilterRounder(const CFeeRate& minIncrementalFee);
+ explicit FeeFilterRounder(const CFeeRate& min_incremental_fee);
- /** Quantize a minimum fee for privacy purpose before broadcast. Not thread-safe due to use of FastRandomContext */
- CAmount round(CAmount currentMinFee);
+ /** Quantize a minimum fee for privacy purpose before broadcast. */
+ CAmount round(CAmount currentMinFee) EXCLUSIVE_LOCKS_REQUIRED(!m_insecure_rand_mutex);
private:
- std::set<double> feeset;
- FastRandomContext insecure_rand;
+ const std::set<double> m_fee_set;
+ Mutex m_insecure_rand_mutex;
+ FastRandomContext insecure_rand GUARDED_BY(m_insecure_rand_mutex);
};
#endif // BITCOIN_POLICY_FEES_H
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 6098caced9..55f47f485b 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -36,10 +36,9 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
// If all the inputs have nSequence >= maxint-1, it still might be
// signaled for RBF if any unconfirmed parents have signaled.
- uint64_t noLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
- pool.CalculateMemPoolAncestors(entry, ancestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
+ pool.CalculateMemPoolAncestors(entry, ancestors, CTxMemPool::Limits::NoLimits(), dummy, false);
for (CTxMemPool::txiter it : ancestors) {
if (SignalsOptInRBF(it->GetTx())) {
diff --git a/src/prevector.h b/src/prevector.h
index a52510930a..7df5a067a2 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -6,7 +6,7 @@
#define BITCOIN_PREVECTOR_H
#include <assert.h>
-#include <stdlib.h>
+#include <cstdlib>
#include <stdint.h>
#include <string.h>
diff --git a/src/protocol.cpp b/src/protocol.cpp
index bdd1cc2aff..23c68b335b 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -44,6 +44,7 @@ const char *CFHEADERS="cfheaders";
const char *GETCFCHECKPT="getcfcheckpt";
const char *CFCHECKPT="cfcheckpt";
const char *WTXIDRELAY="wtxidrelay";
+const char *SENDTXRCNCL="sendtxrcncl";
} // namespace NetMsgType
/** All known message types. Keep this in the same order as the list of
@@ -84,6 +85,7 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
NetMsgType::WTXIDRELAY,
+ NetMsgType::SENDTXRCNCL,
};
const static std::vector<std::string> allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes));
diff --git a/src/protocol.h b/src/protocol.h
index b85dc0d820..17a363b1d3 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -258,6 +258,14 @@ extern const char* CFCHECKPT;
* @since protocol version 70016 as described by BIP 339.
*/
extern const char* WTXIDRELAY;
+/**
+ * Contains 2 1-byte bools, a 4-byte version number and an 8-byte salt.
+ * The 2 booleans indicate that a node is willing to participate in transaction
+ * reconciliation, respectively as an initiator or as a receiver.
+ * The salt is used to compute short txids needed for efficient
+ * txreconciliation, as described by BIP 330.
+ */
+extern const char* SENDTXRCNCL;
}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 36fec74bc9..461987c503 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -4,6 +4,7 @@
#include <psbt.h>
+#include <policy/policy.h>
#include <util/check.h>
#include <util/strencodings.h>
@@ -218,8 +219,14 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
- if (m_tap_tree.has_value() && m_tap_internal_key.IsFullyValid()) {
- TaprootSpendData spenddata = m_tap_tree->GetSpendData();
+ if (!m_tap_tree.empty() && m_tap_internal_key.IsFullyValid()) {
+ TaprootBuilder builder;
+ for (const auto& [depth, leaf_ver, script] : m_tap_tree) {
+ builder.Add((int)depth, script, (int)leaf_ver, /*track=*/true);
+ }
+ assert(builder.IsComplete());
+ builder.Finalize(m_tap_internal_key);
+ TaprootSpendData spenddata = builder.GetSpendData();
sigdata.tr_spenddata.internal_key = m_tap_internal_key;
sigdata.tr_spenddata.Merge(spenddata);
@@ -243,8 +250,8 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
if (!sigdata.tr_spenddata.internal_key.IsNull()) {
m_tap_internal_key = sigdata.tr_spenddata.internal_key;
}
- if (sigdata.tr_builder.has_value()) {
- m_tap_tree = sigdata.tr_builder;
+ if (sigdata.tr_builder.has_value() && sigdata.tr_builder->HasScripts()) {
+ m_tap_tree = sigdata.tr_builder->GetTreeTuples();
}
for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) {
m_tap_bip32_paths.emplace(pubkey, leaf_origin);
@@ -265,13 +272,43 @@ void PSBTOutput::Merge(const PSBTOutput& output)
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
if (m_tap_internal_key.IsNull() && !output.m_tap_internal_key.IsNull()) m_tap_internal_key = output.m_tap_internal_key;
- if (m_tap_tree.has_value() && !output.m_tap_tree.has_value()) m_tap_tree = output.m_tap_tree;
+ if (m_tap_tree.empty() && !output.m_tap_tree.empty()) m_tap_tree = output.m_tap_tree;
}
+
bool PSBTInputSigned(const PSBTInput& input)
{
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
+bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata)
+{
+ CTxOut utxo;
+ assert(psbt.inputs.size() >= input_index);
+ const PSBTInput& input = psbt.inputs[input_index];
+
+ if (input.non_witness_utxo) {
+ // If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
+ COutPoint prevout = psbt.tx->vin[input_index].prevout;
+ if (prevout.n >= input.non_witness_utxo->vout.size()) {
+ return false;
+ }
+ if (input.non_witness_utxo->GetHash() != prevout.hash) {
+ return false;
+ }
+ utxo = input.non_witness_utxo->vout[prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ } else {
+ return false;
+ }
+
+ if (txdata) {
+ return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, *txdata, MissingDataBehavior::FAIL});
+ } else {
+ return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, MissingDataBehavior::FAIL});
+ }
+}
+
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
size_t count = 0;
for (const auto& input : psbt.inputs) {
@@ -325,7 +362,7 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
- if (PSBTInputSigned(input)) {
+ if (PSBTInputSignedAndVerified(psbt, index, txdata)) {
return true;
}
diff --git a/src/psbt.h b/src/psbt.h
index d5c67802c7..37bf142366 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -713,7 +713,7 @@ struct PSBTOutput
CScript witness_script;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
XOnlyPubKey m_tap_internal_key;
- std::optional<TaprootBuilder> m_tap_tree;
+ std::vector<std::tuple<uint8_t, uint8_t, CScript>> m_tap_tree;
std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
std::set<PSBTProprietary> m_proprietary;
@@ -754,15 +754,11 @@ struct PSBTOutput
}
// Write taproot tree
- if (m_tap_tree.has_value()) {
+ if (!m_tap_tree.empty()) {
SerializeToVector(s, PSBT_OUT_TAP_TREE);
std::vector<unsigned char> value;
CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0);
- const auto& tuples = m_tap_tree->GetTreeTuples();
- for (const auto& tuple : tuples) {
- uint8_t depth = std::get<0>(tuple);
- uint8_t leaf_ver = std::get<1>(tuple);
- CScript script = std::get<2>(tuple);
+ for (const auto& [depth, leaf_ver, script] : m_tap_tree) {
s_value << depth;
s_value << leaf_ver;
s_value << script;
@@ -858,10 +854,13 @@ struct PSBTOutput
} else if (key.size() != 1) {
throw std::ios_base::failure("Output Taproot tree key is more than one byte type");
}
- m_tap_tree.emplace();
std::vector<unsigned char> tree_v;
s >> tree_v;
SpanReader s_tree(s.GetType(), s.GetVersion(), tree_v);
+ if (s_tree.empty()) {
+ throw std::ios_base::failure("Output Taproot tree must not be empty");
+ }
+ TaprootBuilder builder;
while (!s_tree.empty()) {
uint8_t depth;
uint8_t leaf_ver;
@@ -875,9 +874,10 @@ struct PSBTOutput
if ((leaf_ver & ~TAPROOT_LEAF_MASK) != 0) {
throw std::ios_base::failure("Output Taproot tree has a leaf with an invalid leaf version");
}
- m_tap_tree->Add((int)depth, script, (int)leaf_ver, true /* track */);
+ m_tap_tree.push_back(std::make_tuple(depth, leaf_ver, script));
+ builder.Add((int)depth, script, (int)leaf_ver, true /* track */);
}
- if (!m_tap_tree->IsComplete()) {
+ if (!builder.IsComplete()) {
throw std::ios_base::failure("Output Taproot tree is malformed");
}
break;
@@ -931,11 +931,6 @@ struct PSBTOutput
}
}
- // Finalize m_tap_tree so that all of the computed things are computed
- if (m_tap_tree.has_value() && m_tap_tree->IsComplete() && m_tap_internal_key.IsFullyValid()) {
- m_tap_tree->Finalize(m_tap_internal_key);
- }
-
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an output map");
}
@@ -1223,9 +1218,12 @@ std::string PSBTRoleName(PSBTRole role);
/** Compute a PrecomputedTransactionData object from a psbt. */
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
-/** Checks whether a PSBTInput is already signed. */
+/** Checks whether a PSBTInput is already signed by checking for non-null finalized fields. */
bool PSBTInputSigned(const PSBTInput& input);
+/** Checks whether a PSBTInput is already signed by doing script verification using final fields. */
+bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata);
+
/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
*
* txdata should be the output of PrecomputePSBTData (which can be shared across
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index f6eee92cec..f522d78be4 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -431,7 +431,7 @@ void BitcoinGUI::createActions()
bool wallet_name_ok;
/*: Title of pop-up window shown when the user is attempting to
-+ restore a wallet. */
+ restore a wallet. */
QString title = tr("Restore Wallet");
//: Label of the input field where the name of the wallet is entered.
QString label = tr("Wallet Name");
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 4489c00932..3df4d4d921 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -60,6 +60,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error reading %s! Transaction data may be missing or incorrect. Rescanning "
"wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Address book data in wallet cannot be identified to belong to "
+"migrated wallets"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."),
@@ -67,9 +70,18 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Dumpfile version is not supported. This version of bitcoin-wallet "
"only supports version 1 dumpfiles. Got dumpfile with version %s"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Duplicate descriptors created during migration. Your wallet may be "
+"corrupted."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and "
"\"bech32\" address types"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Transaction %s in wallet cannot be identified to belong to migrated "
+"wallets"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Unable to produce descriptors for this legacy wallet. Make sure the "
+"wallet is unlocked first"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Failed to rename invalid peers.dat file. Please move or delete it and try "
"again."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -79,6 +91,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"File %s already exists. If you are sure this is what you want, move it out "
"of the way first."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet "
+"forbids connections to IPv4/IPv6"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay "
"fee of %s to prevent stuck transactions)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -98,8 +113,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"be provided."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
-"reaching the Tor network is not provided (no -proxy= and no -onion= given) "
-"or it is explicitly forbidden (-onion=0)"),
+"reaching the Tor network is explicitly forbidden: -onion=0"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
+"reaching the Tor network is not provided: none of -proxy, -onion or -"
+"listenonion is given"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Please check that your computer's date and time are correct! If your clock "
"is wrong, %s will not work properly."),
@@ -156,6 +174,14 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or "
"\"sqlite\"."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Unrecognized descriptor found. Loading wallet %s\n"
+"\n"
+"The wallet might had been created on a newer version.\n"
+"Please try running the latest software version.\n"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Unsupported category-specific logging level -loglevel=%s. Expected -"
+"loglevel=<category>:<loglevel>. Valid categories: %s. Valid loglevels: %s."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unsupported chainstate database format found. Please restart with -reindex-"
"chainstate. This will rebuild the chainstate database."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -176,6 +202,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
QT_TRANSLATE_NOOP("bitcoin-core", ""
"You need to rebuild the database using -reindex to go back to unpruned "
"mode. This will redownload the entire blockchain"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"\n"
+"Unable to cleanup failed migration"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"\n"
+"Unable to restore backup of wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"),
QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"),
QT_TRANSLATE_NOOP("bitcoin-core", "A fatal internal error occurred, see debug.log for details"),
@@ -203,15 +235,25 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading next record from wallet database"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Could not add watchonly tx to watchonly wallet"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Could not delete watchonly transactions"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Couldn't create cursor into database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low for %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Dumpfile checksum does not match. Computed %s, expected %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Failed to create new watchonly wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got key that was not hex: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got value that was not hex: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Keypool ran out, please call keypoolrefill first"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Missing checksum"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: No %s addresses available."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Not all watchonly txs could be deleted"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: This wallet already uses SQLite"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: This wallet is already a descriptor wallet"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to begin reading all records in the database"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to make a backup of your wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to parse version %u as a uint32_t"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to read all records in the database"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to remove watchonly address book data"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to write record to new wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to rescan the wallet during initialization"),
@@ -283,11 +325,13 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to open %s for writing"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to parse -maxuploadtarget: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unable to unload the wallet before migrating"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown address type '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown change type '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown new rules activated (versionbit %i)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported global logging level -loglevel=%s. Valid values: %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks…"),
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index ead977296a..33308cd68c 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1185,10 +1185,10 @@
<item row="6" column="0">
<widget class="QLabel" name="peerRelayTxesLabel">
<property name="toolTip">
- <string>Whether the peer requested us to relay transactions.</string>
+ <string>Whether we relay transactions to this peer (not available while the peer connection is being set up).</string>
</property>
<property name="text">
- <string>Wants Tx Relay</string>
+ <string>Transaction Relay</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
index a1e94f99e6..9ab91f6aa9 100644
--- a/src/qt/forms/intro.ui
+++ b/src/qt/forms/intro.ui
@@ -203,7 +203,7 @@
<item>
<widget class="QLabel" name="lblExplanation1">
<property name="text">
- <string>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</string>
+ <string>When you click OK, %1 will begin to download and process the full %4 block chain (%2 GB) starting with the earliest transactions in %3 when %4 initially launched.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 582f02132a..6be47e94fe 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -286,7 +286,7 @@
<item>
<widget class="QLineEdit" name="externalSignerPath">
<property name="toolTip">
- <string>Full path to a Bitcoin Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</string>
+ <string>Full path to a %1 compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</string>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 5cc21dd40b..b9f0be41e3 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -982,7 +982,7 @@ void PrintSlotException(
std::string description = sender->metaObject()->className();
description += "->";
description += receiver->metaObject()->className();
- PrintExceptionContinue(exception, description.c_str());
+ PrintExceptionContinue(exception, description);
}
void ShowModalDialogAsynchronously(QDialog* dialog)
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 883d3fe24a..586240445e 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -751,7 +751,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<message>
<location line="+8"/>
<source>Restore Wallet</source>
- <extracomment>Title of pop-up window shown when the user is attempting to + restore a wallet.</extracomment>
+ <extracomment>Title of pop-up window shown when the user is attempting to restore a wallet.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1370,12 +1370,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+157"/>
- <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+32"/>
+ <location line="+189"/>
<source>Limit block chain storage to</source>
<translation type="unfinished"></translation>
</message>
@@ -1395,7 +1390,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="-10"/>
+ <source>When you click OK, %1 will begin to download and process the full %4 block chain (%2 GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+20"/>
<source>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
<translation type="unfinished"></translation>
</message>
@@ -5228,7 +5228,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+6"/>
<source>Error: Dumpfile format record is incorrect. Got &quot;%s&quot;, expected &quot;format&quot;.</source>
<translation type="unfinished"></translation>
</message>
@@ -5243,12 +5243,12 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+6"/>
<source>Error: Legacy wallets only support the &quot;legacy&quot;, &quot;p2sh-segwit&quot;, and &quot;bech32&quot; address types</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+12"/>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<translation type="unfinished"></translation>
</message>
@@ -5258,7 +5258,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+6"/>
<source>Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
<translation type="unfinished"></translation>
</message>
@@ -5288,12 +5288,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is not provided (no -proxy= and no -onion= given) or it is explicitly forbidden (-onion=0)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
+ <location line="+10"/>
<source>Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation type="unfinished"></translation>
</message>
@@ -5378,7 +5373,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+11"/>
<source>Unsupported chainstate database format found. Please restart with -reindex-chainstate. This will rebuild the chainstate database.</source>
<translation type="unfinished"></translation>
</message>
@@ -5413,7 +5408,7 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+9"/>
<source>%s is set very high!</source>
<translation type="unfinished"></translation>
</message>
@@ -5448,12 +5443,12 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-65"/>
+ <location line="-79"/>
<source>The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-104"/>
+ <location line="-122"/>
<source>%s request to listen on port %u. This port is considered &quot;bad&quot; and thus it is unlikely that any Bitcoin Core peers connect to it. See doc/p2p-bad-ports.md for details and a full list.</source>
<translation type="unfinished"></translation>
</message>
@@ -5488,12 +5483,73 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+9"/>
+ <source>Error: Address book data in wallet cannot be identified to belong to migrated wallets</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Error: Duplicate descriptors created during migration. Your wallet may be corrupted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Error: Transaction %s in wallet cannot be identified to belong to migrated wallets</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Error: Unable to produce descriptors for this legacy wallet. Make sure the wallet is unlocked first</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Failed to rename invalid peers.dat file. Please move or delete it and try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+114"/>
+ <location line="+9"/>
+ <source>Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+21"/>
+ <source>Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is explicitly forbidden: -onion=0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is not provided: none of -proxy, -onion or -listenonion is given</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+59"/>
+ <source>Unrecognized descriptor found. Loading wallet %s
+
+The wallet might had been created on a newer version.
+Please try running the latest software version.
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Unsupported category-specific logging level -loglevel=%s. Expected -loglevel=&lt;category&gt;:&lt;loglevel&gt;. Valid categories: %s. Valid loglevels: %s.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+24"/>
+ <source>
+Unable to cleanup failed migration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>
+Unable to restore backup of wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
<source>Config setting for %s only applied on %s network when in [%s] section.</source>
<translation type="unfinished"></translation>
</message>
@@ -5594,6 +5650,16 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Error: Could not add watchonly tx to watchonly wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Could not delete watchonly transactions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error: Couldn&apos;t create cursor into database</source>
<translation type="unfinished"></translation>
</message>
@@ -5609,6 +5675,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Error: Failed to create new watchonly wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error: Got key that was not hex: %s</source>
<translation type="unfinished"></translation>
</message>
@@ -5634,11 +5705,46 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Error: Not all watchonly txs could be deleted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: This wallet already uses SQLite</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: This wallet is already a descriptor wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Unable to begin reading all records in the database</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Unable to make a backup of your wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error: Unable to parse version %u as a uint32_t</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>Error: Unable to read all records in the database</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Unable to remove watchonly address book data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error: Unable to write record to new wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -5994,6 +6100,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Unable to unload the wallet before migrating</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Unknown -blockfilterindex value %s.</source>
<translation type="unfinished"></translation>
</message>
@@ -6019,6 +6130,11 @@ Go to File &gt; Open Wallet to load a wallet.
</message>
<message>
<location line="+1"/>
+ <source>Unsupported global logging level -loglevel=%s. Valid values: %s.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
diff --git a/src/qt/locale/bitcoin_en.xlf b/src/qt/locale/bitcoin_en.xlf
index 7b2cfd30cf..a25ccfca72 100644
--- a/src/qt/locale/bitcoin_en.xlf
+++ b/src/qt/locale/bitcoin_en.xlf
@@ -661,7 +661,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<trans-unit id="_msg151">
<source xml:space="preserve">Restore Wallet</source>
<context-group purpose="location"><context context-type="linenumber">435</context></context-group>
- <note annotates="source" from="developer">Title of pop-up window shown when the user is attempting to + restore a wallet.</note>
+ <note annotates="source" from="developer">Title of pop-up window shown when the user is attempting to restore a wallet.</note>
</trans-unit>
<trans-unit id="_msg152">
<source xml:space="preserve">Wallet Name</source>
@@ -1353,25 +1353,25 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context-group purpose="location"><context context-type="linenumber">49</context></context-group>
</trans-unit>
<trans-unit id="_msg298">
- <source xml:space="preserve">When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
- <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
- </trans-unit>
- <trans-unit id="_msg299">
<source xml:space="preserve">Limit block chain storage to</source>
<context-group purpose="location"><context context-type="linenumber">238</context></context-group>
</trans-unit>
- <trans-unit id="_msg300">
+ <trans-unit id="_msg299">
<source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source>
<context-group purpose="location"><context context-type="linenumber">241</context></context-group>
</trans-unit>
- <trans-unit id="_msg301">
+ <trans-unit id="_msg300">
<source xml:space="preserve"> GB</source>
<context-group purpose="location"><context context-type="linenumber">248</context></context-group>
</trans-unit>
- <trans-unit id="_msg302">
+ <trans-unit id="_msg301">
<source xml:space="preserve">This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
<context-group purpose="location"><context context-type="linenumber">216</context></context-group>
</trans-unit>
+ <trans-unit id="_msg302">
+ <source xml:space="preserve">When you click OK, %1 will begin to download and process the full %4 block chain (%2 GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
<trans-unit id="_msg303">
<source xml:space="preserve">If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
<context-group purpose="location"><context context-type="linenumber">226</context></context-group>
@@ -4416,655 +4416,749 @@ Go to File &gt; Open Wallet to load a wallet.
</trans-unit>
<trans-unit id="_msg965">
<source xml:space="preserve">Error: Dumpfile format record is incorrect. Got &quot;%s&quot;, expected &quot;format&quot;.</source>
- <context-group purpose="location"><context context-type="linenumber">62</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">65</context></context-group>
</trans-unit>
<trans-unit id="_msg966">
<source xml:space="preserve">Error: Dumpfile identifier record is incorrect. Got &quot;%s&quot;, expected &quot;%s&quot;.</source>
- <context-group purpose="location"><context context-type="linenumber">64</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">67</context></context-group>
</trans-unit>
<trans-unit id="_msg967">
<source xml:space="preserve">Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source>
- <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
</trans-unit>
<trans-unit id="_msg968">
<source xml:space="preserve">Error: Legacy wallets only support the &quot;legacy&quot;, &quot;p2sh-segwit&quot;, and &quot;bech32&quot; address types</source>
- <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">75</context></context-group>
</trans-unit>
<trans-unit id="_msg969">
<source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
- <context-group purpose="location"><context context-type="linenumber">75</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
</trans-unit>
<trans-unit id="_msg970">
<source xml:space="preserve">File %s already exists. If you are sure this is what you want, move it out of the way first.</source>
- <context-group purpose="location"><context context-type="linenumber">78</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
</trans-unit>
<trans-unit id="_msg971">
<source xml:space="preserve">Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
- <context-group purpose="location"><context context-type="linenumber">81</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
</trans-unit>
<trans-unit id="_msg972">
<source xml:space="preserve">Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start.</source>
- <context-group purpose="location"><context context-type="linenumber">84</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">99</context></context-group>
</trans-unit>
<trans-unit id="_msg973">
<source xml:space="preserve">More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source>
- <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
</trans-unit>
<trans-unit id="_msg974">
<source xml:space="preserve">No dump file provided. To use createfromdump, -dumpfile=&lt;filename&gt; must be provided.</source>
- <context-group purpose="location"><context context-type="linenumber">91</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">106</context></context-group>
</trans-unit>
<trans-unit id="_msg975">
<source xml:space="preserve">No dump file provided. To use dump, -dumpfile=&lt;filename&gt; must be provided.</source>
- <context-group purpose="location"><context context-type="linenumber">94</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">109</context></context-group>
</trans-unit>
<trans-unit id="_msg976">
<source xml:space="preserve">No wallet file format provided. To use createfromdump, -format=&lt;format&gt; must be provided.</source>
- <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
</trans-unit>
<trans-unit id="_msg977">
- <source xml:space="preserve">Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is not provided (no -proxy= and no -onion= given) or it is explicitly forbidden (-onion=0)</source>
- <context-group purpose="location"><context context-type="linenumber">99</context></context-group>
- </trans-unit>
- <trans-unit id="_msg978">
<source xml:space="preserve">Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
- <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">121</context></context-group>
</trans-unit>
- <trans-unit id="_msg979">
+ <trans-unit id="_msg978">
<source xml:space="preserve">Please contribute if you find %s useful. Visit %s for further information about the software.</source>
- <context-group purpose="location"><context context-type="linenumber">106</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">124</context></context-group>
</trans-unit>
- <trans-unit id="_msg980">
+ <trans-unit id="_msg979">
<source xml:space="preserve">Prune configured below the minimum of %d MiB. Please use a higher number.</source>
- <context-group purpose="location"><context context-type="linenumber">109</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">127</context></context-group>
</trans-unit>
- <trans-unit id="_msg981">
+ <trans-unit id="_msg980">
<source xml:space="preserve">Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.</source>
- <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">129</context></context-group>
</trans-unit>
- <trans-unit id="_msg982">
+ <trans-unit id="_msg981">
<source xml:space="preserve">Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
- <context-group purpose="location"><context context-type="linenumber">114</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">132</context></context-group>
</trans-unit>
- <trans-unit id="_msg983">
+ <trans-unit id="_msg982">
<source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source>
- <context-group purpose="location"><context context-type="linenumber">117</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">135</context></context-group>
</trans-unit>
- <trans-unit id="_msg984">
+ <trans-unit id="_msg983">
<source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer&apos;s date and time being set incorrectly. Only rebuild the block database if you are sure that your computer&apos;s date and time are correct</source>
- <context-group purpose="location"><context context-type="linenumber">123</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">141</context></context-group>
</trans-unit>
- <trans-unit id="_msg985">
+ <trans-unit id="_msg984">
<source xml:space="preserve">The block index db contains a legacy &apos;txindex&apos;. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.</source>
- <context-group purpose="location"><context context-type="linenumber">128</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">146</context></context-group>
</trans-unit>
- <trans-unit id="_msg986">
+ <trans-unit id="_msg985">
<source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source>
- <context-group purpose="location"><context context-type="linenumber">132</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">150</context></context-group>
</trans-unit>
- <trans-unit id="_msg987">
+ <trans-unit id="_msg986">
<source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source>
- <context-group purpose="location"><context context-type="linenumber">134</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
</trans-unit>
- <trans-unit id="_msg988">
+ <trans-unit id="_msg987">
<source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
- <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">156</context></context-group>
</trans-unit>
- <trans-unit id="_msg989">
+ <trans-unit id="_msg988">
<source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
- <context-group purpose="location"><context context-type="linenumber">141</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">159</context></context-group>
</trans-unit>
- <trans-unit id="_msg990">
+ <trans-unit id="_msg989">
<source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source>
- <context-group purpose="location"><context context-type="linenumber">144</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">162</context></context-group>
</trans-unit>
- <trans-unit id="_msg991">
+ <trans-unit id="_msg990">
<source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source>
- <context-group purpose="location"><context context-type="linenumber">147</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">165</context></context-group>
</trans-unit>
- <trans-unit id="_msg992">
+ <trans-unit id="_msg991">
<source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
- <context-group purpose="location"><context context-type="linenumber">149</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">167</context></context-group>
</trans-unit>
- <trans-unit id="_msg993">
+ <trans-unit id="_msg992">
<source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
- <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">170</context></context-group>
</trans-unit>
- <trans-unit id="_msg994">
+ <trans-unit id="_msg993">
<source xml:space="preserve">Unknown wallet file format &quot;%s&quot; provided. Please provide one of &quot;bdb&quot; or &quot;sqlite&quot;.</source>
- <context-group purpose="location"><context context-type="linenumber">155</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">173</context></context-group>
</trans-unit>
- <trans-unit id="_msg995">
+ <trans-unit id="_msg994">
<source xml:space="preserve">Unsupported chainstate database format found. Please restart with -reindex-chainstate. This will rebuild the chainstate database.</source>
- <context-group purpose="location"><context context-type="linenumber">158</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">184</context></context-group>
</trans-unit>
- <trans-unit id="_msg996">
+ <trans-unit id="_msg995">
<source xml:space="preserve">Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future.</source>
- <context-group purpose="location"><context context-type="linenumber">161</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">187</context></context-group>
</trans-unit>
- <trans-unit id="_msg997">
+ <trans-unit id="_msg996">
<source xml:space="preserve">Warning: Dumpfile wallet format &quot;%s&quot; does not match command line specified format &quot;%s&quot;.</source>
- <context-group purpose="location"><context context-type="linenumber">165</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">191</context></context-group>
</trans-unit>
- <trans-unit id="_msg998">
+ <trans-unit id="_msg997">
<source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source>
- <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">194</context></context-group>
</trans-unit>
- <trans-unit id="_msg999">
+ <trans-unit id="_msg998">
<source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
- <context-group purpose="location"><context context-type="linenumber">170</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">196</context></context-group>
</trans-unit>
- <trans-unit id="_msg1000">
+ <trans-unit id="_msg999">
<source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source>
- <context-group purpose="location"><context context-type="linenumber">173</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">199</context></context-group>
</trans-unit>
- <trans-unit id="_msg1001">
+ <trans-unit id="_msg1000">
<source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
- <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">202</context></context-group>
</trans-unit>
- <trans-unit id="_msg1002">
+ <trans-unit id="_msg1001">
<source xml:space="preserve">%s is set very high!</source>
- <context-group purpose="location"><context context-type="linenumber">179</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">211</context></context-group>
</trans-unit>
- <trans-unit id="_msg1003">
+ <trans-unit id="_msg1002">
<source xml:space="preserve">-maxmempool must be at least %d MB</source>
- <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">212</context></context-group>
</trans-unit>
- <trans-unit id="_msg1004">
+ <trans-unit id="_msg1003">
<source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source>
- <context-group purpose="location"><context context-type="linenumber">181</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">213</context></context-group>
</trans-unit>
- <trans-unit id="_msg1005">
+ <trans-unit id="_msg1004">
<source xml:space="preserve">Cannot resolve -%s address: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">182</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">214</context></context-group>
</trans-unit>
- <trans-unit id="_msg1006">
+ <trans-unit id="_msg1005">
<source xml:space="preserve">Cannot set -forcednsseed to true when setting -dnsseed to false.</source>
- <context-group purpose="location"><context context-type="linenumber">183</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
</trans-unit>
- <trans-unit id="_msg1007">
+ <trans-unit id="_msg1006">
<source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source>
- <context-group purpose="location"><context context-type="linenumber">184</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">216</context></context-group>
</trans-unit>
- <trans-unit id="_msg1008">
+ <trans-unit id="_msg1007">
<source xml:space="preserve">Cannot write to data directory &apos;%s&apos;; check permissions.</source>
- <context-group purpose="location"><context context-type="linenumber">185</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">217</context></context-group>
</trans-unit>
- <trans-unit id="_msg1009">
+ <trans-unit id="_msg1008">
<source xml:space="preserve">The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.</source>
- <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
</trans-unit>
- <trans-unit id="_msg1010">
+ <trans-unit id="_msg1009">
<source xml:space="preserve">%s request to listen on port %u. This port is considered &quot;bad&quot; and thus it is unlikely that any Bitcoin Core peers connect to it. See doc/p2p-bad-ports.md for details and a full list.</source>
<context-group purpose="location"><context context-type="linenumber">16</context></context-group>
</trans-unit>
- <trans-unit id="_msg1011">
+ <trans-unit id="_msg1010">
<source xml:space="preserve">-reindex-chainstate option is not compatible with -blockfilterindex. Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.</source>
<context-group purpose="location"><context context-type="linenumber">23</context></context-group>
</trans-unit>
- <trans-unit id="_msg1012">
+ <trans-unit id="_msg1011">
<source xml:space="preserve">-reindex-chainstate option is not compatible with -coinstatsindex. Please temporarily disable coinstatsindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.</source>
<context-group purpose="location"><context context-type="linenumber">27</context></context-group>
</trans-unit>
- <trans-unit id="_msg1013">
+ <trans-unit id="_msg1012">
<source xml:space="preserve">-reindex-chainstate option is not compatible with -txindex. Please temporarily disable txindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.</source>
<context-group purpose="location"><context context-type="linenumber">31</context></context-group>
</trans-unit>
- <trans-unit id="_msg1014">
+ <trans-unit id="_msg1013">
<source xml:space="preserve">Assumed-valid: last wallet synchronisation goes beyond available block data. You need to wait for the background validation chain to download more blocks.</source>
<context-group purpose="location"><context context-type="linenumber">35</context></context-group>
</trans-unit>
- <trans-unit id="_msg1015">
+ <trans-unit id="_msg1014">
<source xml:space="preserve">Cannot provide specific connections and have addrman find outgoing connections at the same time.</source>
<context-group purpose="location"><context context-type="linenumber">43</context></context-group>
</trans-unit>
- <trans-unit id="_msg1016">
+ <trans-unit id="_msg1015">
<source xml:space="preserve">Error loading %s: External signer wallet being loaded without external signer support compiled</source>
<context-group purpose="location"><context context-type="linenumber">53</context></context-group>
</trans-unit>
+ <trans-unit id="_msg1016">
+ <source xml:space="preserve">Error: Address book data in wallet cannot be identified to belong to migrated wallets</source>
+ <context-group purpose="location"><context context-type="linenumber">62</context></context-group>
+ </trans-unit>
<trans-unit id="_msg1017">
- <source xml:space="preserve">Failed to rename invalid peers.dat file. Please move or delete it and try again.</source>
+ <source xml:space="preserve">Error: Duplicate descriptors created during migration. Your wallet may be corrupted.</source>
<context-group purpose="location"><context context-type="linenumber">72</context></context-group>
</trans-unit>
<trans-unit id="_msg1018">
- <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source>
- <context-group purpose="location"><context context-type="linenumber">186</context></context-group>
+ <source xml:space="preserve">Error: Transaction %s in wallet cannot be identified to belong to migrated wallets</source>
+ <context-group purpose="location"><context context-type="linenumber">78</context></context-group>
</trans-unit>
<trans-unit id="_msg1019">
- <source xml:space="preserve">Copyright (C) %i-%i</source>
- <context-group purpose="location"><context context-type="linenumber">187</context></context-group>
+ <source xml:space="preserve">Error: Unable to produce descriptors for this legacy wallet. Make sure the wallet is unlocked first</source>
+ <context-group purpose="location"><context context-type="linenumber">81</context></context-group>
</trans-unit>
<trans-unit id="_msg1020">
- <source xml:space="preserve">Corrupted block database detected</source>
- <context-group purpose="location"><context context-type="linenumber">188</context></context-group>
+ <source xml:space="preserve">Failed to rename invalid peers.dat file. Please move or delete it and try again.</source>
+ <context-group purpose="location"><context context-type="linenumber">84</context></context-group>
</trans-unit>
<trans-unit id="_msg1021">
- <source xml:space="preserve">Could not find asmap file %s</source>
- <context-group purpose="location"><context context-type="linenumber">189</context></context-group>
+ <source xml:space="preserve">Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6</source>
+ <context-group purpose="location"><context context-type="linenumber">93</context></context-group>
</trans-unit>
<trans-unit id="_msg1022">
- <source xml:space="preserve">Could not parse asmap file %s</source>
- <context-group purpose="location"><context context-type="linenumber">190</context></context-group>
+ <source xml:space="preserve">Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is explicitly forbidden: -onion=0</source>
+ <context-group purpose="location"><context context-type="linenumber">114</context></context-group>
</trans-unit>
<trans-unit id="_msg1023">
- <source xml:space="preserve">Disk space is too low!</source>
- <context-group purpose="location"><context context-type="linenumber">191</context></context-group>
+ <source xml:space="preserve">Outbound connections restricted to Tor (-onlynet=onion) but the proxy for reaching the Tor network is not provided: none of -proxy, -onion or -listenonion is given</source>
+ <context-group purpose="location"><context context-type="linenumber">117</context></context-group>
</trans-unit>
<trans-unit id="_msg1024">
- <source xml:space="preserve">Do you want to rebuild the block database now?</source>
- <context-group purpose="location"><context context-type="linenumber">192</context></context-group>
+ <source xml:space="preserve">Unrecognized descriptor found. Loading wallet %s
+
+The wallet might had been created on a newer version.
+Please try running the latest software version.
+</source>
+ <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
</trans-unit>
<trans-unit id="_msg1025">
- <source xml:space="preserve">Done loading</source>
- <context-group purpose="location"><context context-type="linenumber">193</context></context-group>
+ <source xml:space="preserve">Unsupported category-specific logging level -loglevel=%s. Expected -loglevel=&lt;category&gt;:&lt;loglevel&gt;. Valid categories: %s. Valid loglevels: %s.</source>
+ <context-group purpose="location"><context context-type="linenumber">181</context></context-group>
</trans-unit>
<trans-unit id="_msg1026">
- <source xml:space="preserve">Dump file %s does not exist.</source>
- <context-group purpose="location"><context context-type="linenumber">194</context></context-group>
+ <source xml:space="preserve">
+Unable to cleanup failed migration</source>
+ <context-group purpose="location"><context context-type="linenumber">205</context></context-group>
</trans-unit>
<trans-unit id="_msg1027">
- <source xml:space="preserve">Error creating %s</source>
- <context-group purpose="location"><context context-type="linenumber">195</context></context-group>
+ <source xml:space="preserve">
+Unable to restore backup of wallet.</source>
+ <context-group purpose="location"><context context-type="linenumber">208</context></context-group>
</trans-unit>
<trans-unit id="_msg1028">
- <source xml:space="preserve">Error initializing block database</source>
- <context-group purpose="location"><context context-type="linenumber">196</context></context-group>
+ <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source>
+ <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
</trans-unit>
<trans-unit id="_msg1029">
- <source xml:space="preserve">Error initializing wallet database environment %s!</source>
- <context-group purpose="location"><context context-type="linenumber">197</context></context-group>
+ <source xml:space="preserve">Copyright (C) %i-%i</source>
+ <context-group purpose="location"><context context-type="linenumber">219</context></context-group>
</trans-unit>
<trans-unit id="_msg1030">
- <source xml:space="preserve">Error loading %s</source>
- <context-group purpose="location"><context context-type="linenumber">198</context></context-group>
+ <source xml:space="preserve">Corrupted block database detected</source>
+ <context-group purpose="location"><context context-type="linenumber">220</context></context-group>
</trans-unit>
<trans-unit id="_msg1031">
- <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source>
- <context-group purpose="location"><context context-type="linenumber">199</context></context-group>
+ <source xml:space="preserve">Could not find asmap file %s</source>
+ <context-group purpose="location"><context context-type="linenumber">221</context></context-group>
</trans-unit>
<trans-unit id="_msg1032">
- <source xml:space="preserve">Error loading %s: Wallet corrupted</source>
- <context-group purpose="location"><context context-type="linenumber">200</context></context-group>
+ <source xml:space="preserve">Could not parse asmap file %s</source>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
</trans-unit>
<trans-unit id="_msg1033">
- <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source>
- <context-group purpose="location"><context context-type="linenumber">201</context></context-group>
+ <source xml:space="preserve">Disk space is too low!</source>
+ <context-group purpose="location"><context context-type="linenumber">223</context></context-group>
</trans-unit>
<trans-unit id="_msg1034">
- <source xml:space="preserve">Error loading block database</source>
- <context-group purpose="location"><context context-type="linenumber">202</context></context-group>
+ <source xml:space="preserve">Do you want to rebuild the block database now?</source>
+ <context-group purpose="location"><context context-type="linenumber">224</context></context-group>
</trans-unit>
<trans-unit id="_msg1035">
- <source xml:space="preserve">Error opening block database</source>
- <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ <source xml:space="preserve">Done loading</source>
+ <context-group purpose="location"><context context-type="linenumber">225</context></context-group>
</trans-unit>
<trans-unit id="_msg1036">
- <source xml:space="preserve">Error reading from database, shutting down.</source>
- <context-group purpose="location"><context context-type="linenumber">204</context></context-group>
+ <source xml:space="preserve">Dump file %s does not exist.</source>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
</trans-unit>
<trans-unit id="_msg1037">
- <source xml:space="preserve">Error reading next record from wallet database</source>
- <context-group purpose="location"><context context-type="linenumber">205</context></context-group>
+ <source xml:space="preserve">Error creating %s</source>
+ <context-group purpose="location"><context context-type="linenumber">227</context></context-group>
</trans-unit>
<trans-unit id="_msg1038">
- <source xml:space="preserve">Error: Couldn&apos;t create cursor into database</source>
- <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ <source xml:space="preserve">Error initializing block database</source>
+ <context-group purpose="location"><context context-type="linenumber">228</context></context-group>
</trans-unit>
<trans-unit id="_msg1039">
- <source xml:space="preserve">Error: Disk space is low for %s</source>
- <context-group purpose="location"><context context-type="linenumber">207</context></context-group>
+ <source xml:space="preserve">Error initializing wallet database environment %s!</source>
+ <context-group purpose="location"><context context-type="linenumber">229</context></context-group>
</trans-unit>
<trans-unit id="_msg1040">
- <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
- <context-group purpose="location"><context context-type="linenumber">208</context></context-group>
+ <source xml:space="preserve">Error loading %s</source>
+ <context-group purpose="location"><context context-type="linenumber">230</context></context-group>
</trans-unit>
<trans-unit id="_msg1041">
- <source xml:space="preserve">Error: Got key that was not hex: %s</source>
- <context-group purpose="location"><context context-type="linenumber">209</context></context-group>
+ <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source>
+ <context-group purpose="location"><context context-type="linenumber">231</context></context-group>
</trans-unit>
<trans-unit id="_msg1042">
- <source xml:space="preserve">Error: Got value that was not hex: %s</source>
- <context-group purpose="location"><context context-type="linenumber">210</context></context-group>
+ <source xml:space="preserve">Error loading %s: Wallet corrupted</source>
+ <context-group purpose="location"><context context-type="linenumber">232</context></context-group>
</trans-unit>
<trans-unit id="_msg1043">
- <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source>
- <context-group purpose="location"><context context-type="linenumber">211</context></context-group>
+ <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source>
+ <context-group purpose="location"><context context-type="linenumber">233</context></context-group>
</trans-unit>
<trans-unit id="_msg1044">
- <source xml:space="preserve">Error: Missing checksum</source>
- <context-group purpose="location"><context context-type="linenumber">212</context></context-group>
+ <source xml:space="preserve">Error loading block database</source>
+ <context-group purpose="location"><context context-type="linenumber">234</context></context-group>
</trans-unit>
<trans-unit id="_msg1045">
- <source xml:space="preserve">Error: No %s addresses available.</source>
- <context-group purpose="location"><context context-type="linenumber">213</context></context-group>
+ <source xml:space="preserve">Error opening block database</source>
+ <context-group purpose="location"><context context-type="linenumber">235</context></context-group>
</trans-unit>
<trans-unit id="_msg1046">
- <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source>
- <context-group purpose="location"><context context-type="linenumber">214</context></context-group>
+ <source xml:space="preserve">Error reading from database, shutting down.</source>
+ <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
</trans-unit>
<trans-unit id="_msg1047">
- <source xml:space="preserve">Error: Unable to write record to new wallet</source>
- <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
+ <source xml:space="preserve">Error reading next record from wallet database</source>
+ <context-group purpose="location"><context context-type="linenumber">237</context></context-group>
</trans-unit>
<trans-unit id="_msg1048">
- <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source>
- <context-group purpose="location"><context context-type="linenumber">216</context></context-group>
+ <source xml:space="preserve">Error: Could not add watchonly tx to watchonly wallet</source>
+ <context-group purpose="location"><context context-type="linenumber">238</context></context-group>
</trans-unit>
<trans-unit id="_msg1049">
- <source xml:space="preserve">Failed to rescan the wallet during initialization</source>
- <context-group purpose="location"><context context-type="linenumber">217</context></context-group>
+ <source xml:space="preserve">Error: Could not delete watchonly transactions</source>
+ <context-group purpose="location"><context context-type="linenumber">239</context></context-group>
</trans-unit>
<trans-unit id="_msg1050">
- <source xml:space="preserve">Failed to verify database</source>
- <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
+ <source xml:space="preserve">Error: Couldn&apos;t create cursor into database</source>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
</trans-unit>
<trans-unit id="_msg1051">
- <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
- <context-group purpose="location"><context context-type="linenumber">219</context></context-group>
+ <source xml:space="preserve">Error: Disk space is low for %s</source>
+ <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
</trans-unit>
<trans-unit id="_msg1052">
- <source xml:space="preserve">Ignoring duplicate -wallet %s.</source>
- <context-group purpose="location"><context context-type="linenumber">220</context></context-group>
+ <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
+ <context-group purpose="location"><context context-type="linenumber">242</context></context-group>
</trans-unit>
<trans-unit id="_msg1053">
- <source xml:space="preserve">Importing…</source>
- <context-group purpose="location"><context context-type="linenumber">221</context></context-group>
+ <source xml:space="preserve">Error: Failed to create new watchonly wallet</source>
+ <context-group purpose="location"><context context-type="linenumber">243</context></context-group>
</trans-unit>
<trans-unit id="_msg1054">
- <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source>
- <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ <source xml:space="preserve">Error: Got key that was not hex: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">244</context></context-group>
</trans-unit>
<trans-unit id="_msg1055">
- <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source>
- <context-group purpose="location"><context context-type="linenumber">223</context></context-group>
+ <source xml:space="preserve">Error: Got value that was not hex: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">245</context></context-group>
</trans-unit>
<trans-unit id="_msg1056">
- <source xml:space="preserve">Input not found or already spent</source>
- <context-group purpose="location"><context context-type="linenumber">224</context></context-group>
+ <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
</trans-unit>
<trans-unit id="_msg1057">
- <source xml:space="preserve">Insufficient funds</source>
- <context-group purpose="location"><context context-type="linenumber">225</context></context-group>
+ <source xml:space="preserve">Error: Missing checksum</source>
+ <context-group purpose="location"><context context-type="linenumber">247</context></context-group>
</trans-unit>
<trans-unit id="_msg1058">
- <source xml:space="preserve">Invalid -i2psam address or hostname: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ <source xml:space="preserve">Error: No %s addresses available.</source>
+ <context-group purpose="location"><context context-type="linenumber">248</context></context-group>
</trans-unit>
<trans-unit id="_msg1059">
- <source xml:space="preserve">Invalid -onion address or hostname: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">227</context></context-group>
+ <source xml:space="preserve">Error: Not all watchonly txs could be deleted</source>
+ <context-group purpose="location"><context context-type="linenumber">249</context></context-group>
</trans-unit>
<trans-unit id="_msg1060">
- <source xml:space="preserve">Invalid -proxy address or hostname: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">228</context></context-group>
+ <source xml:space="preserve">Error: This wallet already uses SQLite</source>
+ <context-group purpose="location"><context context-type="linenumber">250</context></context-group>
</trans-unit>
<trans-unit id="_msg1061">
- <source xml:space="preserve">Invalid P2P permission: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">229</context></context-group>
+ <source xml:space="preserve">Error: This wallet is already a descriptor wallet</source>
+ <context-group purpose="location"><context context-type="linenumber">251</context></context-group>
</trans-unit>
<trans-unit id="_msg1062">
- <source xml:space="preserve">Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">230</context></context-group>
+ <source xml:space="preserve">Error: Unable to begin reading all records in the database</source>
+ <context-group purpose="location"><context context-type="linenumber">252</context></context-group>
</trans-unit>
<trans-unit id="_msg1063">
- <source xml:space="preserve">Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">231</context></context-group>
+ <source xml:space="preserve">Error: Unable to make a backup of your wallet</source>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
</trans-unit>
<trans-unit id="_msg1064">
- <source xml:space="preserve">Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">232</context></context-group>
+ <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source>
+ <context-group purpose="location"><context context-type="linenumber">254</context></context-group>
</trans-unit>
<trans-unit id="_msg1065">
- <source xml:space="preserve">Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
- <context-group purpose="location"><context context-type="linenumber">233</context></context-group>
+ <source xml:space="preserve">Error: Unable to read all records in the database</source>
+ <context-group purpose="location"><context context-type="linenumber">255</context></context-group>
</trans-unit>
<trans-unit id="_msg1066">
- <source xml:space="preserve">Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">234</context></context-group>
+ <source xml:space="preserve">Error: Unable to remove watchonly address book data</source>
+ <context-group purpose="location"><context context-type="linenumber">256</context></context-group>
</trans-unit>
<trans-unit id="_msg1067">
- <source xml:space="preserve">Listening for incoming connections failed (listen returned error %s)</source>
- <context-group purpose="location"><context context-type="linenumber">235</context></context-group>
+ <source xml:space="preserve">Error: Unable to write record to new wallet</source>
+ <context-group purpose="location"><context context-type="linenumber">257</context></context-group>
</trans-unit>
<trans-unit id="_msg1068">
- <source xml:space="preserve">Loading P2P addresses…</source>
- <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
+ <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source>
+ <context-group purpose="location"><context context-type="linenumber">258</context></context-group>
</trans-unit>
<trans-unit id="_msg1069">
- <source xml:space="preserve">Loading banlist…</source>
- <context-group purpose="location"><context context-type="linenumber">237</context></context-group>
+ <source xml:space="preserve">Failed to rescan the wallet during initialization</source>
+ <context-group purpose="location"><context context-type="linenumber">259</context></context-group>
</trans-unit>
<trans-unit id="_msg1070">
- <source xml:space="preserve">Loading block index…</source>
- <context-group purpose="location"><context context-type="linenumber">238</context></context-group>
+ <source xml:space="preserve">Failed to verify database</source>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
</trans-unit>
<trans-unit id="_msg1071">
- <source xml:space="preserve">Loading wallet…</source>
- <context-group purpose="location"><context context-type="linenumber">239</context></context-group>
+ <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
+ <context-group purpose="location"><context context-type="linenumber">261</context></context-group>
</trans-unit>
<trans-unit id="_msg1072">
- <source xml:space="preserve">Missing amount</source>
- <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ <source xml:space="preserve">Ignoring duplicate -wallet %s.</source>
+ <context-group purpose="location"><context context-type="linenumber">262</context></context-group>
</trans-unit>
<trans-unit id="_msg1073">
- <source xml:space="preserve">Missing solving data for estimating transaction size</source>
- <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
+ <source xml:space="preserve">Importing…</source>
+ <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
</trans-unit>
<trans-unit id="_msg1074">
- <source xml:space="preserve">Need to specify a port with -whitebind: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">242</context></context-group>
+ <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <context-group purpose="location"><context context-type="linenumber">264</context></context-group>
</trans-unit>
<trans-unit id="_msg1075">
- <source xml:space="preserve">No addresses available</source>
- <context-group purpose="location"><context context-type="linenumber">243</context></context-group>
+ <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source>
+ <context-group purpose="location"><context context-type="linenumber">265</context></context-group>
</trans-unit>
<trans-unit id="_msg1076">
- <source xml:space="preserve">Not enough file descriptors available.</source>
- <context-group purpose="location"><context context-type="linenumber">244</context></context-group>
+ <source xml:space="preserve">Input not found or already spent</source>
+ <context-group purpose="location"><context context-type="linenumber">266</context></context-group>
</trans-unit>
<trans-unit id="_msg1077">
- <source xml:space="preserve">Prune cannot be configured with a negative value.</source>
- <context-group purpose="location"><context context-type="linenumber">245</context></context-group>
+ <source xml:space="preserve">Insufficient funds</source>
+ <context-group purpose="location"><context context-type="linenumber">267</context></context-group>
</trans-unit>
<trans-unit id="_msg1078">
- <source xml:space="preserve">Prune mode is incompatible with -txindex.</source>
- <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ <source xml:space="preserve">Invalid -i2psam address or hostname: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
</trans-unit>
<trans-unit id="_msg1079">
- <source xml:space="preserve">Pruning blockstore…</source>
- <context-group purpose="location"><context context-type="linenumber">247</context></context-group>
+ <source xml:space="preserve">Invalid -onion address or hostname: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">269</context></context-group>
</trans-unit>
<trans-unit id="_msg1080">
- <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source>
- <context-group purpose="location"><context context-type="linenumber">248</context></context-group>
+ <source xml:space="preserve">Invalid -proxy address or hostname: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">270</context></context-group>
</trans-unit>
<trans-unit id="_msg1081">
- <source xml:space="preserve">Replaying blocks…</source>
- <context-group purpose="location"><context context-type="linenumber">249</context></context-group>
+ <source xml:space="preserve">Invalid P2P permission: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">271</context></context-group>
</trans-unit>
<trans-unit id="_msg1082">
- <source xml:space="preserve">Rescanning…</source>
- <context-group purpose="location"><context context-type="linenumber">250</context></context-group>
+ <source xml:space="preserve">Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">272</context></context-group>
</trans-unit>
<trans-unit id="_msg1083">
- <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source>
- <context-group purpose="location"><context context-type="linenumber">251</context></context-group>
+ <source xml:space="preserve">Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">273</context></context-group>
</trans-unit>
<trans-unit id="_msg1084">
- <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
- <context-group purpose="location"><context context-type="linenumber">252</context></context-group>
+ <source xml:space="preserve">Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">274</context></context-group>
</trans-unit>
<trans-unit id="_msg1085">
- <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source>
- <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ <source xml:space="preserve">Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
</trans-unit>
<trans-unit id="_msg1086">
- <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
- <context-group purpose="location"><context context-type="linenumber">254</context></context-group>
+ <source xml:space="preserve">Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">276</context></context-group>
</trans-unit>
<trans-unit id="_msg1087">
- <source xml:space="preserve">Section [%s] is not recognized.</source>
- <context-group purpose="location"><context context-type="linenumber">255</context></context-group>
+ <source xml:space="preserve">Listening for incoming connections failed (listen returned error %s)</source>
+ <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
</trans-unit>
<trans-unit id="_msg1088">
- <source xml:space="preserve">Signing transaction failed</source>
- <context-group purpose="location"><context context-type="linenumber">256</context></context-group>
+ <source xml:space="preserve">Loading P2P addresses…</source>
+ <context-group purpose="location"><context context-type="linenumber">278</context></context-group>
</trans-unit>
<trans-unit id="_msg1089">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; does not exist</source>
- <context-group purpose="location"><context context-type="linenumber">257</context></context-group>
+ <source xml:space="preserve">Loading banlist…</source>
+ <context-group purpose="location"><context context-type="linenumber">279</context></context-group>
</trans-unit>
<trans-unit id="_msg1090">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is a relative path</source>
- <context-group purpose="location"><context context-type="linenumber">258</context></context-group>
+ <source xml:space="preserve">Loading block index…</source>
+ <context-group purpose="location"><context context-type="linenumber">280</context></context-group>
</trans-unit>
<trans-unit id="_msg1091">
- <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is not a directory</source>
- <context-group purpose="location"><context context-type="linenumber">259</context></context-group>
+ <source xml:space="preserve">Loading wallet…</source>
+ <context-group purpose="location"><context context-type="linenumber">281</context></context-group>
</trans-unit>
<trans-unit id="_msg1092">
- <source xml:space="preserve">Specified blocks directory &quot;%s&quot; does not exist.</source>
- <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ <source xml:space="preserve">Missing amount</source>
+ <context-group purpose="location"><context context-type="linenumber">282</context></context-group>
</trans-unit>
<trans-unit id="_msg1093">
- <source xml:space="preserve">Starting network threads…</source>
- <context-group purpose="location"><context context-type="linenumber">261</context></context-group>
+ <source xml:space="preserve">Missing solving data for estimating transaction size</source>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
</trans-unit>
<trans-unit id="_msg1094">
- <source xml:space="preserve">The source code is available from %s.</source>
- <context-group purpose="location"><context context-type="linenumber">262</context></context-group>
+ <source xml:space="preserve">Need to specify a port with -whitebind: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
</trans-unit>
<trans-unit id="_msg1095">
- <source xml:space="preserve">The specified config file %s does not exist</source>
- <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
+ <source xml:space="preserve">No addresses available</source>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
</trans-unit>
<trans-unit id="_msg1096">
- <source xml:space="preserve">The transaction amount is too small to pay the fee</source>
- <context-group purpose="location"><context context-type="linenumber">264</context></context-group>
+ <source xml:space="preserve">Not enough file descriptors available.</source>
+ <context-group purpose="location"><context context-type="linenumber">286</context></context-group>
</trans-unit>
<trans-unit id="_msg1097">
- <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source>
- <context-group purpose="location"><context context-type="linenumber">265</context></context-group>
+ <source xml:space="preserve">Prune cannot be configured with a negative value.</source>
+ <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
</trans-unit>
<trans-unit id="_msg1098">
- <source xml:space="preserve">This is experimental software.</source>
- <context-group purpose="location"><context context-type="linenumber">266</context></context-group>
+ <source xml:space="preserve">Prune mode is incompatible with -txindex.</source>
+ <context-group purpose="location"><context context-type="linenumber">288</context></context-group>
</trans-unit>
<trans-unit id="_msg1099">
- <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source>
- <context-group purpose="location"><context context-type="linenumber">267</context></context-group>
+ <source xml:space="preserve">Pruning blockstore…</source>
+ <context-group purpose="location"><context context-type="linenumber">289</context></context-group>
</trans-unit>
<trans-unit id="_msg1100">
- <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source>
- <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
+ <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <context-group purpose="location"><context context-type="linenumber">290</context></context-group>
</trans-unit>
<trans-unit id="_msg1101">
- <source xml:space="preserve">Transaction amount too small</source>
- <context-group purpose="location"><context context-type="linenumber">269</context></context-group>
+ <source xml:space="preserve">Replaying blocks…</source>
+ <context-group purpose="location"><context context-type="linenumber">291</context></context-group>
</trans-unit>
<trans-unit id="_msg1102">
- <source xml:space="preserve">Transaction amounts must not be negative</source>
- <context-group purpose="location"><context context-type="linenumber">270</context></context-group>
+ <source xml:space="preserve">Rescanning…</source>
+ <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
</trans-unit>
<trans-unit id="_msg1103">
- <source xml:space="preserve">Transaction change output index out of range</source>
- <context-group purpose="location"><context context-type="linenumber">271</context></context-group>
+ <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">293</context></context-group>
</trans-unit>
<trans-unit id="_msg1104">
- <source xml:space="preserve">Transaction has too long of a mempool chain</source>
- <context-group purpose="location"><context context-type="linenumber">272</context></context-group>
+ <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">294</context></context-group>
</trans-unit>
<trans-unit id="_msg1105">
- <source xml:space="preserve">Transaction must have at least one recipient</source>
- <context-group purpose="location"><context context-type="linenumber">273</context></context-group>
+ <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">295</context></context-group>
</trans-unit>
<trans-unit id="_msg1106">
- <source xml:space="preserve">Transaction needs a change address, but we can&apos;t generate it.</source>
- <context-group purpose="location"><context context-type="linenumber">274</context></context-group>
+ <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
+ <context-group purpose="location"><context context-type="linenumber">296</context></context-group>
</trans-unit>
<trans-unit id="_msg1107">
- <source xml:space="preserve">Transaction too large</source>
- <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
+ <source xml:space="preserve">Section [%s] is not recognized.</source>
+ <context-group purpose="location"><context context-type="linenumber">297</context></context-group>
</trans-unit>
<trans-unit id="_msg1108">
- <source xml:space="preserve">Unable to allocate memory for -maxsigcachesize: &apos;%s&apos; MiB</source>
- <context-group purpose="location"><context context-type="linenumber">276</context></context-group>
+ <source xml:space="preserve">Signing transaction failed</source>
+ <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
</trans-unit>
<trans-unit id="_msg1109">
- <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source>
- <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; does not exist</source>
+ <context-group purpose="location"><context context-type="linenumber">299</context></context-group>
</trans-unit>
<trans-unit id="_msg1110">
- <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source>
- <context-group purpose="location"><context context-type="linenumber">278</context></context-group>
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is a relative path</source>
+ <context-group purpose="location"><context context-type="linenumber">300</context></context-group>
</trans-unit>
<trans-unit id="_msg1111">
- <source xml:space="preserve">Unable to create the PID file &apos;%s&apos;: %s</source>
- <context-group purpose="location"><context context-type="linenumber">279</context></context-group>
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is not a directory</source>
+ <context-group purpose="location"><context context-type="linenumber">301</context></context-group>
</trans-unit>
<trans-unit id="_msg1112">
- <source xml:space="preserve">Unable to find UTXO for external input</source>
- <context-group purpose="location"><context context-type="linenumber">280</context></context-group>
+ <source xml:space="preserve">Specified blocks directory &quot;%s&quot; does not exist.</source>
+ <context-group purpose="location"><context context-type="linenumber">302</context></context-group>
</trans-unit>
<trans-unit id="_msg1113">
- <source xml:space="preserve">Unable to generate initial keys</source>
- <context-group purpose="location"><context context-type="linenumber">281</context></context-group>
+ <source xml:space="preserve">Starting network threads…</source>
+ <context-group purpose="location"><context context-type="linenumber">303</context></context-group>
</trans-unit>
<trans-unit id="_msg1114">
- <source xml:space="preserve">Unable to generate keys</source>
- <context-group purpose="location"><context context-type="linenumber">282</context></context-group>
+ <source xml:space="preserve">The source code is available from %s.</source>
+ <context-group purpose="location"><context context-type="linenumber">304</context></context-group>
</trans-unit>
<trans-unit id="_msg1115">
- <source xml:space="preserve">Unable to open %s for writing</source>
- <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ <source xml:space="preserve">The specified config file %s does not exist</source>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
</trans-unit>
<trans-unit id="_msg1116">
- <source xml:space="preserve">Unable to parse -maxuploadtarget: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
+ <source xml:space="preserve">The transaction amount is too small to pay the fee</source>
+ <context-group purpose="location"><context context-type="linenumber">306</context></context-group>
</trans-unit>
<trans-unit id="_msg1117">
- <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source>
- <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
+ <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source>
+ <context-group purpose="location"><context context-type="linenumber">307</context></context-group>
</trans-unit>
<trans-unit id="_msg1118">
- <source xml:space="preserve">Unknown -blockfilterindex value %s.</source>
- <context-group purpose="location"><context context-type="linenumber">286</context></context-group>
+ <source xml:space="preserve">This is experimental software.</source>
+ <context-group purpose="location"><context context-type="linenumber">308</context></context-group>
</trans-unit>
<trans-unit id="_msg1119">
- <source xml:space="preserve">Unknown address type &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
+ <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source>
+ <context-group purpose="location"><context context-type="linenumber">309</context></context-group>
</trans-unit>
<trans-unit id="_msg1120">
- <source xml:space="preserve">Unknown change type &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">288</context></context-group>
+ <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source>
+ <context-group purpose="location"><context context-type="linenumber">310</context></context-group>
</trans-unit>
<trans-unit id="_msg1121">
- <source xml:space="preserve">Unknown network specified in -onlynet: &apos;%s&apos;</source>
- <context-group purpose="location"><context context-type="linenumber">289</context></context-group>
+ <source xml:space="preserve">Transaction amount too small</source>
+ <context-group purpose="location"><context context-type="linenumber">311</context></context-group>
</trans-unit>
<trans-unit id="_msg1122">
- <source xml:space="preserve">Unknown new rules activated (versionbit %i)</source>
- <context-group purpose="location"><context context-type="linenumber">290</context></context-group>
+ <source xml:space="preserve">Transaction amounts must not be negative</source>
+ <context-group purpose="location"><context context-type="linenumber">312</context></context-group>
</trans-unit>
<trans-unit id="_msg1123">
- <source xml:space="preserve">Unsupported logging category %s=%s.</source>
- <context-group purpose="location"><context context-type="linenumber">291</context></context-group>
+ <source xml:space="preserve">Transaction change output index out of range</source>
+ <context-group purpose="location"><context context-type="linenumber">313</context></context-group>
</trans-unit>
<trans-unit id="_msg1124">
- <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source>
- <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
+ <source xml:space="preserve">Transaction has too long of a mempool chain</source>
+ <context-group purpose="location"><context context-type="linenumber">314</context></context-group>
</trans-unit>
<trans-unit id="_msg1125">
- <source xml:space="preserve">Verifying blocks…</source>
- <context-group purpose="location"><context context-type="linenumber">293</context></context-group>
+ <source xml:space="preserve">Transaction must have at least one recipient</source>
+ <context-group purpose="location"><context context-type="linenumber">315</context></context-group>
</trans-unit>
<trans-unit id="_msg1126">
- <source xml:space="preserve">Verifying wallet(s)…</source>
- <context-group purpose="location"><context context-type="linenumber">294</context></context-group>
+ <source xml:space="preserve">Transaction needs a change address, but we can&apos;t generate it.</source>
+ <context-group purpose="location"><context context-type="linenumber">316</context></context-group>
</trans-unit>
<trans-unit id="_msg1127">
+ <source xml:space="preserve">Transaction too large</source>
+ <context-group purpose="location"><context context-type="linenumber">317</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1128">
+ <source xml:space="preserve">Unable to allocate memory for -maxsigcachesize: &apos;%s&apos; MiB</source>
+ <context-group purpose="location"><context context-type="linenumber">318</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1129">
+ <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source>
+ <context-group purpose="location"><context context-type="linenumber">319</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1130">
+ <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source>
+ <context-group purpose="location"><context context-type="linenumber">320</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1131">
+ <source xml:space="preserve">Unable to create the PID file &apos;%s&apos;: %s</source>
+ <context-group purpose="location"><context context-type="linenumber">321</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1132">
+ <source xml:space="preserve">Unable to find UTXO for external input</source>
+ <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1133">
+ <source xml:space="preserve">Unable to generate initial keys</source>
+ <context-group purpose="location"><context context-type="linenumber">323</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1134">
+ <source xml:space="preserve">Unable to generate keys</source>
+ <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1135">
+ <source xml:space="preserve">Unable to open %s for writing</source>
+ <context-group purpose="location"><context context-type="linenumber">325</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1136">
+ <source xml:space="preserve">Unable to parse -maxuploadtarget: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1137">
+ <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source>
+ <context-group purpose="location"><context context-type="linenumber">327</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1138">
+ <source xml:space="preserve">Unable to unload the wallet before migrating</source>
+ <context-group purpose="location"><context context-type="linenumber">328</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1139">
+ <source xml:space="preserve">Unknown -blockfilterindex value %s.</source>
+ <context-group purpose="location"><context context-type="linenumber">329</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1140">
+ <source xml:space="preserve">Unknown address type &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">330</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1141">
+ <source xml:space="preserve">Unknown change type &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">331</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1142">
+ <source xml:space="preserve">Unknown network specified in -onlynet: &apos;%s&apos;</source>
+ <context-group purpose="location"><context context-type="linenumber">332</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1143">
+ <source xml:space="preserve">Unknown new rules activated (versionbit %i)</source>
+ <context-group purpose="location"><context context-type="linenumber">333</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1144">
+ <source xml:space="preserve">Unsupported global logging level -loglevel=%s. Valid values: %s.</source>
+ <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1145">
+ <source xml:space="preserve">Unsupported logging category %s=%s.</source>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1146">
+ <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source>
+ <context-group purpose="location"><context context-type="linenumber">336</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1147">
+ <source xml:space="preserve">Verifying blocks…</source>
+ <context-group purpose="location"><context context-type="linenumber">337</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1148">
+ <source xml:space="preserve">Verifying wallet(s)…</source>
+ <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1149">
<source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source>
- <context-group purpose="location"><context context-type="linenumber">295</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">339</context></context-group>
</trans-unit>
</group>
</body></file>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 2b6711ca40..7c91eac37e 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -91,7 +91,9 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->thirdPartyTxUrls->setVisible(false);
}
-#ifndef ENABLE_EXTERNAL_SIGNER
+#ifdef ENABLE_EXTERNAL_SIGNER
+ ui->externalSignerPath->setToolTip(ui->externalSignerPath->toolTip().arg(PACKAGE_NAME));
+#else
//: "External signing" means using devices such as hardware wallets.
ui->externalSignerPath->setToolTip(tr("Compiled without external signing support (required for external signing)"));
ui->externalSignerPath->setEnabled(false);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 295450d6b7..a07686ab2b 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1178,8 +1178,12 @@ void RPCConsole::updateDetailWidget()
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
- ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
- ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
+ if (stats->nodeStats.nVersion) {
+ ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
+ }
+ if (!stats->nodeStats.cleanSubVer.empty()) {
+ ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
+ }
ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /*prepend_direction=*/true));
ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
if (stats->nodeStats.m_permission_flags == NetPermissionFlags::None) {
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 4894cac905..331487b51d 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -17,7 +17,7 @@
#include <util/system.h>
#include <util/strencodings.h>
-#include <stdio.h>
+#include <cstdio>
#include <QCloseEvent>
#include <QLabel>
diff --git a/src/random.cpp b/src/random.cpp
index f92e679a00..eab54630b1 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -21,7 +21,7 @@
#include <util/time.h> // for GetTimeMicros()
#include <cmath>
-#include <stdlib.h>
+#include <cstdlib>
#include <thread>
#ifndef WIN32
diff --git a/src/rest.cpp b/src/rest.cpp
index 7f00db2222..a10d8a433f 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -590,6 +590,48 @@ static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std:
}
}
+
+RPCHelpMan getdeploymentinfo();
+
+static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
+{
+ if (!CheckWarmup(req)) return false;
+
+ std::string hash_str;
+ const RESTResponseFormat rf = ParseDataFormat(hash_str, str_uri_part);
+
+ switch (rf) {
+ case RESTResponseFormat::JSON: {
+ JSONRPCRequest jsonRequest;
+ jsonRequest.context = context;
+ jsonRequest.params = UniValue(UniValue::VARR);
+
+ if (!hash_str.empty()) {
+ uint256 hash;
+ if (!ParseHashStr(hash_str, hash)) {
+ return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str);
+ }
+
+ const ChainstateManager* chainman = GetChainman(context, req);
+ if (!chainman) return false;
+ if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(ParseHashV(hash_str, "blockhash")))) {
+ return RESTERR(req, HTTP_BAD_REQUEST, "Block not found");
+ }
+
+ jsonRequest.params.pushKV("blockhash", hash_str);
+ }
+
+ req->WriteHeader("Content-Type", "application/json");
+ req->WriteReply(HTTP_OK, getdeploymentinfo().HandleRequest(jsonRequest).write() + "\n");
+ return true;
+ }
+ default: {
+ return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
+ }
+ }
+
+}
+
static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
{
if (!CheckWarmup(req))
@@ -939,6 +981,8 @@ static const struct {
{"/rest/mempool/", rest_mempool},
{"/rest/headers/", rest_headers},
{"/rest/getutxos", rest_getutxos},
+ {"/rest/deploymentinfo/", rest_deploymentinfo},
+ {"/rest/deploymentinfo", rest_deploymentinfo},
{"/rest/blockhashbyheight/", rest_blockhash_by_height},
};
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f57915e805..bc1028aec3 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -459,6 +459,12 @@ static RPCHelpMan getblockfrompeer()
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
}
+ // Fetching blocks before the node has syncing past their height can prevent block files from
+ // being pruned, so we avoid it if the node is in prune mode.
+ if (node::fPruneMode && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "In prune mode, only blocks that the node has already synced previously can be fetched from a peer");
+ }
+
const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
if (block_has_data) {
throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
@@ -767,7 +773,7 @@ static RPCHelpMan pruneblockchain()
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
int heightParam = request.params[0].getInt<int>();
@@ -833,9 +839,9 @@ static std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::B
// Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
if (pindex) {
- return g_coin_stats_index->LookUpStats(pindex);
+ return g_coin_stats_index->LookUpStats(*pindex);
} else {
- CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
+ CBlockIndex& block_index = *CHECK_NONFATAL(WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock())));
return g_coin_stats_index->LookUpStats(block_index);
}
}
@@ -856,7 +862,7 @@ static RPCHelpMan gettxoutsetinfo()
"Note this call may take some time if you are not using coinstatsindex.\n",
{
{"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
- {"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).", "", {"", "string or numeric"}},
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).", RPCArgOptions{.type_str={"", "string or numeric"}}},
{"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
},
RPCResult{
@@ -908,7 +914,7 @@ static RPCHelpMan gettxoutsetinfo()
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
active_chainstate.ForceFlushStateToDisk();
CCoinsView* coins_view;
@@ -1048,7 +1054,7 @@ static RPCHelpMan gettxout()
fMempool = request.params[2].get_bool();
Coin coin;
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
if (fMempool) {
@@ -1105,7 +1111,7 @@ static RPCHelpMan verifychain()
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
return CVerifyDB().VerifyDB(
active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
},
@@ -1233,7 +1239,7 @@ RPCHelpMan getblockchaininfo()
const ArgsManager& args{EnsureAnyArgsman(request.context)};
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
const int height{tip.nHeight};
@@ -1307,7 +1313,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
}
} // anon namespace
-static RPCHelpMan getdeploymentinfo()
+RPCHelpMan getdeploymentinfo()
{
return RPCHelpMan{"getdeploymentinfo",
"Returns an object containing various state info regarding deployments of consensus changes.",
@@ -1328,7 +1334,7 @@ static RPCHelpMan getdeploymentinfo()
{
const ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- const CChainState& active_chainstate = chainman.ActiveChainstate();
+ const Chainstate& active_chainstate = chainman.ActiveChainstate();
const CBlockIndex* blockindex;
if (request.params[0].isNull()) {
@@ -1726,13 +1732,13 @@ static RPCHelpMan getblockstats()
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
"It won't work for some heights with pruning.\n",
{
- {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", RPCArgOptions{.type_str={"", "string or numeric"}}},
{"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
{
{"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
{"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
},
- "stats"},
+ RPCArgOptions{.oneline_description="stats"}},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2019,6 +2025,40 @@ public:
}
};
+static const auto scan_action_arg_desc = RPCArg{
+ "action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
+ "\"start\" for starting a scan\n"
+ "\"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ "\"status\" for progress report (in %) of the current scan"
+};
+
+static const auto scan_objects_arg_desc = RPCArg{
+ "scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
+ "Every scan object is either a string descriptor or an object:",
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
+ {
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
+ }},
+ },
+ RPCArgOptions{.oneline_description="[scanobjects,...]"},
+};
+
+static const auto scan_result_abort = RPCResult{
+ "when action=='abort'", RPCResult::Type::BOOL, "success",
+ "True if scan will be aborted (not necessarily before this RPC returns), or false if there is no scan to abort"
+};
+static const auto scan_result_status_none = RPCResult{
+ "when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""
+};
+static const auto scan_result_status_some = RPCResult{
+ "when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
+ {{RPCResult::Type::NUM, "progress", "Approximate percent complete"},}
+};
+
+
static RPCHelpMan scantxoutset()
{
// scriptPubKey corresponding to mainnet address 12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S
@@ -2038,21 +2078,8 @@ static RPCHelpMan scantxoutset()
"In the latter case, a range needs to be specified by below if different from 1000.\n"
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
{
- {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
- "\"start\" for starting a scan\n"
- "\"abort\" for aborting the current scan (returns true when abort was successful)\n"
- "\"status\" for progress report (in %) of the current scan"},
- {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
- "Every scan object is either a string descriptor or an object:",
- {
- {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
- {
- {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
- {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
- }},
- },
- "[scanobjects,...]"},
+ scan_action_arg_desc,
+ scan_objects_arg_desc,
},
{
RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
@@ -2069,17 +2096,15 @@ static RPCHelpMan scantxoutset()
{RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
{RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
+ {RPCResult::Type::BOOL, "coinbase", "Whether this is a coinbase output"},
{RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
}},
}},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
}},
- RPCResult{"when action=='abort'", RPCResult::Type::BOOL, "success", "True if scan will be aborted (not necessarily before this RPC returns), or false if there is no scan to abort"},
- RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
- }},
- RPCResult{"when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""},
+ scan_result_abort,
+ scan_result_status_some,
+ scan_result_status_none,
},
RPCExamples{
HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") +
@@ -2148,7 +2173,7 @@ static RPCHelpMan scantxoutset()
{
ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
active_chainstate.ForceFlushStateToDisk();
pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
@@ -2172,6 +2197,7 @@ static RPCHelpMan scantxoutset()
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
+ unspent.pushKV("coinbase", coin.IsCoinBase());
unspent.pushKV("height", (int32_t)coin.nHeight);
unspents.push_back(unspent);
@@ -2186,6 +2212,203 @@ static RPCHelpMan scantxoutset()
};
}
+/** RAII object to prevent concurrency issue when scanning blockfilters */
+static std::atomic<int> g_scanfilter_progress;
+static std::atomic<int> g_scanfilter_progress_height;
+static std::atomic<bool> g_scanfilter_in_progress;
+static std::atomic<bool> g_scanfilter_should_abort_scan;
+class BlockFiltersScanReserver
+{
+private:
+ bool m_could_reserve{false};
+public:
+ explicit BlockFiltersScanReserver() = default;
+
+ bool reserve() {
+ CHECK_NONFATAL(!m_could_reserve);
+ if (g_scanfilter_in_progress.exchange(true)) {
+ return false;
+ }
+ m_could_reserve = true;
+ return true;
+ }
+
+ ~BlockFiltersScanReserver() {
+ if (m_could_reserve) {
+ g_scanfilter_in_progress = false;
+ }
+ }
+};
+
+static RPCHelpMan scanblocks()
+{
+ return RPCHelpMan{"scanblocks",
+ "\nReturn relevant blockhashes for given descriptors.\n"
+ "This call may take several minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
+ {
+ scan_action_arg_desc,
+ scan_objects_arg_desc,
+ RPCArg{"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "Height to start to scan from"},
+ RPCArg{"stop_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"chain tip"}, "Height to stop to scan"},
+ RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"}
+ },
+ {
+ scan_result_status_none,
+ RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::NUM, "from_height", "The height we started the scan from"},
+ {RPCResult::Type::NUM, "to_height", "The height we ended the scan at"},
+ {RPCResult::Type::ARR, "relevant_blocks", "", {{RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"},}},
+ },
+ },
+ RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
+ {RPCResult::Type::NUM, "current_height", "Height of the block currently being scanned"},
+ },
+ },
+ scan_result_abort,
+ },
+ RPCExamples{
+ HelpExampleCli("scanblocks", "start '[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]' 300000") +
+ HelpExampleCli("scanblocks", "start '[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]' 100 150 basic") +
+ HelpExampleCli("scanblocks", "status") +
+ HelpExampleRpc("scanblocks", "\"start\", [\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"], 300000") +
+ HelpExampleRpc("scanblocks", "\"start\", [\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"], 100, 150, \"basic\"") +
+ HelpExampleRpc("scanblocks", "\"status\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ UniValue ret(UniValue::VOBJ);
+ if (request.params[0].get_str() == "status") {
+ BlockFiltersScanReserver reserver;
+ if (reserver.reserve()) {
+ // no scan in progress
+ return NullUniValue;
+ }
+ ret.pushKV("progress", g_scanfilter_progress.load());
+ ret.pushKV("current_height", g_scanfilter_progress_height.load());
+ return ret;
+ } else if (request.params[0].get_str() == "abort") {
+ BlockFiltersScanReserver reserver;
+ if (reserver.reserve()) {
+ // reserve was possible which means no scan was running
+ return false;
+ }
+ // set the abort flag
+ g_scanfilter_should_abort_scan = true;
+ return true;
+ }
+ else if (request.params[0].get_str() == "start") {
+ BlockFiltersScanReserver reserver;
+ if (!reserver.reserve()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
+ }
+ const std::string filtertype_name{request.params[4].isNull() ? "basic" : request.params[4].get_str()};
+
+ BlockFilterType filtertype;
+ if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
+ }
+
+ BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
+ if (!index) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
+ }
+
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
+
+ // set the start-height
+ const CBlockIndex* block = nullptr;
+ const CBlockIndex* stop_block = nullptr;
+ {
+ LOCK(cs_main);
+ CChain& active_chain = chainman.ActiveChain();
+ block = active_chain.Genesis();
+ stop_block = active_chain.Tip();
+ if (!request.params[2].isNull()) {
+ block = active_chain[request.params[2].getInt<int>()];
+ if (!block) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Invalid start_height");
+ }
+ }
+ if (!request.params[3].isNull()) {
+ stop_block = active_chain[request.params[3].getInt<int>()];
+ if (!stop_block || stop_block->nHeight < block->nHeight) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Invalid stop_height");
+ }
+ }
+ }
+ CHECK_NONFATAL(block);
+
+ // loop through the scan objects, add scripts to the needle_set
+ GCSFilter::ElementSet needle_set;
+ for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
+ FlatSigningProvider provider;
+ std::vector<CScript> scripts = EvalDescriptorStringOrObject(scanobject, provider);
+ for (const CScript& script : scripts) {
+ needle_set.emplace(script.begin(), script.end());
+ }
+ }
+ UniValue blocks(UniValue::VARR);
+ const int amount_per_chunk = 10000;
+ const CBlockIndex* start_index = block; // for remembering the start of a blockfilter range
+ std::vector<BlockFilter> filters;
+ const CBlockIndex* start_block = block; // for progress reporting
+ const int total_blocks_to_process = stop_block->nHeight - start_block->nHeight;
+
+ g_scanfilter_should_abort_scan = false;
+ g_scanfilter_progress = 0;
+ g_scanfilter_progress_height = start_block->nHeight;
+
+ while (block) {
+ node.rpc_interruption_point(); // allow a clean shutdown
+ if (g_scanfilter_should_abort_scan) {
+ LogPrintf("scanblocks RPC aborted at height %d.\n", block->nHeight);
+ break;
+ }
+ const CBlockIndex* next = nullptr;
+ {
+ LOCK(cs_main);
+ CChain& active_chain = chainman.ActiveChain();
+ next = active_chain.Next(block);
+ if (block == stop_block) next = nullptr;
+ }
+ if (start_index->nHeight + amount_per_chunk == block->nHeight || next == nullptr) {
+ LogPrint(BCLog::RPC, "Fetching blockfilters from height %d to height %d.\n", start_index->nHeight, block->nHeight);
+ if (index->LookupFilterRange(start_index->nHeight, block, filters)) {
+ for (const BlockFilter& filter : filters) {
+ // compare the elements-set with each filter
+ if (filter.GetFilter().MatchAny(needle_set)) {
+ blocks.push_back(filter.GetBlockHash().GetHex());
+ LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
+ }
+ }
+ }
+ start_index = block;
+
+ // update progress
+ int blocks_processed = block->nHeight - start_block->nHeight;
+ if (total_blocks_to_process > 0) { // avoid division by zero
+ g_scanfilter_progress = (int)(100.0 / total_blocks_to_process * blocks_processed);
+ } else {
+ g_scanfilter_progress = 100;
+ }
+ g_scanfilter_progress_height = block->nHeight;
+ }
+ block = next;
+ }
+ ret.pushKV("from_height", start_block->nHeight);
+ ret.pushKV("to_height", g_scanfilter_progress_height.load());
+ ret.pushKV("relevant_blocks", blocks);
+ }
+ else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
+ }
+ return ret;
+},
+ };
+}
+
static RPCHelpMan getblockfilter()
{
return RPCHelpMan{"getblockfilter",
@@ -2328,7 +2551,7 @@ static RPCHelpMan dumptxoutset()
UniValue CreateUTXOSnapshot(
NodeContext& node,
- CChainState& chainstate,
+ Chainstate& chainstate,
AutoFile& afile,
const fs::path& path,
const fs::path& temppath)
@@ -2421,6 +2644,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
{"blockchain", &verifychain},
{"blockchain", &preciousblock},
{"blockchain", &scantxoutset},
+ {"blockchain", &scanblocks},
{"blockchain", &getblockfilter},
{"hidden", &invalidateblock},
{"hidden", &reconsiderblock},
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index a332fd4892..6cdb5fa48b 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -20,7 +20,7 @@ extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
-class CChainState;
+class Chainstate;
class UniValue;
namespace node {
struct NodeContext;
@@ -54,7 +54,7 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
*/
UniValue CreateUTXOSnapshot(
node::NodeContext& node,
- CChainState& chainstate,
+ Chainstate& chainstate,
AutoFile& afile,
const fs::path& path,
const fs::path& tmppath);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 612dbbdacf..8688263ef5 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -83,6 +83,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 8, "fee_rate"},
{ "sendmany", 9, "verbose" },
{ "deriveaddresses", 1, "range" },
+ { "scanblocks", 1, "scanobjects" },
+ { "scanblocks", 2, "start_height" },
+ { "scanblocks", 3, "stop_height" },
{ "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
diff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp
index aa047bdea8..e50bf00473 100644
--- a/src/rpc/fees.cpp
+++ b/src/rpc/fees.cpp
@@ -64,7 +64,6 @@ static RPCHelpMan estimatesmartfee()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
const NodeContext& node = EnsureAnyNodeContext(request.context);
@@ -157,7 +156,6 @@ static RPCHelpMan estimaterawfee()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index db09a0c7b6..706d783942 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -168,7 +168,7 @@ static RPCHelpMan testmempoolaccept()
NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node);
ChainstateManager& chainman = EnsureChainman(node);
- CChainState& chainstate = chainman.ActiveChainstate();
+ Chainstate& chainstate = chainman.ActiveChainstate();
const PackageMempoolAcceptResult package_result = [&] {
LOCK(::cs_main);
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true);
@@ -449,9 +449,8 @@ static RPCHelpMan getmempoolancestors()
}
CTxMemPool::setEntries setAncestors;
- uint64_t noLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
- mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
+ mempool.CalculateMemPoolAncestors(*it, setAncestors, CTxMemPool::Limits::NoLimits(), dummy, false);
if (!fVerbose) {
UniValue o(UniValue::VARR);
@@ -605,8 +604,7 @@ static RPCHelpMan gettxspendingprevout()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- RPCTypeCheckArgument(request.params[0], UniValue::VARR);
- const UniValue& output_params = request.params[0];
+ const UniValue& output_params = request.params[0].get_array();
if (output_params.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
}
@@ -810,7 +808,7 @@ static RPCHelpMan submitpackage()
NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node);
- CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
+ Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false));
// First catch any errors.
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 1ad704a490..98383fdaca 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -524,7 +524,7 @@ static RPCHelpMan getblocktemplate()
{"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
}},
},
- "\"template_request\""},
+ RPCArgOptions{.oneline_description="\"template_request\""}},
},
{
RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""},
@@ -598,7 +598,7 @@ static RPCHelpMan getblocktemplate()
std::string strMode = "template";
UniValue lpval = NullUniValue;
std::set<std::string> setClientRules;
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
if (!request.params[0].isNull())
{
@@ -730,10 +730,10 @@ static RPCHelpMan getblocktemplate()
// Update block
static CBlockIndex* pindexPrev;
- static int64_t nStart;
+ static int64_t time_start;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
if (pindexPrev != active_chain.Tip() ||
- (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
+ (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = nullptr;
@@ -741,7 +741,7 @@ static RPCHelpMan getblocktemplate()
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrevNew = active_chain.Tip();
- nStart = GetTime();
+ time_start = GetTime();
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e221b3462d..8d7f4e7f5b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -114,7 +114,7 @@ static RPCHelpMan getpeerinfo()
{
{RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
}},
- {RPCResult::Type::BOOL, "relaytxes", /*optional=*/true, "Whether peer has asked us to relay transactions to it"},
+ {RPCResult::Type::BOOL, "relaytxes", /*optional=*/true, "Whether we relay transactions to this peer"},
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
{RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
@@ -123,9 +123,9 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
{RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
{RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
- {RPCResult::Type::NUM, "pingtime", /*optional=*/true, "ping time (if available)"},
- {RPCResult::Type::NUM, "minping", /*optional=*/true, "minimum observed ping time (if any at all)"},
- {RPCResult::Type::NUM, "pingwait", /*optional=*/true, "ping wait (if non-zero)"},
+ {RPCResult::Type::NUM, "pingtime", /*optional=*/true, "The last ping time in milliseconds (ms), if any"},
+ {RPCResult::Type::NUM, "minping", /*optional=*/true, "The minimum observed ping time in milliseconds (ms), if any"},
+ {RPCResult::Type::NUM, "pingwait", /*optional=*/true, "The duration in milliseconds (ms) of an outstanding ping (if non-zero)"},
{RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
@@ -146,7 +146,7 @@ static RPCHelpMan getpeerinfo()
{
{RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"},
}},
- {RPCResult::Type::NUM, "minfeefilter", /*optional=*/true, "The minimum fee rate for transactions this peer accepts"},
+ {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
{RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
{
{RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n"
@@ -200,6 +200,9 @@ static RPCHelpMan getpeerinfo()
ServiceFlags services{fStateStats ? statestats.their_services : ServiceFlags::NODE_NONE};
obj.pushKV("services", strprintf("%016x", services));
obj.pushKV("servicesnames", GetServicesNames(services));
+ if (fStateStats) {
+ obj.pushKV("relaytxes", statestats.m_relay_txs);
+ }
obj.pushKV("lastsend", count_seconds(stats.m_last_send));
obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));
@@ -235,8 +238,6 @@ static RPCHelpMan getpeerinfo()
heights.push_back(height);
}
obj.pushKV("inflight", heights);
- obj.pushKV("relaytxes", statestats.m_relay_txs);
- obj.pushKV("minfeefilter", ValueFromAmount(statestats.m_fee_filter_received));
obj.pushKV("addr_relay_enabled", statestats.m_addr_relay_enabled);
obj.pushKV("addr_processed", statestats.m_addr_processed);
obj.pushKV("addr_rate_limited", statestats.m_addr_rate_limited);
@@ -246,6 +247,7 @@ static RPCHelpMan getpeerinfo()
permissions.push_back(permission);
}
obj.pushKV("permissions", permissions);
+ obj.pushKV("minfeefilter", fStateStats ? ValueFromAmount(statestats.m_fee_filter_received) : 0);
UniValue sendPerMsgType(UniValue::VOBJ);
for (const auto& i : stats.mapSendBytesPerMsgType) {
@@ -947,7 +949,8 @@ static RPCHelpMan addpeeraddress()
bool success{false};
if (LookupHost(addr_string, net_addr, false)) {
- CAddress address{{net_addr, port}, ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
+ CService service{net_addr, port};
+ CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
address.nTime = Now<NodeSeconds>();
// The source address is set equal to the address. This is equivalent to the peer
// announcing itself.
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index 744f809814..a980c609e8 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -273,7 +273,7 @@ static RPCHelpMan deriveaddresses()
UniValue addresses(UniValue::VARR);
- for (int i = range_begin; i <= range_end; ++i) {
+ for (int64_t i = range_begin; i <= range_end; ++i) {
FlatSigningProvider provider;
std::vector<CScript> scripts;
if (!desc->Expand(i, key_provider, scripts, provider)) {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 7ffb499330..d654de1862 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -51,7 +51,7 @@ using node::GetTransaction;
using node::NodeContext;
using node::PSBTAnalysis;
-static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, CChainState& active_chainstate)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, Chainstate& active_chainstate)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
@@ -1241,13 +1241,9 @@ static RPCHelpMan decodepsbt()
}
// Taproot tree
- if (output.m_tap_tree.has_value()) {
+ if (!output.m_tap_tree.empty()) {
UniValue tree(UniValue::VARR);
- const auto& tuples = output.m_tap_tree->GetTreeTuples();
- for (const auto& tuple : tuples) {
- uint8_t depth = std::get<0>(tuple);
- uint8_t leaf_ver = std::get<1>(tuple);
- CScript script = std::get<2>(tuple);
+ for (const auto& [depth, leaf_ver, script] : output.m_tap_tree) {
UniValue elem(UniValue::VOBJ);
elem.pushKV("depth", (int)depth);
elem.pushKV("leaf_ver", (int)leaf_ver);
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index e9987d73be..1d7bd2eb94 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -21,8 +21,6 @@
#include <mutex>
#include <unordered_map>
-using SteadyClock = std::chrono::steady_clock;
-
static GlobalMutex g_rpc_warmup_mutex;
static std::atomic<bool> g_rpc_running{false};
static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex) = true;
@@ -170,7 +168,7 @@ static RPCHelpMan stop()
// to the client (intended for testing)
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
{
- {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /*hidden=*/true},
+ {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", RPCArgOptions{.hidden=true}},
},
RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
RPCExamples{""},
@@ -468,8 +466,7 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
{
- try
- {
+ try {
RPCCommandExecution execution(request.strMethod);
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
@@ -477,9 +474,9 @@ static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& req
} else {
return command.actor(request, result, last_handler);
}
- }
- catch (const std::exception& e)
- {
+ } catch (const UniValue::type_error& e) {
+ throw JSONRPCError(RPC_TYPE_ERROR, e.what());
+ } catch (const std::exception& e) {
throw JSONRPCError(RPC_MISC_ERROR, e.what());
}
}
diff --git a/src/rpc/txoutproof.cpp b/src/rpc/txoutproof.cpp
index dcf6c6bee1..cd8b49bfe1 100644
--- a/src/rpc/txoutproof.cpp
+++ b/src/rpc/txoutproof.cpp
@@ -66,7 +66,7 @@ static RPCHelpMan gettxoutproof()
}
} else {
LOCK(cs_main);
- CChainState& active_chainstate = chainman.ActiveChainstate();
+ Chainstate& active_chainstate = chainman.ActiveChainstate();
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) {
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 24ab21a947..3e98e89791 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -50,7 +50,8 @@ void RPCTypeCheck(const UniValue& params,
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
{
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
+ throw JSONRPCError(RPC_TYPE_ERROR,
+ strprintf("JSON value of type %s is not of expected type %s", uvTypeName(value.type()), uvTypeName(typeExpected.type)));
}
}
@@ -417,8 +418,8 @@ struct Sections {
case RPCArg::Type::BOOL: {
if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
- if (arg.m_type_str.size() != 0 && push_name) {
- left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0);
+ if (arg.m_opts.type_str.size() != 0 && push_name) {
+ left += "\"" + arg.GetName() + "\": " + arg.m_opts.type_str.at(0);
} else {
left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false);
}
@@ -617,7 +618,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
- if (arg.m_hidden) break; // Any arg that follows is also hidden
+ if (arg.m_opts.hidden) break; // Any arg that follows is also hidden
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@@ -638,7 +639,7 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
- if (arg.m_hidden) break; // Any arg that follows is also hidden
+ if (arg.m_opts.hidden) break; // Any arg that follows is also hidden
if (i == 0) ret += "\nArguments:\n";
@@ -703,8 +704,8 @@ std::string RPCArg::ToDescriptionString() const
{
std::string ret;
ret += "(";
- if (m_type_str.size() != 0) {
- ret += m_type_str.at(1);
+ if (m_opts.type_str.size() != 0) {
+ ret += m_opts.type_str.at(1);
} else {
switch (m_type) {
case Type::STR_HEX:
@@ -990,7 +991,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
std::string RPCArg::ToString(const bool oneline) const
{
- if (oneline && !m_oneline_description.empty()) return m_oneline_description;
+ if (oneline && !m_opts.oneline_description.empty()) return m_opts.oneline_description;
switch (m_type) {
case Type::STR_HEX:
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e883dc008e..9aa5df00b1 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -137,6 +137,12 @@ enum class OuterType {
NONE, // Only set on first recursion
};
+struct RPCArgOptions {
+ std::string oneline_description{}; //!< Should be empty unless it is supposed to override the auto-generated summary line
+ std::vector<std::string> type_str{}; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_opts.type_str.at(0) will override the type of the value in a key-value pair, m_opts.type_str.at(1) will override the type in the argument description.
+ bool hidden{false}; //!< For testing only
+};
+
struct RPCArg {
enum class Type {
OBJ,
@@ -169,30 +175,25 @@ struct RPCArg {
using DefaultHint = std::string;
using Default = UniValue;
using Fallback = std::variant<Optional, /* hint for default value */ DefaultHint, /* default constant value */ Default>;
+
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
- const bool m_hidden;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
const std::string m_description;
- const std::string m_oneline_description; //!< Should be empty unless it is supposed to override the auto-generated summary line
- const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description.
+ const RPCArgOptions m_opts;
RPCArg(
const std::string name,
const Type type,
const Fallback fallback,
const std::string description,
- const std::string oneline_description = "",
- const std::vector<std::string> type_str = {},
- const bool hidden = false)
+ RPCArgOptions opts = {})
: m_names{std::move(name)},
m_type{std::move(type)},
- m_hidden{hidden},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
- m_oneline_description{std::move(oneline_description)},
- m_type_str{std::move(type_str)}
+ m_opts{std::move(opts)}
{
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ && type != Type::OBJ_USER_KEYS);
}
@@ -203,16 +204,13 @@ struct RPCArg {
const Fallback fallback,
const std::string description,
const std::vector<RPCArg> inner,
- const std::string oneline_description = "",
- const std::vector<std::string> type_str = {})
+ RPCArgOptions opts = {})
: m_names{std::move(name)},
m_type{std::move(type)},
- m_hidden{false},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
- m_oneline_description{std::move(oneline_description)},
- m_type_str{std::move(type_str)}
+ m_opts{std::move(opts)}
{
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ || type == Type::OBJ_USER_KEYS);
}
@@ -227,7 +225,7 @@ struct RPCArg {
/**
* Return the type string of the argument.
- * Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description).
+ * Set oneline to allow it to be overridden by a custom oneline type string (m_opts.oneline_description).
*/
std::string ToString(bool oneline) const;
/**
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index f783d1dafc..6faf2624fd 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -13,8 +13,8 @@
#include <string>
#include <vector>
-#include <stdlib.h>
#include <assert.h>
+#include <cstdlib>
#include <policy/policy.h>
#include <primitives/transaction.h>
@@ -276,6 +276,8 @@ struct StackSize {
StackSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {};
};
+struct NoDupCheck {};
+
} // namespace internal
//! A node in a miniscript expression.
@@ -301,8 +303,13 @@ private:
const Type typ;
//! Cached script length (computed by CalcScriptLen).
const size_t scriptlen;
- //! Whether a public key appears more than once in this node.
- const bool duplicate_key;
+ //! Whether a public key appears more than once in this node. This value is initialized
+ //! by all constructors except the NoDupCheck ones. The NoDupCheck ones skip the
+ //! computation, requiring it to be done manually by invoking DuplicateKeyCheck().
+ //! DuplicateKeyCheck(), or a non-NoDupCheck constructor, will compute has_duplicate_keys
+ //! for all subnodes as well.
+ mutable std::optional<bool> has_duplicate_keys;
+
//! Compute the length of the script for this miniscript (including children).
size_t CalcScriptLen() const {
@@ -654,6 +661,7 @@ public:
return TreeEvalMaybe<std::string>(false, downfn, upfn);
}
+private:
internal::Ops CalcOps() const {
switch (fragment) {
case Fragment::JUST_1: return {0, 0, {}};
@@ -777,11 +785,14 @@ public:
assert(false);
}
- /** Check whether any key is repeated.
+public:
+ /** Update duplicate key information in this Node.
+ *
* This uses a custom key comparator provided by the context in order to still detect duplicates
* for more complicated types.
*/
- template<typename Ctx> bool ContainsDuplicateKey(const Ctx& ctx) const {
+ template<typename Ctx> void DuplicateKeyCheck(const Ctx& ctx) const
+ {
// We cannot use a lambda here, as lambdas are non assignable, and the set operations
// below require moving the comparators around.
struct Comp {
@@ -789,31 +800,55 @@ public:
Comp(const Ctx& ctx) : ctx_ptr(&ctx) {}
bool operator()(const Key& a, const Key& b) const { return ctx_ptr->KeyCompare(a, b); }
};
- using set = std::set<Key, Comp>;
- auto upfn = [this, &ctx](const Node& node, Span<set> subs) -> std::optional<set> {
- if (&node != this && node.duplicate_key) return {};
+ // state in the recursive computation:
+ // - std::nullopt means "this node has duplicates"
+ // - an std::set means "this node has no duplicate keys, and they are: ...".
+ using keyset = std::set<Key, Comp>;
+ using state = std::optional<keyset>;
+
+ auto upfn = [&ctx](const Node& node, Span<state> subs) -> state {
+ // If this node is already known to have duplicates, nothing left to do.
+ if (node.has_duplicate_keys.has_value() && *node.has_duplicate_keys) return {};
+
+ // Check if one of the children is already known to have duplicates.
+ for (auto& sub : subs) {
+ if (!sub.has_value()) {
+ node.has_duplicate_keys = true;
+ return {};
+ }
+ }
+ // Start building the set of keys involved in this node and children.
+ // Start by keys in this node directly.
size_t keys_count = node.keys.size();
- set key_set{node.keys.begin(), node.keys.end(), Comp(ctx)};
- if (key_set.size() != keys_count) return {};
+ keyset key_set{node.keys.begin(), node.keys.end(), Comp(ctx)};
+ if (key_set.size() != keys_count) {
+ // It already has duplicates; bail out.
+ node.has_duplicate_keys = true;
+ return {};
+ }
- for (auto& sub: subs) {
- keys_count += sub.size();
+ // Merge the keys from the children into this set.
+ for (auto& sub : subs) {
+ keys_count += sub->size();
// Small optimization: std::set::merge is linear in the size of the second arg but
// logarithmic in the size of the first.
- if (key_set.size() < sub.size()) std::swap(key_set, sub);
- key_set.merge(sub);
- if (key_set.size() != keys_count) return {};
+ if (key_set.size() < sub->size()) std::swap(key_set, *sub);
+ key_set.merge(*sub);
+ if (key_set.size() != keys_count) {
+ node.has_duplicate_keys = true;
+ return {};
+ }
}
+ node.has_duplicate_keys = false;
return key_set;
};
- return !TreeEvalMaybe<set>(upfn);
+ TreeEval<state>(upfn);
}
-public:
//! Return the size of the script for this expression (faster than ToScript().size()).
size_t ScriptSize() const { return scriptlen; }
@@ -858,7 +893,7 @@ public:
bool CheckTimeLocksMix() const { return GetType() << "k"_mst; }
//! Check whether there is no duplicate key across this fragment and all its sub-fragments.
- bool CheckDuplicateKey() const { return !duplicate_key; }
+ bool CheckDuplicateKey() const { return has_duplicate_keys && !*has_duplicate_keys; }
//! Whether successful non-malleable satisfactions are guaranteed to be valid.
bool ValidSatisfactions() const { return IsValid() && CheckOpsLimit() && CheckStackSize(); }
@@ -872,13 +907,21 @@ public:
//! Equality testing.
bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; }
- // Constructors with various argument combinations.
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
- template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ // Constructors with various argument combinations, which bypass the duplicate key check.
+ Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+
+ // Constructors with various argument combinations, which do perform the duplicate key check.
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); }
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(arg), val) { DuplicateKeyCheck(ctx);}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(key), val) { DuplicateKeyCheck(ctx); }
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(key), val) { DuplicateKeyCheck(ctx); }
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), val) { DuplicateKeyCheck(ctx); }
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, val) { DuplicateKeyCheck(ctx); }
};
namespace internal {
@@ -965,15 +1008,15 @@ std::optional<std::pair<std::vector<unsigned char>, int>> ParseHexStrEnd(Span<co
}
/** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */
-template<typename Key, typename Ctx>
-void BuildBack(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
+template<typename Key>
+void BuildBack(Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
{
NodeRef<Key> child = std::move(constructed.back());
constructed.pop_back();
if (reverse) {
- constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(child), std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, nt, Vector(std::move(child), std::move(constructed.back())));
} else {
- constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(constructed.back()), std::move(child)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, nt, Vector(std::move(constructed.back()), std::move(child)));
}
}
@@ -987,6 +1030,18 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
{
using namespace spanparsing;
+ // Account for the minimum script size for all parsed fragments so far. It "borrows" 1
+ // script byte from all leaf nodes, counting it instead whenever a space for a recursive
+ // expression is added (through andor, and_*, or_*, thresh). This guarantees that all fragments
+ // increment the script_size by at least one, except for:
+ // - "0", "1": these leafs are only a single byte, so their subtracted-from increment is 0.
+ // This is not an issue however, as "space" for them has to be created by combinators,
+ // which do increment script_size.
+ // - "v:": the v wrapper adds nothing as in some cases it results in no opcode being added
+ // (instead transforming another opcode into its VERIFY form). However, the v: wrapper has
+ // to be interleaved with other fragments to be valid, so this is not a concern.
+ size_t script_size{1};
+
// The two integers are used to hold state for thresh()
std::vector<std::tuple<ParseContext, int64_t, int64_t>> to_parse;
std::vector<NodeRef<Key>> constructed;
@@ -994,14 +1049,16 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
while (!to_parse.empty()) {
+ if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {};
+
// Get the current context we are decoding within
auto [cur_context, n, k] = to_parse.back();
to_parse.pop_back();
switch (cur_context) {
case ParseContext::WRAPPED_EXPR: {
- int colon_index = -1;
- for (int i = 1; i < (int)in.size(); ++i) {
+ std::optional<size_t> colon_index{};
+ for (size_t i = 1; i < in.size(); ++i) {
if (in[i] == ':') {
colon_index = i;
break;
@@ -1009,106 +1066,131 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
if (in[i] < 'a' || in[i] > 'z') break;
}
// If there is no colon, this loop won't execute
- for (int j = 0; j < colon_index; ++j) {
+ bool last_was_v{false};
+ for (size_t j = 0; colon_index && j < *colon_index; ++j) {
+ if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {};
if (in[j] == 'a') {
+ script_size += 2;
to_parse.emplace_back(ParseContext::ALT, -1, -1);
} else if (in[j] == 's') {
+ script_size += 1;
to_parse.emplace_back(ParseContext::SWAP, -1, -1);
} else if (in[j] == 'c') {
+ script_size += 1;
to_parse.emplace_back(ParseContext::CHECK, -1, -1);
} else if (in[j] == 'd') {
+ script_size += 3;
to_parse.emplace_back(ParseContext::DUP_IF, -1, -1);
} else if (in[j] == 'j') {
+ script_size += 4;
to_parse.emplace_back(ParseContext::NON_ZERO, -1, -1);
} else if (in[j] == 'n') {
+ script_size += 1;
to_parse.emplace_back(ParseContext::ZERO_NOTEQUAL, -1, -1);
} else if (in[j] == 'v') {
+ // do not permit "...vv...:"; it's not valid, and also doesn't trigger early
+ // failure as script_size isn't incremented.
+ if (last_was_v) return {};
to_parse.emplace_back(ParseContext::VERIFY, -1, -1);
} else if (in[j] == 'u') {
+ script_size += 4;
to_parse.emplace_back(ParseContext::WRAP_U, -1, -1);
} else if (in[j] == 't') {
+ script_size += 1;
to_parse.emplace_back(ParseContext::WRAP_T, -1, -1);
} else if (in[j] == 'l') {
// The l: wrapper is equivalent to or_i(0,X)
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
+ script_size += 4;
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0));
to_parse.emplace_back(ParseContext::OR_I, -1, -1);
} else {
return {};
}
+ last_was_v = (in[j] == 'v');
}
to_parse.emplace_back(ParseContext::EXPR, -1, -1);
- in = in.subspan(colon_index + 1);
+ if (colon_index) in = in.subspan(*colon_index + 1);
break;
}
case ParseContext::EXPR: {
if (Const("0", in)) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0));
} else if (Const("1", in)) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1));
} else if (Const("pk(", in)) {
auto res = ParseKeyEnd<Key, Ctx>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
+ script_size += 34;
} else if (Const("pkh(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
+ script_size += 24;
} else if (Const("pk_k(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(key))));
in = in.subspan(key_size + 1);
+ script_size += 33;
} else if (Const("pk_h(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(key))));
in = in.subspan(key_size + 1);
+ script_size += 23;
} else if (Const("sha256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::SHA256, std::move(hash)));
in = in.subspan(hash_size + 1);
+ script_size += 38;
} else if (Const("ripemd160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::RIPEMD160, std::move(hash)));
in = in.subspan(hash_size + 1);
+ script_size += 26;
} else if (Const("hash256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH256, std::move(hash)));
in = in.subspan(hash_size + 1);
+ script_size += 38;
} else if (Const("hash160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH160, std::move(hash)));
in = in.subspan(hash_size + 1);
+ script_size += 26;
} else if (Const("after(", in)) {
int arg_size = FindNextChar(in, ')');
if (arg_size < 1) return {};
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, num));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AFTER, num));
in = in.subspan(arg_size + 1);
+ script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff);
} else if (Const("older(", in)) {
int arg_size = FindNextChar(in, ')');
if (arg_size < 1) return {};
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, num));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OLDER, num));
in = in.subspan(arg_size + 1);
+ script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff);
} else if (Const("multi(", in)) {
// Get threshold
int next_comma = FindNextChar(in, ',');
@@ -1128,7 +1210,8 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
}
if (keys.size() < 1 || keys.size() > 20) return {};
if (k < 1 || k > (int64_t)keys.size()) return {};
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), k));
+ script_size += 2 + (keys.size() > 16) + (k > 16) + 34 * keys.size();
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::MULTI, std::move(keys), k));
} else if (Const("thresh(", in)) {
int next_comma = FindNextChar(in, ',');
if (next_comma < 1) return {};
@@ -1138,6 +1221,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
// n = 1 here because we read the first WRAPPED_EXPR before reaching THRESH
to_parse.emplace_back(ParseContext::THRESH, 1, k);
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
+ script_size += 2 + (k > 16) + (k > 0x7f) + (k > 0x7fff) + (k > 0x7fffff);
} else if (Const("andor(", in)) {
to_parse.emplace_back(ParseContext::ANDOR, -1, -1);
to_parse.emplace_back(ParseContext::CLOSE_BRACKET, -1, -1);
@@ -1146,21 +1230,29 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
to_parse.emplace_back(ParseContext::COMMA, -1, -1);
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
+ script_size += 5;
} else {
if (Const("and_n(", in)) {
to_parse.emplace_back(ParseContext::AND_N, -1, -1);
+ script_size += 5;
} else if (Const("and_b(", in)) {
to_parse.emplace_back(ParseContext::AND_B, -1, -1);
+ script_size += 2;
} else if (Const("and_v(", in)) {
to_parse.emplace_back(ParseContext::AND_V, -1, -1);
+ script_size += 1;
} else if (Const("or_b(", in)) {
to_parse.emplace_back(ParseContext::OR_B, -1, -1);
+ script_size += 2;
} else if (Const("or_c(", in)) {
to_parse.emplace_back(ParseContext::OR_C, -1, -1);
+ script_size += 3;
} else if (Const("or_d(", in)) {
to_parse.emplace_back(ParseContext::OR_D, -1, -1);
+ script_size += 4;
} else if (Const("or_i(", in)) {
to_parse.emplace_back(ParseContext::OR_I, -1, -1);
+ script_size += 4;
} else {
return {};
}
@@ -1172,69 +1264,70 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
break;
}
case ParseContext::ALT: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case ParseContext::SWAP: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case ParseContext::CHECK: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case ParseContext::DUP_IF: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case ParseContext::NON_ZERO: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case ParseContext::ZERO_NOTEQUAL: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case ParseContext::VERIFY: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ script_size += (constructed.back()->GetType() << "x"_mst);
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case ParseContext::WRAP_U: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0)));
break;
}
case ParseContext::WRAP_T: {
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_1)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1)));
break;
}
case ParseContext::AND_B: {
- BuildBack(ctx, Fragment::AND_B, constructed);
+ BuildBack(Fragment::AND_B, constructed);
break;
}
case ParseContext::AND_N: {
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
break;
}
case ParseContext::AND_V: {
- BuildBack(ctx, Fragment::AND_V, constructed);
+ BuildBack(Fragment::AND_V, constructed);
break;
}
case ParseContext::OR_B: {
- BuildBack(ctx, Fragment::OR_B, constructed);
+ BuildBack(Fragment::OR_B, constructed);
break;
}
case ParseContext::OR_C: {
- BuildBack(ctx, Fragment::OR_C, constructed);
+ BuildBack(Fragment::OR_C, constructed);
break;
}
case ParseContext::OR_D: {
- BuildBack(ctx, Fragment::OR_D, constructed);
+ BuildBack(Fragment::OR_D, constructed);
break;
}
case ParseContext::OR_I: {
- BuildBack(ctx, Fragment::OR_I, constructed);
+ BuildBack(Fragment::OR_I, constructed);
break;
}
case ParseContext::ANDOR: {
@@ -1242,7 +1335,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
break;
}
case ParseContext::THRESH: {
@@ -1251,6 +1344,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
in = in.subspan(1);
to_parse.emplace_back(ParseContext::THRESH, n+1, k);
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
+ script_size += 2;
} else if (in[0] == ')') {
if (k > n) return {};
in = in.subspan(1);
@@ -1261,7 +1355,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
}
std::reverse(subs.begin(), subs.end());
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::THRESH, std::move(subs), k));
} else {
return {};
}
@@ -1282,8 +1376,11 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
// Sanity checks on the produced miniscript
assert(constructed.size() == 1);
+ assert(constructed[0]->ScriptSize() == script_size);
if (in.size() > 0) return {};
- return std::move(constructed.front());
+ const NodeRef<Key> tl_node = std::move(constructed.front());
+ tl_node->DuplicateKeyCheck(ctx);
+ return tl_node;
}
/** Decode a script into opcode/push pairs.
@@ -1394,12 +1491,12 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
// Constants
if (in[0].first == OP_1) {
++in;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_1));
break;
}
if (in[0].first == OP_0) {
++in;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::JUST_0));
break;
}
// Public keys
@@ -1407,14 +1504,14 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end());
if (!key) return {};
++in;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(*key))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_K, Vector(std::move(*key))));
break;
}
if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) {
auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end());
if (!key) return {};
in += 5;
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(*key))));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::PK_H, Vector(std::move(*key))));
break;
}
// Time locks
@@ -1422,31 +1519,31 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
if (*num < 1 || *num > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, *num));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::OLDER, *num));
break;
}
if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
if (num < 1 || num > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, *num));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::AFTER, *num));
break;
}
// Hashes
if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && (num = ParseScriptNumber(in[5])) && num == 32 && in[6].first == OP_SIZE) {
if (in[2].first == OP_SHA256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::SHA256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_RIPEMD160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::RIPEMD160, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::HASH160, in[1].second));
in += 7;
break;
}
@@ -1467,7 +1564,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
if (!k || *k < 1 || *k > *n) return {};
in += 3 + *n;
std::reverse(keys.begin(), keys.end());
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), *k));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::MULTI, std::move(keys), *k));
break;
}
/** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather
@@ -1562,63 +1659,63 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
case DecodeContext::SWAP: {
if (in >= last || in[0].first != OP_SWAP || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ALT: {
if (in >= last || in[0].first != OP_TOALTSTACK || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::CHECK: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::DUP_IF: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::VERIFY: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::NON_ZERO: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ZERO_NOTEQUAL: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::AND_V: {
if (constructed.size() < 2) return {};
- BuildBack(ctx, Fragment::AND_V, constructed, /*reverse=*/true);
+ BuildBack(Fragment::AND_V, constructed, /*reverse=*/true);
break;
}
case DecodeContext::AND_B: {
if (constructed.size() < 2) return {};
- BuildBack(ctx, Fragment::AND_B, constructed, /*reverse=*/true);
+ BuildBack(Fragment::AND_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_B: {
if (constructed.size() < 2) return {};
- BuildBack(ctx, Fragment::OR_B, constructed, /*reverse=*/true);
+ BuildBack(Fragment::OR_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_C: {
if (constructed.size() < 2) return {};
- BuildBack(ctx, Fragment::OR_C, constructed, /*reverse=*/true);
+ BuildBack(Fragment::OR_C, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_D: {
if (constructed.size() < 2) return {};
- BuildBack(ctx, Fragment::OR_D, constructed, /*reverse=*/true);
+ BuildBack(Fragment::OR_D, constructed, /*reverse=*/true);
break;
}
case DecodeContext::ANDOR: {
@@ -1628,7 +1725,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
NodeRef<Key> right = std::move(constructed.back());
constructed.pop_back();
NodeRef<Key> mid = std::move(constructed.back());
- constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
break;
}
case DecodeContext::THRESH_W: {
@@ -1652,7 +1749,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
constructed.pop_back();
subs.push_back(std::move(sub));
}
- constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, Fragment::THRESH, std::move(subs), k));
break;
}
case DecodeContext::ENDIF: {
@@ -1702,7 +1799,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
if (in >= last) return {};
if (in[0].first == OP_IF) {
++in;
- BuildBack(ctx, Fragment::OR_I, constructed, /*reverse=*/true);
+ BuildBack(Fragment::OR_I, constructed, /*reverse=*/true);
} else if (in[0].first == OP_NOTIF) {
++in;
to_parse.emplace_back(DecodeContext::ANDOR, -1, -1);
@@ -1717,6 +1814,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
}
if (constructed.size() != 1) return {};
const NodeRef<Key> tl_node = std::move(constructed.front());
+ tl_node->DuplicateKeyCheck(ctx);
// Note that due to how ComputeType works (only assign the type to the node if the
// subs' types are valid) this would fail if any node of tree is badly typed.
if (!tl_node->IsValidTopLevel()) return {};
@@ -1733,6 +1831,8 @@ inline NodeRef<typename Ctx::Key> FromString(const std::string& str, const Ctx&
template<typename Ctx>
inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) {
using namespace internal;
+ // A too large Script is necessarily invalid, don't bother parsing it.
+ if (script.size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {};
auto decomposed = DecomposeScript(script);
if (!decomposed) return {};
auto it = decomposed->begin();
diff --git a/src/script/standard.h b/src/script/standard.h
index 1e6769782a..966a52b2c7 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -315,6 +315,8 @@ public:
TaprootSpendData GetSpendData() const;
/** Returns a vector of tuples representing the depth, leaf version, and script */
std::vector<std::tuple<uint8_t, uint8_t, CScript>> GetTreeTuples() const;
+ /** Returns true if there are any tapscripts */
+ bool HasScripts() const { return !m_branch.empty(); }
};
/** Given a TaprootSpendData and the output key, reconstruct its script tree.
diff --git a/src/streams.h b/src/streams.h
index f14d347380..0178df1c49 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -13,11 +13,11 @@
#include <algorithm>
#include <assert.h>
+#include <cstdio>
#include <ios>
#include <limits>
#include <optional>
#include <stdint.h>
-#include <stdio.h>
#include <string.h>
#include <string>
#include <utility>
@@ -269,7 +269,6 @@ public:
// Stream subset
//
bool eof() const { return size() == 0; }
- CDataStream* rdbuf() { return this; }
int in_avail() const { return size(); }
void SetType(int n) { nType = n; }
@@ -488,12 +487,14 @@ public:
AutoFile(const AutoFile&) = delete;
AutoFile& operator=(const AutoFile&) = delete;
- void fclose()
+ int fclose()
{
+ int retval{0};
if (file) {
- ::fclose(file);
+ retval = ::fclose(file);
file = nullptr;
}
+ return retval;
}
/** Get wrapped FILE* with transfer of ownership.
diff --git a/src/support/cleanse.h b/src/support/cleanse.h
index 8c1210a114..b1227770c7 100644
--- a/src/support/cleanse.h
+++ b/src/support/cleanse.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_SUPPORT_CLEANSE_H
#define BITCOIN_SUPPORT_CLEANSE_H
-#include <stdlib.h>
+#include <cstdlib>
/** Secure overwrite a buffer (possibly containing secret data) with zero-bytes. The write
* operation will not be optimized out by the compiler. */
diff --git a/src/sync.h b/src/sync.h
index 7ec4b668ac..1f4e191214 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -111,7 +111,7 @@ public:
return PARENT::try_lock();
}
- using UniqueLock = std::unique_lock<PARENT>;
+ using unique_lock = std::unique_lock<PARENT>;
#ifdef __clang__
//! For negative capabilities in the Clang Thread Safety Analysis.
//! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
@@ -147,11 +147,13 @@ inline void AssertLockNotHeldInline(const char* name, const char* file, int line
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, GlobalMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
-/** Wrapper around std::unique_lock style lock for Mutex. */
-template <typename Mutex, typename Base = typename Mutex::UniqueLock>
-class SCOPED_LOCKABLE UniqueLock : public Base
+/** Wrapper around std::unique_lock style lock for MutexType. */
+template <typename MutexType>
+class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
{
private:
+ using Base = typename MutexType::unique_lock;
+
void Enter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex());
@@ -165,15 +167,15 @@ private:
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
- Base::try_lock();
- if (!Base::owns_lock()) {
- LeaveCritical();
+ if (Base::try_lock()) {
+ return true;
}
- return Base::owns_lock();
+ LeaveCritical();
+ return false;
}
public:
- UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
+ UniqueLock(MutexType& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
{
if (fTry)
TryEnter(pszName, pszFile, nLine);
@@ -181,7 +183,7 @@ public:
Enter(pszName, pszFile, nLine);
}
- UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
+ UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
{
if (!pmutexIn) return;
@@ -241,29 +243,24 @@ public:
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
-template<typename MutexArg>
-using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
-
// When locking a Mutex, require negative capability to ensure the lock
// is not already held
inline Mutex& MaybeCheckNotHeld(Mutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
inline Mutex* MaybeCheckNotHeld(Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
-// When locking a GlobalMutex, just check it is not locked in the surrounding scope
-inline GlobalMutex& MaybeCheckNotHeld(GlobalMutex& cs) LOCKS_EXCLUDED(cs) LOCK_RETURNED(cs) { return cs; }
-inline GlobalMutex* MaybeCheckNotHeld(GlobalMutex* cs) LOCKS_EXCLUDED(cs) LOCK_RETURNED(cs) { return cs; }
-
-// When locking a RecursiveMutex, it's okay to already hold the lock
-// but check that it is not known to be locked in the surrounding scope anyway
-inline RecursiveMutex& MaybeCheckNotHeld(RecursiveMutex& cs) LOCKS_EXCLUDED(cs) LOCK_RETURNED(cs) { return cs; }
-inline RecursiveMutex* MaybeCheckNotHeld(RecursiveMutex* cs) LOCKS_EXCLUDED(cs) LOCK_RETURNED(cs) { return cs; }
+// When locking a GlobalMutex or RecursiveMutex, just check it is not
+// locked in the surrounding scope.
+template <typename MutexType>
+inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
+template <typename MutexType>
+inline MutexType* MaybeCheckNotHeld(MutexType* m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
-#define LOCK(cs) DebugLock<decltype(cs)> UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
+#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
#define LOCK2(cs1, cs2) \
- DebugLock<decltype(cs1)> criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
- DebugLock<decltype(cs2)> criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__);
-#define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
-#define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
+ UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
+ UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__)
+#define TRY_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
+#define WAIT_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
#define ENTER_CRITICAL_SECTION(cs) \
{ \
diff --git a/src/test/banman_tests.cpp b/src/test/banman_tests.cpp
index 27ce9ad638..ecf60834ce 100644
--- a/src/test/banman_tests.cpp
+++ b/src/test/banman_tests.cpp
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(file)
" { \"version\": 1, \"ban_created\": 0, \"banned_until\": 778, \"address\": \"1.0.0.0/8\" }"
"] }",
};
- assert(WriteBinaryFile(banlist_path + ".json", entries_write));
+ BOOST_REQUIRE(WriteBinaryFile(banlist_path + ".json", entries_write));
{
// The invalid entries will be dropped, but the valid one remains
ASSERT_DEBUG_LOG("Dropping entry with unparseable address or subnet (aaaaaaaaa) from ban list");
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(file)
BanMan banman{banlist_path, /*client_interface=*/nullptr, /*default_ban_time=*/0};
banmap_t entries_read;
banman.GetBanned(entries_read);
- assert(entries_read.size() == 1);
+ BOOST_CHECK_EQUAL(entries_read.size(), 1);
}
}
}
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index c8e8cdeeb3..0157e25071 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -4,13 +4,13 @@
#include <boost/test/unit_test.hpp>
-#include <stdlib.h>
-
#include <chain.h>
#include <rpc/blockchain.h>
#include <test/util/setup_common.h>
#include <util/string.h>
+#include <cstdlib>
+
/* Equality between doubles is imprecise. Comparison should be done
* with a small threshold of tolerance, rather than exact equality.
*/
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 78b82b9b20..1c13c0a909 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -7,6 +7,7 @@
#include <consensus/merkle.h>
#include <pow.h>
#include <streams.h>
+#include <test/util/txmempool.h>
#include <test/util/setup_common.h>
diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp
new file mode 100644
index 0000000000..dd7c32cc46
--- /dev/null
+++ b/src/test/blockmanager_tests.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <node/blockstorage.h>
+#include <node/context.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+#include <test/util/setup_common.h>
+
+using node::BlockManager;
+using node::BLOCK_SERIALIZATION_HEADER_SIZE;
+
+// use BasicTestingSetup here for the data directory configuration, setup, and cleanup
+BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
+{
+ const auto params {CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN)};
+ BlockManager blockman {};
+ CChain chain {};
+ // simulate adding a genesis block normally
+ BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, *params, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
+ // simulate what happens during reindex
+ // simulate a well-formed genesis block being found at offset 8 in the blk00000.dat file
+ // the block is found at offset 8 because there is an 8 byte serialization header
+ // consisting of 4 magic bytes + 4 length bytes before each block in a well-formed blk file.
+ FlatFilePos pos{0, BLOCK_SERIALIZATION_HEADER_SIZE};
+ BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, *params, &pos).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
+ // now simulate what happens after reindex for the first new block processed
+ // the actual block contents don't matter, just that it's a block.
+ // verify that the write position is at offset 0x12d.
+ // this is a check to make sure that https://github.com/bitcoin/bitcoin/issues/21379 does not recur
+ // 8 bytes (for serialization header) + 285 (for serialized genesis block) = 293
+ // add another 8 bytes for the second block's serialization header and we get 293 + 8 = 301
+ FlatFilePos actual{blockman.SaveBlockToDisk(params->GenesisBlock(), 1, chain, *params, nullptr)};
+ BOOST_CHECK_EQUAL(actual.nPos, BLOCK_SERIALIZATION_HEADER_SIZE + ::GetSerializeSize(params->GenesisBlock(), CLIENT_VERSION) + BLOCK_SERIALIZATION_HEADER_SIZE);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 132c4e53e7..8a2b0792fd 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -5,6 +5,7 @@
#include <chainparams.h>
#include <index/coinstatsindex.h>
#include <interfaces/chain.h>
+#include <kernel/coinstats.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
#include <util/time.h>
@@ -38,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
}
// CoinStatsIndex should not be found before it is started.
- BOOST_CHECK(!coin_stats_index.LookUpStats(block_index));
+ BOOST_CHECK(!coin_stats_index.LookUpStats(*block_index));
// BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
// is started.
@@ -54,10 +55,10 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
LOCK(cs_main);
genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
- BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index));
+ BOOST_CHECK(coin_stats_index.LookUpStats(*genesis_block_index));
// Check that CoinStatsIndex updates with new blocks.
- BOOST_CHECK(coin_stats_index.LookUpStats(block_index));
+ BOOST_CHECK(coin_stats_index.LookUpStats(*block_index));
const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
std::vector<CMutableTransaction> noTxns;
@@ -71,21 +72,27 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
LOCK(cs_main);
new_block_index = m_node.chainman->ActiveChain().Tip();
}
- BOOST_CHECK(coin_stats_index.LookUpStats(new_block_index));
+ BOOST_CHECK(coin_stats_index.LookUpStats(*new_block_index));
BOOST_CHECK(block_index != new_block_index);
+ // It is not safe to stop and destroy the index until it finishes handling
+ // the last BlockConnected notification. The BlockUntilSyncedToCurrentChain()
+ // call above is sufficient to ensure this, but the
+ // SyncWithValidationInterfaceQueue() call below is also needed to ensure
+ // TSAN always sees the test thread waiting for the notification thread, and
+ // avoid potential false positive reports.
+ SyncWithValidationInterfaceQueue();
+
// Shutdown sequence (c.f. Shutdown() in init.cpp)
coin_stats_index.Stop();
-
- // Rest of shutdown sequence and destructors happen in ~TestingSetup()
}
// Test shutdown between BlockConnected and ChainStateFlushed notifications,
// make sure index is not corrupted and is able to reload.
BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
{
- CChainState& chainstate = Assert(m_node.chainman)->ActiveChainstate();
+ Chainstate& chainstate = Assert(m_node.chainman)->ActiveChainstate();
const CChainParams& params = Params();
{
CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20};
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 7889156d51..7150698e64 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -45,6 +45,8 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
// Disable inactivity checks for this test to avoid interference
connman.SetPeerConnectTimeout(99999s);
@@ -80,10 +82,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
}
// Test starts here
- {
- LOCK(dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
- }
+ BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
+
{
LOCK(dummyNode1.cs_vSend);
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
@@ -93,20 +93,14 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
int64_t nStartTime = GetTime();
// Wait 21 minutes
SetMockTime(nStartTime+21*60);
- {
- LOCK(dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
- }
+ BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in getheaders
{
LOCK(dummyNode1.cs_vSend);
BOOST_CHECK(dummyNode1.vSendMsg.size() > 0);
}
// Wait 3 more minutes
SetMockTime(nStartTime+24*60);
- {
- LOCK(dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in disconnect
- }
+ BOOST_CHECK(peerman.SendMessages(&dummyNode1)); // should result in disconnect
BOOST_CHECK(dummyNode1.fDisconnect == true);
peerman.FinalizeNode(dummyNode1);
@@ -274,6 +268,8 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
@@ -308,10 +304,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[0]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[0]);
peerLogic->UnitTestMisbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
- {
- LOCK(nodes[0]->cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
- }
+ BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
+
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(!banman->IsDiscouraged(other_addr)); // Different address, not discouraged
@@ -330,10 +324,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[1]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[1]);
peerLogic->UnitTestMisbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1);
- {
- LOCK(nodes[1]->cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
- }
+ BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
// [0] is still discouraged/disconnected.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
@@ -341,10 +332,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
BOOST_CHECK(!nodes[1]->fDisconnect);
peerLogic->UnitTestMisbehaving(nodes[1]->GetId(), 1); // [1] reaches discouragement threshold
- {
- LOCK(nodes[1]->cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
- }
+ BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
// Expect both [0] and [1] to be discouraged/disconnected now.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
@@ -367,10 +355,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[2]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[2]);
peerLogic->UnitTestMisbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD);
- {
- LOCK(nodes[2]->cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
- }
+ BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
BOOST_CHECK(banman->IsDiscouraged(addr[2]));
@@ -386,6 +371,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
@@ -411,10 +398,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
dummyNode.fSuccessfullyConnected = true;
peerLogic->UnitTestMisbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
- {
- LOCK(dummyNode.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
- }
+ BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
BOOST_CHECK(banman->IsDiscouraged(addr));
peerLogic->FinalizeNode(dummyNode);
diff --git a/src/test/fuzz/bitdeque.cpp b/src/test/fuzz/bitdeque.cpp
index 01af8320b5..634a3de346 100644
--- a/src/test/fuzz/bitdeque.cpp
+++ b/src/test/fuzz/bitdeque.cpp
@@ -2,11 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util/bitdeque.h>
-
#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/util.h>
+#include <util/bitdeque.h>
#include <deque>
#include <vector>
@@ -54,7 +53,8 @@ FUZZ_TARGET_INIT(bitdeque, InitRandData)
--initlen;
}
- while (provider.remaining_bytes()) {
+ LIMITED_WHILE(provider.remaining_bytes() > 0, 900)
+ {
{
assert(deq.size() == bitdeq.size());
auto it = deq.begin();
@@ -538,5 +538,4 @@ FUZZ_TARGET_INIT(bitdeque, InitRandData)
}
);
}
-
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index c52fca5fe8..f05248ab47 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -12,6 +12,7 @@
#include <key_io.h>
#include <memusage.h>
#include <netbase.h>
+#include <policy/policy.h>
#include <policy/settings.h>
#include <pow.h>
#include <protocol.h>
diff --git a/src/test/fuzz/mempool_utils.h b/src/test/fuzz/mempool_utils.h
deleted file mode 100644
index bfe12e30ba..0000000000
--- a/src/test/fuzz/mempool_utils.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2022 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
-#define BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
-
-#include <validation.h>
-
-class DummyChainState final : public CChainState
-{
-public:
- void SetMempool(CTxMemPool* mempool)
- {
- m_mempool = mempool;
- }
-};
-
-#endif // BITCOIN_TEST_FUZZ_MEMPOOL_UTILS_H
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index 637ba503c6..a3d57dbdd5 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -8,6 +8,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/fuzz/util/mempool.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index 507ce57ec0..eba03da773 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -114,7 +114,7 @@ FUZZ_TARGET_INIT(pow_transition, initialize_pow)
auto current_block{std::make_unique<CBlockIndex>(header)};
current_block->pprev = blocks.empty() ? nullptr : blocks.back().get();
current_block->nHeight = height;
- blocks.emplace_back(std::move(current_block)).get();
+ blocks.emplace_back(std::move(current_block));
}
auto last_block{blocks.back().get()};
unsigned int new_nbits{GetNextWorkRequired(last_block, nullptr, consensus_params)};
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 272c9e6cdc..5a4df735da 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -73,6 +73,8 @@ void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE
SetMockTime(1610000000); // any time to successfully reset ibd
chainstate.ResetIbd();
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
return;
@@ -92,10 +94,7 @@ void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE
GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
- {
- LOCK(p2p_node.cs_sendProcessing);
- g_setup->m_node.peerman->SendMessages(&p2p_node);
- }
+ g_setup->m_node.peerman->SendMessages(&p2p_node);
SyncWithValidationInterfaceQueue();
g_setup->m_node.connman->StopNodes();
}
@@ -131,6 +130,7 @@ FUZZ_TARGET_MSG(pong);
FUZZ_TARGET_MSG(sendaddrv2);
FUZZ_TARGET_MSG(sendcmpct);
FUZZ_TARGET_MSG(sendheaders);
+FUZZ_TARGET_MSG(sendtxrcncl);
FUZZ_TARGET_MSG(tx);
FUZZ_TARGET_MSG(verack);
FUZZ_TARGET_MSG(version);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 12e682416c..1df1717ec3 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -40,6 +40,8 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
SetMockTime(1610000000); // any time to successfully reset ibd
chainstate.ResetIbd();
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
std::vector<CNode*> peers;
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
@@ -70,10 +72,7 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
connman.ProcessMessagesOnce(random_node);
} catch (const std::ios_base::failure&) {
}
- {
- LOCK(random_node.cs_sendProcessing);
- g_setup->m_node.peerman->SendMessages(&random_node);
- }
+ g_setup->m_node.peerman->SendMessages(&random_node);
}
SyncWithValidationInterfaceQueue();
g_setup->m_node.connman->StopNodes();
diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp
index 1a06ae886e..678fc7a5aa 100644
--- a/src/test/fuzz/rbf.cpp
+++ b/src/test/fuzz/rbf.cpp
@@ -9,7 +9,9 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/fuzz/util/mempool.h>
#include <test/util/setup_common.h>
+#include <test/util/txmempool.h>
#include <txmempool.h>
#include <cstdint>
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 26913a41d2..f32046e69f 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -151,6 +151,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"preciousblock",
"pruneblockchain",
"reconsiderblock",
+ "scanblocks",
"scantxoutset",
"sendrawtransaction",
"setmocktime",
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index 3191367870..46ca8e47e9 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -8,11 +8,12 @@
#include <node/miner.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
-#include <test/fuzz/mempool_utils.h>
#include <test/fuzz/util.h>
+#include <test/fuzz/util/mempool.h>
#include <test/util/mining.h>
#include <test/util/script.h>
#include <test/util/setup_common.h>
+#include <test/util/txmempool.h>
#include <util/rbf.h>
#include <validation.h>
#include <validationinterface.h>
@@ -85,7 +86,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)));
}
-void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
+void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Chainstate& chainstate)
{
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
@@ -108,7 +109,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
SyncWithValidationInterfaceQueue();
}
-void MockTime(FuzzedDataProvider& fuzzed_data_provider, const CChainState& chainstate)
+void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)
{
const auto time = ConsumeTime(fuzzed_data_provider,
chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
diff --git a/src/test/fuzz/txorphan.cpp b/src/test/fuzz/txorphan.cpp
index 943f3f5fdf..55060f31cf 100644
--- a/src/test/fuzz/txorphan.cpp
+++ b/src/test/fuzz/txorphan.cpp
@@ -45,7 +45,7 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
// if true, allow duplicate input when constructing tx
const bool duplicate_input = fuzzed_data_provider.ConsumeBool();
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
+ LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
{
// construct transaction
const CTransactionRef tx = [&] {
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 38626d4bcf..d495a6bfe3 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -16,7 +16,7 @@
#include <memory>
FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
- : m_fuzzed_data_provider{fuzzed_data_provider}
+ : m_fuzzed_data_provider{fuzzed_data_provider}, m_selectable{fuzzed_data_provider.ConsumeBool()}
{
m_socket = fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET);
}
@@ -254,6 +254,24 @@ int FuzzedSock::GetSockName(sockaddr* name, socklen_t* name_len) const
return 0;
}
+bool FuzzedSock::SetNonBlocking() const
+{
+ constexpr std::array setnonblocking_errnos{
+ EBADF,
+ EPERM,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, setnonblocking_errnos);
+ return false;
+ }
+ return true;
+}
+
+bool FuzzedSock::IsSelectable() const
+{
+ return m_selectable;
+}
+
bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
constexpr std::array wait_errnos{
@@ -307,8 +325,8 @@ CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::option
int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min, const std::optional<int64_t>& max) noexcept
{
// Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) disables mocktime.
- static const int64_t time_min{ParseISO8601DateTime("2000-01-01T00:00:01Z")};
- static const int64_t time_max{ParseISO8601DateTime("2100-12-31T23:59:59Z")};
+ static const int64_t time_min{946684801}; // 2000-01-01T00:00:01Z
+ static const int64_t time_max{4133980799}; // 2100-12-31T23:59:59Z
return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.value_or(time_min), max.value_or(time_max));
}
@@ -478,21 +496,6 @@ CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) no
return tx_destination;
}
-CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
-{
- // Avoid:
- // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
- //
- // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
- const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
- assert(MoneyRange(fee));
- const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
- const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
- const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
- const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
- return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
-}
-
bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
{
for (const CTxIn& tx_in : tx.vin) {
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 6d652c922b..dfe4855326 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -23,7 +23,6 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/net.h>
-#include <txmempool.h>
#include <uint256.h>
#include <version.h>
@@ -48,6 +47,13 @@ class FuzzedSock : public Sock
*/
mutable std::optional<uint8_t> m_peek_data;
+ /**
+ * Whether to pretend that the socket is select(2)-able. This is randomly set in the
+ * constructor. It should remain constant so that repeated calls to `IsSelectable()`
+ * return the same value.
+ */
+ const bool m_selectable;
+
public:
explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider);
@@ -73,6 +79,10 @@ public:
int GetSockName(sockaddr* name, socklen_t* name_len) const override;
+ bool SetNonBlocking() const override;
+
+ bool IsSelectable() const override;
+
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override;
@@ -213,8 +223,6 @@ template <typename WeakEnumType, size_t size>
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
}
-[[nodiscard]] CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept;
-
[[nodiscard]] CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept;
template <typename T>
@@ -328,7 +336,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
}
inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, node_id_in); }
-void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept;
+void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
class FuzzedFileProvider
{
diff --git a/src/test/fuzz/util/mempool.cpp b/src/test/fuzz/util/mempool.cpp
new file mode 100644
index 0000000000..d0053f77d2
--- /dev/null
+++ b/src/test/fuzz/util/mempool.cpp
@@ -0,0 +1,27 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <consensus/amount.h>
+#include <primitives/transaction.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/util.h>
+#include <test/fuzz/util/mempool.h>
+#include <txmempool.h>
+
+#include <limits>
+
+CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
+{
+ // Avoid:
+ // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
+ //
+ // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
+ const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/std::numeric_limits<CAmount>::max() / CAmount{100'000})};
+ assert(MoneyRange(fee));
+ const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
+ const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
+ return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
+}
diff --git a/src/test/fuzz/util/mempool.h b/src/test/fuzz/util/mempool.h
new file mode 100644
index 0000000000..4304e5294e
--- /dev/null
+++ b/src/test/fuzz/util/mempool.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_FUZZ_UTIL_MEMPOOL_H
+#define BITCOIN_TEST_FUZZ_UTIL_MEMPOOL_H
+
+#include <primitives/transaction.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <txmempool.h>
+#include <validation.h>
+
+class DummyChainState final : public Chainstate
+{
+public:
+ void SetMempool(CTxMemPool* mempool)
+ {
+ m_mempool = mempool;
+ }
+};
+
+[[nodiscard]] CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept;
+
+#endif // BITCOIN_TEST_FUZZ_UTIL_MEMPOOL_H
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index 8241dff189..9a90de8911 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -9,9 +9,10 @@
#include <node/mempool_persist_args.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
-#include <test/fuzz/mempool_utils.h>
#include <test/fuzz/util.h>
+#include <test/fuzz/util/mempool.h>
#include <test/util/setup_common.h>
+#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/time.h>
#include <validation.h>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 8c745b07b9..393311e4e4 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/policy.h>
+#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/system.h>
#include <util/time.h>
@@ -203,7 +204,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
CTxMemPool::setEntries setAncestorsCalculated;
std::string dummy;
- BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
+ BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2'000'000LL).FromTx(tx7), setAncestorsCalculated, CTxMemPool::Limits::NoLimits(), dummy), true);
BOOST_CHECK(setAncestorsCalculated == setAncestors);
pool.addUnchecked(entry.FromTx(tx7), setAncestors);
@@ -261,7 +262,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx10.vout[0].nValue = 10 * COIN;
setAncestorsCalculated.clear();
- BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(4).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
+ BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200'000LL).Time(4).FromTx(tx10), setAncestorsCalculated, CTxMemPool::Limits::NoLimits(), dummy), true);
BOOST_CHECK(setAncestorsCalculated == setAncestors);
pool.addUnchecked(entry.FromTx(tx10), setAncestors);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 9f5fb17b60..9bc29e3599 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
@@ -10,6 +9,7 @@
#include <node/miner.h>
#include <policy/policy.h>
#include <script/standard.h>
+#include <test/util/txmempool.h>
#include <timedata.h>
#include <txmempool.h>
#include <uint256.h>
@@ -30,15 +30,24 @@ using node::CBlockTemplate;
namespace miner_tests {
struct MinerTestingSetup : public TestingSetup {
- void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
- void TestBasicMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
- void TestPrioritisedMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
- bool TestSequenceLocks(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
+ void TestPackageSelection(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void TestBasicMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void TestPrioritisedMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ bool TestSequenceLocks(const CTransaction& tx, CTxMemPool& tx_mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
- CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
+ CCoinsViewMemPool view_mempool{&m_node.chainman->ActiveChainstate().CoinsTip(), tx_mempool};
return CheckSequenceLocksAtTip(m_node.chainman->ActiveChain().Tip(), view_mempool, tx);
}
- BlockAssembler AssemblerForTest(const CChainParams& params);
+ CTxMemPool& MakeMempool()
+ {
+ // Delete the previous mempool to ensure with valgrind that the old
+ // pointer is not accessed, when the new one should be accessed
+ // instead.
+ m_node.mempool.reset();
+ m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
+ return *m_node.mempool;
+ }
+ BlockAssembler AssemblerForTest(CTxMemPool& tx_mempool);
};
} // namespace miner_tests
@@ -46,13 +55,13 @@ BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
-BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
+BlockAssembler MinerTestingSetup::AssemblerForTest(CTxMemPool& tx_mempool)
{
BlockAssembler::Options options;
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options};
+ return BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options};
}
constexpr static struct {
@@ -89,8 +98,10 @@ static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip)
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
-void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
+void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
@@ -105,21 +116,21 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
- m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
uint256 hashMediumFeeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a high fee, but depends on the first transaction
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
uint256 hashHighFeeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
@@ -129,7 +140,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vin[0].prevout.hash = hashHighFeeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
uint256 hashFreeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(0).FromTx(tx));
size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
// Calculate a fee on child transaction that will put the package just
@@ -139,8 +150,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vin[0].prevout.hash = hashFreeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
uint256 hashLowFeeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ tx_mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx));
+ pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
@@ -150,11 +161,11 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
+ tx_mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ tx_mempool.addUnchecked(entry.Fee(feeToUse + 2).FromTx(tx));
+ pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
@@ -167,7 +178,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 100000000;
tx.vout[1].nValue = 100000000; // 1BTC output
uint256 hashFreeTx2 = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
// This tx can't be mined by itself
tx.vin[0].prevout.hash = hashFreeTx2;
@@ -175,8 +186,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
uint256 hashLowFeeTx2 = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ tx_mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
+ pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
// Verify that this tx isn't selected.
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -188,13 +199,13 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
// as well.
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
- m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ tx_mempool.addUnchecked(entry.Fee(10000).FromTx(tx));
+ pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
-void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)
+void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)
{
uint256 hash;
CMutableTransaction tx;
@@ -202,172 +213,205 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
entry.nFee = 11;
entry.nHeight = 11;
- // Just to make sure we can still make simple blocks
- auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
- BOOST_CHECK(pblocktemplate);
-
- const CAmount BLOCKSUBSIDY = 50*COIN;
+ const CAmount BLOCKSUBSIDY = 50 * COIN;
const CAmount LOWFEE = CENT;
const CAmount HIGHFEE = COIN;
- const CAmount HIGHERFEE = 4*COIN;
+ const CAmount HIGHERFEE = 4 * COIN;
- // block sigops > limit: 1000 CHECKMULTISIG + 1
- tx.vin.resize(1);
- // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG
- tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1;
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vin[0].prevout.n = 0;
- tx.vout.resize(1);
- tx.vout[0].nValue = BLOCKSUBSIDY;
- for (unsigned int i = 0; i < 1001; ++i)
{
- tx.vout[0].nValue -= LOWFEE;
- hash = tx.GetHash();
- bool spendsCoinbase = i == 0; // only first tx spends coinbase
- // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // Just to make sure we can still make simple blocks
+ auto pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
+ BOOST_CHECK(pblocktemplate);
+
+ // block sigops > limit: 1000 CHECKMULTISIG + 1
+ tx.vin.resize(1);
+ // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG
+ tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1;
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vout.resize(1);
+ tx.vout[0].nValue = BLOCKSUBSIDY;
+ for (unsigned int i = 0; i < 1001; ++i) {
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ bool spendsCoinbase = i == 0; // only first tx spends coinbase
+ // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ }
+
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
}
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
- m_node.mempool->clear();
+ {
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vout[0].nValue = BLOCKSUBSIDY;
+ for (unsigned int i = 0; i < 1001; ++i) {
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ bool spendsCoinbase = i == 0; // only first tx spends coinbase
+ // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ }
+ BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
+ }
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vout[0].nValue = BLOCKSUBSIDY;
- for (unsigned int i = 0; i < 1001; ++i)
{
- tx.vout[0].nValue -= LOWFEE;
- hash = tx.GetHash();
- bool spendsCoinbase = i == 0; // only first tx spends coinbase
- // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // block size > limit
+ tx.vin[0].scriptSig = CScript();
+ // 18 * (520char + DROP) + OP_1 = 9433 bytes
+ std::vector<unsigned char> vchData(520);
+ for (unsigned int i = 0; i < 18; ++i) {
+ tx.vin[0].scriptSig << vchData << OP_DROP;
+ }
+ tx.vin[0].scriptSig << OP_1;
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vout[0].nValue = BLOCKSUBSIDY;
+ for (unsigned int i = 0; i < 128; ++i) {
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ bool spendsCoinbase = i == 0; // only first tx spends coinbase
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ }
+ BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- m_node.mempool->clear();
-
- // block size > limit
- tx.vin[0].scriptSig = CScript();
- // 18 * (520char + DROP) + OP_1 = 9433 bytes
- std::vector<unsigned char> vchData(520);
- for (unsigned int i = 0; i < 18; ++i)
- tx.vin[0].scriptSig << vchData << OP_DROP;
- tx.vin[0].scriptSig << OP_1;
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vout[0].nValue = BLOCKSUBSIDY;
- for (unsigned int i = 0; i < 128; ++i)
+
{
- tx.vout[0].nValue -= LOWFEE;
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // orphan in tx_mempool, template creation fails
hash = tx.GetHash();
- bool spendsCoinbase = i == 0; // only first tx spends coinbase
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- m_node.mempool->clear();
-
- // orphan in *m_node.mempool, template creation fails
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- m_node.mempool->clear();
- // child with higher feerate than parent
- tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
- tx.vin.resize(2);
- tx.vin[1].scriptSig = CScript() << OP_1;
- tx.vin[1].prevout.hash = txFirst[0]->GetHash();
- tx.vin[1].prevout.n = 0;
- tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- m_node.mempool->clear();
+ {
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
- // coinbase in *m_node.mempool, template creation fails
- tx.vin.resize(1);
- tx.vin[0].prevout.SetNull();
- tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
- tx.vout[0].nValue = 0;
- hash = tx.GetHash();
- // give it a fee so it'll get mined
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- // Should throw bad-cb-multiple
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
- m_node.mempool->clear();
+ // child with higher feerate than parent
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vin[0].prevout.hash = txFirst[1]->GetHash();
+ tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ tx.vin.resize(2);
+ tx.vin[1].scriptSig = CScript() << OP_1;
+ tx.vin[1].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[1].prevout.n = 0;
+ tx.vout[0].nValue = tx.vout[0].nValue + BLOCKSUBSIDY - HIGHERFEE; // First txn output + fresh coinbase - new txn fee
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
+ }
- // double spend txn pair in *m_node.mempool, template creation fails
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
- tx.vout[0].scriptPubKey = CScript() << OP_1;
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- tx.vout[0].scriptPubKey = CScript() << OP_2;
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- m_node.mempool->clear();
-
- // subsidy changing
- int nHeight = m_node.chainman->ActiveChain().Height();
- // Create an actual 209999-long block chain (without valid blocks).
- while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
- CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
- CBlockIndex* next = new CBlockIndex();
- next->phashBlock = new uint256(InsecureRand256());
- m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
- next->pprev = prev;
- next->nHeight = prev->nHeight + 1;
- next->BuildSkip();
- m_node.chainman->ActiveChain().SetTip(*next);
+ {
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // coinbase in tx_mempool, template creation fails
+ tx.vin.resize(1);
+ tx.vin[0].prevout.SetNull();
+ tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
+ tx.vout[0].nValue = 0;
+ hash = tx.GetHash();
+ // give it a fee so it'll get mined
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ // Should throw bad-cb-multiple
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- // Extend to a 210000-long block chain.
- while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
- CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
- CBlockIndex* next = new CBlockIndex();
- next->phashBlock = new uint256(InsecureRand256());
- m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
- next->pprev = prev;
- next->nHeight = prev->nHeight + 1;
- next->BuildSkip();
- m_node.chainman->ActiveChain().SetTip(*next);
+
+ {
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // double spend txn pair in tx_mempool, template creation fails
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
+ tx.vout[0].scriptPubKey = CScript() << OP_1;
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx.vout[0].scriptPubKey = CScript() << OP_2;
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- // invalid p2sh txn in *m_node.mempool, template creation fails
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vin[0].prevout.n = 0;
- tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
- CScript script = CScript() << OP_0;
- tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
- tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
- tx.vout[0].nValue -= LOWFEE;
- hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- // Should throw block-validation-failed
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
- m_node.mempool->clear();
-
- // Delete the dummy blocks again.
- while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
- CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
- m_node.chainman->ActiveChain().SetTip(*Assert(del->pprev));
- m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
- delete del->phashBlock;
- delete del;
+ {
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
+ // subsidy changing
+ int nHeight = m_node.chainman->ActiveChain().Height();
+ // Create an actual 209999-long block chain (without valid blocks).
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
+ CBlockIndex* next = new CBlockIndex();
+ next->phashBlock = new uint256(InsecureRand256());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
+ next->pprev = prev;
+ next->nHeight = prev->nHeight + 1;
+ next->BuildSkip();
+ m_node.chainman->ActiveChain().SetTip(*next);
+ }
+ BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
+ // Extend to a 210000-long block chain.
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
+ CBlockIndex* next = new CBlockIndex();
+ next->phashBlock = new uint256(InsecureRand256());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
+ next->pprev = prev;
+ next->nHeight = prev->nHeight + 1;
+ next->BuildSkip();
+ m_node.chainman->ActiveChain().SetTip(*next);
+ }
+ BOOST_CHECK(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
+
+ // invalid p2sh txn in tx_mempool, template creation fails
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vout[0].nValue = BLOCKSUBSIDY - LOWFEE;
+ CScript script = CScript() << OP_0;
+ tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ tx_mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ // Should throw block-validation-failed
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
+
+ // Delete the dummy blocks again.
+ while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
+ m_node.chainman->ActiveChain().SetTip(*Assert(del->pprev));
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
+ delete del->phashBlock;
+ delete del;
+ }
}
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
// non-final txs in mempool
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
const int flags{LOCKTIME_VERIFY_SEQUENCE};
@@ -388,9 +432,9 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes
- BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
{
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
@@ -402,9 +446,9 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes
- BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
const int SEQUENCE_LOCK_TIME = 512; // Sequence locks pass 512 seconds later
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i)
@@ -425,9 +469,9 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
prevheights[0] = baseheight + 3;
tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails
- BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass
+ BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
@@ -436,9 +480,9 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails
- BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass
+ BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
@@ -447,15 +491,16 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes
- BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass
+ BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
tx.vin[0].nSequence = 1;
- BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
- BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass
+ BOOST_CHECK(TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks pass
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
- BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
+ BOOST_CHECK(!TestSequenceLocks(CTransaction{tx}, tx_mempool)); // Sequence locks fail
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ auto pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
+ BOOST_CHECK(pblocktemplate);
// None of the of the absolute height/time locked tx should have made
// it into the template because we still check IsFinalTx in CreateNewBlock,
@@ -470,12 +515,15 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
m_node.chainman->ActiveChain().Tip()->nHeight++;
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
}
-void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
+void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
+ CTxMemPool& tx_mempool{MakeMempool()};
+ LOCK(tx_mempool.cs);
+
TestMemPoolEntryHelper entry;
// Test that a tx below min fee but prioritised is included
@@ -487,29 +535,29 @@ void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, c
tx.vout.resize(1);
tx.vout[0].nValue = 5000000000LL; // 0 fee
uint256 hashFreePrioritisedTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- m_node.mempool->PrioritiseTransaction(hashFreePrioritisedTx, 5 * COIN);
+ tx_mempool.addUnchecked(entry.Fee(0).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.PrioritiseTransaction(hashFreePrioritisedTx, 5 * COIN);
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
- m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
uint256 hashMediumFeeTx = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- m_node.mempool->PrioritiseTransaction(hashMediumFeeTx, -5 * COIN);
+ tx_mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.PrioritiseTransaction(hashMediumFeeTx, -5 * COIN);
// This tx also has a low fee, but is prioritised
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 1000; // 1000 satoshi fee
uint256 hashPrioritsedChild = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- m_node.mempool->PrioritiseTransaction(hashPrioritsedChild, 2 * COIN);
+ tx_mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ tx_mempool.PrioritiseTransaction(hashPrioritsedChild, 2 * COIN);
// Test that transaction selection properly updates ancestor fee calculations as prioritised
// parents get included in a block. Create a transaction with two prioritised ancestors, each
@@ -520,21 +568,21 @@ void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, c
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
tx.vout[0].nValue = 5000000000LL; // 0 fee
uint256 hashFreeParent = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
- m_node.mempool->PrioritiseTransaction(hashFreeParent, 10 * COIN);
+ tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
+ tx_mempool.PrioritiseTransaction(hashFreeParent, 10 * COIN);
tx.vin[0].prevout.hash = hashFreeParent;
tx.vout[0].nValue = 5000000000LL; // 0 fee
uint256 hashFreeChild = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
- m_node.mempool->PrioritiseTransaction(hashFreeChild, 1 * COIN);
+ tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
+ tx_mempool.PrioritiseTransaction(hashFreeChild, 1 * COIN);
tx.vin[0].prevout.hash = hashFreeChild;
tx.vout[0].nValue = 5000000000LL; // 0 fee
uint256 hashFreeGrandchild = tx.GetHash();
- m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
+ tx_mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
- auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ auto pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey);
BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashFreeParent);
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashFreePrioritisedTx);
@@ -553,15 +601,12 @@ void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, c
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
// Note that by default, these tests run with size accounting enabled.
- const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
- const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
- fCheckpointsEnabled = false;
-
+ CTxMemPool& tx_mempool{*m_node.mempool};
// Simple block creation, nothing special yet:
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(tx_mempool).CreateNewBlock(scriptPubKey));
// We can't make transactions until we have inputs
// Therefore, load 110 blocks :)
@@ -593,23 +638,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
LOCK(cs_main);
- LOCK(m_node.mempool->cs);
- TestBasicMining(chainparams, scriptPubKey, txFirst, baseheight);
+ TestBasicMining(scriptPubKey, txFirst, baseheight);
m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
- m_node.mempool->clear();
- TestPackageSelection(chainparams, scriptPubKey, txFirst);
+ TestPackageSelection(scriptPubKey, txFirst);
m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
- m_node.mempool->clear();
-
- TestPrioritisedMining(chainparams, scriptPubKey, txFirst);
- fCheckpointsEnabled = true;
+ TestPrioritisedMining(scriptPubKey, txFirst);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 95e8476b77..9387c01e73 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -296,6 +296,9 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
// Same when the duplicates are on different levels in the tree
const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey());
+ // Sanity check the opposite is true, too. An otherwise sane Miniscript with no duplicate keys is sane.
+ const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", CONVERTER);
+ BOOST_CHECK(ms_nondup && ms_nondup->CheckDuplicateKey() && ms_nondup->IsSane());
// Test we find the first insane sub closer to be a leaf node. This fragment is insane for two reasons:
// 1. It can be spent without a signature
// 2. It contains timelock mixes
diff --git a/src/test/minisketch_tests.cpp b/src/test/minisketch_tests.cpp
index 9c53ace633..81f2aad623 100644
--- a/src/test/minisketch_tests.cpp
+++ b/src/test/minisketch_tests.cpp
@@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(minisketch_test)
Minisketch sketch_c = std::move(sketch_ar);
sketch_c.Merge(sketch_br);
auto dec = sketch_c.Decode(errors);
- BOOST_CHECK(dec.has_value());
+ BOOST_REQUIRE(dec.has_value());
auto sols = std::move(*dec);
std::sort(sols.begin(), sols.end());
for (uint32_t i = 0; i < a_not_b; ++i) BOOST_CHECK_EQUAL(sols[i], start_a + i);
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index f6642d3218..f24509dd97 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -805,6 +805,8 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
{
+ LOCK(NetEventsInterface::g_msgproc_mutex);
+
// Tests the following scenario:
// * -bind=3.4.5.6:20001 is specified
// * we make an outbound connection to a peer
@@ -840,7 +842,7 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
const int64_t time{0};
const CNetMsgMaker msg_maker{PROTOCOL_VERSION};
- // Force CChainState::IsInitialBlockDownload() to return false.
+ // Force Chainstate::IsInitialBlockDownload() to return false.
// Otherwise PushAddress() isn't called by PeerManager::ProcessMessage().
TestChainState& chainstate =
*static_cast<TestChainState*>(&m_node.chainman->ActiveChainstate());
@@ -889,10 +891,7 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
}
};
- {
- LOCK(peer.cs_sendProcessing);
- m_node.peerman->SendMessages(&peer);
- }
+ m_node.peerman->SendMessages(&peer);
BOOST_CHECK(sent);
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index c2d2fa37b4..0e1e9ae211 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -84,12 +84,12 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
}
-bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port)
+bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port, bool validPort=true)
{
std::string hostOut;
uint16_t portOut{0};
- SplitHostPort(test, portOut, hostOut);
- return hostOut == host && port == portOut;
+ bool validPortOut = SplitHostPort(test, portOut, hostOut);
+ return hostOut == host && portOut == port && validPortOut == validPort;
}
BOOST_AUTO_TEST_CASE(netbase_splithost)
@@ -109,6 +109,23 @@ BOOST_AUTO_TEST_CASE(netbase_splithost)
BOOST_CHECK(TestSplitHost(":8333", "", 8333));
BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
BOOST_CHECK(TestSplitHost("", "", 0));
+ BOOST_CHECK(TestSplitHost(":65535", "", 65535));
+ BOOST_CHECK(TestSplitHost(":65536", ":65536", 0, false));
+ BOOST_CHECK(TestSplitHost(":-1", ":-1", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:70001", "[]:70001", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:-1", "[]:-1", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:-0", "[]:-0", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:0", "", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:1/2", "[]:1/2", 0, false));
+ BOOST_CHECK(TestSplitHost("[]:1E2", "[]:1E2", 0, false));
+ BOOST_CHECK(TestSplitHost("127.0.0.1:65536", "127.0.0.1:65536", 0, false));
+ BOOST_CHECK(TestSplitHost("127.0.0.1:0", "127.0.0.1", 0, false));
+ BOOST_CHECK(TestSplitHost("127.0.0.1:", "127.0.0.1:", 0, false));
+ BOOST_CHECK(TestSplitHost("127.0.0.1:1/2", "127.0.0.1:1/2", 0, false));
+ BOOST_CHECK(TestSplitHost("127.0.0.1:1E2", "127.0.0.1:1E2", 0, false));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org:65536", "www.bitcoincore.org:65536", 0, false));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org:0", "www.bitcoincore.org", 0, false));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org:", "www.bitcoincore.org:", 0, false));
}
bool static TestParse(std::string src, std::string canon)
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 3f66a8fc46..b652dc44c0 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -4,6 +4,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
+#include <test/util/txmempool.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/time.h>
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index c489ac04e9..d4487cd941 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -4,8 +4,8 @@
#include <event2/event.h>
+#include <cstdlib>
#include <map>
-#include <stdlib.h>
#include <support/events.h>
diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp
index c88cd36688..d362c85560 100644
--- a/src/test/rbf_tests.cpp
+++ b/src/test/rbf_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/rbf.h>
#include <random.h>
+#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/system.h>
#include <util/time.h>
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 55486db6b2..d78f62972f 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <key.h>
diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp
index f160bb08a5..11f4be7fef 100644
--- a/src/test/system_tests.cpp
+++ b/src/test/system_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <test/util/setup_common.h>
-#include <util/system.h>
+#include <common/run_command.h>
#include <univalue.h>
#ifdef ENABLE_EXTERNAL_SIGNER
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 62c7ddb673..643d9221fe 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -69,11 +69,16 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
}
}
+ // It is not safe to stop and destroy the index until it finishes handling
+ // the last BlockConnected notification. The BlockUntilSyncedToCurrentChain()
+ // call above is sufficient to ensure this, but the
+ // SyncWithValidationInterfaceQueue() call below is also needed to ensure
+ // TSAN always sees the test thread waiting for the notification thread, and
+ // avoid potential false positive reports.
+ SyncWithValidationInterfaceQueue();
+
// shutdown sequence (c.f. Shutdown() in init.cpp)
txindex.Stop();
-
- // Let scheduler events finish running to avoid accessing any memory related to txindex after it is destructed
- SyncWithValidationInterfaceQueue();
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txreconciliation_tests.cpp b/src/test/txreconciliation_tests.cpp
new file mode 100644
index 0000000000..bd74998002
--- /dev/null
+++ b/src/test/txreconciliation_tests.cpp
@@ -0,0 +1,86 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <node/txreconciliation.h>
+
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(txreconciliation_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(RegisterPeerTest)
+{
+ TxReconciliationTracker tracker(1);
+ const uint64_t salt = 0;
+
+ // Prepare a peer for reconciliation.
+ tracker.PreRegisterPeer(0);
+
+ // Both roles are false, don't register.
+ BOOST_CHECK(tracker.RegisterPeer(/*peer_id=*/0, /*is_peer_inbound=*/true,
+ /*is_peer_recon_initiator=*/false,
+ /*is_peer_recon_responder=*/false,
+ /*peer_recon_version=*/1, salt) ==
+ ReconciliationRegisterResult::PROTOCOL_VIOLATION);
+
+ // Invalid roles for the given connection direction.
+ BOOST_CHECK(tracker.RegisterPeer(0, true, false, true, 1, salt) == ReconciliationRegisterResult::PROTOCOL_VIOLATION);
+ BOOST_CHECK(tracker.RegisterPeer(0, false, true, false, 1, salt) == ReconciliationRegisterResult::PROTOCOL_VIOLATION);
+
+ // Invalid version.
+ BOOST_CHECK(tracker.RegisterPeer(0, true, true, false, 0, salt) == ReconciliationRegisterResult::PROTOCOL_VIOLATION);
+
+ // Valid registration.
+ BOOST_REQUIRE(!tracker.IsPeerRegistered(0));
+ BOOST_REQUIRE(tracker.RegisterPeer(0, true, true, false, 1, salt) == ReconciliationRegisterResult::SUCCESS);
+ BOOST_CHECK(tracker.IsPeerRegistered(0));
+
+ // Reconciliation version is higher than ours, should be able to register.
+ BOOST_REQUIRE(!tracker.IsPeerRegistered(1));
+ tracker.PreRegisterPeer(1);
+ BOOST_REQUIRE(tracker.RegisterPeer(1, true, true, false, 2, salt) == ReconciliationRegisterResult::SUCCESS);
+ BOOST_CHECK(tracker.IsPeerRegistered(1));
+
+ // Do not register if there were no pre-registration for the peer.
+ BOOST_REQUIRE(tracker.RegisterPeer(100, true, true, false, 1, salt) == ReconciliationRegisterResult::NOT_FOUND);
+ BOOST_CHECK(!tracker.IsPeerRegistered(100));
+}
+
+BOOST_AUTO_TEST_CASE(ForgetPeerTest)
+{
+ TxReconciliationTracker tracker(1);
+ NodeId peer_id0 = 0;
+
+ // Removing peer after pre-registring works and does not let to register the peer.
+ tracker.PreRegisterPeer(peer_id0);
+ tracker.ForgetPeer(peer_id0);
+ BOOST_CHECK(tracker.RegisterPeer(peer_id0, true, true, false, 1, 1) == ReconciliationRegisterResult::NOT_FOUND);
+
+ // Removing peer after it is registered works.
+ tracker.PreRegisterPeer(peer_id0);
+ BOOST_REQUIRE(!tracker.IsPeerRegistered(peer_id0));
+ BOOST_REQUIRE(tracker.RegisterPeer(peer_id0, true, true, false, 1, 1) == ReconciliationRegisterResult::SUCCESS);
+ BOOST_CHECK(tracker.IsPeerRegistered(peer_id0));
+ tracker.ForgetPeer(peer_id0);
+ BOOST_CHECK(!tracker.IsPeerRegistered(peer_id0));
+}
+
+BOOST_AUTO_TEST_CASE(IsPeerRegisteredTest)
+{
+ TxReconciliationTracker tracker(1);
+ NodeId peer_id0 = 0;
+
+ BOOST_REQUIRE(!tracker.IsPeerRegistered(peer_id0));
+ tracker.PreRegisterPeer(peer_id0);
+ BOOST_REQUIRE(!tracker.IsPeerRegistered(peer_id0));
+
+ BOOST_REQUIRE(tracker.RegisterPeer(peer_id0, true, true, false, 1, 1) == ReconciliationRegisterResult::SUCCESS);
+ BOOST_CHECK(tracker.IsPeerRegistered(peer_id0));
+
+ tracker.ForgetPeer(peer_id0);
+ BOOST_CHECK(!tracker.IsPeerRegistered(peer_id0));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index 13e0e684b8..0ca63810f3 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -7,25 +7,38 @@
#include <clientversion.h>
#include <fs.h>
+#include <logging.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <rpc/blockchain.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <univalue.h>
-#include <boost/test/unit_test.hpp>
-
const auto NoMalleation = [](AutoFile& file, node::SnapshotMetadata& meta){};
/**
* Create and activate a UTXO snapshot, optionally providing a function to
* malleate the snapshot.
+ *
+ * If `reset_chainstate` is true, reset the original chainstate back to the genesis
+ * block. This allows us to simulate more realistic conditions in which a snapshot is
+ * loaded into an otherwise mostly-uninitialized datadir. It also allows us to test
+ * conditions that would otherwise cause shutdowns based on the IBD chainstate going
+ * past the snapshot it generated.
*/
template<typename F = decltype(NoMalleation)>
static bool
-CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F malleation = NoMalleation)
+CreateAndActivateUTXOSnapshot(
+ TestingSetup* fixture,
+ F malleation = NoMalleation,
+ bool reset_chainstate = false,
+ bool in_memory_chainstate = false)
{
+ node::NodeContext& node = fixture->m_node;
+ fs::path root = fixture->m_path_root;
+
// Write out a snapshot to the test's tempdir.
//
int height;
@@ -36,8 +49,8 @@ CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F ma
UniValue result = CreateUTXOSnapshot(
node, node.chainman->ActiveChainstate(), auto_outfile, snapshot_path, snapshot_path);
- BOOST_TEST_MESSAGE(
- "Wrote UTXO snapshot to " << fs::PathToString(snapshot_path.make_preferred()) << ": " << result.write());
+ LogPrintf(
+ "Wrote UTXO snapshot to %s: %s", fs::PathToString(snapshot_path.make_preferred()), result.write());
// Read the written snapshot in and then activate it.
//
@@ -48,7 +61,38 @@ CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F ma
malleation(auto_infile, metadata);
- return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory=*/true);
+ if (reset_chainstate) {
+ {
+ // What follows is code to selectively reset chainstate data without
+ // disturbing the existing BlockManager instance, which is needed to
+ // recognize the headers chain previously generated by the chainstate we're
+ // removing. Without those headers, we can't activate the snapshot below.
+ //
+ // This is a stripped-down version of node::LoadChainstate which
+ // preserves the block index.
+ LOCK(::cs_main);
+ uint256 gen_hash = node.chainman->ActiveChainstate().m_chain[0]->GetBlockHash();
+ node.chainman->ResetChainstates();
+ node.chainman->InitializeChainstate(node.mempool.get());
+ Chainstate& chain = node.chainman->ActiveChainstate();
+ Assert(chain.LoadGenesisBlock());
+ // These cache values will be corrected shortly in `MaybeRebalanceCaches`.
+ chain.InitCoinsDB(1 << 20, true, false, "");
+ chain.InitCoinsCache(1 << 20);
+ chain.CoinsTip().SetBestBlock(gen_hash);
+ chain.setBlockIndexCandidates.insert(node.chainman->m_blockman.LookupBlockIndex(gen_hash));
+ chain.LoadChainTip();
+ node.chainman->MaybeRebalanceCaches();
+ }
+ BlockValidationState state;
+ if (!node.chainman->ActiveChainstate().ActivateBestChain(state)) {
+ throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
+ }
+ Assert(
+ 0 == WITH_LOCK(node.chainman->GetMutex(), return node.chainman->ActiveHeight()));
+ }
+
+ return node.chainman->ActivateSnapshot(auto_infile, metadata, in_memory_chainstate);
}
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 21273ac5c1..2e3e16e681 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -44,10 +44,7 @@ void ConnmanTestMsg::Handshake(CNode& node,
(void)connman.ReceiveMsgFrom(node, msg_version);
node.fPauseSend = false;
connman.ProcessMessagesOnce(node);
- {
- LOCK(node.cs_sendProcessing);
- peerman.SendMessages(&node);
- }
+ peerman.SendMessages(&node);
if (node.fDisconnect) return;
assert(node.nVersion == version);
assert(node.GetCommonVersion() == std::min(version, PROTOCOL_VERSION));
@@ -60,10 +57,7 @@ void ConnmanTestMsg::Handshake(CNode& node,
(void)connman.ReceiveMsgFrom(node, msg_verack);
node.fPauseSend = false;
connman.ProcessMessagesOnce(node);
- {
- LOCK(node.cs_sendProcessing);
- peerman.SendMessages(&node);
- }
+ peerman.SendMessages(&node);
assert(node.fSuccessfullyConnected == true);
}
}
diff --git a/src/test/util/net.h b/src/test/util/net.h
index b339bee32a..9ae7981980 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -44,9 +44,10 @@ struct ConnmanTestMsg : public CConnman {
ServiceFlags remote_services,
ServiceFlags local_services,
int32_t version,
- bool relay_txs);
+ bool relay_txs)
+ EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
- void ProcessMessagesOnce(CNode& node) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); }
+ void ProcessMessagesOnce(CNode& node) EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); }
void NodeReceiveMsgBytes(CNode& node, Span<const uint8_t> msg_bytes, bool& complete) const;
@@ -165,6 +166,10 @@ public:
return 0;
}
+ bool SetNonBlocking() const override { return true; }
+
+ bool IsSelectable() const override { return true; }
+
bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event* occurred = nullptr) const override
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index c3989a12fe..0d0db176f9 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -36,6 +36,7 @@
#include <shutdown.h>
#include <streams.h>
#include <test/util/net.h>
+#include <test/util/txmempool.h>
#include <timedata.h>
#include <txdb.h>
#include <txmempool.h>
@@ -60,7 +61,6 @@ using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::CalculateCacheSizes;
using node::LoadChainstate;
-using node::NodeContext;
using node::RegenerateCommitments;
using node::VerifyLoadedChainstate;
@@ -146,7 +146,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
m_node.chain = interfaces::MakeChain(m_node);
- fCheckBlockIndex = true;
static bool noui_connected = false;
if (!noui_connected) {
noui_connect();
@@ -162,19 +161,6 @@ BasicTestingSetup::~BasicTestingSetup()
gArgs.ClearArgs();
}
-CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node)
-{
- CTxMemPool::Options mempool_opts{
- .estimator = node.fee_estimator.get(),
- // Default to always checking mempool regardless of
- // chainparams.DefaultConsistencyChecks for tests
- .check_ratio = 1,
- };
- const auto err{ApplyArgsManOptions(*node.args, ::Params(), mempool_opts)};
- Assert(!err);
- return mempool_opts;
-}
-
ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
: BasicTestingSetup(chainName, extra_args)
{
@@ -194,14 +180,13 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
.adjusted_time_callback = GetAdjustedTime,
+ .check_block_index = true,
};
m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(m_cache_sizes.block_tree_db, true);
- // Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
StartScriptCheckWorkerThreads(script_check_threads);
- g_parallel_script_checks = true;
}
ChainTestingSetup::~ChainTestingSetup()
@@ -220,17 +205,12 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.chainman.reset();
}
-TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
- : ChainTestingSetup(chainName, extra_args)
+void TestingSetup::LoadVerifyActivateChainstate()
{
- // Ideally we'd move all the RPC tests to the functional testing framework
- // instead of unit tests, but for now we need these here.
- RegisterAllCoreRPCCommands(tableRPC);
-
node::ChainstateLoadOptions options;
options.mempool = Assert(m_node.mempool.get());
- options.block_tree_db_in_memory = true;
- options.coins_db_in_memory = true;
+ options.block_tree_db_in_memory = m_block_tree_db_in_memory;
+ options.coins_db_in_memory = m_coins_db_in_memory;
options.reindex = node::fReindex;
options.reindex_chainstate = m_args.GetBoolArg("-reindex-chainstate", false);
options.prune = node::fPruneMode;
@@ -246,6 +226,22 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
+}
+
+TestingSetup::TestingSetup(
+ const std::string& chainName,
+ const std::vector<const char*>& extra_args,
+ const bool coins_db_in_memory,
+ const bool block_tree_db_in_memory)
+ : ChainTestingSetup(chainName, extra_args),
+ m_coins_db_in_memory(coins_db_in_memory),
+ m_block_tree_db_in_memory(block_tree_db_in_memory)
+{
+ // Ideally we'd move all the RPC tests to the functional testing framework
+ // instead of unit tests, but for now we need these here.
+ RegisterAllCoreRPCCommands(tableRPC);
+
+ LoadVerifyActivateChainstate();
m_node.netgroupman = std::make_unique<NetGroupManager>(/*asmap=*/std::vector<bool>());
m_node.addrman = std::make_unique<AddrMan>(*m_node.netgroupman,
@@ -263,8 +259,12 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup(const std::string& chain_name, const std::vector<const char*>& extra_args)
- : TestingSetup{chain_name, extra_args}
+TestChain100Setup::TestChain100Setup(
+ const std::string& chain_name,
+ const std::vector<const char*>& extra_args,
+ const bool coins_db_in_memory,
+ const bool block_tree_db_in_memory)
+ : TestingSetup{CBaseChainParams::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -296,7 +296,7 @@ void TestChain100Setup::mineBlocks(int num_blocks)
CBlock TestChain100Setup::CreateBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
- CChainState& chainstate)
+ Chainstate& chainstate)
{
CBlock block = BlockAssembler{chainstate, nullptr}.CreateNewBlock(scriptPubKey)->block;
@@ -314,7 +314,7 @@ CBlock TestChain100Setup::CreateBlock(
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
- CChainState* chainstate)
+ Chainstate* chainstate)
{
if (!chainstate) {
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
@@ -423,17 +423,6 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex
return mempool_transactions;
}
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
-{
- return FromTx(MakeTransactionRef(tx));
-}
-
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) const
-{
- return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
- spendsCoinbase, sigOpCost, lp);
-}
-
/**
* @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
* with 9 txs.
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index ed2c5db7e6..dfa36039a2 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -8,21 +8,23 @@
#include <chainparamsbase.h>
#include <fs.h>
#include <key.h>
-#include <util/system.h>
#include <node/caches.h>
#include <node/context.h>
+#include <primitives/transaction.h>
#include <pubkey.h>
#include <random.h>
#include <stdexcept>
-#include <txmempool.h>
#include <util/check.h>
#include <util/string.h>
+#include <util/system.h>
#include <util/vector.h>
#include <functional>
#include <type_traits>
#include <vector>
+class Chainstate;
+
/** This is connected to the logger. Can be used to redirect logs to any other log */
extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
@@ -90,9 +92,6 @@ struct BasicTestingSetup {
ArgsManager m_args;
};
-
-CTxMemPool::Options MemPoolOptionsForTest(const node::NodeContext& node);
-
/** Testing setup that performs all steps up until right before
* ChainstateManager gets initialized. Meant for testing ChainstateManager
* initialization behaviour.
@@ -107,7 +106,16 @@ struct ChainTestingSetup : public BasicTestingSetup {
/** Testing setup that configures a complete environment.
*/
struct TestingSetup : public ChainTestingSetup {
- explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
+ bool m_coins_db_in_memory{true};
+ bool m_block_tree_db_in_memory{true};
+
+ void LoadVerifyActivateChainstate();
+
+ explicit TestingSetup(
+ const std::string& chainName = CBaseChainParams::MAIN,
+ const std::vector<const char*>& extra_args = {},
+ const bool coins_db_in_memory = true,
+ const bool block_tree_db_in_memory = true);
};
/** Identical to TestingSetup, but chain set to regtest */
@@ -124,8 +132,11 @@ class CScript;
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public TestingSetup {
- TestChain100Setup(const std::string& chain_name = CBaseChainParams::REGTEST,
- const std::vector<const char*>& extra_args = {});
+ TestChain100Setup(
+ const std::string& chain_name = CBaseChainParams::REGTEST,
+ const std::vector<const char*>& extra_args = {},
+ const bool coins_db_in_memory = true,
+ const bool block_tree_db_in_memory = true);
/**
* Create a new block with just given transactions, coinbase paying to
@@ -134,7 +145,7 @@ struct TestChain100Setup : public TestingSetup {
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
- CChainState* chainstate = nullptr);
+ Chainstate* chainstate = nullptr);
/**
* Create a new block with just given transactions, coinbase paying to
@@ -143,7 +154,7 @@ struct TestChain100Setup : public TestingSetup {
CBlock CreateBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
- CChainState& chainstate);
+ Chainstate& chainstate);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);
@@ -201,33 +212,6 @@ std::unique_ptr<T> MakeNoLogFileContext(const std::string& chain_name = CBaseCha
return std::make_unique<T>(chain_name, arguments);
}
-class CTxMemPoolEntry;
-
-struct TestMemPoolEntryHelper
-{
- // Default values
- CAmount nFee;
- int64_t nTime;
- unsigned int nHeight;
- bool spendsCoinbase;
- unsigned int sigOpCost;
- LockPoints lp;
-
- TestMemPoolEntryHelper() :
- nFee(0), nTime(0), nHeight(1),
- spendsCoinbase(false), sigOpCost(4) { }
-
- CTxMemPoolEntry FromTx(const CMutableTransaction& tx) const;
- CTxMemPoolEntry FromTx(const CTransactionRef& tx) const;
-
- // Change the default value
- TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; }
- TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; }
- TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
- TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
- TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
-};
-
CBlock getBlock13b8a();
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
diff --git a/src/test/util/txmempool.cpp b/src/test/util/txmempool.cpp
new file mode 100644
index 0000000000..12cc1a4a3d
--- /dev/null
+++ b/src/test/util/txmempool.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/util/txmempool.h>
+
+#include <chainparams.h>
+#include <node/context.h>
+#include <node/mempool_args.h>
+#include <txmempool.h>
+#include <util/check.h>
+#include <util/time.h>
+#include <util/translation.h>
+
+using node::NodeContext;
+
+CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node)
+{
+ CTxMemPool::Options mempool_opts{
+ .estimator = node.fee_estimator.get(),
+ // Default to always checking mempool regardless of
+ // chainparams.DefaultConsistencyChecks for tests
+ .check_ratio = 1,
+ };
+ const auto err{ApplyArgsManOptions(*node.args, ::Params(), mempool_opts)};
+ Assert(!err);
+ return mempool_opts;
+}
+
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
+{
+ return FromTx(MakeTransactionRef(tx));
+}
+
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) const
+{
+ return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
+ spendsCoinbase, sigOpCost, lp);
+}
diff --git a/src/test/util/txmempool.h b/src/test/util/txmempool.h
new file mode 100644
index 0000000000..70b9ed88db
--- /dev/null
+++ b/src/test/util/txmempool.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_TXMEMPOOL_H
+#define BITCOIN_TEST_UTIL_TXMEMPOOL_H
+
+#include <txmempool.h>
+
+namespace node {
+struct NodeContext;
+}
+
+CTxMemPool::Options MemPoolOptionsForTest(const node::NodeContext& node);
+
+struct TestMemPoolEntryHelper
+{
+ // Default values
+ CAmount nFee{0};
+ int64_t nTime{0};
+ unsigned int nHeight{1};
+ bool spendsCoinbase{false};
+ unsigned int sigOpCost{4};
+ LockPoints lp;
+
+ CTxMemPoolEntry FromTx(const CMutableTransaction& tx) const;
+ CTxMemPoolEntry FromTx(const CTransactionRef& tx) const;
+
+ // Change the default value
+ TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; }
+ TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; }
+ TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
+ TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
+ TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
+};
+
+#endif // BITCOIN_TEST_UTIL_TXMEMPOOL_H
diff --git a/src/test/util/validation.h b/src/test/util/validation.h
index b0bc717b6c..cbe7745b81 100644
--- a/src/test/util/validation.h
+++ b/src/test/util/validation.h
@@ -9,7 +9,7 @@
class CValidationInterface;
-struct TestChainState : public CChainState {
+struct TestChainState : public Chainstate {
/** Reset the ibd cache to its initial state */
void ResetIbd();
/** Toggle IsInitialBlockDownload from true to false */
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 0f9f332dc6..009c27927f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -26,6 +26,7 @@
#include <util/bitdeque.h>
#include <array>
+#include <cmath>
#include <fstream>
#include <limits>
#include <map>
@@ -282,14 +283,10 @@ BOOST_AUTO_TEST_CASE(util_TrimString)
BOOST_CHECK_EQUAL(TrimStringView(std::string("\x05\x04\x03\x02\x01\x00", 6), std::string("\x05\x04\x03\x02\x01\x00", 6)), "");
}
-BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime)
+BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
{
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(0), "1970-01-01T00:00:00Z");
-
- BOOST_CHECK_EQUAL(ParseISO8601DateTime("1970-01-01T00:00:00Z"), 0);
- BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0);
- BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777);
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 98294b9028..f868c0d4e6 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -18,7 +18,7 @@
BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, ChainTestingSetup)
-//! Test resizing coins-related CChainState caches during runtime.
+//! Test resizing coins-related Chainstate caches during runtime.
//!
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
@@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
- CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
+ Chainstate& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
@@ -89,7 +89,8 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
// After adding some blocks to the tip, best block should have changed.
BOOST_CHECK(::g_best_block != curr_tip);
- BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+ BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(
+ this, NoMalleation, /*reset_chainstate=*/ true));
// Ensure our active chain is the snapshot chainstate.
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.IsSnapshotActive()));
@@ -106,8 +107,8 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
- CChainState& background_cs{*[&] {
- for (CChainState* cs : chainman.GetAll()) {
+ Chainstate& background_cs{*[&] {
+ for (Chainstate* cs : chainman.GetAll()) {
if (cs != &chainman.ActiveChainstate()) {
return cs;
}
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 7b7bd05b5c..22b9af1201 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -10,6 +10,7 @@
#include <sync.h>
#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
+#include <timedata.h>
#include <uint256.h>
#include <validation.h>
#include <validationinterface.h>
@@ -32,13 +33,13 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
ChainstateManager& manager = *m_node.chainman;
CTxMemPool& mempool = *m_node.mempool;
- std::vector<CChainState*> chainstates;
+ std::vector<Chainstate*> chainstates;
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
+ Chainstate& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
@@ -63,7 +64,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Create a snapshot-based chainstate.
//
const uint256 snapshot_blockhash = GetRandHash();
- CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
+ Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(
&mempool, snapshot_blockhash));
chainstates.push_back(&c2);
@@ -111,11 +112,11 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
manager.m_total_coinsdb_cache = max_cache;
manager.m_total_coinstip_cache = max_cache;
- std::vector<CChainState*> chainstates;
+ std::vector<Chainstate*> chainstates;
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
+ Chainstate& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
@@ -133,7 +134,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
+ Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(&mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
@@ -154,167 +155,245 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
}
-//! Test basic snapshot activation.
-BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
-{
- ChainstateManager& chainman = *Assert(m_node.chainman);
-
- size_t initial_size;
- size_t initial_total_coins{100};
-
- // Make some initial assertions about the contents of the chainstate.
+struct SnapshotTestSetup : TestChain100Setup {
+ // Run with coinsdb on the filesystem to support, e.g., moving invalidated
+ // chainstate dirs to "*_invalid".
+ //
+ // Note that this means the tests run considerably slower than in-memory DB
+ // tests, but we can't otherwise test this functionality since it relies on
+ // destructive filesystem operations.
+ SnapshotTestSetup() : TestChain100Setup{
+ {},
+ {},
+ /*coins_db_in_memory=*/false,
+ /*block_tree_db_in_memory=*/false,
+ }
{
- LOCK(::cs_main);
- CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
- initial_size = ibd_coinscache.GetCacheSize();
- size_t total_coins{0};
-
- for (CTransactionRef& txn : m_coinbase_txns) {
- COutPoint op{txn->GetHash(), 0};
- BOOST_CHECK(ibd_coinscache.HaveCoin(op));
- total_coins++;
- }
-
- BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
- BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
}
- // Snapshot should refuse to load at this height.
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
- BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
- BOOST_CHECK(!chainman.SnapshotBlockhash());
-
- // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
- // be found.
- constexpr int snapshot_height = 110;
- mineBlocks(10);
- initial_size += 10;
- initial_total_coins += 10;
-
- // Should not load malleated snapshots
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
- m_node, m_path_root, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
- // A UTXO is missing but count is correct
- metadata.m_coins_count -= 1;
-
- COutPoint outpoint;
- Coin coin;
-
- auto_infile >> outpoint;
- auto_infile >> coin;
- }));
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
- m_node, m_path_root, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
- // Coins count is larger than coins in file
- metadata.m_coins_count += 1;
- }));
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
- m_node, m_path_root, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
- // Coins count is smaller than coins in file
- metadata.m_coins_count -= 1;
- }));
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
- m_node, m_path_root, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
- // Wrong hash
- metadata.m_base_blockhash = uint256::ZERO;
- }));
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
- m_node, m_path_root, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
- // Wrong hash
- metadata.m_base_blockhash = uint256::ONE;
- }));
-
- BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
-
- // Ensure our active chain is the snapshot chainstate.
- BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
- BOOST_CHECK_EQUAL(
- *chainman.ActiveChainstate().m_from_snapshot_blockhash,
- *chainman.SnapshotBlockhash());
-
- // Ensure that the genesis block was not marked assumed-valid.
- BOOST_CHECK(WITH_LOCK(::cs_main, return !chainman.ActiveChain().Genesis()->IsAssumedValid()));
-
- const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
- const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
-
- BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx);
-
- // To be checked against later when we try loading a subsequent snapshot.
- uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
-
- // Make some assertions about the both chainstates. These checks ensure the
- // legacy chainstate hasn't changed and that the newly created chainstate
- // reflects the expected content.
+ std::tuple<Chainstate*, Chainstate*> SetupSnapshot()
{
- LOCK(::cs_main);
- int chains_tested{0};
+ ChainstateManager& chainman = *Assert(m_node.chainman);
- for (CChainState* chainstate : chainman.GetAll()) {
- BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
- CCoinsViewCache& coinscache = chainstate->CoinsTip();
+ BOOST_CHECK(!chainman.IsSnapshotActive());
- // Both caches will be empty initially.
- BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
+ {
+ LOCK(::cs_main);
+ BOOST_CHECK(!chainman.IsSnapshotValidated());
+ BOOST_CHECK(!node::FindSnapshotChainstateDir());
+ }
+ size_t initial_size;
+ size_t initial_total_coins{100};
+
+ // Make some initial assertions about the contents of the chainstate.
+ {
+ LOCK(::cs_main);
+ CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
+ initial_size = ibd_coinscache.GetCacheSize();
size_t total_coins{0};
for (CTransactionRef& txn : m_coinbase_txns) {
COutPoint op{txn->GetHash(), 0};
- BOOST_CHECK(coinscache.HaveCoin(op));
+ BOOST_CHECK(ibd_coinscache.HaveCoin(op));
total_coins++;
}
- BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
- chains_tested++;
+ BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
}
- BOOST_CHECK_EQUAL(chains_tested, 2);
- }
+ Chainstate& validation_chainstate = chainman.ActiveChainstate();
+
+ // Snapshot should refuse to load at this height.
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(this));
+ BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
+ BOOST_CHECK(!chainman.SnapshotBlockhash());
+
+ // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
+ // be found.
+ constexpr int snapshot_height = 110;
+ mineBlocks(10);
+ initial_size += 10;
+ initial_total_coins += 10;
+
+ // Should not load malleated snapshots
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // A UTXO is missing but count is correct
+ metadata.m_coins_count -= 1;
+
+ COutPoint outpoint;
+ Coin coin;
+
+ auto_infile >> outpoint;
+ auto_infile >> coin;
+ }));
+
+ BOOST_CHECK(!node::FindSnapshotChainstateDir());
+
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Coins count is larger than coins in file
+ metadata.m_coins_count += 1;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Coins count is smaller than coins in file
+ metadata.m_coins_count -= 1;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Wrong hash
+ metadata.m_base_blockhash = uint256::ZERO;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Wrong hash
+ metadata.m_base_blockhash = uint256::ONE;
+ }));
+
+ BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(this));
+ BOOST_CHECK(fs::exists(*node::FindSnapshotChainstateDir()));
+
+ // Ensure our active chain is the snapshot chainstate.
+ BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
+ BOOST_CHECK_EQUAL(
+ *chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ *chainman.SnapshotBlockhash());
+
+ Chainstate& snapshot_chainstate = chainman.ActiveChainstate();
+
+ {
+ LOCK(::cs_main);
- // Mine some new blocks on top of the activated snapshot chainstate.
- constexpr size_t new_coins{100};
- mineBlocks(new_coins); // Defined in TestChain100Setup.
+ fs::path found = *node::FindSnapshotChainstateDir();
- {
- LOCK(::cs_main);
- size_t coins_in_active{0};
- size_t coins_in_background{0};
- size_t coins_missing_from_background{0};
+ // Note: WriteSnapshotBaseBlockhash() is implicitly tested above.
+ BOOST_CHECK_EQUAL(
+ *node::ReadSnapshotBaseBlockhash(found),
+ *chainman.SnapshotBlockhash());
- for (CChainState* chainstate : chainman.GetAll()) {
- BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
- CCoinsViewCache& coinscache = chainstate->CoinsTip();
- bool is_background = chainstate != &chainman.ActiveChainstate();
+ // Ensure that the genesis block was not marked assumed-valid.
+ BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
+ }
- for (CTransactionRef& txn : m_coinbase_txns) {
- COutPoint op{txn->GetHash(), 0};
- if (coinscache.HaveCoin(op)) {
- (is_background ? coins_in_background : coins_in_active)++;
- } else if (is_background) {
- coins_missing_from_background++;
+ const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
+ const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
+
+ BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx);
+
+ // To be checked against later when we try loading a subsequent snapshot.
+ uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
+
+ // Make some assertions about the both chainstates. These checks ensure the
+ // legacy chainstate hasn't changed and that the newly created chainstate
+ // reflects the expected content.
+ {
+ LOCK(::cs_main);
+ int chains_tested{0};
+
+ for (Chainstate* chainstate : chainman.GetAll()) {
+ BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
+ CCoinsViewCache& coinscache = chainstate->CoinsTip();
+
+ // Both caches will be empty initially.
+ BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
+
+ size_t total_coins{0};
+
+ for (CTransactionRef& txn : m_coinbase_txns) {
+ COutPoint op{txn->GetHash(), 0};
+ BOOST_CHECK(coinscache.HaveCoin(op));
+ total_coins++;
}
+
+ BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
+ BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
+ chains_tested++;
}
+
+ BOOST_CHECK_EQUAL(chains_tested, 2);
}
- BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
- BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
- BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
+ // Mine some new blocks on top of the activated snapshot chainstate.
+ constexpr size_t new_coins{100};
+ mineBlocks(new_coins); // Defined in TestChain100Setup.
+
+ {
+ LOCK(::cs_main);
+ size_t coins_in_active{0};
+ size_t coins_in_background{0};
+ size_t coins_missing_from_background{0};
+
+ for (Chainstate* chainstate : chainman.GetAll()) {
+ BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
+ CCoinsViewCache& coinscache = chainstate->CoinsTip();
+ bool is_background = chainstate != &chainman.ActiveChainstate();
+
+ for (CTransactionRef& txn : m_coinbase_txns) {
+ COutPoint op{txn->GetHash(), 0};
+ if (coinscache.HaveCoin(op)) {
+ (is_background ? coins_in_background : coins_in_active)++;
+ } else if (is_background) {
+ coins_missing_from_background++;
+ }
+ }
+ }
+
+ BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
+ BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
+ BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
+ }
+
+ // Snapshot should refuse to load after one has already loaded.
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(this));
+
+ // Snapshot blockhash should be unchanged.
+ BOOST_CHECK_EQUAL(
+ *chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ loaded_snapshot_blockhash);
+ return std::make_tuple(&validation_chainstate, &snapshot_chainstate);
}
- // Snapshot should refuse to load after one has already loaded.
- BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+ // Simulate a restart of the node by flushing all state to disk, clearing the
+ // existing ChainstateManager, and unloading the block index.
+ //
+ // @returns a reference to the "restarted" ChainstateManager
+ ChainstateManager& SimulateNodeRestart()
+ {
+ ChainstateManager& chainman = *Assert(m_node.chainman);
+
+ BOOST_TEST_MESSAGE("Simulating node restart");
+ {
+ LOCK(::cs_main);
+ for (Chainstate* cs : chainman.GetAll()) {
+ cs->ForceFlushStateToDisk();
+ }
+ chainman.ResetChainstates();
+ BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
+ const ChainstateManager::Options chainman_opts{
+ .chainparams = ::Params(),
+ .adjusted_time_callback = GetAdjustedTime,
+ };
+ // For robustness, ensure the old manager is destroyed before creating a
+ // new one.
+ m_node.chainman.reset();
+ m_node.chainman.reset(new ChainstateManager(chainman_opts));
+ }
+ return *Assert(m_node.chainman);
+ }
+};
- // Snapshot blockhash should be unchanged.
- BOOST_CHECK_EQUAL(
- *chainman.ActiveChainstate().m_from_snapshot_blockhash,
- loaded_snapshot_blockhash);
+//! Test basic snapshot activation.
+BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, SnapshotTestSetup)
+{
+ this->SetupSnapshot();
}
//! Test LoadBlockIndex behavior when multiple chainstates are in use.
//!
-//! - First, verfiy that setBlockIndexCandidates is as expected when using a single,
+//! - First, verify that setBlockIndexCandidates is as expected when using a single,
//! fully-validating chainstate.
//!
//! - Then mark a region of the chain BLOCK_ASSUMED_VALID and introduce a second chainstate
@@ -326,7 +405,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
{
ChainstateManager& chainman = *Assert(m_node.chainman);
CTxMemPool& mempool = *m_node.mempool;
- CChainState& cs1 = chainman.ActiveChainstate();
+ Chainstate& cs1 = chainman.ActiveChainstate();
int num_indexes{0};
int num_assumed_valid{0};
@@ -338,7 +417,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
CBlockIndex* assumed_tip{WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip())};
auto reload_all_block_indexes = [&]() {
- for (CChainState* cs : chainman.GetAll()) {
+ for (Chainstate* cs : chainman.GetAll()) {
LOCK(::cs_main);
cs->UnloadBlockIndex();
BOOST_CHECK(cs->setBlockIndexCandidates.empty());
@@ -373,8 +452,8 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
- CChainState& cs2 = WITH_LOCK(::cs_main,
- return chainman.InitializeChainstate(&mempool, GetRandHash()));
+ Chainstate& cs2 = WITH_LOCK(::cs_main,
+ return chainman.ActivateExistingSnapshot(&mempool, GetRandHash()));
reload_all_block_indexes();
@@ -390,4 +469,59 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes);
}
+//! Ensure that snapshot chainstates initialize properly when found on disk.
+BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
+{
+ this->SetupSnapshot();
+
+ ChainstateManager& chainman = *Assert(m_node.chainman);
+
+ fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir();
+ BOOST_CHECK(fs::exists(snapshot_chainstate_dir));
+ BOOST_CHECK_EQUAL(snapshot_chainstate_dir, gArgs.GetDataDirNet() / "chainstate_snapshot");
+
+ BOOST_CHECK(chainman.IsSnapshotActive());
+ const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),
+ return chainman.ActiveTip()->GetBlockHash());
+
+ auto all_chainstates = chainman.GetAll();
+ BOOST_CHECK_EQUAL(all_chainstates.size(), 2);
+
+ // Test that simulating a shutdown (resetting ChainstateManager) and then performing
+ // chainstate reinitializing successfully cleans up the background-validation
+ // chainstate data, and we end up with a single chainstate that is at tip.
+ ChainstateManager& chainman_restarted = this->SimulateNodeRestart();
+
+ BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate");
+
+ // This call reinitializes the chainstates.
+ this->LoadVerifyActivateChainstate();
+
+ {
+ LOCK(chainman_restarted.GetMutex());
+ BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 2);
+ BOOST_CHECK(chainman_restarted.IsSnapshotActive());
+ BOOST_CHECK(!chainman_restarted.IsSnapshotValidated());
+
+ BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash);
+ BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);
+ }
+
+ BOOST_TEST_MESSAGE(
+ "Ensure we can mine blocks on top of the initialized snapshot chainstate");
+ mineBlocks(10);
+ {
+ LOCK(chainman_restarted.GetMutex());
+ BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220);
+
+ // Background chainstate should be unaware of new blocks on the snapshot
+ // chainstate.
+ for (Chainstate* cs : chainman_restarted.GetAll()) {
+ if (cs != &chainman_restarted.ActiveChainstate()) {
+ BOOST_CHECK_EQUAL(cs->m_chain.Height(), 110);
+ }
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 74b2af6858..c06e6c8d3b 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -13,11 +13,11 @@ BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
//! Test utilities for detecting when we need to flush the coins cache based
//! on estimated memory usage.
//!
-//! @sa CChainState::GetCoinsCacheSizeState()
+//! @sa Chainstate::GetCoinsCacheSizeState()
//!
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
- CChainState& chainstate{m_node.chainman->ActiveChainstate()};
+ Chainstate& chainstate{m_node.chainman->ActiveChainstate()};
constexpr bool is_64_bit = sizeof(void*) == 8;
diff --git a/src/txdb.h b/src/txdb.h
index a04596f7bb..8c41e26f6a 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -9,6 +9,7 @@
#include <coins.h>
#include <dbwrapper.h>
#include <sync.h>
+#include <fs.h>
#include <memory>
#include <optional>
@@ -72,6 +73,9 @@ public:
//! Dynamically alter the underlying leveldb cache size.
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! @returns filesystem path to on-disk storage or std::nullopt if in memory.
+ std::optional<fs::path> StoragePath() { return m_db->StoragePath(); }
};
/** Access to the block database (blocks/index/) */
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index b151953d0d..84ed2e9ef5 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -24,38 +24,6 @@
#include <cmath>
#include <optional>
-// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
-struct update_descendant_state
-{
- update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
-};
-
-struct update_ancestor_state
-{
- update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
- int64_t modifySigOpsCost;
-};
-
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
{
AssertLockHeld(cs_main);
@@ -146,7 +114,9 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendan
modifyCount++;
cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
// Update ancestor state for each descendant
- mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
+ mapTx.modify(mapTx.iterator_to(descendant), [=](CTxMemPoolEntry& e) {
+ e.UpdateAncestorState(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost());
+ });
// Don't directly remove the transaction here -- doing so would
// invalidate iterators in cachedDescendants. Mark it for removal
// by inserting into descendants_to_remove.
@@ -155,7 +125,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendan
}
}
}
- mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
+ mapTx.modify(updateIt, [=](CTxMemPoolEntry& e) { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); });
}
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate)
@@ -175,7 +145,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256>& vHashes
// Iterate in reverse, so that whenever we are looking at a transaction
// we are sure that all in-mempool descendants have already been processed.
// This maximizes the benefit of the descendant cache and guarantees that
- // CTxMemPool::m_children will be updated, an assumption made in
+ // CTxMemPoolEntry::m_children will be updated, an assumption made in
// UpdateForDescendants.
for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) {
// calculate children from mapNextTx
@@ -184,7 +154,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256>& vHashes
continue;
}
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
- // First calculate the children, and update CTxMemPool::m_children to
+ // First calculate the children, and update CTxMemPoolEntry::m_children to
// include them, and update their CTxMemPoolEntry::m_parents to include this tx.
// we cache the in-mempool children to avoid duplicate updates
{
@@ -217,10 +187,7 @@ bool CTxMemPool::CalculateAncestorsAndCheckLimits(size_t entry_size,
size_t entry_count,
setEntries& setAncestors,
CTxMemPoolEntry::Parents& staged_ancestors,
- uint64_t limitAncestorCount,
- uint64_t limitAncestorSize,
- uint64_t limitDescendantCount,
- uint64_t limitDescendantSize,
+ const Limits& limits,
std::string &errString) const
{
size_t totalSizeWithAncestors = entry_size;
@@ -233,14 +200,14 @@ bool CTxMemPool::CalculateAncestorsAndCheckLimits(size_t entry_size,
staged_ancestors.erase(stage);
totalSizeWithAncestors += stageit->GetTxSize();
- if (stageit->GetSizeWithDescendants() + entry_size > limitDescendantSize) {
- errString = strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantSize);
+ if (stageit->GetSizeWithDescendants() + entry_size > static_cast<uint64_t>(limits.descendant_size_vbytes)) {
+ errString = strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limits.descendant_size_vbytes);
return false;
- } else if (stageit->GetCountWithDescendants() + entry_count > limitDescendantCount) {
- errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantCount);
+ } else if (stageit->GetCountWithDescendants() + entry_count > static_cast<uint64_t>(limits.descendant_count)) {
+ errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limits.descendant_count);
return false;
- } else if (totalSizeWithAncestors > limitAncestorSize) {
- errString = strprintf("exceeds ancestor size limit [limit: %u]", limitAncestorSize);
+ } else if (totalSizeWithAncestors > static_cast<uint64_t>(limits.ancestor_size_vbytes)) {
+ errString = strprintf("exceeds ancestor size limit [limit: %u]", limits.ancestor_size_vbytes);
return false;
}
@@ -252,8 +219,8 @@ bool CTxMemPool::CalculateAncestorsAndCheckLimits(size_t entry_size,
if (setAncestors.count(parent_it) == 0) {
staged_ancestors.insert(parent);
}
- if (staged_ancestors.size() + setAncestors.size() + entry_count > limitAncestorCount) {
- errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount);
+ if (staged_ancestors.size() + setAncestors.size() + entry_count > static_cast<uint64_t>(limits.ancestor_count)) {
+ errString = strprintf("too many unconfirmed ancestors [limit: %u]", limits.ancestor_count);
return false;
}
}
@@ -263,10 +230,7 @@ bool CTxMemPool::CalculateAncestorsAndCheckLimits(size_t entry_size,
}
bool CTxMemPool::CheckPackageLimits(const Package& package,
- uint64_t limitAncestorCount,
- uint64_t limitAncestorSize,
- uint64_t limitDescendantCount,
- uint64_t limitDescendantSize,
+ const Limits& limits,
std::string &errString) const
{
CTxMemPoolEntry::Parents staged_ancestors;
@@ -277,8 +241,8 @@ bool CTxMemPool::CheckPackageLimits(const Package& package,
std::optional<txiter> piter = GetIter(input.prevout.hash);
if (piter) {
staged_ancestors.insert(**piter);
- if (staged_ancestors.size() + package.size() > limitAncestorCount) {
- errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
+ if (staged_ancestors.size() + package.size() > static_cast<uint64_t>(limits.ancestor_count)) {
+ errString = strprintf("too many unconfirmed parents [limit: %u]", limits.ancestor_count);
return false;
}
}
@@ -290,8 +254,7 @@ bool CTxMemPool::CheckPackageLimits(const Package& package,
setEntries setAncestors;
const auto ret = CalculateAncestorsAndCheckLimits(total_size, package.size(),
setAncestors, staged_ancestors,
- limitAncestorCount, limitAncestorSize,
- limitDescendantCount, limitDescendantSize, errString);
+ limits, errString);
// It's possible to overestimate the ancestor/descendant totals.
if (!ret) errString.insert(0, "possibly ");
return ret;
@@ -299,10 +262,7 @@ bool CTxMemPool::CheckPackageLimits(const Package& package,
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
setEntries &setAncestors,
- uint64_t limitAncestorCount,
- uint64_t limitAncestorSize,
- uint64_t limitDescendantCount,
- uint64_t limitDescendantSize,
+ const Limits& limits,
std::string &errString,
bool fSearchForParents /* = true */) const
{
@@ -317,8 +277,8 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
if (piter) {
staged_ancestors.insert(**piter);
- if (staged_ancestors.size() + 1 > limitAncestorCount) {
- errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
+ if (staged_ancestors.size() + 1 > static_cast<uint64_t>(limits.ancestor_count)) {
+ errString = strprintf("too many unconfirmed parents [limit: %u]", limits.ancestor_count);
return false;
}
}
@@ -332,8 +292,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
return CalculateAncestorsAndCheckLimits(entry.GetTxSize(), /*entry_count=*/1,
setAncestors, staged_ancestors,
- limitAncestorCount, limitAncestorSize,
- limitDescendantCount, limitDescendantSize, errString);
+ limits, errString);
}
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
@@ -347,7 +306,7 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors
const int64_t updateSize = updateCount * it->GetTxSize();
const CAmount updateFee = updateCount * it->GetModifiedFee();
for (txiter ancestorIt : setAncestors) {
- mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount));
+ mapTx.modify(ancestorIt, [=](CTxMemPoolEntry& e) { e.UpdateDescendantState(updateSize, updateFee, updateCount); });
}
}
@@ -362,7 +321,7 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto
updateFee += ancestorIt->GetModifiedFee();
updateSigOpsCost += ancestorIt->GetSigOpCost();
}
- mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOpsCost));
+ mapTx.modify(it, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(updateSize, updateFee, updateCount, updateSigOpsCost); });
}
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
@@ -377,7 +336,6 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
{
// For each entry, walk back all ancestors and decrement size associated with this
// transaction
- const uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
if (updateDescendants) {
// updateDescendants should be true whenever we're not recursively
// removing a tx and all its descendants, eg when a transaction is
@@ -393,7 +351,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
CAmount modifyFee = -removeIt->GetModifiedFee();
int modifySigOps = -removeIt->GetSigOpCost();
for (txiter dit : setDescendants) {
- mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps));
+ mapTx.modify(dit, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(modifySize, modifyFee, -1, modifySigOps); });
}
}
}
@@ -420,7 +378,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// mempool parents we'd calculate by searching, and it's important that
// we use the cached notion of ancestor transactions as the set of
// things to update for removal.
- CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
+ CalculateMemPoolAncestors(entry, setAncestors, Limits::NoLimits(), dummy, false);
// Note that UpdateAncestorsOf severs the child links that point to
// removeIt in the entries for the parents of removeIt.
UpdateAncestorsOf(false, removeIt, setAncestors);
@@ -774,9 +732,8 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
assert(std::equal(setParentCheck.begin(), setParentCheck.end(), it->GetMemPoolParentsConst().begin(), comp));
// Verify ancestor state is correct.
setEntries setAncestors;
- uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
- CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
+ CalculateMemPoolAncestors(*it, setAncestors, Limits::NoLimits(), dummy);
uint64_t nCountCheck = setAncestors.size() + 1;
uint64_t nSizeCheck = it->GetTxSize();
CAmount nFeesCheck = it->GetModifiedFee();
@@ -938,18 +895,17 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
mapTx.modify(it, [&nFeeDelta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(nFeeDelta); });
// Now update all ancestors' modified fees with descendants
setEntries setAncestors;
- uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
- CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
+ CalculateMemPoolAncestors(*it, setAncestors, Limits::NoLimits(), dummy, false);
for (txiter ancestorIt : setAncestors) {
- mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0));
+ mapTx.modify(ancestorIt, [=](CTxMemPoolEntry& e){ e.UpdateDescendantState(0, nFeeDelta, 0);});
}
// Now update all descendants' modified fees with ancestors
setEntries setDescendants;
CalculateDescendants(it, setDescendants);
setDescendants.erase(it);
for (txiter descendantIt : setDescendants) {
- mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0));
+ mapTx.modify(descendantIt, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(0, nFeeDelta, 0, 0); });
}
++nTransactionsUpdated;
}
@@ -1079,9 +1035,8 @@ int CTxMemPool::Expire(std::chrono::seconds time)
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimate)
{
setEntries setAncestors;
- uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
- CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
+ CalculateMemPoolAncestors(entry, setAncestors, Limits::NoLimits(), dummy);
return addUnchecked(entry, setAncestors, validFeeEstimate);
}
diff --git a/src/txmempool.h b/src/txmempool.h
index d06816ba97..4afaac0506 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -35,7 +35,7 @@
class CBlockIndex;
class CChain;
-class CChainState;
+class Chainstate;
extern RecursiveMutex cs_main;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
@@ -526,6 +526,8 @@ public:
typedef std::set<txiter, CompareIteratorByHash> setEntries;
+ using Limits = kernel::MemPoolLimits;
+
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
private:
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
@@ -545,19 +547,22 @@ private:
/**
* Helper function to calculate all in-mempool ancestors of staged_ancestors and apply ancestor
* and descendant limits (including staged_ancestors thsemselves, entry_size and entry_count).
- * param@[in] entry_size Virtual size to include in the limits.
- * param@[in] entry_count How many entries to include in the limits.
- * param@[in] staged_ancestors Should contain entries in the mempool.
- * param@[out] setAncestors Will be populated with all mempool ancestors.
+ *
+ * @param[in] entry_size Virtual size to include in the limits.
+ * @param[in] entry_count How many entries to include in the limits.
+ * @param[out] setAncestors Will be populated with all mempool ancestors.
+ * @param[in] staged_ancestors Should contain entries in the mempool.
+ * @param[in] limits Maximum number and size of ancestors and descendants
+ * @param[out] errString Populated with error reason if any limits are hit
+ *
+ * @return true if no limits were hit and all in-mempool ancestors were calculated, false
+ * otherwise
*/
bool CalculateAncestorsAndCheckLimits(size_t entry_size,
size_t entry_count,
setEntries& setAncestors,
CTxMemPoolEntry::Parents &staged_ancestors,
- uint64_t limitAncestorCount,
- uint64_t limitAncestorSize,
- uint64_t limitDescendantCount,
- uint64_t limitDescendantSize,
+ const Limits& limits,
std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
@@ -576,8 +581,6 @@ public:
const bool m_require_standard;
const bool m_full_rbf;
- using Limits = kernel::MemPoolLimits;
-
const Limits m_limits;
/** Create a new CTxMemPool.
@@ -668,38 +671,41 @@ public:
*/
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
- /** Try to calculate all in-mempool ancestors of entry.
- * (these are all calculated including the tx itself)
- * limitAncestorCount = max number of ancestors
- * limitAncestorSize = max size of ancestors
- * limitDescendantCount = max number of descendants any ancestor can have
- * limitDescendantSize = max size of descendants any ancestor can have
- * errString = populated with error reason if any limits are hit
- * fSearchForParents = whether to search a tx's vin for in-mempool parents, or
- * look up parents from mapLinks. Must be true for entries not in the mempool
+ /**
+ * Try to calculate all in-mempool ancestors of entry.
+ * (these are all calculated including the tx itself)
+ *
+ * @param[in] entry CTxMemPoolEntry of which all in-mempool ancestors are calculated
+ * @param[out] setAncestors Will be populated with all mempool ancestors.
+ * @param[in] limits Maximum number and size of ancestors and descendants
+ * @param[out] errString Populated with error reason if any limits are hit
+ * @param[in] fSearchForParents Whether to search a tx's vin for in-mempool parents, or look
+ * up parents from mapLinks. Must be true for entries not in
+ * the mempool
+ *
+ * @return true if no limits were hit and all in-mempool ancestors were calculated, false
+ * otherwise
*/
- bool CalculateMemPoolAncestors(const CTxMemPoolEntry& entry, setEntries& setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string& errString, bool fSearchForParents = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ bool CalculateMemPoolAncestors(const CTxMemPoolEntry& entry,
+ setEntries& setAncestors,
+ const Limits& limits,
+ std::string& errString,
+ bool fSearchForParents = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Calculate all in-mempool ancestors of a set of transactions not already in the mempool and
* check ancestor and descendant limits. Heuristics are used to estimate the ancestor and
* descendant count of all entries if the package were to be added to the mempool. The limits
* are applied to the union of all package transactions. For example, if the package has 3
- * transactions and limitAncestorCount = 25, the union of all 3 sets of ancestors (including the
+ * transactions and limits.ancestor_count = 25, the union of all 3 sets of ancestors (including the
* transactions themselves) must be <= 22.
* @param[in] package Transaction package being evaluated for acceptance
* to mempool. The transactions need not be direct
* ancestors/descendants of each other.
- * @param[in] limitAncestorCount Max number of txns including ancestors.
- * @param[in] limitAncestorSize Max virtual size including ancestors.
- * @param[in] limitDescendantCount Max number of txns including descendants.
- * @param[in] limitDescendantSize Max virtual size including descendants.
+ * @param[in] limits Maximum number and size of ancestors and descendants
* @param[out] errString Populated with error reason if a limit is hit.
*/
bool CheckPackageLimits(const Package& package,
- uint64_t limitAncestorCount,
- uint64_t limitAncestorSize,
- uint64_t limitDescendantCount,
- uint64_t limitDescendantSize,
+ const Limits& limits,
std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Populate setDescendants with all in-mempool descendants of hash.
@@ -825,7 +831,7 @@ private:
* mempool but may have child transactions in the mempool, eg during a
* chain reorg.
*
- * @pre CTxMemPool::m_children is correct for the given tx and all
+ * @pre CTxMemPoolEntry::m_children is correct for the given tx and all
* descendants.
* @pre cachedDescendants is an accurate cache where each entry has all
* descendants of the corresponding key, including those that should
diff --git a/src/uint256.h b/src/uint256.h
index 5c3a2f5409..e74b9ff7b1 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_UINT256_H
#define BITCOIN_UINT256_H
+#include <crypto/common.h>
#include <span.h>
#include <assert.h>
@@ -84,15 +85,7 @@ public:
uint64_t GetUint64(int pos) const
{
- const uint8_t* ptr = m_data + pos * 8;
- return ((uint64_t)ptr[0]) | \
- ((uint64_t)ptr[1]) << 8 | \
- ((uint64_t)ptr[2]) << 16 | \
- ((uint64_t)ptr[3]) << 24 | \
- ((uint64_t)ptr[4]) << 32 | \
- ((uint64_t)ptr[5]) << 40 | \
- ((uint64_t)ptr[6]) << 48 | \
- ((uint64_t)ptr[7]) << 56;
+ return ReadLE64(m_data + pos * 8);
}
template<typename Stream>
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index ccaf56a271..850d0a1cbc 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -19,6 +19,11 @@ class UniValue {
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
+ class type_error : public std::runtime_error
+ {
+ using std::runtime_error::runtime_error;
+ };
+
UniValue() { typ = VNULL; }
UniValue(UniValue::VType initialType, const std::string& initialStr = "") {
typ = initialType;
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index 12a434dd0e..4448981d3e 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -210,7 +210,7 @@ const UniValue& UniValue::operator[](size_t index) const
void UniValue::checkType(const VType& expected) const
{
if (typ != expected) {
- throw std::runtime_error{"JSON value of type " + std::string{uvTypeName(typ)} + " is not of expected type " +
+ throw type_error{"JSON value of type " + std::string{uvTypeName(typ)} + " is not of expected type " +
std::string{uvTypeName(expected)}};
}
}
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 33a35a6d59..390cb6c11b 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -49,6 +49,11 @@ bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBi
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
+bilingual_str InvalidPortErrMsg(const std::string& optname, const std::string& invalid_value)
+{
+ return strprintf(_("Invalid port specified in %s: '%s'"), optname, invalid_value);
+}
+
bilingual_str AmountHighWarn(const std::string& optname)
{
return strprintf(_("%s is set very high!"), optname);
diff --git a/src/util/error.h b/src/util/error.h
index 0429de651a..27916501f0 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -39,6 +39,8 @@ bilingual_str TransactionErrorString(const TransactionError error);
bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind);
+bilingual_str InvalidPortErrMsg(const std::string& optname, const std::string& strPort);
+
bilingual_str AmountHighWarn(const std::string& optname);
bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue);
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 125dbc7f18..84ac2759fa 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -117,6 +117,34 @@ int Sock::GetSockName(sockaddr* name, socklen_t* name_len) const
return getsockname(m_socket, name, name_len);
}
+bool Sock::SetNonBlocking() const
+{
+#ifdef WIN32
+ u_long on{1};
+ if (ioctlsocket(m_socket, FIONBIO, &on) == SOCKET_ERROR) {
+ return false;
+ }
+#else
+ const int flags{fcntl(m_socket, F_GETFL, 0)};
+ if (flags == SOCKET_ERROR) {
+ return false;
+ }
+ if (fcntl(m_socket, F_SETFL, flags | O_NONBLOCK) == SOCKET_ERROR) {
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool Sock::IsSelectable() const
+{
+#if defined(USE_POLL) || defined(WIN32)
+ return true;
+#else
+ return m_socket < FD_SETSIZE;
+#endif
+}
+
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
// We need a `shared_ptr` owning `this` for `WaitMany()`, but don't want
@@ -185,10 +213,10 @@ bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per
SOCKET socket_max{0};
for (const auto& [sock, events] : events_per_sock) {
- const auto& s = sock->m_socket;
- if (!IsSelectableSocket(s)) {
+ if (!sock->IsSelectable()) {
return false;
}
+ const auto& s = sock->m_socket;
if (events.requested & RECV) {
FD_SET(s, &recv);
}
diff --git a/src/util/sock.h b/src/util/sock.h
index 38a7dc80d6..7912284904 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -133,6 +133,18 @@ public:
*/
[[nodiscard]] virtual int GetSockName(sockaddr* name, socklen_t* name_len) const;
+ /**
+ * Set the non-blocking option on the socket.
+ * @return true if set successfully
+ */
+ [[nodiscard]] virtual bool SetNonBlocking() const;
+
+ /**
+ * Check if the underlying socket can be used for `select(2)` (or the `Wait()` method).
+ * @return true if selectable
+ */
+ [[nodiscard]] virtual bool IsSelectable() const;
+
using Event = uint8_t;
/**
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index b5ac151374..e28ca8e73a 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -97,8 +97,9 @@ std::vector<Byte> ParseHex(std::string_view str)
template std::vector<std::byte> ParseHex(std::string_view);
template std::vector<uint8_t> ParseHex(std::string_view);
-void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
+bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
{
+ bool valid = false;
size_t colon = in.find_last_of(':');
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
bool fHaveColon = colon != in.npos;
@@ -109,13 +110,18 @@ void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
if (ParseUInt16(in.substr(colon + 1), &n)) {
in = in.substr(0, colon);
portOut = n;
+ valid = (portOut != 0);
}
+ } else {
+ valid = true;
}
if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
hostOut = in.substr(1, in.size() - 2);
} else {
hostOut = in;
}
+
+ return valid;
}
std::string EncodeBase64(Span<const unsigned char> input)
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 14867b21b2..94bc6cc2f3 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -88,7 +88,16 @@ std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
*/
std::string EncodeBase32(std::string_view str, bool pad = true);
-void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut);
+/**
+ * Splits socket address string into host string and port value.
+ * Validates port value.
+ *
+ * @param[in] in The socket address string to split.
+ * @param[out] portOut Port-portion of the input, if found and parsable.
+ * @param[out] hostOut Host-portion of the input, if found.
+ * @return true if port-portion is absent or within its allowed range, otherwise false
+ */
+bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut);
// LocaleIndependentAtoi is provided for backwards compatibility reasons.
//
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 1953a9f836..d7a0793ea8 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -5,19 +5,6 @@
#include <util/system.h>
-#ifdef ENABLE_EXTERNAL_SIGNER
-#if defined(__GNUC__)
-// Boost 1.78 requires the following workaround.
-// See: https://github.com/boostorg/process/issues/235
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnarrowing"
-#endif
-#include <boost/process.hpp>
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
-#endif // ENABLE_EXTERNAL_SIGNER
-
#include <chainparamsbase.h>
#include <fs.h>
#include <sync.h>
@@ -831,7 +818,7 @@ std::string HelpMessageOpt(const std::string &option, const std::string &message
std::string("\n\n");
}
-static std::string FormatException(const std::exception* pex, const char* pszThread)
+static std::string FormatException(const std::exception* pex, std::string_view thread_name)
{
#ifdef WIN32
char pszModule[MAX_PATH] = "";
@@ -841,15 +828,15 @@ static std::string FormatException(const std::exception* pex, const char* pszThr
#endif
if (pex)
return strprintf(
- "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
+ "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, thread_name);
else
return strprintf(
- "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
+ "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, thread_name);
}
-void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
+void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name)
{
- std::string message = FormatException(pex, pszThread);
+ std::string message = FormatException(pex, thread_name);
LogPrintf("\n\n************************\n%s\n", message);
tfm::format(std::cerr, "\n\n************************\n%s\n", message);
}
@@ -935,6 +922,20 @@ static bool GetConfigOptions(std::istream& stream, const std::string& filepath,
return true;
}
+bool IsConfSupported(KeyInfo& key, std::string& error) {
+ if (key.name == "conf") {
+ error = "conf cannot be set in the configuration file; use includeconf= if you want to include additional config files";
+ return false;
+ }
+ if (key.name == "reindex") {
+ // reindex can be set in a config file but it is strongly discouraged as this will cause the node to reindex on
+ // every restart. Allow the config but throw a warning
+ LogPrintf("Warning: reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary\n");
+ return true;
+ }
+ return true;
+}
+
bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
@@ -945,6 +946,7 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
for (const std::pair<std::string, std::string>& option : options) {
KeyInfo key = InterpretKey(option.first);
std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
+ if (!IsConfSupported(key, error)) return false;
if (flags) {
std::optional<util::SettingsValue> value = InterpretValue(key, &option.second, *flags, error);
if (!value) {
@@ -1332,44 +1334,6 @@ void runCommand(const std::string& strCommand)
}
#endif
-UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
-{
-#ifdef ENABLE_EXTERNAL_SIGNER
- namespace bp = boost::process;
-
- UniValue result_json;
- bp::opstream stdin_stream;
- bp::ipstream stdout_stream;
- bp::ipstream stderr_stream;
-
- if (str_command.empty()) return UniValue::VNULL;
-
- bp::child c(
- str_command,
- bp::std_out > stdout_stream,
- bp::std_err > stderr_stream,
- bp::std_in < stdin_stream
- );
- if (!str_std_in.empty()) {
- stdin_stream << str_std_in << std::endl;
- }
- stdin_stream.pipe().close();
-
- std::string result;
- std::string error;
- std::getline(stdout_stream, result);
- std::getline(stderr_stream, error);
-
- c.wait();
- const int n_error = c.exit_code();
- if (n_error) throw std::runtime_error(strprintf("RunCommandParseJSON error: process(%s) returned %d: %s\n", str_command, n_error, error));
- if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
-
- return result_json;
-#else
- throw std::runtime_error("Compiled without external signing support (required for external signing).");
-#endif // ENABLE_EXTERNAL_SIGNER
-}
void SetupEnvironment()
{
diff --git a/src/util/system.h b/src/util/system.h
index 756e6642f2..29629e547e 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -51,7 +51,7 @@ bool error(const char* fmt, const Args&... args)
return false;
}
-void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
+void PrintExceptionContinue(const std::exception* pex, std::string_view thread_name);
/**
* Ensure file contents are fully committed to disk, using a platform-specific
@@ -107,14 +107,6 @@ std::string ShellEscape(const std::string& arg);
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
-/**
- * Execute a command which returns JSON, and parse the result.
- *
- * @param str_command The command to execute, including any arguments
- * @param str_std_in string to pass to stdin
- * @return parsed JSON
- */
-UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
/**
* Most paths passed as configuration arguments are treated as relative to
diff --git a/src/util/thread.cpp b/src/util/thread.cpp
index f9f427ba20..ae98abdb3d 100644
--- a/src/util/thread.cpp
+++ b/src/util/thread.cpp
@@ -10,10 +10,12 @@
#include <exception>
#include <functional>
+#include <string>
+#include <utility>
-void util::TraceThread(const char* thread_name, std::function<void()> thread_func)
+void util::TraceThread(std::string_view thread_name, std::function<void()> thread_func)
{
- util::ThreadRename(thread_name);
+ util::ThreadRename(std::string{thread_name});
try {
LogPrintf("%s thread start\n", thread_name);
thread_func();
diff --git a/src/util/thread.h b/src/util/thread.h
index ca2eccc0c3..b80bf046a0 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -6,12 +6,13 @@
#define BITCOIN_UTIL_THREAD_H
#include <functional>
+#include <string>
namespace util {
/**
* A wrapper for do-something-once thread functions.
*/
-void TraceThread(const char* thread_name, std::function<void()> thread_func);
+void TraceThread(std::string_view thread_name, std::function<void()> thread_func);
} // namespace util
diff --git a/src/util/time.cpp b/src/util/time.cpp
index f6d37347f8..0b20849079 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -12,8 +12,6 @@
#include <util/time.h>
#include <util/check.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
#include <atomic>
#include <chrono>
#include <ctime>
@@ -142,20 +140,6 @@ std::string FormatISO8601Date(int64_t nTime) {
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}
-int64_t ParseISO8601DateTime(const std::string& str)
-{
- static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
- static const std::locale loc(std::locale::classic(),
- new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
- std::istringstream iss(str);
- iss.imbue(loc);
- boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
- iss >> ptime;
- if (ptime.is_not_a_date_time() || epoch > ptime)
- return 0;
- return (ptime - epoch).total_seconds();
-}
-
struct timeval MillisToTimeval(int64_t nTimeout)
{
struct timeval timeout;
diff --git a/src/util/time.h b/src/util/time.h
index 4f9bde5d56..10a581a44c 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -57,6 +57,7 @@ constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.cou
using HoursDouble = std::chrono::duration<double, std::chrono::hours::period>;
using SecondsDouble = std::chrono::duration<double, std::chrono::seconds::period>;
+using MillisecondsDouble = std::chrono::duration<double, std::chrono::milliseconds::period>;
/**
* DEPRECATED
@@ -109,7 +110,6 @@ T GetTime()
*/
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
-int64_t ParseISO8601DateTime(const std::string& str);
/**
* Convert milliseconds to a struct timeval for e.g. select.
diff --git a/src/validation.cpp b/src/validation.cpp
index 3e17d4cd87..debdc2ae74 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -82,9 +82,6 @@ using node::SnapshotMetadata;
using node::UndoReadFromDisk;
using node::UnlinkPrunedFiles;
-#define MICRO 0.000001
-#define MILLI 0.001
-
/** Maximum kilobytes for transactions to store for processing during reorg */
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
/** Time to wait between writing blocks/block index to disk. */
@@ -123,15 +120,8 @@ RecursiveMutex cs_main;
GlobalMutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
-bool g_parallel_script_checks{false};
-bool fCheckBlockIndex = false;
-bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
-int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
-
-uint256 hashAssumeValid;
-arith_uint256 nMinimumChainWork;
-const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
+const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -273,7 +263,7 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
coins_cache.Uncache(removed);
}
-static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static bool IsCurrentForFeeEstimation(Chainstate& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
if (active_chainstate.IsInitialBlockDownload())
@@ -286,7 +276,7 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
return true;
}
-void CChainState::MaybeUpdateMempoolForReorg(
+void Chainstate::MaybeUpdateMempoolForReorg(
DisconnectedBlockTransactions& disconnectpool,
bool fAddToMempool)
{
@@ -424,11 +414,13 @@ namespace {
class MemPoolAccept
{
public:
- explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
- m_limit_ancestors(m_pool.m_limits.ancestor_count),
- m_limit_ancestor_size(m_pool.m_limits.ancestor_size_vbytes),
- m_limit_descendants(m_pool.m_limits.descendant_count),
- m_limit_descendant_size(m_pool.m_limits.descendant_size_vbytes) {
+ explicit MemPoolAccept(CTxMemPool& mempool, Chainstate& active_chainstate) :
+ m_pool(mempool),
+ m_view(&m_dummy),
+ m_viewmempool(&active_chainstate.CoinsTip(), m_pool),
+ m_active_chainstate(active_chainstate),
+ m_limits{m_pool.m_limits}
+ {
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -658,15 +650,9 @@ private:
CCoinsViewMemPool m_viewmempool;
CCoinsView m_dummy;
- CChainState& m_active_chainstate;
+ Chainstate& m_active_chainstate;
- // The package limits in effect at the time of invocation.
- const size_t m_limit_ancestors;
- const size_t m_limit_ancestor_size;
- // These may be modified while evaluating a transaction (eg to account for
- // in-mempool conflicts; see below).
- size_t m_limit_descendants;
- size_t m_limit_descendant_size;
+ CTxMemPool::Limits m_limits;
/** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */
bool m_rbf{false};
@@ -879,12 +865,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
assert(ws.m_iters_conflicting.size() == 1);
CTxMemPool::txiter conflict = *ws.m_iters_conflicting.begin();
- m_limit_descendants += 1;
- m_limit_descendant_size += conflict->GetSizeWithDescendants();
+ m_limits.descendant_count += 1;
+ m_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
}
std::string errString;
- if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
+ if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limits, errString)) {
ws.m_ancestors.clear();
// If CalculateMemPoolAncestors fails second time, we want the original error string.
std::string dummy_err_string;
@@ -899,8 +885,16 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// to be secure by simply only having two immediately-spendable
// outputs - one for each counterparty. For more info on the uses for
// this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
+ CTxMemPool::Limits cpfp_carve_out_limits{
+ .ancestor_count = 2,
+ .ancestor_size_vbytes = m_limits.ancestor_size_vbytes,
+ .descendant_count = m_limits.descendant_count + 1,
+ .descendant_size_vbytes = m_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT,
+ };
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
- !m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
+ !m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors,
+ cpfp_carve_out_limits,
+ dummy_err_string)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", errString);
}
}
@@ -976,8 +970,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
{ return !m_pool.exists(GenTxid::Txid(tx->GetHash()));}));
std::string err_string;
- if (!m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants,
- m_limit_descendant_size, err_string)) {
+ if (!m_pool.CheckPackageLimits(txns, m_limits, err_string)) {
// This is a package-wide error, separate from an individual transaction error.
return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
}
@@ -1121,9 +1114,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
// last calculation done in PreChecks, since package ancestors have already been submitted.
std::string unused_err_string;
- if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limit_ancestors,
- m_limit_ancestor_size, m_limit_descendants,
- m_limit_descendant_size, unused_err_string)) {
+ if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limits, unused_err_string)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
Assume(false);
@@ -1411,12 +1402,12 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
} // anon namespace
-MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTransactionRef& tx,
+MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTransactionRef& tx,
int64_t accept_time, bool bypass_limits, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
- const CChainParams& chainparams{active_chainstate.m_params};
+ const CChainParams& chainparams{active_chainstate.m_chainman.GetParams()};
assert(active_chainstate.GetMempool() != nullptr);
CTxMemPool& pool{*active_chainstate.GetMempool()};
@@ -1438,7 +1429,7 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTr
return result;
}
-PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
+PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxMemPool& pool,
const Package& package, bool test_accept)
{
AssertLockHeld(cs_main);
@@ -1446,7 +1437,7 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
std::vector<COutPoint> coins_to_uncache;
- const CChainParams& chainparams = active_chainstate.m_params;
+ const CChainParams& chainparams = active_chainstate.m_chainman.GetParams();
const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
if (test_accept) {
@@ -1497,32 +1488,31 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(
+Chainstate::Chainstate(
CTxMemPool* mempool,
BlockManager& blockman,
ChainstateManager& chainman,
std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_blockman(blockman),
- m_params(chainman.GetParams()),
m_chainman(chainman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
-void CChainState::InitCoinsDB(
+void Chainstate::InitCoinsDB(
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
fs::path leveldb_name)
{
if (m_from_snapshot_blockhash) {
- leveldb_name += "_" + m_from_snapshot_blockhash->ToString();
+ leveldb_name += node::SNAPSHOT_CHAINSTATE_SUFFIX;
}
m_coins_views = std::make_unique<CoinsViews>(
leveldb_name, cache_size_bytes, in_memory, should_wipe);
}
-void CChainState::InitCoinsCache(size_t cache_size_bytes)
+void Chainstate::InitCoinsCache(size_t cache_size_bytes)
{
AssertLockHeld(::cs_main);
assert(m_coins_views != nullptr);
@@ -1532,10 +1522,10 @@ void CChainState::InitCoinsCache(size_t cache_size_bytes)
// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
// is a performance-related implementation detail. This function must be marked
-// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
+// `const` so that `CValidationInterface` clients (which are given a `const Chainstate*`)
// can call it.
//
-bool CChainState::IsInitialBlockDownload() const
+bool Chainstate::IsInitialBlockDownload() const
{
// Optimization: pre-test latch before taking the lock.
if (m_cached_finished_ibd.load(std::memory_order_relaxed))
@@ -1548,10 +1538,12 @@ bool CChainState::IsInitialBlockDownload() const
return true;
if (m_chain.Tip() == nullptr)
return true;
- if (m_chain.Tip()->nChainWork < nMinimumChainWork)
+ if (m_chain.Tip()->nChainWork < m_chainman.MinimumChainWork()) {
return true;
- if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
+ }
+ if (m_chain.Tip()->Time() < NodeClock::now() - m_chainman.m_options.max_tip_age) {
return true;
+ }
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
m_cached_finished_ibd.store(true, std::memory_order_relaxed);
return false;
@@ -1577,7 +1569,7 @@ static void AlertNotify(const std::string& strMessage)
#endif
}
-void CChainState::CheckForkWarningConditions()
+void Chainstate::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
@@ -1596,7 +1588,7 @@ void CChainState::CheckForkWarningConditions()
}
// Called both upon regular invalid block discovery *and* InvalidateBlock
-void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
+void Chainstate::InvalidChainFound(CBlockIndex* pindexNew)
{
AssertLockHeld(cs_main);
if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) {
@@ -1619,7 +1611,7 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
// which does its own setBlockIndexCandidates management.
-void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
+void Chainstate::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
{
AssertLockHeld(cs_main);
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
@@ -1824,7 +1816,7 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
* When FAILED is returned, view is left in an indeterminate state. */
-DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
+DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
{
AssertLockHeld(::cs_main);
bool fClean = true;
@@ -1840,11 +1832,21 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return DISCONNECT_FAILED;
}
+ // Ignore blocks that contain transactions which are 'overwritten' by later transactions,
+ // unless those are already completely spent.
+ // See https://github.com/bitcoin/bitcoin/issues/22596 for additional information.
+ // Note: the blocks specified here are different than the ones used in ConnectBlock because DisconnectBlock
+ // unwinds the blocks in reverse. As a result, the inconsistency is not discovered until the earlier
+ // blocks with the duplicate coinbase transactions are disconnected.
+ bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
+ (pindex->nHeight==91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")));
+
// undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) {
const CTransaction &tx = *(block.vtx[i]);
uint256 hash = tx.GetHash();
bool is_coinbase = tx.IsCoinBase();
+ bool is_bip30_exception = (is_coinbase && !fEnforceBIP30);
// Check that all outputs are available and match the outputs in the block itself
// exactly.
@@ -1854,7 +1856,9 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
Coin coin;
bool is_spent = view.SpendCoin(out, &coin);
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
- fClean = false; // transaction output mismatch
+ if (!is_bip30_exception) {
+ fClean = false; // transaction output mismatch
+ }
}
}
}
@@ -1965,19 +1969,19 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
}
-static int64_t nTimeCheck = 0;
-static int64_t nTimeForks = 0;
-static int64_t nTimeConnect = 0;
-static int64_t nTimeVerify = 0;
-static int64_t nTimeUndo = 0;
-static int64_t nTimeIndex = 0;
-static int64_t nTimeTotal = 0;
-static int64_t nBlocksTotal = 0;
+static SteadyClock::duration time_check{};
+static SteadyClock::duration time_forks{};
+static SteadyClock::duration time_connect{};
+static SteadyClock::duration time_verify{};
+static SteadyClock::duration time_undo{};
+static SteadyClock::duration time_index{};
+static SteadyClock::duration time_total{};
+static int64_t num_blocks_total = 0;
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
* can fail if those validity checks fail (among other reasons). */
-bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex,
+bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, bool fJustCheck)
{
AssertLockHeld(cs_main);
@@ -1985,8 +1989,10 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
uint256 block_hash{block.GetHash()};
assert(*pindex->phashBlock == block_hash);
+ const bool parallel_script_checks{scriptcheckqueue.HasThreads()};
- int64_t nTimeStart = GetTimeMicros();
+ const auto time_start{SteadyClock::now()};
+ const CChainParams& params{m_chainman.GetParams()};
// Check it again in case a previous version let a bad block in
// NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
@@ -2001,7 +2007,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
// m_adjusted_time_callback() to go backward).
- if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
+ if (!CheckBlock(block, state, params.GetConsensus(), !fJustCheck, !fJustCheck)) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
// corrupted, so this should be impossible unless we're having hardware
@@ -2015,28 +2021,28 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock());
- nBlocksTotal++;
+ num_blocks_total++;
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
- if (block_hash == m_params.GetConsensus().hashGenesisBlock) {
+ if (block_hash == params.GetConsensus().hashGenesisBlock) {
if (!fJustCheck)
view.SetBestBlock(pindex->GetBlockHash());
return true;
}
bool fScriptChecks = true;
- if (!hashAssumeValid.IsNull()) {
+ if (!m_chainman.AssumedValidBlock().IsNull()) {
// We've been configured with the hash of a block which has been externally verified to have a valid history.
// A suitable default value is included with the software and updated from time to time. Because validity
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
// This setting doesn't force the selection of any particular chain but makes validating some faster by
// effectively caching the result of part of the verification.
- BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
+ BlockMap::const_iterator it{m_blockman.m_block_index.find(m_chainman.AssumedValidBlock())};
if (it != m_blockman.m_block_index.end()) {
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex &&
- m_chainman.m_best_header->nChainWork >= nMinimumChainWork) {
+ m_chainman.m_best_header->nChainWork >= m_chainman.MinimumChainWork()) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
// Script verification is skipped when connecting blocks under the
// assumevalid block. Assuming the assumevalid block is valid this
@@ -2049,15 +2055,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// it hard to hide the implication of the demand. This also avoids having release candidates
// that are hardly doing any signature verification at all in testing without having to
// artificially set the default assumed verified block further back.
- // The test against nMinimumChainWork prevents the skipping when denied access to any chain at
+ // The test against the minimum chain work prevents the skipping when denied access to any chain at
// least as good as the expected chain.
- fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
+ fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
}
}
}
- int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart;
- LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO, nTimeCheck * MILLI / nBlocksTotal);
+ const auto time_1{SteadyClock::now()};
+ time_check += time_1 - time_start;
+ LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_1 - time_start),
+ Ticks<SecondsDouble>(time_check),
+ Ticks<MillisecondsDouble>(time_check) / num_blocks_total);
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
@@ -2128,9 +2138,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// post BIP34 before approximately height 486,000,000. After block
// 1,983,702 testnet3 starts doing unnecessary BIP30 checking again.
assert(pindex->pprev);
- CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(m_params.GetConsensus().BIP34Height);
+ CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(params.GetConsensus().BIP34Height);
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
- fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == m_params.GetConsensus().BIP34Hash));
+ fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == params.GetConsensus().BIP34Hash));
// TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a
// consensus change that ensures coinbases at those heights cannot
@@ -2155,8 +2165,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Get the script flags for this block
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
- int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
- LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal);
+ const auto time_2{SteadyClock::now()};
+ time_forks += time_2 - time_1;
+ LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_2 - time_1),
+ Ticks<SecondsDouble>(time_forks),
+ Ticks<MillisecondsDouble>(time_forks) / num_blocks_total);
CBlockUndo blockundo;
@@ -2165,7 +2179,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// in multiple threads). Preallocate the vector size so a new allocation
// doesn't invalidate pointers into the vector, and keep txsdata in scope
// for as long as `control`.
- CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
+ CCheckQueueControl<CScriptCheck> control(fScriptChecks && parallel_script_checks ? &scriptcheckqueue : nullptr);
std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
std::vector<int> prevheights;
@@ -2224,7 +2238,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@@ -2240,10 +2254,15 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
}
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
}
- int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
- LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
+ const auto time_3{SteadyClock::now()};
+ time_connect += time_3 - time_2;
+ LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(),
+ Ticks<MillisecondsDouble>(time_3 - time_2), Ticks<MillisecondsDouble>(time_3 - time_2) / block.vtx.size(),
+ nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_3 - time_2) / (nInputs - 1),
+ Ticks<SecondsDouble>(time_connect),
+ Ticks<MillisecondsDouble>(time_connect) / num_blocks_total);
- CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, m_params.GetConsensus());
+ CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, params.GetConsensus());
if (block.vtx[0]->GetValueOut() > blockReward) {
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)\n", block.vtx[0]->GetValueOut(), blockReward);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount");
@@ -2253,18 +2272,27 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
LogPrintf("ERROR: %s: CheckQueue failed\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "block-validation-failed");
}
- int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
- LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
+ const auto time_4{SteadyClock::now()};
+ time_verify += time_4 - time_2;
+ LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1,
+ Ticks<MillisecondsDouble>(time_4 - time_2),
+ nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_4 - time_2) / (nInputs - 1),
+ Ticks<SecondsDouble>(time_verify),
+ Ticks<MillisecondsDouble>(time_verify) / num_blocks_total);
if (fJustCheck)
return true;
- if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
+ if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, params)) {
return false;
}
- int64_t nTime5 = GetTimeMicros(); nTimeUndo += nTime5 - nTime4;
- LogPrint(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeUndo * MICRO, nTimeUndo * MILLI / nBlocksTotal);
+ const auto time_5{SteadyClock::now()};
+ time_undo += time_5 - time_4;
+ LogPrint(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_5 - time_4),
+ Ticks<SecondsDouble>(time_undo),
+ Ticks<MillisecondsDouble>(time_undo) / num_blocks_total);
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
@@ -2274,8 +2302,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
- int64_t nTime6 = GetTimeMicros(); nTimeIndex += nTime6 - nTime5;
- LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
+ const auto time_6{SteadyClock::now()};
+ time_index += time_6 - time_5;
+ LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_6 - time_5),
+ Ticks<SecondsDouble>(time_index),
+ Ticks<MillisecondsDouble>(time_index) / num_blocks_total);
TRACE6(validation, block_connected,
block_hash.data(),
@@ -2283,13 +2315,13 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
block.vtx.size(),
nInputs,
nSigOpsCost,
- nTime5 - nTimeStart // in microseconds (µs)
+ time_5 - time_start // in microseconds (µs)
);
return true;
}
-CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
+CoinsCacheSizeState Chainstate::GetCoinsCacheSizeState()
{
AssertLockHeld(::cs_main);
return this->GetCoinsCacheSizeState(
@@ -2297,7 +2329,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
m_mempool ? m_mempool->m_max_size_bytes : 0);
}
-CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
+CoinsCacheSizeState Chainstate::GetCoinsCacheSizeState(
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes)
{
@@ -2321,7 +2353,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
return CoinsCacheSizeState::OK;
}
-bool CChainState::FlushStateToDisk(
+bool Chainstate::FlushStateToDisk(
BlockValidationState &state,
FlushStateMode mode,
int nManualPruneHeight)
@@ -2370,7 +2402,7 @@ bool CChainState::FlushStateToDisk(
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
+ m_blockman.FindFilesToPrune(setFilesToPrune, m_chainman.GetParams().PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
m_blockman.m_check_for_pruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -2464,7 +2496,7 @@ bool CChainState::FlushStateToDisk(
return true;
}
-void CChainState::ForceFlushStateToDisk()
+void Chainstate::ForceFlushStateToDisk()
{
BlockValidationState state;
if (!this->FlushStateToDisk(state, FlushStateMode::ALWAYS)) {
@@ -2472,7 +2504,7 @@ void CChainState::ForceFlushStateToDisk()
}
}
-void CChainState::PruneAndFlush()
+void Chainstate::PruneAndFlush()
{
BlockValidationState state;
m_blockman.m_check_for_pruning = true;
@@ -2519,18 +2551,20 @@ static void UpdateTipLog(
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");
}
-void CChainState::UpdateTip(const CBlockIndex* pindexNew)
+void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
{
AssertLockHeld(::cs_main);
const auto& coins_tip = this->CoinsTip();
+ const CChainParams& params{m_chainman.GetParams()};
+
// The remainder of the function isn't relevant if we are not acting on
// the active chainstate, so return if need be.
if (this != &m_chainman.ActiveChainstate()) {
// Only log every so often so that we don't bury log messages at the tip.
constexpr int BACKGROUND_LOG_INTERVAL = 2000;
if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
- UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] ", "");
+ UpdateTipLog(coins_tip, pindexNew, params, __func__, "[background validation] ", "");
}
return;
}
@@ -2551,7 +2585,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(m_chainman, bit);
- ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache.at(bit));
+ ThresholdState state = checker.GetStateFor(pindex, params.GetConsensus(), warningcache.at(bit));
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
@@ -2562,7 +2596,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
}
}
}
- UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
+ UpdateTipLog(coins_tip, pindexNew, params, __func__, "", warning_messages.original);
}
/** Disconnect m_chain's tip.
@@ -2575,7 +2609,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool)
+bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool)
{
AssertLockHeld(cs_main);
if (m_mempool) AssertLockHeld(m_mempool->cs);
@@ -2586,11 +2620,11 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
- if (!ReadBlockFromDisk(block, pindexDelete, m_params.GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pindexDelete, m_chainman.GetConsensus())) {
return error("DisconnectTip(): Failed to read block");
}
// Apply the block atomically to the chain state.
- int64_t nStart = GetTimeMicros();
+ const auto time_start{SteadyClock::now()};
{
CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
@@ -2599,7 +2633,8 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
bool flushed = view.Flush();
assert(flushed);
}
- LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
+ LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n",
+ Ticks<MillisecondsDouble>(SteadyClock::now() - time_start));
{
// Prune locks that began at or after the tip should be moved backward so they get a chance to reorg
@@ -2639,11 +2674,11 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
return true;
}
-static int64_t nTimeReadFromDiskTotal = 0;
-static int64_t nTimeConnectTotal = 0;
-static int64_t nTimeFlush = 0;
-static int64_t nTimeChainState = 0;
-static int64_t nTimePostConnect = 0;
+static SteadyClock::duration time_read_from_disk_total{};
+static SteadyClock::duration time_connect_total{};
+static SteadyClock::duration time_flush{};
+static SteadyClock::duration time_chainstate{};
+static SteadyClock::duration time_post_connect{};
struct PerBlockConnectTrace {
CBlockIndex* pindex = nullptr;
@@ -2691,18 +2726,18 @@ public:
*
* The block is added to connectTrace if connection succeeds.
*/
-bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool)
+bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool)
{
AssertLockHeld(cs_main);
if (m_mempool) AssertLockHeld(m_mempool->cs);
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
- int64_t nTime1 = GetTimeMicros();
+ const auto time_1{SteadyClock::now()};
std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
- if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_params.GetConsensus())) {
+ if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_chainman.GetConsensus())) {
return AbortNode(state, "Failed to read block");
}
pthisBlock = pblockNew;
@@ -2712,9 +2747,13 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
}
const CBlock& blockConnecting = *pthisBlock;
// Apply the block atomically to the chain state.
- int64_t nTime2 = GetTimeMicros(); nTimeReadFromDiskTotal += nTime2 - nTime1;
- int64_t nTime3;
- LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDiskTotal * MICRO, nTimeReadFromDiskTotal * MILLI / nBlocksTotal);
+ const auto time_2{SteadyClock::now()};
+ time_read_from_disk_total += time_2 - time_1;
+ SteadyClock::time_point time_3;
+ LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_2 - time_1),
+ Ticks<SecondsDouble>(time_read_from_disk_total),
+ Ticks<MillisecondsDouble>(time_read_from_disk_total) / num_blocks_total);
{
CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view);
@@ -2724,20 +2763,32 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
InvalidBlockFound(pindexNew, state);
return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), state.ToString());
}
- nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
- assert(nBlocksTotal > 0);
- LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
+ time_3 = SteadyClock::now();
+ time_connect_total += time_3 - time_2;
+ assert(num_blocks_total > 0);
+ LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_3 - time_2),
+ Ticks<SecondsDouble>(time_connect_total),
+ Ticks<MillisecondsDouble>(time_connect_total) / num_blocks_total);
bool flushed = view.Flush();
assert(flushed);
}
- int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
- LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);
+ const auto time_4{SteadyClock::now()};
+ time_flush += time_4 - time_3;
+ LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_4 - time_3),
+ Ticks<SecondsDouble>(time_flush),
+ Ticks<MillisecondsDouble>(time_flush) / num_blocks_total);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
return false;
}
- int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
- LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
+ const auto time_5{SteadyClock::now()};
+ time_chainstate += time_5 - time_4;
+ LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_5 - time_4),
+ Ticks<SecondsDouble>(time_chainstate),
+ Ticks<MillisecondsDouble>(time_chainstate) / num_blocks_total);
// Remove conflicting transactions from the mempool.;
if (m_mempool) {
m_mempool->removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
@@ -2747,9 +2798,17 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
m_chain.SetTip(*pindexNew);
UpdateTip(pindexNew);
- int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
- LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
- LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * MILLI, nTimeTotal * MICRO, nTimeTotal * MILLI / nBlocksTotal);
+ const auto time_6{SteadyClock::now()};
+ time_post_connect += time_6 - time_5;
+ time_total += time_6 - time_1;
+ LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_6 - time_5),
+ Ticks<SecondsDouble>(time_post_connect),
+ Ticks<MillisecondsDouble>(time_post_connect) / num_blocks_total);
+ LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<MillisecondsDouble>(time_6 - time_1),
+ Ticks<SecondsDouble>(time_total),
+ Ticks<MillisecondsDouble>(time_total) / num_blocks_total);
connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
return true;
@@ -2759,7 +2818,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
* Return the tip of the chain with the most work in it, that isn't
* known to be invalid (it's however far from certain to be valid).
*/
-CBlockIndex* CChainState::FindMostWorkChain()
+CBlockIndex* Chainstate::FindMostWorkChain()
{
AssertLockHeld(::cs_main);
do {
@@ -2818,7 +2877,7 @@ CBlockIndex* CChainState::FindMostWorkChain()
}
/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */
-void CChainState::PruneBlockIndexCandidates() {
+void Chainstate::PruneBlockIndexCandidates() {
// Note that we can't delete the current block itself, as we may need to return to it later in case a
// reorganization to a better block fails.
std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
@@ -2835,7 +2894,7 @@ void CChainState::PruneBlockIndexCandidates() {
*
* @returns true unless a system error occurred
*/
-bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
+bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
if (m_mempool) AssertLockHeld(m_mempool->cs);
@@ -2927,7 +2986,7 @@ static SynchronizationState GetSynchronizationState(bool init)
return SynchronizationState::INIT_DOWNLOAD;
}
-static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
+static bool NotifyHeaderTip(Chainstate& chainstate) LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
static CBlockIndex* pindexHeaderOld = nullptr;
@@ -2957,7 +3016,7 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
-bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
+bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
AssertLockNotHeld(m_chainstate_mutex);
@@ -3059,7 +3118,7 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
return true;
}
-bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
+bool Chainstate::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
{
AssertLockNotHeld(m_chainstate_mutex);
AssertLockNotHeld(::cs_main);
@@ -3090,7 +3149,7 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
return ActivateBestChain(state, std::shared_ptr<const CBlock>());
}
-bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
+bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
{
AssertLockNotHeld(m_chainstate_mutex);
AssertLockNotHeld(::cs_main);
@@ -3233,7 +3292,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
return true;
}
-void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
+void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
int nHeight = pindex->nHeight;
@@ -3266,7 +3325,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
+void Chainstate::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
{
AssertLockHeld(cs_main);
pindexNew->nTx = block.vtx.size();
@@ -3469,7 +3528,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work");
// Check against checkpoints
- if (fCheckpointsEnabled) {
+ if (chainman.m_options.checkpoints_enabled) {
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
@@ -3737,7 +3796,7 @@ void ChainstateManager::ReportHeadersPresync(const arith_uint256& work, int64_t
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
+bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
{
const CBlock& block = *pblock;
@@ -3783,10 +3842,12 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// If our tip is behind, a peer could try to send us
// low-work blocks on a fake chain that we would never
// request; don't process these.
- if (pindex->nChainWork < nMinimumChainWork) return true;
+ if (pindex->nChainWork < m_chainman.MinimumChainWork()) return true;
}
- if (!CheckBlock(block, state, m_params.GetConsensus()) ||
+ const CChainParams& params{m_chainman.GetParams()};
+
+ if (!CheckBlock(block, state, params.GetConsensus()) ||
!ContextualCheckBlock(block, state, m_chainman, pindex->pprev)) {
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
@@ -3803,7 +3864,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
try {
- FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp)};
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, params, dbp)};
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3862,7 +3923,7 @@ bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& blo
MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept)
{
AssertLockHeld(cs_main);
- CChainState& active_chainstate = ActiveChainstate();
+ Chainstate& active_chainstate = ActiveChainstate();
if (!active_chainstate.GetMempool()) {
TxValidationState state;
state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool");
@@ -3875,7 +3936,7 @@ MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef&
bool TestBlockValidity(BlockValidationState& state,
const CChainParams& chainparams,
- CChainState& chainstate,
+ Chainstate& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
const std::function<NodeClock::time_point()>& adjusted_time_callback,
@@ -3907,7 +3968,7 @@ bool TestBlockValidity(BlockValidationState& state,
}
/* This function is called from the RPC code for pruneblockchain */
-void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
+void PruneBlockFilesManual(Chainstate& active_chainstate, int nManualPruneHeight)
{
BlockValidationState state;
if (!active_chainstate.FlushStateToDisk(
@@ -3916,14 +3977,14 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
}
}
-void CChainState::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_function)
+void Chainstate::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_function)
{
if (!m_mempool) return;
::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
m_mempool->SetLoadTried(!ShutdownRequested());
}
-bool CChainState::LoadChainTip()
+bool Chainstate::LoadChainTip()
{
AssertLockHeld(cs_main);
const CCoinsViewCache& coins_cache = CoinsTip();
@@ -3947,7 +4008,7 @@ bool CChainState::LoadChainTip()
tip->GetBlockHash().ToString(),
m_chain.Height(),
FormatISO8601DateTime(tip->GetBlockTime()),
- GuessVerificationProgress(m_params.TxData(), tip));
+ GuessVerificationProgress(m_chainman.GetParams().TxData(), tip));
return true;
}
@@ -3962,7 +4023,7 @@ CVerifyDB::~CVerifyDB()
}
bool CVerifyDB::VerifyDB(
- CChainState& chainstate,
+ Chainstate& chainstate,
const Consensus::Params& consensus_params,
CCoinsView& coinsview,
int nCheckLevel, int nCheckDepth)
@@ -4078,12 +4139,12 @@ bool CVerifyDB::VerifyDB(
}
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
-bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs)
+bool Chainstate::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs)
{
AssertLockHeld(cs_main);
// TODO: merge with ConnectBlock
CBlock block;
- if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pindex, m_chainman.GetConsensus())) {
return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4099,7 +4160,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
return true;
}
-bool CChainState::ReplayBlocks()
+bool Chainstate::ReplayBlocks()
{
LOCK(cs_main);
@@ -4135,7 +4196,7 @@ bool CChainState::ReplayBlocks()
while (pindexOld != pindexFork) {
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
CBlock block;
- if (!ReadBlockFromDisk(block, pindexOld, m_params.GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pindexOld, m_chainman.GetConsensus())) {
return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
}
LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);
@@ -4167,7 +4228,7 @@ bool CChainState::ReplayBlocks()
return true;
}
-bool CChainState::NeedsRedownload() const
+bool Chainstate::NeedsRedownload() const
{
AssertLockHeld(cs_main);
@@ -4185,7 +4246,7 @@ bool CChainState::NeedsRedownload() const
return false;
}
-void CChainState::UnloadBlockIndex()
+void Chainstate::UnloadBlockIndex()
{
AssertLockHeld(::cs_main);
nBlockSequenceId = 1;
@@ -4254,7 +4315,7 @@ bool ChainstateManager::LoadBlockIndex()
// detecting "holistically" whether the block index under consideration
// relied on an assumed-valid ancestor, but this proved to be too slow to
// be practical.
- for (CChainState* chainstate : GetAll()) {
+ for (Chainstate* chainstate : GetAll()) {
if (chainstate->reliesOnAssumedValid() ||
pindex->nHeight < first_assumed_valid_height) {
chainstate->setBlockIndexCandidates.insert(pindex);
@@ -4283,20 +4344,22 @@ bool ChainstateManager::LoadBlockIndex()
return true;
}
-bool CChainState::LoadGenesisBlock()
+bool Chainstate::LoadGenesisBlock()
{
LOCK(cs_main);
+ const CChainParams& params{m_chainman.GetParams()};
+
// Check whether we're already initialized by checking for genesis in
// m_blockman.m_block_index. Note that we can't use m_chain here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
- if (m_blockman.m_block_index.count(m_params.GenesisBlock().GetHash()))
+ if (m_blockman.m_block_index.count(params.GenesisBlock().GetHash()))
return true;
try {
- const CBlock& block = m_params.GenesisBlock();
- FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, m_params, nullptr)};
+ const CBlock& block = params.GenesisBlock();
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, params, nullptr)};
if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed", __func__);
}
@@ -4309,7 +4372,7 @@ bool CChainState::LoadGenesisBlock()
return true;
}
-void CChainState::LoadExternalBlockFile(
+void Chainstate::LoadExternalBlockFile(
FILE* fileIn,
FlatFilePos* dbp,
std::multimap<uint256, FlatFilePos>* blocks_with_unknown_parent)
@@ -4319,7 +4382,8 @@ void CChainState::LoadExternalBlockFile(
// Either both should be specified (-reindex), or neither (-loadblock).
assert(!dbp == !blocks_with_unknown_parent);
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
+ const CChainParams& params{m_chainman.GetParams()};
int nLoaded = 0;
try {
@@ -4336,10 +4400,10 @@ void CChainState::LoadExternalBlockFile(
try {
// locate a header
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
- blkdat.FindByte(m_params.MessageStart()[0]);
+ blkdat.FindByte(params.MessageStart()[0]);
nRewind = blkdat.GetPos() + 1;
blkdat >> buf;
- if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
+ if (memcmp(buf, params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
continue;
}
// read size
@@ -4365,7 +4429,7 @@ void CChainState::LoadExternalBlockFile(
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
- if (hash != m_params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
+ if (hash != params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
if (dbp && blocks_with_unknown_parent) {
@@ -4384,13 +4448,13 @@ void CChainState::LoadExternalBlockFile(
if (state.IsError()) {
break;
}
- } else if (hash != m_params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
+ } else if (hash != params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
}
}
// Activate the genesis block so normal node progress can continue
- if (hash == m_params.GetConsensus().hashGenesisBlock) {
+ if (hash == params.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
if (!ActivateBestChain(state, nullptr)) {
break;
@@ -4411,7 +4475,7 @@ void CChainState::LoadExternalBlockFile(
while (range.first != range.second) {
std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
- if (ReadBlockFromDisk(*pblockrecursive, it->second, m_params.GetConsensus())) {
+ if (ReadBlockFromDisk(*pblockrecursive, it->second, params.GetConsensus())) {
LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(),
head.ToString());
LOCK(cs_main);
@@ -4427,18 +4491,29 @@ void CChainState::LoadExternalBlockFile(
}
}
} catch (const std::exception& e) {
- LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what());
+ // historical bugs added extra data to the block files that does not deserialize cleanly.
+ // commonly this data is between readable blocks, but it does not really matter. such data is not fatal to the import process.
+ // the code that reads the block files deals with invalid data by simply ignoring it.
+ // it continues to search for the next {4 byte magic message start bytes + 4 byte length + block} that does deserialize cleanly
+ // and passes all of the other block validation checks dealing with POW and the merkle root, etc...
+ // we merely note with this informational log message when unexpected data is encountered.
+ // we could also be experiencing a storage system read error, or a read of a previous bad write. these are possible, but
+ // less likely scenarios. we don't have enough information to tell a difference here.
+ // the reindex process is not the place to attempt to clean and/or compact the block files. if so desired, a studious node operator
+ // may use knowledge of the fact that the block files are not entirely pristine in order to prepare a set of pristine, and
+ // perhaps ordered, block files for later reindexing.
+ LogPrint(BCLog::REINDEX, "%s: unexpected data at file offset 0x%x - %s. continuing\n", __func__, (nRewind - 1), e.what());
}
}
} catch (const std::runtime_error& e) {
AbortNode(std::string("System error: ") + e.what());
}
- LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
+ LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
}
-void CChainState::CheckBlockIndex()
+void Chainstate::CheckBlockIndex()
{
- if (!fCheckBlockIndex) {
+ if (!m_chainman.ShouldCheckBlockIndex()) {
return;
}
@@ -4511,7 +4586,7 @@ void CChainState::CheckBlockIndex()
// Begin: actual consistency checks.
if (pindex->pprev == nullptr) {
// Genesis block checks.
- assert(pindex->GetBlockHash() == m_params.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
+ assert(pindex->GetBlockHash() == m_chainman.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
}
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
@@ -4658,7 +4733,7 @@ void CChainState::CheckBlockIndex()
assert(nNodes == forward.size());
}
-std::string CChainState::ToString()
+std::string Chainstate::ToString()
{
AssertLockHeld(::cs_main);
CBlockIndex* tip = m_chain.Tip();
@@ -4667,7 +4742,7 @@ std::string CChainState::ToString()
tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
}
-bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
+bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
{
AssertLockHeld(::cs_main);
if (coinstip_size == m_coinstip_cache_size_bytes &&
@@ -4728,10 +4803,10 @@ std::optional<uint256> ChainstateManager::SnapshotBlockhash() const
return std::nullopt;
}
-std::vector<CChainState*> ChainstateManager::GetAll()
+std::vector<Chainstate*> ChainstateManager::GetAll()
{
LOCK(::cs_main);
- std::vector<CChainState*> out;
+ std::vector<Chainstate*> out;
if (!IsSnapshotValidated() && m_ibd_chainstate) {
out.push_back(m_ibd_chainstate.get());
@@ -4744,28 +4819,15 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(
- CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
+Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
{
AssertLockHeld(::cs_main);
- bool is_snapshot = snapshot_blockhash.has_value();
- std::unique_ptr<CChainState>& to_modify =
- is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
-
- if (to_modify) {
- throw std::logic_error("should not be overwriting a chainstate");
- }
- to_modify.reset(new CChainState(mempool, m_blockman, *this, snapshot_blockhash));
-
- // Snapshot chainstates and initial IBD chaintates always become active.
- if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
- LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
- m_active_chainstate = to_modify.get();
- } else {
- throw std::logic_error("unexpected chainstate activation");
- }
+ assert(!m_ibd_chainstate);
+ assert(!m_active_chainstate);
- return *to_modify;
+ m_ibd_chainstate = std::make_unique<Chainstate>(mempool, m_blockman, *this);
+ m_active_chainstate = m_ibd_chainstate.get();
+ return *m_active_chainstate;
}
const AssumeutxoData* ExpectedAssumeutxo(
@@ -4780,6 +4842,46 @@ const AssumeutxoData* ExpectedAssumeutxo(
return nullptr;
}
+static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
+ AssertLockHeld(::cs_main);
+
+ if (is_snapshot) {
+ fs::path base_blockhash_path = db_path / node::SNAPSHOT_BLOCKHASH_FILENAME;
+
+ if (fs::exists(base_blockhash_path)) {
+ bool removed = fs::remove(base_blockhash_path);
+ if (!removed) {
+ LogPrintf("[snapshot] failed to remove file %s\n",
+ fs::PathToString(base_blockhash_path));
+ }
+ } else {
+ LogPrintf("[snapshot] snapshot chainstate dir being removed lacks %s file\n",
+ fs::PathToString(node::SNAPSHOT_BLOCKHASH_FILENAME));
+ }
+ }
+
+ std::string path_str = fs::PathToString(db_path);
+ LogPrintf("Removing leveldb dir at %s\n", path_str);
+
+ // We have to destruct before this call leveldb::DB in order to release the db
+ // lock, otherwise `DestroyDB` will fail. See `leveldb::~DBImpl()`.
+ const bool destroyed = dbwrapper::DestroyDB(path_str, {}).ok();
+
+ if (!destroyed) {
+ LogPrintf("error: leveldb DestroyDB call failed on %s\n", path_str);
+ }
+
+ // Datadir should be removed from filesystem; otherwise initialization may detect
+ // it on subsequent statups and get confused.
+ //
+ // If the base_blockhash_path removal above fails in the case of snapshot
+ // chainstates, this will return false since leveldb won't remove a non-empty
+ // directory.
+ return destroyed && !fs::exists(db_path);
+}
+
bool ChainstateManager::ActivateSnapshot(
AutoFile& coins_file,
const SnapshotMetadata& metadata,
@@ -4825,7 +4927,7 @@ bool ChainstateManager::ActivateSnapshot(
}
auto snapshot_chainstate = WITH_LOCK(::cs_main,
- return std::make_unique<CChainState>(
+ return std::make_unique<Chainstate>(
/*mempool=*/nullptr, m_blockman, *this, base_blockhash));
{
@@ -4837,11 +4939,34 @@ bool ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
}
- const bool snapshot_ok = this->PopulateAndValidateSnapshot(
+ bool snapshot_ok = this->PopulateAndValidateSnapshot(
*snapshot_chainstate, coins_file, metadata);
+ // If not in-memory, persist the base blockhash for use during subsequent
+ // initialization.
+ if (!in_memory) {
+ LOCK(::cs_main);
+ if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
+ snapshot_ok = false;
+ }
+ }
if (!snapshot_ok) {
- WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
+ LOCK(::cs_main);
+ this->MaybeRebalanceCaches();
+
+ // PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
+ // has been created, so only attempt removal if we got that far.
+ if (auto snapshot_datadir = node::FindSnapshotChainstateDir()) {
+ // We have to destruct leveldb::DB in order to release the db lock, otherwise
+ // DestroyDB() (in DeleteCoinsDBFromDisk()) will fail. See `leveldb::~DBImpl()`.
+ // Destructing the chainstate (and so resetting the coinsviews object) does this.
+ snapshot_chainstate.reset();
+ bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true);
+ if (!removed) {
+ AbortNode(strprintf("Failed to remove snapshot chainstate dir (%s). "
+ "Manually remove it before restarting.\n", fs::PathToString(*snapshot_datadir)));
+ }
+ }
return false;
}
@@ -4875,7 +5000,7 @@ static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_load
}
bool ChainstateManager::PopulateAndValidateSnapshot(
- CChainState& snapshot_chainstate,
+ Chainstate& snapshot_chainstate,
AutoFile& coins_file,
const SnapshotMetadata& metadata)
{
@@ -4969,7 +5094,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// Important that we set this. This and the coins_cache accesses above are
// sort of a layer violation, but either we reach into the innards of
- // CCoinsViewCache here or we have to invert some of the CChainState to
+ // CCoinsViewCache here or we have to invert some of the Chainstate to
// embed them in a snapshot-activation-specific CCoinsViewCache bulk load
// method.
coins_cache.SetBestBlock(base_blockhash);
@@ -5048,7 +5173,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
index->nStatus |= BLOCK_ASSUMED_VALID;
}
- // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
+ // Fake BLOCK_OPT_WITNESS so that Chainstate::NeedsRedownload()
// won't ask to rewind the entire assumed-valid chain on startup.
if (DeploymentActiveAt(*index, *this, Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
@@ -5071,7 +5196,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
return true;
}
-CChainState& ChainstateManager::ActiveChainstate() const
+Chainstate& ChainstateManager::ActiveChainstate() const
{
LOCK(::cs_main);
assert(m_active_chainstate);
@@ -5115,6 +5240,29 @@ void ChainstateManager::MaybeRebalanceCaches()
}
}
+void ChainstateManager::ResetChainstates()
+{
+ m_ibd_chainstate.reset();
+ m_snapshot_chainstate.reset();
+ m_active_chainstate = nullptr;
+}
+
+/**
+ * Apply default chain params to nullopt members.
+ * This helps to avoid coding errors around the accidental use of the compare
+ * operators that accept nullopt, thus ignoring the intended default value.
+ */
+static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts)
+{
+ if (!opts.check_block_index.has_value()) opts.check_block_index = opts.chainparams.DefaultConsistencyChecks();
+ if (!opts.minimum_chain_work.has_value()) opts.minimum_chain_work = UintToArith256(opts.chainparams.GetConsensus().nMinimumChainWork);
+ if (!opts.assumed_valid_block.has_value()) opts.assumed_valid_block = opts.chainparams.GetConsensus().defaultAssumeValid;
+ Assert(opts.adjusted_time_callback);
+ return std::move(opts);
+}
+
+ChainstateManager::ChainstateManager(Options options) : m_options{Flatten(std::move(options))} {}
+
ChainstateManager::~ChainstateManager()
{
LOCK(::cs_main);
@@ -5126,3 +5274,31 @@ ChainstateManager::~ChainstateManager()
i.clear();
}
}
+
+bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool)
+{
+ assert(!m_snapshot_chainstate);
+ std::optional<fs::path> path = node::FindSnapshotChainstateDir();
+ if (!path) {
+ return false;
+ }
+ std::optional<uint256> base_blockhash = node::ReadSnapshotBaseBlockhash(*path);
+ if (!base_blockhash) {
+ return false;
+ }
+ LogPrintf("[snapshot] detected active snapshot chainstate (%s) - loading\n",
+ fs::PathToString(*path));
+
+ this->ActivateExistingSnapshot(mempool, *base_blockhash);
+ return true;
+}
+
+Chainstate& ChainstateManager::ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash)
+{
+ assert(!m_snapshot_chainstate);
+ m_snapshot_chainstate =
+ std::make_unique<Chainstate>(mempool, m_blockman, *this, base_blockhash);
+ LogPrintf("[snapshot] switching active chainstate to %s\n", m_snapshot_chainstate->ToString());
+ m_active_chainstate = m_snapshot_chainstate.get();
+ return *m_snapshot_chainstate;
+}
diff --git a/src/validation.h b/src/validation.h
index 7f5039aaea..b8151dc1fc 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -43,7 +43,7 @@
#include <utility>
#include <vector>
-class CChainState;
+class Chainstate;
class CBlockTreeDB;
class CTxMemPool;
class ChainstateManager;
@@ -63,11 +63,6 @@ struct Params;
static const int MAX_SCRIPTCHECK_THREADS = 15;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
-static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
-static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
-static const bool DEFAULT_TXINDEX = false;
-static constexpr bool DEFAULT_COINSTATSINDEX{false};
-static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
/** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ActiveChain().Tip() will not be pruned. */
@@ -96,20 +91,6 @@ extern GlobalMutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
/** Used to notify getblocktemplate RPC of new tips. */
extern uint256 g_best_block;
-/** Whether there are dedicated script-checking threads running.
- * False indicates all script checking is done on the main threadMessageHandler thread.
- */
-extern bool g_parallel_script_checks;
-extern bool fCheckBlockIndex;
-extern bool fCheckpointsEnabled;
-/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
-extern int64_t nMaxTipAge;
-
-/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
-extern uint256 hashAssumeValid;
-
-/** Minimum work we will assume exists on some valid chain. */
-extern arith_uint256 nMinimumChainWork;
/** Documentation for argument 'checklevel'. */
extern const std::vector<std::string> CHECKLEVEL_DOC;
@@ -127,7 +108,7 @@ bool AbortNode(BlockValidationState& state, const std::string& strMessage, const
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
/** Prune block files up to a given height */
-void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
+void PruneBlockFilesManual(Chainstate& active_chainstate, int nManualPruneHeight);
/**
* Validation result for a single transaction mempool acceptance.
@@ -240,7 +221,7 @@ struct PackageMempoolAcceptResult
*
* @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason.
*/
-MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTransactionRef& tx,
+MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTransactionRef& tx,
int64_t accept_time, bool bypass_limits, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -252,7 +233,7 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTr
* If a transaction fails, validation will exit early and some results may be missing. It is also
* possible for the package to be partially submitted.
*/
-PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
+PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxMemPool& pool,
const Package& txns, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -333,7 +314,7 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
bool TestBlockValidity(BlockValidationState& state,
const CChainParams& chainparams,
- CChainState& chainstate,
+ Chainstate& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
const std::function<NodeClock::time_point()>& adjusted_time_callback,
@@ -352,7 +333,7 @@ public:
CVerifyDB();
~CVerifyDB();
bool VerifyDB(
- CChainState& chainstate,
+ Chainstate& chainstate,
const Consensus::Params& consensus_params,
CCoinsView& coinsview,
int nCheckLevel,
@@ -368,7 +349,7 @@ enum DisconnectResult
class ConnectTrace;
-/** @see CChainState::FlushStateToDisk */
+/** @see Chainstate::FlushStateToDisk */
enum class FlushStateMode {
NONE,
IF_NEEDED,
@@ -421,7 +402,7 @@ enum class CoinsCacheSizeState
};
/**
- * CChainState stores and provides an API to update our local knowledge of the
+ * Chainstate stores and provides an API to update our local knowledge of the
* current best chain.
*
* Eventually, the API here is targeted at being exposed externally as a
@@ -434,7 +415,7 @@ enum class CoinsCacheSizeState
* whereas block information and metadata independent of the current tip is
* kept in `BlockManager`.
*/
-class CChainState
+class Chainstate
{
protected:
/**
@@ -472,19 +453,15 @@ protected:
public:
//! Reference to a BlockManager instance which itself is shared across all
- //! CChainState instances.
+ //! Chainstate instances.
node::BlockManager& m_blockman;
- /** Chain parameters for this chainstate */
- /* TODO: replace with m_chainman.GetParams() */
- const CChainParams& m_params;
-
//! The chainstate manager that owns this chainstate. The reference is
//! necessary so that this instance can check whether it is the active
//! chainstate within deeply nested method calls.
ChainstateManager& m_chainman;
- explicit CChainState(
+ explicit Chainstate(
CTxMemPool* mempool,
node::BlockManager& blockman,
ChainstateManager& chainman,
@@ -705,7 +682,7 @@ public:
/**
* Make various assertions about the state of the block index.
*
- * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
+ * By default this only executes fully when using the Regtest chain; see: m_options.check_block_index.
*/
void CheckBlockIndex();
@@ -814,7 +791,7 @@ private:
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_ibd_chainstate GUARDED_BY(::cs_main);
+ std::unique_ptr<Chainstate> m_ibd_chainstate GUARDED_BY(::cs_main);
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
//! non-null, it is always our active chainstate.
@@ -825,7 +802,7 @@ private:
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_snapshot_chainstate GUARDED_BY(::cs_main);
+ std::unique_ptr<Chainstate> m_snapshot_chainstate GUARDED_BY(::cs_main);
//! Points to either the ibd or snapshot chainstate; indicates our
//! most-work chain.
@@ -836,7 +813,7 @@ private:
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- CChainState* m_active_chainstate GUARDED_BY(::cs_main) {nullptr};
+ Chainstate* m_active_chainstate GUARDED_BY(::cs_main) {nullptr};
//! If true, the assumed-valid chainstate has been fully validated
//! by the background validation chainstate.
@@ -846,7 +823,7 @@ private:
//! Internal helper for ActivateSnapshot().
[[nodiscard]] bool PopulateAndValidateSnapshot(
- CChainState& snapshot_chainstate,
+ Chainstate& snapshot_chainstate,
AutoFile& coins_file,
const node::SnapshotMetadata& metadata);
@@ -862,7 +839,7 @@ private:
BlockValidationState& state,
CBlockIndex** ppindex,
bool min_pow_checked) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- friend CChainState;
+ friend Chainstate;
/** Most recent headers presync progress update, for rate-limiting. */
std::chrono::time_point<std::chrono::steady_clock> m_last_presync_update GUARDED_BY(::cs_main) {};
@@ -870,13 +847,13 @@ private:
public:
using Options = kernel::ChainstateManagerOpts;
- explicit ChainstateManager(Options options) : m_options{std::move(options)}
- {
- Assert(m_options.adjusted_time_callback);
- }
+ explicit ChainstateManager(Options options);
const CChainParams& GetParams() const { return m_options.chainparams; }
const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); }
+ bool ShouldCheckBlockIndex() const { return *Assert(m_options.check_block_index); }
+ const arith_uint256& MinimumChainWork() const { return *Assert(m_options.minimum_chain_work); }
+ const uint256& AssumedValidBlock() const { return *Assert(m_options.assumed_valid_block); }
/**
* Alias for ::cs_main.
@@ -929,26 +906,20 @@ public:
//! coins databases. This will be split somehow across chainstates.
int64_t m_total_coinsdb_cache{0};
- //! Instantiate a new chainstate and assign it based upon whether it is
- //! from a snapshot.
+ //! Instantiate a new chainstate.
//!
//! @param[in] mempool The mempool to pass to the chainstate
// constructor
- //! @param[in] snapshot_blockhash If given, signify that this chainstate
- //! is based on a snapshot.
- CChainState& InitializeChainstate(
- CTxMemPool* mempool,
- const std::optional<uint256>& snapshot_blockhash = std::nullopt)
- LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ Chainstate& InitializeChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
- std::vector<CChainState*> GetAll();
+ std::vector<Chainstate*> GetAll();
//! Construct and activate a Chainstate on the basis of UTXO snapshot data.
//!
//! Steps:
//!
- //! - Initialize an unused CChainState.
+ //! - Initialize an unused Chainstate.
//! - Load its `CoinsViews` contents from `coins_file`.
//! - Verify that the hash of the resulting coinsdb matches the expected hash
//! per assumeutxo chain parameters.
@@ -961,7 +932,7 @@ public:
AutoFile& coins_file, const node::SnapshotMetadata& metadata, bool in_memory);
//! The most-work chain.
- CChainState& ActiveChainstate() const;
+ Chainstate& ActiveChainstate() const;
CChain& ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) { return ActiveChainstate().m_chain; }
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) { return ActiveChain().Height(); }
CBlockIndex* ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) { return ActiveChain().Tip(); }
@@ -1053,6 +1024,17 @@ public:
* information. */
void ReportHeadersPresync(const arith_uint256& work, int64_t height, int64_t timestamp);
+ //! When starting up, search the datadir for a chainstate based on a UTXO
+ //! snapshot that is in the process of being validated.
+ bool DetectSnapshotChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ void ResetChainstates() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ //! Switch the active chainstate to one based on a UTXO snapshot that was loaded
+ //! previously.
+ Chainstate& ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
~ChainstateManager();
};
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index deb1293cd4..3a9d277f65 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -533,7 +533,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
void BerkeleyEnvironment::Flush(bool fShutdown)
{
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
// Flush log data to the actual data file on all files that are not in use
LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
if (!fDbEnvInit)
@@ -561,7 +561,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
no_dbs_accessed = false;
}
}
- LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
if (fShutdown) {
char** listp;
if (no_dbs_accessed) {
@@ -591,14 +591,14 @@ bool BerkeleyDatabase::PeriodicFlush()
const std::string strFile = fs::PathToString(m_filename);
LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
// Flush wallet file so it's self contained
env->CloseDb(m_filename);
env->CheckpointLSN(strFile);
m_refcount = -1;
- LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
+ LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
return true;
}
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 98925e6b10..9cf2b677e6 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -560,8 +560,12 @@ public:
options.create_flags = wallet_creation_flags;
options.create_passphrase = passphrase;
bilingual_str error;
- util::Result<std::unique_ptr<Wallet>> wallet{MakeWallet(m_context, CreateWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
- return wallet ? std::move(wallet) : util::Error{error};
+ std::unique_ptr<Wallet> wallet{MakeWallet(m_context, CreateWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
+ if (wallet) {
+ return {std::move(wallet)};
+ } else {
+ return util::Error{error};
+ }
}
util::Result<std::unique_ptr<Wallet>> loadWallet(const std::string& name, std::vector<bilingual_str>& warnings) override
{
@@ -570,15 +574,23 @@ public:
ReadDatabaseArgs(*m_context.args, options);
options.require_existing = true;
bilingual_str error;
- util::Result<std::unique_ptr<Wallet>> wallet{MakeWallet(m_context, LoadWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
- return wallet ? std::move(wallet) : util::Error{error};
+ std::unique_ptr<Wallet> wallet{MakeWallet(m_context, LoadWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
+ if (wallet) {
+ return {std::move(wallet)};
+ } else {
+ return util::Error{error};
+ }
}
util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
{
DatabaseStatus status;
bilingual_str error;
- util::Result<std::unique_ptr<Wallet>> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
- return wallet ? std::move(wallet) : util::Error{error};
+ std::unique_ptr<Wallet> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
+ if (wallet) {
+ return {std::move(wallet)};
+ } else {
+ return util::Error{error};
+ }
}
std::string getWalletDir() override
{
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 35df151e84..1d2d7d2a10 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -293,10 +293,7 @@ RPCHelpMan importaddress()
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- {
- LOCK(pwallet->cs_wallet);
- pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
- }
+ pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
}
return UniValue::VNULL;
@@ -474,10 +471,7 @@ RPCHelpMan importpubkey()
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- {
- LOCK(pwallet->cs_wallet);
- pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
- }
+ pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
}
return UniValue::VNULL;
@@ -1266,15 +1260,15 @@ RPCHelpMan importmulti()
{
{"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
{"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
- /*oneline_description=*/"", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
+ RPCArgOptions{.type_str={"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}}
},
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n"
- " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
- " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
- " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
- " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
- " creation time of all keys being imported by the importmulti call will be scanned.",
- /*oneline_description=*/"", {"timestamp | \"now\"", "integer / string"}
+ "or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
+ "key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
+ "\"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
+ "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
+ "creation time of all keys being imported by the importmulti call will be scanned.",
+ RPCArgOptions{.type_str={"timestamp | \"now\"", "integer / string"}}
},
{"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
{"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
@@ -1296,12 +1290,12 @@ RPCHelpMan importmulti()
},
},
},
- "\"requests\""},
+ RPCArgOptions{.oneline_description="\"requests\""}},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
{"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Scan the chain and mempool for wallet transactions after all imports."},
},
- "\"options\""},
+ RPCArgOptions{.oneline_description="\"options\""}},
},
RPCResult{
RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
@@ -1363,7 +1357,18 @@ RPCHelpMan importmulti()
UniValue response(UniValue::VARR);
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(*pwallet);
+
+ // Check all requests are watchonly
+ bool is_watchonly{true};
+ for (size_t i = 0; i < requests.size(); ++i) {
+ const UniValue& request = requests[i];
+ if (!request.exists("watchonly") || !request["watchonly"].get_bool()) {
+ is_watchonly = false;
+ break;
+ }
+ }
+ // Wallet does not need to be unlocked if all requests are watchonly
+ if (!is_watchonly) EnsureWalletIsUnlocked(wallet);
// Verify all timestamps are present before importing any keys.
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
@@ -1395,10 +1400,7 @@ RPCHelpMan importmulti()
}
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
- {
- LOCK(pwallet->cs_wallet);
- pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
- }
+ pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
@@ -1587,7 +1589,8 @@ RPCHelpMan importdescriptors()
return RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
- "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
+ "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
+ "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
@@ -1598,18 +1601,18 @@ RPCHelpMan importdescriptors()
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
{"next_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "If a ranged descriptor is set to active, this specifies the next index to generate addresses from"},
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
- " Use the string \"now\" to substitute the current synced blockchain time.\n"
- " \"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
- " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
+ "Use the string \"now\" to substitute the current synced blockchain time.\n"
+ "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
+ "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
"of all descriptors being imported will be scanned as well as the mempool.",
- /*oneline_description=*/"", {"timestamp | \"now\"", "integer / string"}
+ RPCArgOptions{.type_str={"timestamp | \"now\"", "integer / string"}}
},
{"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"},
},
},
},
- "\"requests\""},
+ RPCArgOptions{.oneline_description="\"requests\""}},
},
RPCResult{
RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
@@ -1689,10 +1692,7 @@ RPCHelpMan importdescriptors()
// Rescan the blockchain using the lowest timestamp
if (rescan) {
int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver, true /* update */);
- {
- LOCK(pwallet->cs_wallet);
- pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
- }
+ pwallet->ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
@@ -1749,7 +1749,7 @@ RPCHelpMan listdescriptors()
},
RPCResult{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
- {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects",
+ {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects (sorted by descriptor string representation)",
{
{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "desc", "Descriptor string representation"},
@@ -1888,7 +1888,9 @@ RPCHelpMan restorewallet()
{
return RPCHelpMan{
"restorewallet",
- "\nRestore and loads a wallet from backup.\n",
+ "\nRestore and loads a wallet from backup.\n"
+ "\nThe rescan is significantly faster if a descriptor wallet is restored"
+ "\nand block filters are available (using startup option \"-blockfilterindex=1\").\n",
{
{"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name that will be applied to the restored wallet"},
{"backup_file", RPCArg::Type::STR, RPCArg::Optional::NO, "The backup file that will be used to restore the wallet."},
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index 50766f20eb..9c0c953a7a 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -290,8 +290,6 @@ RPCHelpMan lockunspent()
LOCK(pwallet->cs_wallet);
- RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
-
bool fUnlock = request.params[0].get_bool();
const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
@@ -304,9 +302,7 @@ RPCHelpMan lockunspent()
return true;
}
- RPCTypeCheckArgument(request.params[1], UniValue::VARR);
-
- const UniValue& output_params = request.params[1];
+ const UniValue& output_params = request.params[1].get_array();
// Create and validate the COutPoints first.
@@ -520,7 +516,7 @@ RPCHelpMan listunspent()
{"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
{"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
},
- "query_options"},
+ RPCArgOptions{.oneline_description="query_options"}},
},
RPCResult{
RPCResult::Type::ARR, "", "",
@@ -566,19 +562,16 @@ RPCHelpMan listunspent()
int nMinDepth = 1;
if (!request.params[0].isNull()) {
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
nMinDepth = request.params[0].getInt<int>();
}
int nMaxDepth = 9999999;
if (!request.params[1].isNull()) {
- RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
nMaxDepth = request.params[1].getInt<int>();
}
std::set<CTxDestination> destinations;
if (!request.params[2].isNull()) {
- RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -594,7 +587,6 @@ RPCHelpMan listunspent()
bool include_unsafe = true;
if (!request.params[3].isNull()) {
- RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
include_unsafe = request.params[3].get_bool();
}
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index 7eefa76a3e..6cb33fc11e 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -317,7 +317,7 @@ RPCHelpMan sendmany()
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
{
- {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
+ {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", RPCArgOptions{.oneline_description="\"\""}},
{"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
@@ -338,7 +338,7 @@ RPCHelpMan sendmany()
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
"\"" + FeeModes("\"\n\"") + "\""},
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra infomration about the transaction."},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
},
{
RPCResult{"if verbose is not set or set to false",
@@ -503,7 +503,6 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
coinControl.fAllowWatchOnly = options.get_bool();
}
else {
- RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(options,
{
{"add_inputs", UniValueType(UniValue::VBOOL)},
@@ -775,7 +774,7 @@ RPCHelpMan fundrawtransaction()
},
},
FundTxDoc()),
- "options"},
+ RPCArgOptions{.oneline_description="options"}},
{"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
"If iswitness is not present, heuristic tests will be used in decoding.\n"
"If true, only witness deserialization will be tried.\n"
@@ -969,7 +968,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
"\"" + FeeModes("\"\n\"") + "\""},
},
- "options"},
+ RPCArgOptions{.oneline_description="options"}},
},
RPCResult{
RPCResult::Type::OBJ, "", "", Cat(
@@ -1137,7 +1136,7 @@ RPCHelpMan send()
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
Cat<std::vector<RPCArg>>(
{
- {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
+ {"add_inputs", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false when \"inputs\" are specified, true otherwise"},"Automatically include coins from the wallet to cover the target amount.\n"},
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
@@ -1174,7 +1173,7 @@ RPCHelpMan send()
},
},
FundTxDoc()),
- "options"},
+ RPCArgOptions{.oneline_description="options"}},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1282,7 +1281,7 @@ RPCHelpMan sendall()
},
FundTxDoc()
),
- "options"
+ RPCArgOptions{.oneline_description="options"}
},
},
RPCResult{
@@ -1380,7 +1379,7 @@ RPCHelpMan sendall()
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
}
const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
- if (!tx || pwallet->IsMine(tx->tx->vout[input.prevout.n]) != (coin_control.fAllowWatchOnly ? ISMINE_ALL : ISMINE_SPENDABLE)) {
+ if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & (coin_control.fAllowWatchOnly ? ISMINE_ALL : ISMINE_SPENDABLE))) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
}
total_input_value += tx->tx->vout[input.prevout.n].nValue;
@@ -1402,6 +1401,10 @@ RPCHelpMan sendall()
const CAmount fee_from_size{fee_rate.GetFee(tx_size.vsize)};
const CAmount effective_value{total_input_value - fee_from_size};
+ if (fee_from_size > pwallet->m_default_max_tx_fee) {
+ throw JSONRPCError(RPC_WALLET_ERROR, TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED).original);
+ }
+
if (effective_value <= 0) {
if (send_max) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Total value of UTXO pool too low to pay for transaction, try using lower feerate.");
@@ -1410,6 +1413,11 @@ RPCHelpMan sendall()
}
}
+ // If this transaction is too large, e.g. because the wallet has many UTXOs, it will be rejected by the node's mempool.
+ if (tx_size.weight > MAX_STANDARD_TX_WEIGHT) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Transaction too large.");
+ }
+
CAmount output_amounts_claimed{0};
for (const CTxOut& out : rawTx.vout) {
output_amounts_claimed += out.nValue;
@@ -1582,7 +1590,7 @@ RPCHelpMan walletcreatefundedpsbt()
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
Cat<std::vector<RPCArg>>(
{
- {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
+ {"add_inputs", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false when \"inputs\" are specified, true otherwise"}, "Automatically include coins from the wallet to cover the target amount.\n"},
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
@@ -1603,7 +1611,7 @@ RPCHelpMan walletcreatefundedpsbt()
},
},
FundTxDoc()),
- "options"},
+ RPCArgOptions{.oneline_description="options"}},
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
@@ -1644,7 +1652,6 @@ RPCHelpMan walletcreatefundedpsbt()
bool rbf{wallet.m_signal_rbf};
const UniValue &replaceable_arg = options["replaceable"];
if (!replaceable_arg.isNull()) {
- RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
rbf = replaceable_arg.isTrue();
}
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 0e13e4756b..0fe4ed703d 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -839,7 +839,9 @@ RPCHelpMan rescanblockchain()
{
return RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
+ "The rescan is significantly faster when used on a descriptor wallet\n"
+ "and block filters are available (using startup option \"-blockfilterindex=1\").\n",
{
{"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "block height where the rescan should start"},
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp
index 3c1634324e..1aa2a87e99 100644
--- a/src/wallet/rpc/util.cpp
+++ b/src/wallet/rpc/util.cpp
@@ -12,10 +12,26 @@
#include <univalue.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
namespace wallet {
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
+int64_t ParseISO8601DateTime(const std::string& str)
+{
+ static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
+ static const std::locale loc(std::locale::classic(),
+ new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
+ std::istringstream iss(str);
+ iss.imbue(loc);
+ boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
+ iss >> ptime;
+ if (ptime.is_not_a_date_time() || epoch > ptime)
+ return 0;
+ return (ptime - epoch).total_seconds();
+}
+
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h
index 2ca28914e7..87d34f7c11 100644
--- a/src/wallet/rpc/util.h
+++ b/src/wallet/rpc/util.h
@@ -45,6 +45,8 @@ std::string LabelFromValue(const UniValue& value);
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry);
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error);
+
+int64_t ParseISO8601DateTime(const std::string& str);
} // namespace wallet
#endif // BITCOIN_WALLET_RPC_UTIL_H
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 41654579c6..4c534d64ec 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -28,6 +28,9 @@ util::Result<CTxDestination> LegacyScriptPubKeyMan::GetNewDestination(const Outp
}
assert(type != OutputType::BECH32M);
+ // Fill-up keypool if needed
+ TopUp();
+
LOCK(cs_KeyStore);
// Generate a new key that is added to wallet
@@ -304,6 +307,9 @@ util::Result<CTxDestination> LegacyScriptPubKeyMan::GetReservedDestination(const
return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")};
}
+ // Fill-up keypool if needed
+ TopUp();
+
if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")};
}
@@ -586,7 +592,7 @@ bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sig
// or solving information, even if not able to sign fully.
return true;
} else {
- // If, given the stuff in sigdata, we could make a valid sigature, then we can provide for this script
+ // If, given the stuff in sigdata, we could make a valid signature, then we can provide for this script
ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, script, sigdata);
if (!sigdata.signatures.empty()) {
// If we could make signatures, make sure we have a private key to actually make a signature
@@ -1983,7 +1989,7 @@ util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const
std::optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType();
assert(desc_addr_type);
if (type != *desc_addr_type) {
- throw std::runtime_error(std::string(__func__) + ": Types are inconsistent");
+ throw std::runtime_error(std::string(__func__) + ": Types are inconsistent. Stored type does not match type of newly generated address");
}
TopUp();
@@ -2001,11 +2007,8 @@ util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const
}
CTxDestination dest;
- std::optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType();
- if (out_script_type && out_script_type == type) {
- ExtractDestination(scripts_temp[0], dest);
- } else {
- throw std::runtime_error(std::string(__func__) + ": Types are inconsistent. Stored type does not match type of newly generated address");
+ if (!ExtractDestination(scripts_temp[0], dest)) {
+ return util::Error{_("Error: Cannot extract destination from the generated scriptpubkey")}; // shouldn't happen
}
m_wallet_descriptor.next_index++;
WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor);
@@ -2642,16 +2645,26 @@ const WalletDescriptor DescriptorScriptPubKeyMan::GetWalletDescriptor() const
const std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys() const
{
+ return GetScriptPubKeys(0);
+}
+
+const std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys(int32_t minimum_index) const
+{
LOCK(cs_desc_man);
std::unordered_set<CScript, SaltedSipHasher> script_pub_keys;
script_pub_keys.reserve(m_map_script_pub_keys.size());
- for (auto const& script_pub_key: m_map_script_pub_keys) {
- script_pub_keys.insert(script_pub_key.first);
+ for (auto const& [script_pub_key, index] : m_map_script_pub_keys) {
+ if (index >= minimum_index) script_pub_keys.insert(script_pub_key);
}
return script_pub_keys;
}
+int32_t DescriptorScriptPubKeyMan::GetEndRange() const
+{
+ return m_max_cached_index + 1;
+}
+
bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool priv) const
{
LOCK(cs_desc_man);
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 3ab489c374..eb77015956 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -643,6 +643,8 @@ public:
const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
const std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
+ const std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys(int32_t minimum_index) const;
+ int32_t GetEndRange() const;
bool GetDescriptorString(std::string& out, const bool priv) const;
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index d84310c323..6833f9a095 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -260,32 +260,24 @@ CoinsResult AvailableCoins(const CWallet& wallet,
// Filter by spendable outputs only
if (!spendable && only_spendable) continue;
- // If the Output is P2SH and spendable, we want to know if it is
+ // Obtain script type
+ std::vector<std::vector<uint8_t>> script_solutions;
+ TxoutType type = Solver(output.scriptPubKey, script_solutions);
+
+ // If the output is P2SH and solvable, we want to know if it is
// a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
- // this from the redeemScript. If the Output is not spendable, it will be classified
+ // this from the redeemScript. If the output is not solvable, it will be classified
// as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
- CScript script;
bool is_from_p2sh{false};
- if (output.scriptPubKey.IsPayToScriptHash() && solvable) {
- CTxDestination destination;
- if (!ExtractDestination(output.scriptPubKey, destination))
- continue;
- const CScriptID& hash = CScriptID(std::get<ScriptHash>(destination));
- if (!provider->GetCScript(hash, script))
- continue;
+ if (type == TxoutType::SCRIPTHASH && solvable) {
+ CScript script;
+ if (!provider->GetCScript(CScriptID(uint160(script_solutions[0])), script)) continue;
+ type = Solver(script, script_solutions);
is_from_p2sh = true;
- } else {
- script = output.scriptPubKey;
}
- COutput coin(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
-
- // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
- // We don't need those here, so we are leaving them in return_values_unused
- std::vector<std::vector<uint8_t>> return_values_unused;
- TxoutType type;
- type = Solver(script, return_values_unused);
- result.Add(GetOutputType(type, is_from_p2sh), coin);
+ result.Add(GetOutputType(type, is_from_p2sh),
+ COutput(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate));
// Cache total amount as we go
result.total_amount += output.nValue;
@@ -590,7 +582,13 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& a
if (coin_control.HasSelected() && !coin_control.m_allow_other_inputs) {
SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL);
result.AddInput(preset_inputs);
- if (result.GetSelectedValue() < nTargetValue) return std::nullopt;
+
+ if (!coin_selection_params.m_subtract_fee_outputs && result.GetSelectedEffectiveValue() < nTargetValue) {
+ return std::nullopt;
+ } else if (result.GetSelectedValue() < nTargetValue) {
+ return std::nullopt;
+ }
+
result.ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
return result;
}
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 30a7fb9eb6..23f024247d 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -922,5 +922,52 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
}
+BOOST_AUTO_TEST_CASE(SelectCoins_effective_value_test)
+{
+ // Test that the effective value is used to check whether preset inputs provide sufficient funds when subtract_fee_outputs is not used.
+ // This test creates a coin whose value is higher than the target but whose effective value is lower than the target.
+ // The coin is selected using coin control, with m_allow_other_inputs = false. SelectCoins should fail due to insufficient funds.
+
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ CoinsResult available_coins;
+ {
+ std::unique_ptr<CWallet> dummyWallet = std::make_unique<CWallet>(m_node.chain.get(), "dummy", m_args, CreateMockWalletDatabase());
+ dummyWallet->LoadWallet();
+ LOCK(dummyWallet->cs_wallet);
+ dummyWallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ dummyWallet->SetupDescriptorScriptPubKeyMans();
+
+ add_coin(available_coins, *dummyWallet, 100000); // 0.001 BTC
+ }
+
+ CAmount target{99900}; // 0.000999 BTC
+
+ FastRandomContext rand;
+ CoinSelectionParams cs_params{
+ rand,
+ /*change_output_size=*/34,
+ /*change_spend_size=*/148,
+ /*min_change_target=*/1000,
+ /*effective_feerate=*/CFeeRate(3000),
+ /*long_term_feerate=*/CFeeRate(1000),
+ /*discard_feerate=*/CFeeRate(1000),
+ /*tx_noinputs_size=*/0,
+ /*avoid_partial=*/false,
+ };
+ CCoinControl cc;
+ cc.m_allow_other_inputs = false;
+ COutput output = available_coins.All().at(0);
+ cc.SetInputWeight(output.outpoint, 148);
+ cc.SelectExternal(output.outpoint, output.txout);
+
+ const auto result = SelectCoins(*wallet, available_coins, target, cc, cs_params);
+ BOOST_CHECK(!result);
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet
diff --git a/src/wallet/test/feebumper_tests.cpp b/src/wallet/test/feebumper_tests.cpp
index 6add86dc3d..2480a9b4e1 100644
--- a/src/wallet/test/feebumper_tests.cpp
+++ b/src/wallet/test/feebumper_tests.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.
+#include <consensus/validation.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <util/strencodings.h>
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 90a328179e..1a0708c43a 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -30,7 +30,7 @@ static void GroupCoins(FuzzedDataProvider& fuzzed_data_provider, const std::vect
bool valid_outputgroup{false};
for (auto& coin : coins) {
output_group.Insert(coin, /*ancestors=*/0, /*descendants=*/0, positive_only);
- // If positive_only was specified, nothing may have been inserted, leading to an empty outpout group
+ // If positive_only was specified, nothing may have been inserted, leading to an empty output group
// that would be invalid for the BnB algorithm
valid_outputgroup = !positive_only || output_group.GetSelectionAmount() > 0;
if (valid_outputgroup && fuzzed_data_provider.ConsumeBool()) {
diff --git a/src/test/fuzz/parse_iso8601.cpp b/src/wallet/test/fuzz/parse_iso8601.cpp
index 0fef9a9a1d..5be248c2fb 100644
--- a/src/test/fuzz/parse_iso8601.cpp
+++ b/src/wallet/test/fuzz/parse_iso8601.cpp
@@ -5,6 +5,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <util/time.h>
+#include <wallet/rpc/util.h>
#include <cassert>
#include <cstdint>
@@ -20,7 +21,7 @@ FUZZ_TARGET(parse_iso8601)
const std::string iso8601_datetime = FormatISO8601DateTime(random_time);
(void)FormatISO8601Date(random_time);
- const int64_t parsed_time_1 = ParseISO8601DateTime(iso8601_datetime);
+ const int64_t parsed_time_1 = wallet::ParseISO8601DateTime(iso8601_datetime);
if (random_time >= 0) {
assert(parsed_time_1 >= 0);
if (iso8601_datetime.length() == 20) {
@@ -28,6 +29,6 @@ FUZZ_TARGET(parse_iso8601)
}
}
- const int64_t parsed_time_2 = ParseISO8601DateTime(random_string);
+ const int64_t parsed_time_2 = wallet::ParseISO8601DateTime(random_string);
assert(parsed_time_2 >= 0);
}
diff --git a/src/wallet/test/rpc_util_tests.cpp b/src/wallet/test/rpc_util_tests.cpp
new file mode 100644
index 0000000000..32f6f5ab46
--- /dev/null
+++ b/src/wallet/test/rpc_util_tests.cpp
@@ -0,0 +1,24 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <wallet/rpc/util.h>
+
+#include <boost/test/unit_test.hpp>
+
+namespace wallet {
+
+BOOST_AUTO_TEST_SUITE(wallet_util_tests)
+
+BOOST_AUTO_TEST_CASE(util_ParseISO8601DateTime)
+{
+ BOOST_CHECK_EQUAL(ParseISO8601DateTime("1970-01-01T00:00:00Z"), 0);
+ BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0);
+ BOOST_CHECK_EQUAL(ParseISO8601DateTime("2000-01-01T00:00:01Z"), 946684801);
+ BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777);
+ BOOST_CHECK_EQUAL(ParseISO8601DateTime("2100-12-31T23:59:59Z"), 4133980799);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace wallet
diff --git a/src/wallet/test/walletload_tests.cpp b/src/wallet/test/walletload_tests.cpp
new file mode 100644
index 0000000000..f45b69a418
--- /dev/null
+++ b/src/wallet/test/walletload_tests.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://www.opensource.org/licenses/mit-license.php.
+
+#include <wallet/wallet.h>
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+namespace wallet {
+
+BOOST_AUTO_TEST_SUITE(walletload_tests)
+
+class DummyDescriptor final : public Descriptor {
+private:
+ std::string desc;
+public:
+ explicit DummyDescriptor(const std::string& descriptor) : desc(descriptor) {};
+ ~DummyDescriptor() = default;
+
+ std::string ToString() const override { return desc; }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; }
+
+ bool IsRange() const override { return false; }
+ bool IsSolvable() const override { return false; }
+ bool IsSingleType() const override { return true; }
+ bool ToPrivateString(const SigningProvider& provider, std::string& out) const override { return false; }
+ bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const override { return false; }
+ bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const override { return false; };
+ bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override { return false; }
+ void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const override {}
+};
+
+BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup)
+{
+ std::unique_ptr<WalletDatabase> database = CreateMockWalletDatabase();
+ {
+ // Write unknown active descriptor
+ WalletBatch batch(*database, false);
+ std::string unknown_desc = "trx(tpubD6NzVbkrYhZ4Y4S7m6Y5s9GD8FqEMBy56AGphZXuagajudVZEnYyBahZMgHNCTJc2at82YX6s8JiL1Lohu5A3v1Ur76qguNH4QVQ7qYrBQx/86'/1'/0'/0/*)#8pn8tzdt";
+ WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(unknown_desc), 0, 0, 0, 0);
+ BOOST_CHECK(batch.WriteDescriptor(uint256(), wallet_descriptor));
+ BOOST_CHECK(batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(OutputType::UNKNOWN), uint256(), false));
+ }
+
+ {
+ // Now try to load the wallet and verify the error.
+ const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", m_args, std::move(database)));
+ BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::UNKNOWN_DESCRIPTOR);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 0949045ff0..431e970edc 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -5,6 +5,7 @@
#include <wallet/wallet.h>
+#include <blockfilter.h>
#include <chain.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
@@ -21,6 +22,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <psbt.h>
+#include <random.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/signingprovider.h>
@@ -124,7 +126,7 @@ bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet
interfaces::Chain& chain = wallet->chain();
std::string name = wallet->GetName();
- // Unregister with the validation interface which also drops shared ponters.
+ // Unregister with the validation interface which also drops shared pointers.
wallet->m_chain_notifications_handler.reset();
LOCK(context.wallets_mutex);
std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet);
@@ -260,6 +262,64 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s
return nullptr;
}
}
+
+class FastWalletRescanFilter
+{
+public:
+ FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
+ {
+ // fast rescanning via block filters is only supported by descriptor wallets right now
+ assert(!m_wallet.IsLegacy());
+
+ // create initial filter with scripts from all ScriptPubKeyMans
+ for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
+ auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
+ assert(desc_spkm != nullptr);
+ AddScriptPubKeys(desc_spkm);
+ // save each range descriptor's end for possible future filter updates
+ if (desc_spkm->IsHDEnabled()) {
+ m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());
+ }
+ }
+ }
+
+ void UpdateIfNeeded()
+ {
+ // repopulate filter with new scripts if top-up has happened since last iteration
+ for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {
+ auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};
+ assert(desc_spkm != nullptr);
+ int32_t current_range_end{desc_spkm->GetEndRange()};
+ if (current_range_end > last_range_end) {
+ AddScriptPubKeys(desc_spkm, last_range_end);
+ m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;
+ }
+ }
+ }
+
+ std::optional<bool> MatchesBlock(const uint256& block_hash) const
+ {
+ return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC, block_hash, m_filter_set);
+ }
+
+private:
+ const CWallet& m_wallet;
+ /** Map for keeping track of each range descriptor's last seen end range.
+ * This information is used to detect whether new addresses were derived
+ * (that is, if the current end range is larger than the saved end range)
+ * after processing a block and hence a filter set update is needed to
+ * take possible keypool top-ups into account.
+ */
+ std::map<uint256, int32_t> m_last_range_ends;
+ GCSFilter::ElementSet m_filter_set;
+
+ void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)
+ {
+ for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {
+ m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());
+ }
+ }
+};
} // namespace
std::shared_ptr<CWallet> LoadWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
@@ -386,25 +446,31 @@ std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& b
ReadDatabaseArgs(*context.args, options);
options.require_existing = true;
- if (!fs::exists(backup_file)) {
- error = Untranslated("Backup file does not exist");
- status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
- return nullptr;
- }
-
const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name));
+ auto wallet_file = wallet_path / "wallet.dat";
+ std::shared_ptr<CWallet> wallet;
- if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) {
- error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path)));
- status = DatabaseStatus::FAILED_ALREADY_EXISTS;
- return nullptr;
- }
+ try {
+ if (!fs::exists(backup_file)) {
+ error = Untranslated("Backup file does not exist");
+ status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
+ return nullptr;
+ }
- auto wallet_file = wallet_path / "wallet.dat";
- fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
+ if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) {
+ error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path)));
+ status = DatabaseStatus::FAILED_ALREADY_EXISTS;
+ return nullptr;
+ }
- auto wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
+ fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
+ wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
+ } catch (const std::exception& e) {
+ assert(!wallet);
+ if (!error.empty()) error += Untranslated("\n");
+ error += strprintf(Untranslated("Unexpected exception: %s"), e.what());
+ }
if (!wallet) {
fs::remove(wallet_file);
fs::remove(wallet_path);
@@ -882,7 +948,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
- // Refresh mempool status without waiting for transactionRemovedFromMempool
+ // Refresh mempool status without waiting for transactionRemovedFromMempool or transactionAddedToMempool
RefreshMempoolStatus(wtx, chain());
WalletBatch batch(GetDatabase());
@@ -1748,7 +1814,11 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
uint256 block_hash = start_block;
ScanResult result;
- WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
+ std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;
+ if (!IsLegacy() && chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
+
+ WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),
+ fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");
fAbortRescan = false;
ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
@@ -1775,9 +1845,22 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current);
}
- // Read block data
- CBlock block;
- chain().findBlock(block_hash, FoundBlock().data(block));
+ bool fetch_block{true};
+ if (fast_rescan_filter) {
+ fast_rescan_filter->UpdateIfNeeded();
+ auto matches_block{fast_rescan_filter->MatchesBlock(block_hash)};
+ if (matches_block.has_value()) {
+ if (*matches_block) {
+ LogPrint(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (filter matched)\n", block_height, block_hash.ToString());
+ } else {
+ result.last_scanned_block = block_hash;
+ result.last_scanned_height = block_height;
+ fetch_block = false;
+ }
+ } else {
+ LogPrint(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (WARNING: block filter not found!)\n", block_height, block_hash.ToString());
+ }
+ }
// Find next block separately from reading data above, because reading
// is slow and there might be a reorg while it is read.
@@ -1786,35 +1869,41 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
uint256 next_block_hash;
chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash)));
- if (!block.IsNull()) {
- LOCK(cs_wallet);
- if (!block_still_active) {
- // Abort scan if current block is no longer active, to prevent
- // marking transactions as coming from the wrong block.
- result.last_failed_block = block_hash;
- result.status = ScanResult::FAILURE;
- break;
- }
- for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, fUpdate, /*rescanning_old_block=*/true);
- }
- // scan succeeded, record block as most recent successfully scanned
- result.last_scanned_block = block_hash;
- result.last_scanned_height = block_height;
+ if (fetch_block) {
+ // Read block data
+ CBlock block;
+ chain().findBlock(block_hash, FoundBlock().data(block));
+
+ if (!block.IsNull()) {
+ LOCK(cs_wallet);
+ if (!block_still_active) {
+ // Abort scan if current block is no longer active, to prevent
+ // marking transactions as coming from the wrong block.
+ result.last_failed_block = block_hash;
+ result.status = ScanResult::FAILURE;
+ break;
+ }
+ for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
+ SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, fUpdate, /*rescanning_old_block=*/true);
+ }
+ // scan succeeded, record block as most recent successfully scanned
+ result.last_scanned_block = block_hash;
+ result.last_scanned_height = block_height;
- if (save_progress && next_interval) {
- CBlockLocator loc = m_chain->getActiveChainLocator(block_hash);
+ if (save_progress && next_interval) {
+ CBlockLocator loc = m_chain->getActiveChainLocator(block_hash);
- if (!loc.IsNull()) {
- WalletLogPrintf("Saving scan progress %d.\n", block_height);
- WalletBatch batch(GetDatabase());
- batch.WriteBestBlock(loc);
+ if (!loc.IsNull()) {
+ WalletLogPrintf("Saving scan progress %d.\n", block_height);
+ WalletBatch batch(GetDatabase());
+ batch.WriteBestBlock(loc);
+ }
}
+ } else {
+ // could not scan block, keep scanning but record this block as the most recent failure
+ result.last_failed_block = block_hash;
+ result.status = ScanResult::FAILURE;
}
- } else {
- // could not scan block, keep scanning but record this block as the most recent failure
- result.last_failed_block = block_hash;
- result.status = ScanResult::FAILURE;
}
if (max_height && block_height >= *max_height) {
break;
@@ -1897,11 +1986,29 @@ std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
return result;
}
+bool CWallet::ShouldResend() const
+{
+ // Don't attempt to resubmit if the wallet is configured to not broadcast
+ if (!fBroadcastTransactions) return false;
+
+ // During reindex, importing and IBD, old wallet transactions become
+ // unconfirmed. Don't resend them as that would spam other nodes.
+ // We only allow forcing mempool submission when not relaying to avoid this spam.
+ if (!chain().isReadyToBroadcast()) return false;
+
+ // Do this infrequently and randomly to avoid giving away
+ // that these are our transactions.
+ if (NodeClock::now() < m_next_resend) return false;
+
+ return true;
+}
+
+NodeClock::time_point CWallet::GetDefaultNextResend() { return FastRandomContext{}.rand_uniform_delay(NodeClock::now() + 12h, 24h); }
+
// Resubmit transactions from the wallet to the mempool, optionally asking the
// mempool to relay them. On startup, we will do this for all unconfirmed
// transactions but will not ask the mempool to relay them. We do this on startup
-// to ensure that our own mempool is aware of our transactions, and to also
-// initialize nNextResend so that the actual rebroadcast is scheduled. There
+// to ensure that our own mempool is aware of our transactions. There
// is a privacy side effect here as not broadcasting on startup also means that we won't
// inform the world of our wallet's state, particularly if the wallet (or node) is not
// yet synced.
@@ -1920,7 +2027,7 @@ std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
// The `force` option results in all unconfirmed transactions being submitted to
// the mempool. This does not necessarily result in those transactions being relayed,
// that depends on the `relay` option. Periodic rebroadcast uses the pattern
-// relay=true force=false (also the default values), while loading into the mempool
+// relay=true force=false, while loading into the mempool
// (on start, or after import) uses relay=false force=true.
void CWallet::ResubmitWalletTransactions(bool relay, bool force)
{
@@ -1928,17 +2035,6 @@ void CWallet::ResubmitWalletTransactions(bool relay, bool force)
// even if forcing.
if (!fBroadcastTransactions) return;
- // During reindex, importing and IBD, old wallet transactions become
- // unconfirmed. Don't resend them as that would spam other nodes.
- // We only allow forcing mempool submission when not relaying to avoid this spam.
- if (!force && relay && !chain().isReadyToBroadcast()) return;
-
- // Do this infrequently and randomly to avoid giving away
- // that these are our transactions.
- if (!force && GetTime() < nNextResend) return;
- // resend 12-36 hours from now, ~1 day on average.
- nNextResend = GetTime() + (12 * 60 * 60) + GetRand(24 * 60 * 60);
-
int submitted_tx_count = 0;
{ // cs_wallet scope
@@ -1951,7 +2047,7 @@ void CWallet::ResubmitWalletTransactions(bool relay, bool force)
// Only rebroadcast unconfirmed txs
if (!wtx.isUnconfirmed()) continue;
- // attempt to rebroadcast all txes more than 5 minutes older than
+ // Attempt to rebroadcast all txes more than 5 minutes older than
// the last block, or all txs if forcing.
if (!force && wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
to_submit.insert(&wtx);
@@ -1973,7 +2069,9 @@ void CWallet::ResubmitWalletTransactions(bool relay, bool force)
void MaybeResendWalletTxs(WalletContext& context)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
+ if (!pwallet->ShouldResend()) continue;
pwallet->ResubmitWalletTransactions(/*relay=*/true, /*force=*/false);
+ pwallet->SetNextResend();
}
}
@@ -2104,6 +2202,7 @@ SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkh
CScript script_pub_key = GetScriptForDestination(pkhash);
for (const auto& spk_man_pair : m_spk_managers) {
if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
+ LOCK(cs_wallet); // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order
return spk_man_pair.second->SignMessage(message, pkhash, str_sig);
}
}
@@ -2369,7 +2468,6 @@ util::Result<CTxDestination> CWallet::GetNewDestination(const OutputType type, c
return util::Error{strprintf(_("Error: No %s addresses available."), FormatOutputType(type))};
}
- spk_man->TopUp();
auto op_dest = spk_man->GetNewDestination(type);
if (op_dest) {
SetAddressBook(*op_dest, label, "receive");
@@ -2463,10 +2561,7 @@ util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool int
return util::Error{strprintf(_("Error: No %s addresses available."), FormatOutputType(type))};
}
- if (nIndex == -1)
- {
- m_spk_man->TopUp();
-
+ if (nIndex == -1) {
CKeyPool keypool;
auto op_address = m_spk_man->GetReservedDestination(type, internal, nIndex, keypool);
if (!op_address) return op_address;
@@ -2786,7 +2881,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
ArgsManager& args = *Assert(context.args);
const std::string& walletFile = database->Filename();
- int64_t nStart = GetTimeMillis();
+ const auto start{SteadyClock::now()};
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
const std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, args, std::move(database)), ReleaseWallet);
@@ -2819,8 +2914,12 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."
" Rescanning wallet."), walletFile));
rescan_required = true;
- }
- else {
+ } else if (nLoadWalletRet == DBErrors::UNKNOWN_DESCRIPTOR) {
+ error = strprintf(_("Unrecognized descriptor found. Loading wallet %s\n\n"
+ "The wallet might had been created on a newer version.\n"
+ "Please try running the latest software version.\n"), walletFile);
+ return nullptr;
+ } else {
error = strprintf(_("Error loading %s"), walletFile);
return nullptr;
}
@@ -3003,7 +3102,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
- walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", GetTimeMillis() - nStart);
+ walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
@@ -3100,12 +3199,14 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
// If a block is pruned after this check, we will load the wallet,
// but fail the rescan with a generic error.
- error = chain.hasAssumedValidChain() ?
- _(
- "Assumed-valid: last wallet synchronisation goes beyond "
- "available block data. You need to wait for the background "
- "validation chain to download more blocks.") :
- _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)");
+ error = chain.havePruned() ?
+ _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)") :
+ strprintf(_(
+ "Error loading wallet. Wallet requires blocks to be downloaded, "
+ "and software does not currently support loading wallets while "
+ "blocks are being downloaded out of order when using assumeutxo "
+ "snapshots. Wallet should be able to load successfully after "
+ "node sync reaches height %s"), block_height);
return false;
}
}
@@ -3185,14 +3286,12 @@ bool CWallet::UpgradeWallet(int version, bilingual_str& error)
void CWallet::postInitProcess()
{
- LOCK(cs_wallet);
-
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
ResubmitWalletTransactions(/*relay=*/false, /*force=*/true);
// Update wallet transactions with current mempool transactions.
- chain().requestMempoolTransactions(*this);
+ WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));
}
bool CWallet::BackupWallet(const std::string& strDest) const
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index f4ea5f1920..137320acda 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -20,6 +20,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/time.h>
#include <util/ui_change_type.h>
#include <validationinterface.h>
#include <wallet/crypter.h>
@@ -195,7 +196,7 @@ public:
util::Result<CTxDestination> GetReservedDestination(bool internal);
//! Return reserved address
void ReturnDestination();
- //! Keep the address. Do not return it's key to the keypool when this object goes out of scope
+ //! Keep the address. Do not return its key to the keypool when this object goes out of scope
void KeepDestination();
};
@@ -250,7 +251,7 @@ private:
int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE};
/** The next scheduled rebroadcast of wallet transactions. */
- int64_t nNextResend = 0;
+ NodeClock::time_point m_next_resend{GetDefaultNextResend()};
/** Whether this wallet will submit newly created transactions to the node's mempool and
* prompt rebroadcasts (see ResendWalletTransactions()). */
bool fBroadcastTransactions = false;
@@ -348,6 +349,8 @@ private:
*/
static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings);
+ static NodeClock::time_point GetDefaultNextResend();
+
public:
/**
* Main wallet lock.
@@ -399,7 +402,6 @@ public:
TxItems wtxOrdered;
int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
- uint64_t nAccountingEntryNumber = 0;
std::map<CTxDestination, CAddressBookData> m_address_book GUARDED_BY(cs_wallet);
const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -537,6 +539,10 @@ public:
};
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate, const bool save_progress);
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
+ /** Set the next time this wallet should resend transactions to 12-36 hours from now, ~1 day on average. */
+ void SetNextResend() { m_next_resend = GetDefaultNextResend(); }
+ /** Return true if all conditions for periodically resending transactions are met. */
+ bool ShouldResend() const;
void ResubmitWalletTransactions(bool relay, bool force);
OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 30406a22f9..6a8f0d2481 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -314,6 +314,7 @@ public:
std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
std::map<uint160, CHDChain> m_hd_chains;
bool tx_corrupt{false};
+ bool descriptor_unknown{false};
CWalletScanState() = default;
};
@@ -627,7 +628,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
uint256 id;
ssKey >> id;
WalletDescriptor desc;
- ssValue >> desc;
+ try {
+ ssValue >> desc;
+ } catch (const std::ios_base::failure& e) {
+ strErr = e.what();
+ wss.descriptor_unknown = true;
+ return false;
+ }
if (wss.m_descriptor_caches.count(id) == 0) {
wss.m_descriptor_caches[id] = DescriptorCache();
}
@@ -767,6 +774,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
DBErrors result = DBErrors::LOAD_OK;
LOCK(pwallet->cs_wallet);
+
+ // Last client version to open this wallet
+ int last_client = CLIENT_VERSION;
+ bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
+ pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
+
try {
int nMinVersion = 0;
if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
@@ -832,6 +845,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Set tx_corrupt back to false so that the error is only printed once (per corrupt tx)
wss.tx_corrupt = false;
result = DBErrors::CORRUPT;
+ } else if (wss.descriptor_unknown) {
+ strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName());
+ strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " :
+ "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. ";
+ strErr += "Please try running the latest software version";
+ pwallet->WalletLogPrintf("%s\n", strErr);
+ return DBErrors::UNKNOWN_DESCRIPTOR;
} else {
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
@@ -884,11 +904,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (result != DBErrors::LOAD_OK)
return result;
- // Last client version to open this wallet
- int last_client = CLIENT_VERSION;
- bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
- pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
-
pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 6aa25fae03..da6efe534b 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -51,7 +51,8 @@ enum class DBErrors
EXTERNAL_SIGNER_SUPPORT_REQUIRED,
LOAD_FAIL,
NEED_REWRITE,
- NEED_RESCAN
+ NEED_RESCAN,
+ UNKNOWN_DESCRIPTOR
};
namespace DBKeys {
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index fa3944e32b..97c2599366 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
-
+#include <cstdint>
#include <memory>
#include <string>
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 26618735f6..6ee134f392 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -3,13 +3,23 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <zmq/zmqnotificationinterface.h>
+
+#include <logging.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <util/system.h>
+#include <validationinterface.h>
+#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqpublishnotifier.h>
#include <zmq/zmqutil.h>
#include <zmq.h>
-#include <validation.h>
-#include <util/system.h>
+#include <cassert>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr)
{
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 8f81bfd63f..585e900ca6 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -5,10 +5,14 @@
#ifndef BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
#define BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
+#include <primitives/transaction.h>
#include <validationinterface.h>
+
+#include <cstdint>
#include <list>
#include <memory>
+class CBlock;
class CBlockIndex;
class CZMQAbstractNotifier;
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 011336bf72..eaf3455296 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -6,22 +6,37 @@
#include <chain.h>
#include <chainparams.h>
+#include <crypto/common.h>
+#include <logging.h>
+#include <netaddress.h>
#include <netbase.h>
#include <node/blockstorage.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
#include <rpc/server.h>
+#include <serialize.h>
#include <streams.h>
-#include <util/system.h>
-#include <validation.h> // For cs_main
+#include <sync.h>
+#include <uint256.h>
+#include <version.h>
#include <zmq/zmqutil.h>
#include <zmq.h>
+#include <cassert>
#include <cstdarg>
#include <cstddef>
+#include <cstdint>
+#include <cstring>
#include <map>
#include <optional>
#include <string>
#include <utility>
+#include <vector>
+
+namespace Consensus {
+struct Params;
+}
using node::ReadBlockFromDisk;
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index c1d66bddb1..fcedd1aabe 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -7,7 +7,11 @@
#include <zmq/zmqabstractnotifier.h>
+#include <cstddef>
+#include <cstdint>
+
class CBlockIndex;
+class CTransaction;
class CZMQAbstractPublishNotifier : public CZMQAbstractNotifier
{
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index ec6d1cbba3..047e6bf9b7 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -11,6 +11,11 @@
#include <univalue.h>
+#include <list>
+#include <string>
+
+class JSONRPCRequest;
+
namespace {
static RPCHelpMan getzmqnotifications()