aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am19
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qt.include4
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/Makefile.test_fuzz.include2
-rw-r--r--src/Makefile.test_util.include2
-rw-r--r--src/addrdb.cpp1
-rw-r--r--src/arith_uint256.cpp4
-rw-r--r--src/banman.cpp20
-rw-r--r--src/bench/addrman.cpp11
-rw-r--r--src/bench/bench.cpp5
-rw-r--r--src/bench/checkblock.cpp8
-rw-r--r--src/bench/coin_selection.cpp13
-rw-r--r--src/bench/rpc_blockchain.cpp4
-rw-r--r--src/bench/wallet_balance.cpp6
-rw-r--r--src/bitcoin-tx.cpp10
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/blockfilter.cpp33
-rw-r--r--src/chain.cpp8
-rw-r--r--src/chain.h31
-rw-r--r--src/chainparamsseeds.h9
-rw-r--r--src/common/bloom.cpp23
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/crypto/chacha_poly_aead.cpp2
-rw-r--r--src/cuckoocache.h27
-rw-r--r--src/dbwrapper.h6
-rw-r--r--src/dummywallet.cpp8
-rw-r--r--src/fs.cpp120
-rw-r--r--src/fs.h135
-rw-r--r--src/hash.h15
-rw-r--r--src/httpserver.cpp10
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/index/blockfilterindex.cpp2
-rw-r--r--src/index/coinstatsindex.cpp6
-rw-r--r--src/index/coinstatsindex.h2
-rw-r--r--src/index/txindex.cpp6
-rw-r--r--src/init.cpp89
-rw-r--r--src/init.h12
-rw-r--r--src/init/bitcoin-gui.cpp2
-rw-r--r--src/init/bitcoin-node.cpp6
-rw-r--r--src/init/bitcoin-qt.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/interfaces/chain.h7
-rw-r--r--src/interfaces/init.h4
-rw-r--r--src/interfaces/node.h14
-rw-r--r--src/interfaces/wallet.h50
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/net.cpp68
-rw-r--r--src/net.h31
-rw-r--r--src/net_processing.cpp173
-rw-r--r--src/net_processing.h9
-rw-r--r--src/node/blockstorage.cpp180
-rw-r--r--src/node/blockstorage.h56
-rw-r--r--src/node/caches.cpp2
-rw-r--r--src/node/caches.h2
-rw-r--r--src/node/chainstate.cpp4
-rw-r--r--src/node/chainstate.h10
-rw-r--r--src/node/coin.cpp2
-rw-r--r--src/node/coin.h5
-rw-r--r--src/node/coinstats.cpp2
-rw-r--r--src/node/coinstats.h8
-rw-r--r--src/node/context.cpp2
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interfaces.cpp17
-rw-r--r--src/node/miner.cpp2
-rw-r--r--src/node/miner.h2
-rw-r--r--src/node/minisketchwrapper.cpp2
-rw-r--r--src/node/minisketchwrapper.h2
-rw-r--r--src/node/psbt.cpp2
-rw-r--r--src/node/psbt.h2
-rw-r--r--src/node/transaction.cpp2
-rw-r--r--src/node/transaction.h5
-rw-r--r--src/node/ui_interface.h3
-rw-r--r--src/node/utxo_snapshot.h2
-rw-r--r--src/policy/packages.h1
-rw-r--r--src/primitives/transaction.h36
-rw-r--r--src/psbt.cpp2
-rw-r--r--src/pubkey.h4
-rw-r--r--src/qt/bitcoin.cpp7
-rw-r--r--src/qt/clientmodel.cpp7
-rw-r--r--src/qt/coincontroldialog.cpp3
-rw-r--r--src/qt/coincontroldialog.h8
-rw-r--r--src/qt/forms/debugwindow.ui82
-rw-r--r--src/qt/forms/optionsdialog.ui10
-rw-r--r--src/qt/guiconstants.h12
-rw-r--r--src/qt/guiutil.cpp23
-rw-r--r--src/qt/guiutil.h4
-rw-r--r--src/qt/intro.cpp10
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/optionsmodel.cpp15
-rw-r--r--src/qt/optionsmodel.h3
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/psbtoperationsdialog.cpp8
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp67
-rw-r--r--src/qt/sendcoinsdialog.h15
-rw-r--r--src/qt/test/addressbooktests.cpp11
-rw-r--r--src/qt/test/test_main.cpp5
-rw-r--r--src/qt/test/util.cpp4
-rw-r--r--src/qt/test/util.h8
-rw-r--r--src/qt/test/wallettests.cpp12
-rw-r--r--src/qt/transactiondesc.cpp14
-rw-r--r--src/qt/transactionrecord.cpp19
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/transactiontablemodel.cpp9
-rw-r--r--src/qt/transactionview.cpp5
-rw-r--r--src/qt/walletcontroller.cpp10
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletmodel.cpp16
-rw-r--r--src/qt/walletmodel.h7
-rw-r--r--src/random.cpp7
-rw-r--r--src/random.h14
-rw-r--r--src/rest.cpp5
-rw-r--r--src/rpc/blockchain.cpp257
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp7
-rw-r--r--src/rpc/misc.cpp2
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp16
-rw-r--r--src/rpc/rawtransaction_util.cpp2
-rw-r--r--src/rpc/request.cpp9
-rw-r--r--src/rpc/server_util.cpp2
-rw-r--r--src/rpc/server_util.h16
-rw-r--r--src/rpc/util.cpp16
-rw-r--r--src/rpc/util.h9
-rw-r--r--src/scheduler.cpp12
-rw-r--r--src/scheduler.h6
-rw-r--r--src/script/bitcoinconsensus.cpp17
-rw-r--r--src/script/interpreter.cpp17
-rw-r--r--src/script/interpreter.h12
-rw-r--r--src/script/sign.cpp2
-rw-r--r--src/serialize.h77
-rw-r--r--src/span.h9
-rw-r--r--src/streams.h86
-rw-r--r--src/support/allocators/zeroafterfree.h2
-rw-r--r--src/test/README.md28
-rw-r--r--src/test/addrman_tests.cpp70
-rw-r--r--src/test/arith_uint256_tests.cpp12
-rw-r--r--src/test/blockfilter_index_tests.cpp4
-rw-r--r--src/test/bloom_tests.cpp12
-rw-r--r--src/test/coins_tests.cpp4
-rw-r--r--src/test/coinstatsindex_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp48
-rw-r--r--src/test/fs_tests.cpp18
-rw-r--r--src/test/fuzz/addrman.cpp18
-rw-r--r--src/test/fuzz/autofile.cpp8
-rw-r--r--src/test/fuzz/buffered_file.cpp6
-rw-r--r--src/test/fuzz/chain.cpp23
-rw-r--r--src/test/fuzz/coins_view.cpp6
-rw-r--r--src/test/fuzz/connman.cpp16
-rw-r--r--src/test/fuzz/deserialize.cpp15
-rw-r--r--src/test/fuzz/fuzz.cpp31
-rw-r--r--src/test/fuzz/golomb_rice.cpp16
-rw-r--r--src/test/fuzz/integer.cpp5
-rw-r--r--src/test/fuzz/minisketch.cpp2
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp4
-rw-r--r--src/test/fuzz/psbt.cpp4
-rw-r--r--src/test/fuzz/rpc.cpp3
-rw-r--r--src/test/fuzz/script.cpp19
-rw-r--r--src/test/fuzz/signature_checker.cpp2
-rw-r--r--src/test/fuzz/string.cpp10
-rw-r--r--src/test/fuzz/tx_pool.cpp2
-rw-r--r--src/test/fuzz/util.cpp6
-rw-r--r--src/test/fuzz/util.h6
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp3
-rw-r--r--src/test/fuzz/versionbits.cpp35
-rw-r--r--src/test/getarg_tests.cpp6
-rw-r--r--src/test/interfaces_tests.cpp1
-rw-r--r--src/test/main.cpp15
-rw-r--r--src/test/miner_tests.cpp5
-rw-r--r--src/test/minisketch_tests.cpp2
-rw-r--r--src/test/net_tests.cpp2
-rw-r--r--src/test/pow_tests.cpp2
-rw-r--r--src/test/prevector_tests.cpp16
-rw-r--r--src/test/script_tests.cpp25
-rw-r--r--src/test/serialize_tests.cpp52
-rw-r--r--src/test/settings_tests.cpp8
-rw-r--r--src/test/sighash_tests.cpp7
-rw-r--r--src/test/skiplist_tests.cpp4
-rw-r--r--src/test/streams_tests.cpp29
-rw-r--r--src/test/transaction_tests.cpp4
-rw-r--r--src/test/txpackage_tests.cpp232
-rw-r--r--src/test/util/blockfilter.cpp4
-rw-r--r--src/test/util/chainstate.h6
-rw-r--r--src/test/util/mining.cpp3
-rw-r--r--src/test/util/mining.h8
-rw-r--r--src/test/util/setup_common.cpp57
-rw-r--r--src/test/util/setup_common.h10
-rw-r--r--src/test/util/wallet.cpp2
-rw-r--r--src/test/util/wallet.h6
-rw-r--r--src/test/util_tests.cpp59
-rw-r--r--src/test/validation_block_tests.cpp2
-rw-r--r--src/test/validation_chainstate_tests.cpp1
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp5
-rw-r--r--src/test/validation_flush_tests.cpp2
-rw-r--r--src/txdb.cpp5
-rw-r--r--src/txdb.h3
-rw-r--r--src/txmempool.cpp32
-rw-r--r--src/txmempool.h90
-rw-r--r--src/uint256.h6
-rw-r--r--src/util/asmap.cpp1
-rw-r--r--src/util/fastrange.h51
-rw-r--r--src/util/golombrice.h2
-rw-r--r--src/util/settings.cpp12
-rw-r--r--src/util/strencodings.h19
-rw-r--r--src/util/syscall_sandbox.cpp1
-rw-r--r--src/util/system.cpp40
-rw-r--r--src/validation.cpp224
-rw-r--r--src/validation.h49
-rw-r--r--src/versionbits.cpp31
-rw-r--r--src/versionbits.h12
-rw-r--r--src/wallet/bdb.cpp13
-rw-r--r--src/wallet/bdb.h2
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coincontrol.h21
-rw-r--r--src/wallet/coinselection.cpp2
-rw-r--r--src/wallet/coinselection.h2
-rw-r--r--src/wallet/context.cpp2
-rw-r--r--src/wallet/context.h4
-rw-r--r--src/wallet/crypter.cpp2
-rw-r--r--src/wallet/crypter.h2
-rw-r--r--src/wallet/db.cpp24
-rw-r--r--src/wallet/db.h2
-rw-r--r--src/wallet/dump.cpp26
-rw-r--r--src/wallet/dump.h6
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp2
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h2
-rw-r--r--src/wallet/feebumper.cpp2
-rw-r--r--src/wallet/feebumper.h9
-rw-r--r--src/wallet/fees.cpp2
-rw-r--r--src/wallet/fees.h7
-rw-r--r--src/wallet/init.cpp8
-rw-r--r--src/wallet/interfaces.cpp5
-rw-r--r--src/wallet/ismine.h5
-rw-r--r--src/wallet/load.cpp6
-rw-r--r--src/wallet/load.h5
-rw-r--r--src/wallet/receive.cpp4
-rw-r--r--src/wallet/receive.h2
-rw-r--r--src/wallet/rpc/addresses.cpp2
-rw-r--r--src/wallet/rpc/backup.cpp13
-rw-r--r--src/wallet/rpc/coins.cpp6
-rw-r--r--src/wallet/rpc/encrypt.cpp2
-rw-r--r--src/wallet/rpc/signmessage.cpp2
-rw-r--r--src/wallet/rpc/spend.cpp81
-rw-r--r--src/wallet/rpc/transactions.cpp6
-rw-r--r--src/wallet/rpc/util.cpp4
-rw-r--r--src/wallet/rpc/util.h9
-rw-r--r--src/wallet/rpc/wallet.cpp2
-rw-r--r--src/wallet/rpc/wallet.h2
-rw-r--r--src/wallet/salvage.cpp2
-rw-r--r--src/wallet/salvage.h2
-rw-r--r--src/wallet/scriptpubkeyman.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/spend.cpp20
-rw-r--r--src/wallet/spend.h2
-rw-r--r--src/wallet/sqlite.cpp20
-rw-r--r--src/wallet/sqlite.h3
-rw-r--r--src/wallet/test/coinselector_tests.cpp2
-rw-r--r--src/wallet/test/db_tests.cpp9
-rw-r--r--src/wallet/test/fuzz/notifications.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.cpp11
-rw-r--r--src/wallet/test/init_test_fixture.h2
-rw-r--r--src/wallet/test/init_tests.cpp5
-rw-r--r--src/wallet/test/ismine_tests.cpp2
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/spend_tests.cpp53
-rw-r--r--src/wallet/test/util.cpp2
-rw-r--r--src/wallet/test/util.h5
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp55
-rw-r--r--src/wallet/test/wallet_transaction_tests.cpp2
-rw-r--r--src/wallet/test/walletdb_tests.cpp2
-rw-r--r--src/wallet/transaction.cpp2
-rw-r--r--src/wallet/transaction.h2
-rw-r--r--src/wallet/wallet.cpp69
-rw-r--r--src/wallet/wallet.h16
-rw-r--r--src/wallet/walletdb.cpp4
-rw-r--r--src/wallet/walletdb.h23
-rw-r--r--src/wallet/wallettool.cpp2
-rw-r--r--src/wallet/wallettool.h2
-rw-r--r--src/wallet/walletutil.cpp2
-rw-r--r--src/wallet/walletutil.h2
-rw-r--r--src/walletinitinterface.h4
-rw-r--r--src/zmq/zmqpublishnotifier.cpp12
291 files changed, 3013 insertions, 1652 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 994038a03b..0b177480c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@ EXTRA_LIBRARIES =
BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
-LIBBITCOIN_SERVER=libbitcoin_server.a
+LIBBITCOIN_NODE=libbitcoin_node.a
LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a
LIBBITCOIN_CLI=libbitcoin_cli.a
@@ -61,7 +61,7 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_IPC) \
$(LIBBITCOIN_WALLET) \
@@ -240,6 +240,7 @@ BITCOIN_CORE_H = \
util/check.h \
util/epochguard.h \
util/error.h \
+ util/fastrange.h \
util/fees.h \
util/getuniquepath.h \
util/golombrice.h \
@@ -317,9 +318,9 @@ ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
# 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_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
-libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-libbitcoin_server_a_SOURCES = \
+libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+libbitcoin_node_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
banman.cpp \
@@ -382,10 +383,10 @@ libbitcoin_server_a_SOURCES = \
$(BITCOIN_CORE_H)
if ENABLE_WALLET
-libbitcoin_server_a_SOURCES += wallet/init.cpp
+libbitcoin_node_a_SOURCES += wallet/init.cpp
endif
if !ENABLE_WALLET
-libbitcoin_server_a_SOURCES += dummywallet.cpp
+libbitcoin_node_a_SOURCES += dummywallet.cpp
endif
if ENABLE_ZMQ
@@ -670,13 +671,13 @@ bitcoind_SOURCES = $(bitcoin_daemon_sources) init/bitcoind.cpp
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoind_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoind_LDFLAGS = $(bitcoin_bin_ldflags)
-bitcoind_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
+bitcoind_LDADD = $(LIBBITCOIN_NODE) $(bitcoin_bin_ldadd)
bitcoin_node_SOURCES = $(bitcoin_daemon_sources) init/bitcoin-node.cpp
bitcoin_node_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_node_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_node_LDFLAGS = $(bitcoin_bin_ldflags)
-bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
+bitcoin_node_LDADD = $(LIBBITCOIN_NODE) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 2a8e4a0aac..2feb31a9e9 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -51,7 +51,7 @@ 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_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 6c0e6f7c6f..f4d4641feb 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -323,7 +323,7 @@ bitcoin_qt_sources = qt/main.cpp
if TARGET_WINDOWS
bitcoin_qt_sources += $(BITCOIN_QT_RC)
endif
-bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
+bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_NODE)
if ENABLE_WALLET
bitcoin_qt_ldadd += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
@@ -357,7 +357,7 @@ SECONDARY: $(QT_QM)
$(srcdir)/qt/bitcoinstrings.cpp: FORCE
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
- $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
+ $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index a0900f2691..b92d2cb6e2 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -44,7 +44,7 @@ endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
-qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBTEST_UTIL)
+qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_NODE) $(LIBTEST_UTIL)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 9fe2a3cf8a..801745d0c6 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -38,7 +38,7 @@ BITCOIN_TEST_SUITE = \
FUZZ_SUITE_LD_COMMON = \
$(LIBTEST_UTIL) \
$(LIBTEST_FUZZ) \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
@@ -197,7 +197,7 @@ if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
-test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
+test_test_bitcoin_LDADD += $(LIBBITCOIN_NODE) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) $(MINISKETCH_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include
index 2d772f2fca..9574454fd2 100644
--- a/src/Makefile.test_fuzz.include
+++ b/src/Makefile.test_fuzz.include
@@ -19,7 +19,7 @@ libtest_fuzz_a_SOURCES = \
test/fuzz/util.cpp \
$(TEST_FUZZ_H)
-LIBTEST_FUZZ += $(LIBBITCOIN_SERVER)
+LIBTEST_FUZZ += $(LIBBITCOIN_NODE)
LIBTEST_FUZZ += $(LIBBITCOIN_COMMON)
LIBTEST_FUZZ += $(LIBBITCOIN_UTIL)
LIBTEST_FUZZ += $(LIBBITCOIN_CRYPTO_BASE)
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 0a3b99e7d2..92cb8a5ce6 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -35,7 +35,7 @@ libtest_util_a_SOURCES = \
test/util/wallet.cpp \
$(TEST_UTIL_H)
-LIBTEST_UTIL += $(LIBBITCOIN_SERVER)
+LIBTEST_UTIL += $(LIBBITCOIN_NODE)
LIBTEST_UTIL += $(LIBBITCOIN_COMMON)
LIBTEST_UTIL += $(LIBBITCOIN_UTIL)
LIBTEST_UTIL += $(LIBBITCOIN_CRYPTO_BASE)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 94518b88d8..4f22e688db 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -9,6 +9,7 @@
#include <chainparams.h>
#include <clientversion.h>
#include <cstdint>
+#include <fs.h>
#include <hash.h>
#include <logging/timer.h>
#include <netbase.h>
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index 0bebb0cf54..f7f62dfc68 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -96,7 +96,7 @@ base_uint<BITS>& base_uint<BITS>::operator/=(const base_uint& b)
while (shift >= 0) {
if (num >= div) {
num -= div;
- pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result.
+ pn[shift / 32] |= (1U << (shift & 31)); // set a bit of the result.
}
div >>= 1; // shift back.
shift--;
@@ -236,7 +236,7 @@ uint32_t arith_uint256::GetCompact(bool fNegative) const
nCompact >>= 8;
nSize++;
}
- assert((nCompact & ~0x007fffff) == 0);
+ assert((nCompact & ~0x007fffffU) == 0);
assert(nSize < 256);
nCompact |= nSize << 24;
nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0);
diff --git a/src/banman.cpp b/src/banman.cpp
index 95b927c1ff..b28e3f7f7c 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -7,6 +7,7 @@
#include <netaddress.h>
#include <node/ui_interface.h>
+#include <sync.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
@@ -39,18 +40,23 @@ BanMan::~BanMan()
void BanMan::DumpBanlist()
{
- SweepBanned(); // clean unused entries (if bantime has expired)
-
- if (!BannedSetIsDirty()) return;
-
- int64_t n_start = GetTimeMillis();
+ static Mutex dump_mutex;
+ LOCK(dump_mutex);
banmap_t banmap;
- GetBanned(banmap);
- if (m_ban_db.Write(banmap)) {
+ {
+ LOCK(m_cs_banned);
+ SweepBanned();
+ if (!BannedSetIsDirty()) return;
+ banmap = m_banned;
SetBannedSetDirty(false);
}
+ int64_t n_start = GetTimeMillis();
+ 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);
}
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index 2d94e835f0..3ca58b923e 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -16,6 +16,9 @@
static constexpr size_t NUM_SOURCES = 64;
static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256;
+static const std::vector<bool> EMPTY_ASMAP;
+static constexpr uint32_t ADDRMAN_CONSISTENCY_CHECK_RATIO{0};
+
static std::vector<CAddress> g_sources;
static std::vector<std::vector<CAddress>> g_addresses;
@@ -74,14 +77,14 @@ static void AddrManAdd(benchmark::Bench& bench)
CreateAddresses();
bench.run([&] {
- AddrMan addrman{/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0};
+ AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
AddAddressesToAddrMan(addrman);
});
}
static void AddrManSelect(benchmark::Bench& bench)
{
- AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
FillAddrMan(addrman);
@@ -93,7 +96,7 @@ static void AddrManSelect(benchmark::Bench& bench)
static void AddrManGetAddr(benchmark::Bench& bench)
{
- AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
FillAddrMan(addrman);
@@ -122,7 +125,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
//
// This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in
// AddrMan::Good() will still be noticeable.
- AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman{EMPTY_ASMAP, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
AddAddressesToAddrMan(addrman);
markSomeAsGood(addrman);
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index f696396e12..5c24b712a7 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
+#include <fs.h>
#include <test/util/setup_common.h>
#include <chrono>
@@ -19,6 +20,8 @@ using namespace std::chrono_literals;
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
+const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS{};
+
namespace {
void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& benchmarkResults, const std::string& filename, const char* tpl)
@@ -27,7 +30,7 @@ void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& bench
// nothing to write, bail out
return;
}
- std::ofstream fout(filename);
+ std::ofstream fout{fs::PathFromString(filename)};
if (fout.is_open()) {
ankerl::nanobench::render(tpl, benchmarkResults, fout);
} else {
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index a9f3f5f84d..52e5cb743f 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -17,8 +17,8 @@
static void DeserializeBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
bench.unit("block").run([&] {
CBlock block;
@@ -31,8 +31,8 @@ static void DeserializeBlockTest(benchmark::Bench& bench)
static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
ArgsManager bench_args;
const auto chainParams = CreateChainParams(bench_args, CBaseChainParams::MAIN);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 4734ac558c..609c592d20 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -11,6 +11,19 @@
#include <set>
+using node::NodeContext;
+using wallet::AttemptSelection;
+using wallet::CInputCoin;
+using wallet::COutput;
+using wallet::CWallet;
+using wallet::CWalletTx;
+using wallet::CoinEligibilityFilter;
+using wallet::CoinSelectionParams;
+using wallet::CreateDummyWalletDatabase;
+using wallet::OutputGroup;
+using wallet::SelectCoinsBnB;
+using wallet::TxStateInactive;
+
static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs)
{
static int nextLockTime = 0;
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index 9bc31461d2..2143bcf950 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -23,8 +23,8 @@ struct TestBlockAndIndex {
TestBlockAndIndex()
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
stream >> block;
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index 15e9a1ab78..d4b8794c6d 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -14,6 +14,12 @@
#include <optional>
+using wallet::CWallet;
+using wallet::CreateMockWalletDatabase;
+using wallet::DBErrors;
+using wallet::GetBalance;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+
static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_mine)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index edec883264..ec07114d6e 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -12,6 +12,7 @@
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
+#include <fs.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -158,7 +159,7 @@ static void RegisterLoad(const std::string& strInput)
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
- FILE *f = fopen(filename.c_str(), "r");
+ FILE *f = fsbridge::fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
@@ -433,13 +434,16 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
if (pos==0)
throw std::runtime_error("TX output value not specified");
- if (pos != std::string::npos) {
+ if (pos == std::string::npos) {
+ pos = 0;
+ } else {
// Extract and validate VALUE
value = ExtractAndValidateValue(strInput.substr(0, pos));
+ ++pos;
}
// extract and validate DATA
- std::string strData = strInput.substr(pos + 1, std::string::npos);
+ const std::string strData{strInput.substr(pos, std::string::npos)};
if (!IsHex(strData))
throw std::runtime_error("invalid TX output data");
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index c896d5892d..65c37f182f 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -123,7 +123,7 @@ int main(int argc, char* argv[])
ECCVerifyHandle globalVerifyHandle;
ECC_Start();
- if (!WalletTool::ExecuteWalletToolFunc(args, command->command)) {
+ if (!wallet::WalletTool::ExecuteWalletToolFunc(args, command->command)) {
return EXIT_FAILURE;
}
ECC_Stop();
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 30dfbfd8cd..6432e8849d 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -30,6 +30,8 @@
#include <functional>
#include <optional>
+using node::NodeContext;
+
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
UrlDecodeFn* const URL_DECODE = urlDecode;
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index 68cc6462d0..63a9ba498f 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -24,43 +24,12 @@ static const std::map<BlockFilterType, std::string> g_filter_types = {
{BlockFilterType::BASIC, "basic"},
};
-// Map a value x that is uniformly distributed in the range [0, 2^64) to a
-// value uniformly distributed in [0, n) by returning the upper 64 bits of
-// x * n.
-//
-// See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-static uint64_t MapIntoRange(uint64_t x, uint64_t n)
-{
-#ifdef __SIZEOF_INT128__
- return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
-#else
- // To perform the calculation on 64-bit numbers without losing the
- // result to overflow, split the numbers into the most significant and
- // least significant 32 bits and perform multiplication piece-wise.
- //
- // See: https://stackoverflow.com/a/26855440
- uint64_t x_hi = x >> 32;
- uint64_t x_lo = x & 0xFFFFFFFF;
- uint64_t n_hi = n >> 32;
- uint64_t n_lo = n & 0xFFFFFFFF;
-
- uint64_t ac = x_hi * n_hi;
- uint64_t ad = x_hi * n_lo;
- uint64_t bc = x_lo * n_hi;
- uint64_t bd = x_lo * n_lo;
-
- uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
- uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
- return upper64;
-#endif
-}
-
uint64_t GCSFilter::HashToRange(const Element& element) const
{
uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
.Write(element.data(), element.size())
.Finalize();
- return MapIntoRange(hash, m_F);
+ return FastRange64(hash, m_F);
}
std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const
diff --git a/src/chain.cpp b/src/chain.cpp
index 5d182e1af8..b8158f7b0b 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -4,6 +4,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
+#include <util/time.h>
+
+std::string CBlockFileInfo::ToString() const
+{
+ return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
+}
void CChain::SetTip(CBlockIndex *pindex) {
if (pindex == nullptr) {
@@ -145,7 +151,7 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr
if (r.bits() > 63) {
return sign * std::numeric_limits<int64_t>::max();
}
- return sign * r.GetLow64();
+ return sign * int64_t(r.GetLow64());
}
/** Find the last common ancestor two blocks have.
diff --git a/src/chain.h b/src/chain.h
index 55bdf4cd56..24b5026aba 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -10,6 +10,7 @@
#include <consensus/params.h>
#include <flatfile.h>
#include <primitives/block.h>
+#include <sync.h>
#include <tinyformat.h>
#include <uint256.h>
@@ -37,6 +38,8 @@ static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
*/
static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60;
+extern RecursiveMutex cs_main;
+
class CBlockFileInfo
{
public:
@@ -161,13 +164,13 @@ public:
int nHeight{0};
//! Which # file this block is stored in (blk?????.dat)
- int nFile{0};
+ int nFile GUARDED_BY(::cs_main){0};
//! Byte offset within blk?????.dat where this block's data is stored
- unsigned int nDataPos{0};
+ unsigned int nDataPos GUARDED_BY(::cs_main){0};
//! Byte offset within rev?????.dat where this block's undo data is stored
- unsigned int nUndoPos{0};
+ unsigned int nUndoPos GUARDED_BY(::cs_main){0};
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
arith_uint256 nChainWork{};
@@ -195,7 +198,7 @@ public:
//! load to avoid the block index being spuriously rewound.
//! @sa NeedsRedownload
//! @sa ActivateSnapshot
- uint32_t nStatus{0};
+ uint32_t nStatus GUARDED_BY(::cs_main){0};
//! block header
int32_t nVersion{0};
@@ -223,8 +226,9 @@ public:
{
}
- FlatFilePos GetBlockPos() const
+ FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
@@ -233,8 +237,9 @@ public:
return ret;
}
- FlatFilePos GetUndoPos() const
+ FlatFilePos GetUndoPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
@@ -306,7 +311,9 @@ public:
//! Check whether this block index entry is valid up to the passed validity level.
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
@@ -315,12 +322,17 @@ public:
//! @returns true if the block is assumed-valid; this means it is queued to be
//! validated by a background chainstate.
- bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
+ bool IsAssumedValid() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+ {
+ AssertLockHeld(::cs_main);
+ return nStatus & BLOCK_ASSUMED_VALID;
+ }
//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
- bool RaiseValidity(enum BlockStatus nUpTo)
+ bool RaiseValidity(enum BlockStatus nUpTo) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK) return false;
@@ -370,6 +382,7 @@ public:
SERIALIZE_METHODS(CDiskBlockIndex, obj)
{
+ LOCK(::cs_main);
int _nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT_MODE(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
@@ -462,7 +475,7 @@ public:
/** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */
int Height() const
{
- return vChain.size() - 1;
+ return int(vChain.size()) - 1;
}
/** Set/initialize a chain with a given tip. */
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 953a09d5e7..2edb556a35 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -683,22 +683,19 @@ static const uint8_t chainparams_seed_main[] = {
0x04,0x20,0x98,0xc6,0x44,0x27,0x90,0x41,0xa6,0x98,0xf9,0x25,0x6c,0x59,0x0f,0x06,0x6d,0x44,0x59,0x0e,0xb2,0x46,0xb0,0xa4,0x37,0x88,0x69,0x8f,0xc1,0x32,0xcd,0x9f,0x15,0xd7,0x20,0x8d,
0x04,0x20,0xaa,0x3a,0x16,0x86,0xea,0x59,0x09,0x04,0x78,0xe5,0x10,0x92,0xe1,0x1d,0xad,0xf7,0x56,0x2b,0xac,0xb0,0x97,0x29,0x63,0x30,0xf4,0x1b,0xcf,0xde,0xf3,0x28,0x0a,0x29,0x20,0x8d,
0x04,0x20,0xbc,0x27,0xae,0x89,0xc1,0x67,0x73,0x0a,0x08,0x02,0xdf,0xb7,0xcc,0x94,0xc7,0x9f,0xf4,0x72,0x7a,0x9b,0x20,0x0c,0x5c,0x11,0x3d,0x22,0xd6,0x13,0x88,0x66,0x74,0xbf,0x20,0x8d,
- 0x05,0x20,0xe1,0xd6,0xb8,0xfa,0xdd,0xeb,0x03,0x32,0x30,0x3b,0x20,0x6a,0xbc,0xaf,0x99,0x4f,0xa0,0xa2,0x72,0x48,0xfe,0x44,0xe0,0xf6,0x03,0xc1,0xbd,0xb6,0x24,0xd0,0xf6,0xb8,0x00,0x00,
- 0x05,0x20,0xf4,0xb7,0xb4,0xcd,0xf5,0xb6,0x54,0x82,0x27,0x6d,0x29,0x7b,0x06,0x7f,0x52,0x59,0xa0,0xb4,0xdc,0xf7,0x6f,0xb4,0x71,0xcf,0xcc,0xfb,0x6b,0x86,0xc2,0x57,0x80,0xc6,0x00,0x00,
0x05,0x20,0x07,0x61,0x26,0xd7,0x6c,0x05,0xbf,0xf6,0x2d,0x8c,0xca,0xc4,0x65,0xd3,0xd3,0xb2,0x49,0xe9,0xcc,0x53,0x1e,0xca,0x77,0x84,0xb6,0x10,0x5e,0xc2,0x5a,0xfe,0x28,0xb3,0x00,0x00,
0x05,0x20,0x0a,0x26,0x27,0x45,0xb1,0x1e,0xfc,0x27,0x03,0x32,0x0e,0x65,0x9e,0x3c,0x64,0x0e,0x33,0x50,0x3d,0x6c,0x90,0x17,0x0e,0x29,0xee,0x5a,0x58,0xdf,0x08,0xde,0xbf,0x73,0x00,0x00,
0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x00,0x00,
0x05,0x20,0x19,0xe7,0x0d,0x3f,0xfe,0x9e,0x0e,0x8e,0x73,0x40,0x40,0xc3,0xba,0x8f,0x41,0xaf,0xf1,0x7b,0xa6,0x83,0x1b,0xc3,0xa4,0xe0,0x6d,0x6c,0x57,0xa7,0x36,0x5d,0x09,0xce,0x00,0x00,
- 0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x00,0x00,
0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x00,0x00,
0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x00,0x00,
- 0x05,0x20,0x46,0xce,0x21,0x81,0x48,0xce,0xa7,0x8a,0x98,0xca,0xb1,0x0b,0x51,0xa5,0xc8,0xff,0x39,0xc5,0x1a,0xa3,0xd3,0x02,0x32,0xa3,0x29,0xad,0x79,0xb8,0x7f,0x34,0x51,0x33,0x00,0x00,
0x05,0x20,0x4e,0x77,0x2e,0x12,0x91,0x67,0x6b,0x94,0xc4,0x92,0x2f,0x19,0x67,0x7d,0xcd,0x47,0x02,0xad,0xf8,0x60,0x72,0xed,0x73,0xf1,0x10,0x99,0x2c,0x05,0x61,0x66,0x55,0xd9,0x00,0x00,
0x05,0x20,0x53,0x94,0xa6,0x3e,0x14,0x82,0xd4,0xf9,0xd3,0xa7,0x53,0x33,0x05,0xce,0x72,0x64,0xed,0x74,0x09,0x63,0x8f,0x24,0xef,0xda,0x12,0xa1,0x55,0xe0,0xd8,0xbb,0xd3,0x58,0x00,0x00,
- 0x05,0x20,0x55,0x62,0x32,0x7d,0x82,0x32,0x4f,0x9d,0xdf,0x24,0x5c,0xed,0x8e,0x1a,0x5a,0x8d,0xc6,0x50,0xb4,0x32,0xd5,0x85,0xef,0xb0,0xfa,0x7c,0xf9,0xbb,0x25,0x89,0x6b,0x03,0x00,0x00,
- 0x05,0x20,0x91,0xcf,0xa2,0x5b,0x04,0x33,0x69,0x66,0xb0,0x72,0x27,0x54,0xbe,0xcd,0xd8,0x08,0xeb,0x95,0x55,0x5a,0xc2,0x79,0x91,0x3a,0xd9,0xf2,0x2c,0x73,0x9f,0x78,0x50,0xca,0x00,0x00,
+ 0x05,0x20,0x91,0x06,0xd1,0x9e,0xbd,0xab,0xc4,0x61,0xb3,0x0a,0xc2,0x3b,0x29,0xf3,0x10,0x38,0xee,0xbd,0x9d,0xe3,0x99,0x97,0x30,0x70,0x6e,0xe6,0xfb,0x6a,0x3c,0x07,0x3d,0xfd,0x00,0x00,
0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x00,0x00,
0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x00,0x00,
+ 0x06,0x10,0xfc,0x32,0x17,0xea,0xe4,0x15,0xc3,0xbf,0x98,0x08,0x14,0x9d,0xb5,0xa2,0xc9,0xaa,0x20,0x8d,
+ 0x06,0x10,0xfc,0xc7,0xbe,0x49,0xcc,0xd1,0xdc,0x91,0x31,0x25,0xf0,0xda,0x45,0x7d,0x08,0xce,0x20,0x8d,
};
static const uint8_t chainparams_seed_test[] = {
diff --git a/src/common/bloom.cpp b/src/common/bloom.cpp
index c3603b5d2a..8b32a6c94a 100644
--- a/src/common/bloom.cpp
+++ b/src/common/bloom.cpp
@@ -11,6 +11,7 @@
#include <script/standard.h>
#include <span.h>
#include <streams.h>
+#include <util/fastrange.h>
#include <algorithm>
#include <cmath>
@@ -61,7 +62,7 @@ void CBloomFilter::insert(const COutPoint& outpoint)
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- insert(stream);
+ insert(MakeUCharSpan(stream));
}
bool CBloomFilter::contains(Span<const unsigned char> vKey) const
@@ -82,7 +83,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- return contains(stream);
+ return contains(MakeUCharSpan(stream));
}
bool CBloomFilter::IsWithinSizeConstraints() const
@@ -191,14 +192,6 @@ static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak,
return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash);
}
-
-// A replacement for x % n. This assumes that x and n are 32bit integers, and x is a uniformly random distributed 32bit value
-// which should be the case for a good hash.
-// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-static inline uint32_t FastMod(uint32_t x, size_t n) {
- return ((uint64_t)x * (uint64_t)n) >> 32;
-}
-
void CRollingBloomFilter::insert(Span<const unsigned char> vKey)
{
if (nEntriesThisGeneration == nEntriesPerGeneration) {
@@ -223,10 +216,10 @@ void CRollingBloomFilter::insert(Span<const unsigned char> vKey)
uint32_t h = RollingBloomHash(n, nTweak, vKey);
int bit = h & 0x3F;
/* FastMod works with the upper bits of h, so it is safe to ignore that the lower bits of h are already used for bit. */
- uint32_t pos = FastMod(h, data.size());
+ uint32_t pos = FastRange32(h, data.size());
/* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */
- data[pos & ~1] = (data[pos & ~1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration & 1)) << bit;
- data[pos | 1] = (data[pos | 1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration >> 1)) << bit;
+ data[pos & ~1U] = (data[pos & ~1U] & ~(uint64_t{1} << bit)) | (uint64_t(nGeneration & 1)) << bit;
+ data[pos | 1] = (data[pos | 1] & ~(uint64_t{1} << bit)) | (uint64_t(nGeneration >> 1)) << bit;
}
}
@@ -235,9 +228,9 @@ bool CRollingBloomFilter::contains(Span<const unsigned char> vKey) const
for (int n = 0; n < nHashFuncs; n++) {
uint32_t h = RollingBloomHash(n, nTweak, vKey);
int bit = h & 0x3F;
- uint32_t pos = FastMod(h, data.size());
+ uint32_t pos = FastRange32(h, data.size());
/* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */
- if (!(((data[pos & ~1] | data[pos | 1]) >> bit) & 1)) {
+ if (!(((data[pos & ~1U] | data[pos | 1]) >> bit) & 1)) {
return false;
}
}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 067f1e4f4e..5ea62cf3ed 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -8,6 +8,7 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <key_io.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
@@ -152,6 +153,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include
CTxDestination address;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
+ out.pushKV("desc", InferDescriptor(scriptPubKey, DUMMY_SIGNING_PROVIDER)->ToString());
if (include_hex) out.pushKV("hex", HexStr(scriptPubKey));
std::vector<std::vector<unsigned char>> solns;
diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp
index 19087b7d75..4f3e6f7fa3 100644
--- a/src/crypto/chacha_poly_aead.cpp
+++ b/src/crypto/chacha_poly_aead.cpp
@@ -73,7 +73,7 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
return false;
}
memory_cleanse(expected_tag, sizeof(expected_tag));
- // MAC has been successfully verified, make sure we don't covert it in decryption
+ // MAC has been successfully verified, make sure we don't convert it in decryption
src_len -= POLY1305_TAGLEN;
}
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
index 15cb55c3ce..d0dc61c7e6 100644
--- a/src/cuckoocache.h
+++ b/src/cuckoocache.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_CUCKOOCACHE_H
#define BITCOIN_CUCKOOCACHE_H
+#include <util/fastrange.h>
+
#include <algorithm> // std::find
#include <array>
#include <atomic>
@@ -219,13 +221,8 @@ private:
* One option would be to implement the same trick the compiler uses and compute the
* constants for exact division based on the size, as described in "{N}-bit Unsigned
* Division via {N}-bit Multiply-Add" by Arch D. Robison in 2005. But that code is
- * somewhat complicated and the result is still slower than other options:
- *
- * Instead we treat the 32-bit random number as a Q32 fixed-point number in the range
- * [0, 1) and simply multiply it by the size. Then we just shift the result down by
- * 32-bits to get our bucket number. The result has non-uniformity the same as a
- * mod, but it is much faster to compute. More about this technique can be found at
- * https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ .
+ * somewhat complicated and the result is still slower than an even simpler option:
+ * see the FastRange32 function in util/fastrange.h.
*
* The resulting non-uniformity is also more equally distributed which would be
* advantageous for something like linear probing, though it shouldn't matter
@@ -241,14 +238,14 @@ private:
*/
inline std::array<uint32_t, 8> compute_hashes(const Element& e) const
{
- return {{(uint32_t)(((uint64_t)hash_function.template operator()<0>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<1>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<2>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<3>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<4>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<5>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<6>(e) * (uint64_t)size) >> 32),
- (uint32_t)(((uint64_t)hash_function.template operator()<7>(e) * (uint64_t)size) >> 32)}};
+ return {{FastRange32(hash_function.template operator()<0>(e), size),
+ FastRange32(hash_function.template operator()<1>(e), size),
+ FastRange32(hash_function.template operator()<2>(e), size),
+ FastRange32(hash_function.template operator()<3>(e), size),
+ FastRange32(hash_function.template operator()<4>(e), size),
+ FastRange32(hash_function.template operator()<5>(e), size),
+ FastRange32(hash_function.template operator()<6>(e), size),
+ FastRange32(hash_function.template operator()<7>(e), size)}};
}
/** invalid returns a special index that can never be inserted to
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 12db0fffcc..1109cb5888 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -147,7 +147,7 @@ public:
template<typename K> bool GetKey(K& key) {
leveldb::Slice slKey = piter->key();
try {
- CDataStream ssKey(MakeUCharSpan(slKey), SER_DISK, CLIENT_VERSION);
+ CDataStream ssKey{MakeByteSpan(slKey), SER_DISK, CLIENT_VERSION};
ssKey >> key;
} catch (const std::exception&) {
return false;
@@ -158,7 +158,7 @@ public:
template<typename V> bool GetValue(V& value) {
leveldb::Slice slValue = piter->value();
try {
- CDataStream ssValue(MakeUCharSpan(slValue), SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue{MakeByteSpan(slValue), SER_DISK, CLIENT_VERSION};
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
ssValue >> value;
} catch (const std::exception&) {
@@ -244,7 +244,7 @@ public:
dbwrapper_private::HandleError(status);
}
try {
- CDataStream ssValue(MakeUCharSpan(strValue), SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue{MakeByteSpan(strValue), SER_DISK, CLIENT_VERSION};
ssValue.Xor(obfuscate_key);
ssValue >> value;
} catch (const std::exception&) {
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 23e432e767..2b94ed611b 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -6,7 +6,6 @@
#include <walletinitinterface.h>
class ArgsManager;
-class CWallet;
namespace interfaces {
class Chain;
@@ -21,7 +20,7 @@ public:
bool HasWalletSupport() const override {return false;}
void AddWalletOptions(ArgsManager& argsman) const override;
bool ParameterInteraction() const override {return true;}
- void Construct(NodeContext& node) const override {LogPrintf("No wallet support compiled in!\n");}
+ void Construct(node::NodeContext& node) const override {LogPrintf("No wallet support compiled in!\n");}
};
void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
@@ -59,11 +58,6 @@ const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
namespace interfaces {
-std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args)
{
throw std::logic_error("Wallet function called in non-wallet build.");
diff --git a/src/fs.cpp b/src/fs.cpp
index 34a0348578..219fdee959 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -7,7 +7,6 @@
#ifndef WIN32
#include <cstring>
#include <fcntl.h>
-#include <string>
#include <sys/file.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -20,6 +19,9 @@
#include <windows.h>
#endif
+#include <cassert>
+#include <string>
+
namespace fsbridge {
FILE *fopen(const fs::path& p, const char *mode)
@@ -35,7 +37,7 @@ FILE *fopen(const fs::path& p, const char *mode)
fs::path AbsPathJoin(const fs::path& base, const fs::path& path)
{
assert(base.is_absolute());
- return fs::absolute(path, base);
+ return path.empty() ? base : fs::path(base / path);
}
#ifndef WIN32
@@ -151,118 +153,4 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e)
#endif
}
-#ifdef WIN32
-#ifdef __GLIBCXX__
-
-// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wswitch"
-#endif
-static std::string openmodeToStr(std::ios_base::openmode mode)
-{
- switch (mode & ~std::ios_base::ate) {
- case std::ios_base::out:
- case std::ios_base::out | std::ios_base::trunc:
- return "w";
- case std::ios_base::out | std::ios_base::app:
- case std::ios_base::app:
- return "a";
- case std::ios_base::in:
- return "r";
- case std::ios_base::in | std::ios_base::out:
- return "r+";
- case std::ios_base::in | std::ios_base::out | std::ios_base::trunc:
- return "w+";
- case std::ios_base::in | std::ios_base::out | std::ios_base::app:
- case std::ios_base::in | std::ios_base::app:
- return "a+";
- case std::ios_base::out | std::ios_base::binary:
- case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
- return "wb";
- case std::ios_base::out | std::ios_base::app | std::ios_base::binary:
- case std::ios_base::app | std::ios_base::binary:
- return "ab";
- case std::ios_base::in | std::ios_base::binary:
- return "rb";
- case std::ios_base::in | std::ios_base::out | std::ios_base::binary:
- return "r+b";
- case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
- return "w+b";
- case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary:
- case std::ios_base::in | std::ios_base::app | std::ios_base::binary:
- return "a+b";
- default:
- return std::string();
- }
-}
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
-{
- close();
- mode |= std::ios_base::in;
- m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
- if (m_file == nullptr) {
- return;
- }
- m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
- rdbuf(&m_filebuf);
- if (mode & std::ios_base::ate) {
- seekg(0, std::ios_base::end);
- }
-}
-
-void ifstream::close()
-{
- if (m_file != nullptr) {
- m_filebuf.close();
- fclose(m_file);
- }
- m_file = nullptr;
-}
-
-void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
-{
- close();
- mode |= std::ios_base::out;
- m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
- if (m_file == nullptr) {
- return;
- }
- m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
- rdbuf(&m_filebuf);
- if (mode & std::ios_base::ate) {
- seekp(0, std::ios_base::end);
- }
-}
-
-void ofstream::close()
-{
- if (m_file != nullptr) {
- m_filebuf.close();
- fclose(m_file);
- }
- m_file = nullptr;
-}
-#else // __GLIBCXX__
-
-#if BOOST_VERSION >= 107700
-static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(boost::filesystem::path())) == sizeof(wchar_t),
-#else
-static_assert(sizeof(*boost::filesystem::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
-#endif // BOOST_VERSION >= 107700
- "Warning: This build is using boost::filesystem ofstream and ifstream "
- "implementations which will fail to open paths containing multibyte "
- "characters. You should delete this static_assert to ignore this warning, "
- "or switch to a different C++ standard library like the Microsoft C++ "
- "Standard Library (where boost uses non-standard extensions to construct "
- "stream objects with wide filenames), or the GNU libstdc++ library (where "
- "a more complicated workaround has been implemented above).");
-
-#endif // __GLIBCXX__
-#endif // WIN32
-
} // fsbridge
diff --git a/src/fs.h b/src/fs.h
index c179be7607..d2299db168 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -5,46 +5,42 @@
#ifndef BITCOIN_FS_H
#define BITCOIN_FS_H
-#include <stdio.h>
-#include <string>
-#if defined WIN32 && defined __GLIBCXX__
-#include <ext/stdio_filebuf.h>
-#endif
-
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
#include <tinyformat.h>
+#include <cstdio>
+#include <filesystem>
+#include <iomanip>
+#include <ios>
+#include <ostream>
+#include <string>
+#include <utility>
+
/** Filesystem operations and types */
namespace fs {
-using namespace boost::filesystem;
+using namespace std::filesystem;
/**
- * Path class wrapper to prepare application code for transition from
- * boost::filesystem library to std::filesystem implementation. The main
- * purpose of the class is to define fs::path::u8string() and fs::u8path()
- * functions not present in boost. It also blocks calls to the
- * fs::path(std::string) implicit constructor and the fs::path::string()
- * method, which worked well in the boost::filesystem implementation, but have
- * unsafe and unpredictable behavior on Windows in the std::filesystem
- * implementation (see implementation note in \ref PathToString for details).
+ * Path class wrapper to block calls to the fs::path(std::string) implicit
+ * constructor and the fs::path::string() method, which have unsafe and
+ * unpredictable behavior on Windows (see implementation note in
+ * \ref PathToString for details)
*/
-class path : public boost::filesystem::path
+class path : public std::filesystem::path
{
public:
- using boost::filesystem::path::path;
+ using std::filesystem::path::path;
// Allow path objects arguments for compatibility.
- path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {}
- path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; }
- path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; }
+ path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {}
+ path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; }
+ path& operator/=(std::filesystem::path path) { std::filesystem::path::operator/=(std::move(path)); return *this; }
// Allow literal string arguments, which are safe as long as the literals are ASCII.
- path(const char* c) : boost::filesystem::path(c) {}
- path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; }
- path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; }
- path& append(const char* c) { boost::filesystem::path::append(c); return *this; }
+ path(const char* c) : std::filesystem::path(c) {}
+ path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; }
+ path& operator/=(const char* c) { std::filesystem::path::operator/=(c); return *this; }
+ path& append(const char* c) { std::filesystem::path::append(c); return *this; }
// Disallow std::string arguments to avoid locale-dependent decoding on windows.
path(std::string) = delete;
@@ -55,34 +51,30 @@ public:
// Disallow std::string conversion method to avoid locale-dependent encoding on windows.
std::string string() const = delete;
- // Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem.
- std::string u8string() const { return boost::filesystem::path::string(); }
+ // Required for path overloads in <fstream>.
+ // See https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=96e0367ead5d8dcac3bec2865582e76e2fbab190
+ path& make_preferred() { std::filesystem::path::make_preferred(); return *this; }
+ path filename() const { return std::filesystem::path::filename(); }
};
-// Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem.
-static inline path u8path(const std::string& string)
-{
- return boost::filesystem::path(string);
-}
-
-// Disallow implicit std::string conversion for system_complete to avoid
+// Disallow implicit std::string conversion for absolute to avoid
// locale-dependent encoding on windows.
-static inline path system_complete(const path& p)
+static inline path absolute(const path& p)
{
- return boost::filesystem::system_complete(p);
+ return std::filesystem::absolute(p);
}
// Disallow implicit std::string conversion for exists to avoid
// locale-dependent encoding on windows.
static inline bool exists(const path& p)
{
- return boost::filesystem::exists(p);
+ return std::filesystem::exists(p);
}
// Allow explicit quoted stream I/O.
static inline auto quoted(const std::string& s)
{
- return boost::io::quoted(s, '&');
+ return std::quoted(s, '"', '&');
}
// Allow safe path append operations.
@@ -92,8 +84,15 @@ static inline path operator+(path p1, path p2)
return p1;
}
+// Disallow implicit std::string conversion for copy_file
+// to avoid locale-dependent encoding on Windows.
+static inline bool copy_file(const path& from, const path& to, copy_options options)
+{
+ return std::filesystem::copy_file(from, to, options);
+}
+
/**
- * Convert path object to byte string. On POSIX, paths natively are byte
+ * Convert path object to a byte string. On POSIX, paths natively are byte
* strings, so this is trivial. On Windows, paths natively are Unicode, so an
* encoding step is necessary. The inverse of \ref PathToString is \ref
* PathFromString. The strings returned and parsed by these functions can be
@@ -105,7 +104,7 @@ static inline path operator+(path p1, path p2)
* appropriate to use in applications requiring UTF-8, where
* fs::path::u8string() and fs::u8path() methods should be used instead. Other
* applications could require still different encodings. For example, JSON, XML,
- * or URI applications might prefer to use higher level escapes (\uXXXX or
+ * or URI applications might prefer to use higher-level escapes (\uXXXX or
* &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications
* may require encoding paths with their respective UTF-8 derivatives WTF-8,
* PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives).
@@ -126,7 +125,7 @@ static inline std::string PathToString(const path& path)
return path.u8string();
#else
static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform");
- return path.boost::filesystem::path::string();
+ return path.std::filesystem::path::string();
#endif
}
@@ -138,7 +137,7 @@ static inline path PathFromString(const std::string& string)
#ifdef WIN32
return u8path(string);
#else
- return boost::filesystem::path(string);
+ return std::filesystem::path(string);
#endif
}
} // namespace fs
@@ -179,60 +178,12 @@ namespace fsbridge {
};
std::string get_filesystem_error_message(const fs::filesystem_error& e);
-
- // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows.
- //
- // On Windows, it is only possible to reliably access multibyte file paths through
- // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't
- // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't
- // provide them (in contrast to the Microsoft C++ library, see
- // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032),
- // Boost is forced to fall back to `char` constructors which may not work properly.
- //
- // Work around this issue by creating stream objects with `_wfopen` in
- // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed
- // with an upgrade to C++17, where streams can be constructed directly from
- // `std::filesystem::path` objects.
-
-#if defined WIN32 && defined __GLIBCXX__
- class ifstream : public std::istream
- {
- public:
- ifstream() = default;
- explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); }
- ~ifstream() { close(); }
- void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in);
- bool is_open() { return m_filebuf.is_open(); }
- void close();
-
- private:
- __gnu_cxx::stdio_filebuf<char> m_filebuf;
- FILE* m_file = nullptr;
- };
- class ofstream : public std::ostream
- {
- public:
- ofstream() = default;
- explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); }
- ~ofstream() { close(); }
- void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out);
- bool is_open() { return m_filebuf.is_open(); }
- void close();
-
- private:
- __gnu_cxx::stdio_filebuf<char> m_filebuf;
- FILE* m_file = nullptr;
- };
-#else // !(WIN32 && __GLIBCXX__)
- typedef fs::ifstream ifstream;
- typedef fs::ofstream ofstream;
-#endif // WIN32 && __GLIBCXX__
};
// Disallow path operator<< formatting in tinyformat to avoid locale-dependent
// encoding on windows.
namespace tinyformat {
-template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete;
+template<> inline void formatValue(std::ostream&, const char*, const char*, int, const std::filesystem::path&) = delete;
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
} // namespace tinyformat
diff --git a/src/hash.h b/src/hash.h
index 1456a899d8..9f582842c1 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -111,8 +111,9 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
- void write(const char *pch, size_t size) {
- ctx.Write((const unsigned char*)pch, size);
+ void write(Span<const std::byte> src)
+ {
+ ctx.Write(UCharCast(src.data()), src.size());
}
/** Compute the double-SHA256 hash of all data written to this object.
@@ -162,18 +163,18 @@ private:
public:
explicit CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- source->read(pch, nSize);
- this->write(pch, nSize);
+ source->read(dst);
+ this->write(dst);
}
void ignore(size_t nSize)
{
- char data[1024];
+ std::byte data[1024];
while (nSize > 0) {
size_t now = std::min<size_t>(nSize, 1024);
- read(data, now);
+ read({data, now});
nSize -= now;
}
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 03328ac42c..e00c68585e 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -2,6 +2,10 @@
// 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 <httpserver.h>
#include <chainparamsbase.h>
@@ -597,7 +601,13 @@ CService HTTPRequest::GetPeer() const
// evhttp retains ownership over returned address string
const char* address = "";
uint16_t port = 0;
+
+#ifdef HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
+ evhttp_connection_get_peer(con, &address, &port);
+#else
evhttp_connection_get_peer(con, (char**)&address, &port);
+#endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
+
peer = LookupNumeric(address, port);
}
return peer;
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 8e94c33c1c..2e3d500cd1 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -14,6 +14,8 @@
#include <validation.h> // For g_chainman
#include <warnings.h>
+using node::ReadBlockFromDisk;
+
constexpr uint8_t DB_BEST_BLOCK{'B'};
constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index f931e6eb0b..4f99eddfd7 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -9,6 +9,8 @@
#include <node/blockstorage.h>
#include <util/system.h>
+using node::UndoReadFromDisk;
+
/* The index database stores three items for each block: the disk location of the encoded filter,
* its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
* height, and those belonging to blocks that have been reorganized out of the active chain are
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index e4ee0e674b..ef247dc119 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -12,6 +12,12 @@
#include <undo.h>
#include <validation.h>
+using node::CCoinsStats;
+using node::GetBogoSize;
+using node::ReadBlockFromDisk;
+using node::TxOutSer;
+using node::UndoReadFromDisk;
+
static constexpr uint8_t DB_BLOCK_HASH{'s'};
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
static constexpr uint8_t DB_MUHASH{'M'};
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index a575b37c7c..d2a6c9c964 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -52,7 +52,7 @@ public:
explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
// Look up stats for a specific block using CBlockIndex
- bool LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const;
+ bool LookUpStats(const CBlockIndex* block_index, node::CCoinsStats& coins_stats) const;
};
/// The global UTXO set hash object.
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index bacaa1c16c..e1d807f39a 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -9,6 +9,8 @@
#include <util/system.h>
#include <validation.h>
+using node::OpenBlockFile;
+
constexpr uint8_t DB_TXINDEX{'t'};
std::unique_ptr<TxIndex> g_txindex;
@@ -57,7 +59,9 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
// Exclude genesis block transaction because outputs are not spendable.
if (pindex->nHeight == 0) return true;
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
+ CDiskTxPos pos{
+ WITH_LOCK(::cs_main, return pindex->GetBlockPos()),
+ GetSizeOfCompactSize(block.vtx.size())};
std::vector<std::pair<uint256, CDiskTxPos>> vPos;
vPos.reserve(block.vtx.size());
for (const auto& tx : block.vtx) {
diff --git a/src/init.cpp b/src/init.cpp
index b11eb762f2..84564cf2aa 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -72,10 +72,13 @@
#include <validationinterface.h>
#include <walletinitinterface.h>
+#include <condition_variable>
+#include <cstdint>
+#include <cstdio>
+#include <fstream>
#include <functional>
#include <set>
-#include <stdint.h>
-#include <stdio.h>
+#include <string>
#include <thread>
#include <vector>
@@ -95,6 +98,22 @@
#include <zmq/zmqrpc.h>
#endif
+using node::CacheSizes;
+using node::CalculateCacheSizes;
+using node::ChainstateLoadVerifyError;
+using node::ChainstateLoadingError;
+using node::CleanupBlockRevFiles;
+using node::DEFAULT_PRINTPRIORITY;
+using node::DEFAULT_STOPAFTERBLOCKIMPORT;
+using node::LoadChainstate;
+using node::NodeContext;
+using node::ThreadImport;
+using node::VerifyLoadedChainstate;
+using node::fHavePruned;
+using node::fPruneMode;
+using node::fReindex;
+using node::nPruneTarget;
+
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
@@ -121,7 +140,7 @@ static fs::path GetPidFile(const ArgsManager& args)
[[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
{
- fsbridge::ofstream file{GetPidFile(args)};
+ std::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
@@ -1404,31 +1423,31 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
uiInterface.InitMessage(_("Loading block index…").translated);
const int64_t load_block_index_start_time = GetTimeMillis();
- std::optional<ChainstateLoadingError> rv;
+ std::optional<ChainstateLoadingError> maybe_load_error;
try {
- rv = LoadChainstate(fReset,
- chainman,
- Assert(node.mempool.get()),
- fPruneMode,
- chainparams.GetConsensus(),
- fReindexChainState,
- cache_sizes.block_tree_db,
- cache_sizes.coins_db,
- cache_sizes.coins,
- false,
- false,
- ShutdownRequested,
- []() {
- uiInterface.ThreadSafeMessageBox(
- _("Error reading from database, shutting down."),
- "", CClientUIInterface::MSG_ERROR);
- });
+ maybe_load_error = LoadChainstate(fReset,
+ chainman,
+ Assert(node.mempool.get()),
+ fPruneMode,
+ chainparams.GetConsensus(),
+ fReindexChainState,
+ cache_sizes.block_tree_db,
+ cache_sizes.coins_db,
+ cache_sizes.coins,
+ /*block_tree_db_in_memory=*/false,
+ /*coins_db_in_memory=*/false,
+ /*shutdown_requested=*/ShutdownRequested,
+ /*coins_error_cb=*/[]() {
+ uiInterface.ThreadSafeMessageBox(
+ _("Error reading from database, shutting down."),
+ "", CClientUIInterface::MSG_ERROR);
+ });
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- rv = ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
+ maybe_load_error = ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
}
- if (rv.has_value()) {
- switch (rv.value()) {
+ if (maybe_load_error.has_value()) {
+ switch (maybe_load_error.value()) {
case ChainstateLoadingError::ERROR_LOADING_BLOCK_DB:
strLoadError = _("Error loading block database");
break;
@@ -1462,7 +1481,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
break;
}
} else {
- std::optional<ChainstateLoadVerifyError> rv2;
+ std::optional<ChainstateLoadVerifyError> maybe_verify_error;
try {
uiInterface.InitMessage(_("Verifying blocks…").translated);
auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
@@ -1470,19 +1489,19 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
- rv2 = VerifyLoadedChainstate(chainman,
- fReset,
- fReindexChainState,
- chainparams.GetConsensus(),
- check_blocks,
- args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- static_cast<int64_t(*)()>(GetTime));
+ maybe_verify_error = VerifyLoadedChainstate(chainman,
+ fReset,
+ fReindexChainState,
+ chainparams.GetConsensus(),
+ check_blocks,
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
+ /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- rv2 = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
+ maybe_verify_error = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
}
- if (rv2.has_value()) {
- switch (rv2.value()) {
+ if (maybe_verify_error.has_value()) {
+ switch (maybe_verify_error.value()) {
case ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE:
strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. "
diff --git a/src/init.h b/src/init.h
index 0956d9d5ed..1292cc1a3a 100644
--- a/src/init.h
+++ b/src/init.h
@@ -16,14 +16,16 @@ static constexpr bool DEFAULT_DAEMON = false;
static constexpr bool DEFAULT_DAEMONWAIT = false;
class ArgsManager;
-struct NodeContext;
namespace interfaces {
struct BlockAndHeaderTipInfo;
}
+namespace node {
+struct NodeContext;
+} // namespace node
/** Interrupt threads */
-void Interrupt(NodeContext& node);
-void Shutdown(NodeContext& node);
+void Interrupt(node::NodeContext& node);
+void Shutdown(node::NodeContext& node);
//!Initialize the logging infrastructure
void InitLogging(const ArgsManager& args);
//!Parameter interaction: change current parameters depending on various rules
@@ -55,13 +57,13 @@ bool AppInitLockDataDirectory();
/**
* Initialize node and wallet interface pointers. Has no prerequisites or side effects besides allocating memory.
*/
-bool AppInitInterfaces(NodeContext& node);
+bool AppInitInterfaces(node::NodeContext& node);
/**
* Bitcoin core main initialization.
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/
-bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
+bool AppInitMain(node::NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
/**
* Register all arguments with the ArgsManager
diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp
index f13bd4a05d..e297b48718 100644
--- a/src/init/bitcoin-gui.cpp
+++ b/src/init/bitcoin-gui.cpp
@@ -33,7 +33,7 @@ public:
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
interfaces::Ipc* ipc() override { return m_ipc.get(); }
- NodeContext m_node;
+ node::NodeContext m_node;
std::unique_ptr<interfaces::Ipc> m_ipc;
};
} // namespace
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index be6dfefe7d..511a872bc0 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -20,7 +20,7 @@ const char* EXE_NAME = "bitcoin-node";
class BitcoinNodeInit : public interfaces::Init
{
public:
- BitcoinNodeInit(NodeContext& node, const char* arg0)
+ BitcoinNodeInit(node::NodeContext& node, const char* arg0)
: m_node(node),
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
{
@@ -35,14 +35,14 @@ public:
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
interfaces::Ipc* ipc() override { return m_ipc.get(); }
- NodeContext& m_node;
+ node::NodeContext& m_node;
std::unique_ptr<interfaces::Ipc> m_ipc;
};
} // namespace
} // namespace init
namespace interfaces {
-std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status)
+std::unique_ptr<Init> MakeNodeInit(node::NodeContext& node, int argc, char* argv[], int& exit_status)
{
auto init = std::make_unique<init::BitcoinNodeInit>(node, argc > 0 ? argv[0] : "");
// Check if bitcoin-node is being invoked as an IPC server. If so, then
diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp
index 17a074cb8d..37d4e426c5 100644
--- a/src/init/bitcoin-qt.cpp
+++ b/src/init/bitcoin-qt.cpp
@@ -29,7 +29,7 @@ public:
return MakeWalletLoader(chain, *Assert(m_node.args));
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
- NodeContext m_node;
+ node::NodeContext m_node;
};
} // namespace
} // namespace init
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 9c2e44569a..2addff07c1 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -12,6 +12,8 @@
#include <memory>
+using node::NodeContext;
+
namespace init {
namespace {
class BitcoindInit : public interfaces::Init
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 9eb0d680e3..ddfb4bda95 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -28,7 +28,9 @@ enum class RBFTransactionState;
struct bilingual_str;
struct CBlockLocator;
struct FeeCalculation;
+namespace node {
struct NodeContext;
+} // namespace node
namespace interfaces {
@@ -114,9 +116,6 @@ public:
//! or one of its ancestors.
virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
- //! Check if transaction will be final given chain height current time.
- virtual bool checkFinalTx(const CTransaction& tx) = 0;
-
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0;
@@ -316,7 +315,7 @@ public:
};
//! Return implementation of Chain interface.
-std::unique_ptr<Chain> MakeChain(NodeContext& node);
+std::unique_ptr<Chain> MakeChain(node::NodeContext& node);
} // namespace interfaces
diff --git a/src/interfaces/init.h b/src/interfaces/init.h
index a4ecf4b5d1..2153076366 100644
--- a/src/interfaces/init.h
+++ b/src/interfaces/init.h
@@ -7,7 +7,9 @@
#include <memory>
+namespace node {
struct NodeContext;
+} // namespace node
namespace interfaces {
class Chain;
@@ -40,7 +42,7 @@ public:
//! status code to exit with. If this returns non-null, the caller can start up
//! normally and use the Init object to spawn and connect to other processes
//! while it is running.
-std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status);
+std::unique_ptr<Init> MakeNodeInit(node::NodeContext& node, int argc, char* argv[], int& exit_status);
//! Return implementation of Init interface for the wallet process.
std::unique_ptr<Init> MakeWalletInit(int argc, char* argv[], int& exit_status);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 8143839850..9c1b196d61 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -22,7 +22,6 @@
#include <vector>
class BanMan;
-class CCoinControl;
class CFeeRate;
class CNodeStats;
class Coin;
@@ -32,8 +31,13 @@ class proxyType;
enum class SynchronizationState;
enum class TransactionError;
struct CNodeStateStats;
-struct NodeContext;
struct bilingual_str;
+namespace node {
+struct NodeContext;
+} // namespace node
+namespace wallet {
+class CCoinControl;
+} // namespace wallet
namespace interfaces {
class Handler;
@@ -242,12 +246,12 @@ public:
//! Get and set internal node context. Useful for testing, but not
//! accessible across processes.
- virtual NodeContext* context() { return nullptr; }
- virtual void setContext(NodeContext* context) { }
+ virtual node::NodeContext* context() { return nullptr; }
+ virtual void setContext(node::NodeContext* context) { }
};
//! Return implementation of Node interface.
-std::unique_ptr<Node> MakeNode(NodeContext& context);
+std::unique_ptr<Node> MakeNode(node::NodeContext& context);
//! Block tip (could be a header or not, depends on the subscribed signal).
struct BlockTip {
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index c81ec30227..f26ac866dc 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <consensus/amount.h>
+#include <fs.h>
#include <interfaces/chain.h> // For ChainClient
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
@@ -23,19 +24,21 @@
#include <utility>
#include <vector>
-class CCoinControl;
class CFeeRate;
class CKey;
-class CWallet;
enum class FeeReason;
enum class OutputType;
enum class TransactionError;
+struct PartiallySignedTransaction;
+struct bilingual_str;
+namespace wallet {
+class CCoinControl;
+class CWallet;
enum isminetype : unsigned int;
struct CRecipient;
-struct PartiallySignedTransaction;
struct WalletContext;
-struct bilingual_str;
using isminefilter = std::underlying_type<isminetype>::type;
+} // namespace wallet
namespace interfaces {
@@ -107,7 +110,7 @@ public:
//! Look up address in wallet, return whether exists.
virtual bool getAddress(const CTxDestination& dest,
std::string* name,
- isminetype* is_mine,
+ wallet::isminetype* is_mine,
std::string* purpose) = 0;
//! Get wallet address list.
@@ -135,8 +138,8 @@ public:
virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
//! Create transaction.
- virtual CTransactionRef createTransaction(const std::vector<CRecipient>& recipients,
- const CCoinControl& coin_control,
+ virtual CTransactionRef createTransaction(const std::vector<wallet::CRecipient>& recipients,
+ const wallet::CCoinControl& coin_control,
bool sign,
int& change_pos,
CAmount& fee,
@@ -158,7 +161,7 @@ public:
//! Create bump transaction.
virtual bool createBumpTransaction(const uint256& txid,
- const CCoinControl& coin_control,
+ const wallet::CCoinControl& coin_control,
std::vector<bilingual_str>& errors,
CAmount& old_fee,
CAmount& new_fee,
@@ -213,19 +216,19 @@ public:
virtual CAmount getBalance() = 0;
//! Get available balance.
- virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0;
+ virtual CAmount getAvailableBalance(const wallet::CCoinControl& coin_control) = 0;
//! Return whether transaction input belongs to wallet.
- virtual isminetype txinIsMine(const CTxIn& txin) = 0;
+ virtual wallet::isminetype txinIsMine(const CTxIn& txin) = 0;
//! Return whether transaction output belongs to wallet.
- virtual isminetype txoutIsMine(const CTxOut& txout) = 0;
+ virtual wallet::isminetype txoutIsMine(const CTxOut& txout) = 0;
//! Return debit amount if transaction input belongs to wallet.
- virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0;
+ virtual CAmount getDebit(const CTxIn& txin, wallet::isminefilter filter) = 0;
//! Return credit amount if transaction input belongs to wallet.
- virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0;
+ virtual CAmount getCredit(const CTxOut& txout, wallet::isminefilter filter) = 0;
//! Return AvailableCoins + LockedCoins grouped by wallet address.
//! (put change in one group with wallet address)
@@ -240,7 +243,7 @@ public:
//! Get minimum fee.
virtual CAmount getMinimumFee(unsigned int tx_bytes,
- const CCoinControl& coin_control,
+ const wallet::CCoinControl& coin_control,
int* returned_target,
FeeReason* reason) = 0;
@@ -307,7 +310,7 @@ public:
virtual std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) = 0;
//! Return pointer to internal wallet class, useful for testing.
- virtual CWallet* wallet() { return nullptr; }
+ virtual wallet::CWallet* wallet() { return nullptr; }
};
//! Wallet chain client that in addition to having chain client methods for
@@ -326,7 +329,7 @@ public:
virtual std::string getWalletDir() = 0;
//! Restore backup wallet
- virtual std::unique_ptr<Wallet> restoreWallet(const std::string& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+ virtual std::unique_ptr<Wallet> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
//! Return available wallets in wallet directory.
virtual std::vector<std::string> listWalletDir() = 0;
@@ -341,18 +344,18 @@ public:
virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
//! Return pointer to internal context, useful for testing.
- virtual WalletContext* context() { return nullptr; }
+ virtual wallet::WalletContext* context() { return nullptr; }
};
//! Information about one wallet address.
struct WalletAddress
{
CTxDestination dest;
- isminetype is_mine;
+ wallet::isminetype is_mine;
std::string name;
std::string purpose;
- WalletAddress(CTxDestination dest, isminetype is_mine, std::string name, std::string purpose)
+ WalletAddress(CTxDestination dest, wallet::isminetype is_mine, std::string name, std::string purpose)
: dest(std::move(dest)), is_mine(is_mine), name(std::move(name)), purpose(std::move(purpose))
{
}
@@ -382,10 +385,10 @@ struct WalletBalances
struct WalletTx
{
CTransactionRef tx;
- std::vector<isminetype> txin_is_mine;
- std::vector<isminetype> txout_is_mine;
+ std::vector<wallet::isminetype> txin_is_mine;
+ std::vector<wallet::isminetype> txout_is_mine;
std::vector<CTxDestination> txout_address;
- std::vector<isminetype> txout_address_is_mine;
+ std::vector<wallet::isminetype> txout_address_is_mine;
CAmount credit;
CAmount debit;
CAmount change;
@@ -402,7 +405,6 @@ struct WalletTxStatus
int depth_in_main_chain;
unsigned int time_received;
uint32_t lock_time;
- bool is_final;
bool is_trusted;
bool is_abandoned;
bool is_coinbase;
@@ -420,7 +422,7 @@ struct WalletTxOut
//! Return implementation of Wallet interface. This function is defined in
//! dummywallet.cpp and throws if the wallet component is not compiled.
-std::unique_ptr<Wallet> MakeWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet);
+std::unique_ptr<Wallet> MakeWallet(wallet::WalletContext& context, const std::shared_ptr<wallet::CWallet>& wallet);
//! Return implementation of ChainClient interface for a wallet loader. This
//! function will be undefined in builds where ENABLE_WALLET is false.
diff --git a/src/logging.cpp b/src/logging.cpp
index 6edcebf87e..764941c8ea 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <fs.h>
#include <logging.h>
#include <util/threadnames.h>
#include <util/string.h>
diff --git a/src/logging.h b/src/logging.h
index 31f0bdc690..710e6c4c32 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -13,6 +13,7 @@
#include <atomic>
#include <cstdint>
+#include <functional>
#include <list>
#include <mutex>
#include <string>
diff --git a/src/net.cpp b/src/net.cpp
index 159451e230..bee8710062 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <compat.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
+#include <fs.h>
#include <i2p.h>
#include <net_permissions.h>
#include <netaddress.h>
@@ -112,9 +113,9 @@ static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256
//
bool fDiscover = true;
bool fListen = true;
-RecursiveMutex cs_mapLocalHost;
-std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
-static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
+Mutex g_maplocalhost_mutex;
+std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
+static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
std::string strSubVersion;
void CConnman::AddAddrFetch(const std::string& strDest)
@@ -137,7 +138,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
int nBestScore = -1;
int nBestReachability = -1;
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
for (const auto& entry : mapLocalHost)
{
int nScore = entry.second.nScore;
@@ -193,7 +194,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
static int GetnScore(const CService& addr)
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
const auto it = mapLocalHost.find(addr);
return (it != mapLocalHost.end()) ? it->second.nScore : 0;
}
@@ -264,7 +265,7 @@ bool AddLocal(const CService& addr_, int nScore)
LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore);
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
const auto [it, is_newly_added] = mapLocalHost.emplace(addr, LocalServiceInfo());
LocalServiceInfo &info = it->second;
if (is_newly_added || nScore >= info.nScore) {
@@ -283,7 +284,7 @@ bool AddLocal(const CNetAddr &addr, int nScore)
void RemoveLocal(const CService& addr)
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
LogPrintf("RemoveLocal(%s)\n", addr.ToString());
mapLocalHost.erase(addr);
}
@@ -292,13 +293,13 @@ void SetReachable(enum Network net, bool reachable)
{
if (net == NET_UNROUTABLE || net == NET_INTERNAL)
return;
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
vfLimited[net] = !reachable;
}
bool IsReachable(enum Network net)
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
return !vfLimited[net];
}
@@ -310,7 +311,7 @@ bool IsReachable(const CNetAddr &addr)
/** vote for a local address */
bool SeenLocal(const CService& addr)
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
const auto it = mapLocalHost.find(addr);
if (it == mapLocalHost.end()) return false;
++it->second.nScore;
@@ -321,7 +322,7 @@ bool SeenLocal(const CService& addr)
/** check whether a given address is potentially local */
bool IsLocal(const CService& addr)
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
return mapLocalHost.count(addr) > 0;
}
@@ -561,12 +562,14 @@ std::string ConnectionTypeAsString(ConnectionType conn_type)
CService CNode::GetAddrLocal() const
{
- LOCK(cs_addrLocal);
+ AssertLockNotHeld(m_addr_local_mutex);
+ LOCK(m_addr_local_mutex);
return addrLocal;
}
void CNode::SetAddrLocal(const CService& addrLocalIn) {
- LOCK(cs_addrLocal);
+ AssertLockNotHeld(m_addr_local_mutex);
+ LOCK(m_addr_local_mutex);
if (addrLocal.IsValid()) {
error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString());
} else {
@@ -603,7 +606,7 @@ void CNode::CopyStats(CNodeStats& stats)
X(m_addr_name);
X(nVersion);
{
- LOCK(cs_SubVer);
+ LOCK(m_subver_mutex);
X(cleanSubVer);
}
stats.fInbound = IsInboundConn();
@@ -665,7 +668,7 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
// Store received bytes per message command
// to prevent a memory DOS, only allow valid commands
- auto i = mapRecvBytesPerMsgCmd.find(msg.m_command);
+ auto i = mapRecvBytesPerMsgCmd.find(msg.m_type);
if (i == mapRecvBytesPerMsgCmd.end()) {
i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
}
@@ -755,7 +758,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
CNetMessage msg(std::move(vRecv));
// store command string, time, and sizes
- msg.m_command = hdr.GetCommand();
+ msg.m_type = hdr.GetCommand();
msg.m_time = time;
msg.m_message_size = hdr.nMessageSize;
msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
@@ -768,7 +771,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
// Check checksum and header command string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n",
- SanitizeString(msg.m_command), msg.m_message_size,
+ SanitizeString(msg.m_type), msg.m_message_size,
HexStr(Span{hash}.first(CMessageHeader::CHECKSUM_SIZE)),
HexStr(hdr.pchChecksum),
m_node_id);
@@ -1899,8 +1902,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
auto start = GetTime<std::chrono::microseconds>();
// Minimum time before next feeler connection (in microseconds).
- auto next_feeler = PoissonNextSend(start, FEELER_INTERVAL);
- auto next_extra_block_relay = PoissonNextSend(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
+ auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
@@ -2020,7 +2023,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
//
// This is similar to the logic for trying extra outbound (full-relay)
// peers, except:
- // - we do this all the time on a poisson timer, rather than just when
+ // - we do this all the time on an exponential timer, rather than just when
// our tip is stale
// - we potentially disconnect our next-youngest block-relay-only peer, if our
// newest block-relay-only peer delivers a block more recently.
@@ -2029,10 +2032,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Because we can promote these connections to block-relay-only
// connections, they do not get their own ConnectionType enum
// (similar to how we deal with extra outbound peers).
- next_extra_block_relay = PoissonNextSend(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ next_extra_block_relay = GetExponentialRand(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
conn_type = ConnectionType::BLOCK_RELAY;
} else if (now > next_feeler) {
- next_feeler = PoissonNextSend(now, FEELER_INTERVAL);
+ next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
fFeeler = true;
} else {
@@ -3074,23 +3077,6 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-std::chrono::microseconds CConnman::PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval)
-{
- if (m_next_send_inv_to_incoming.load() < now) {
- // If this function were called from multiple threads simultaneously
- // it would possible that both update the next send variable, and return a different result to their caller.
- // This is not possible in practice as only the net processing thread invokes this function.
- m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval);
- }
- return m_next_send_inv_to_incoming;
-}
-
-std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
-{
- double unscaled = -log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
- return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
-}
-
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
{
return CSipHasher(nSeed0, nSeed1).Write(id);
@@ -3122,11 +3108,11 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
CAutoFile f(fsbridge::fopen(path, "ab"), SER_DISK, CLIENT_VERSION);
ser_writedata64(f, now.count());
- f.write(msg_type.data(), msg_type.length());
+ f.write(MakeByteSpan(msg_type));
for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) {
f << uint8_t{'\0'};
}
uint32_t size = data.size();
ser_writedata32(f, size);
- f.write((const char*)data.data(), data.size());
+ f.write(AsBytes(data));
}
diff --git a/src/net.h b/src/net.h
index be564dbade..3f4c8e38ec 100644
--- a/src/net.h
+++ b/src/net.h
@@ -230,8 +230,8 @@ struct LocalServiceInfo {
uint16_t nPort;
};
-extern RecursiveMutex cs_mapLocalHost;
-extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
+extern Mutex g_maplocalhost_mutex;
+extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
extern const std::string NET_MESSAGE_COMMAND_OTHER;
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
@@ -278,7 +278,7 @@ public:
/** Transport protocol agnostic message container.
* Ideally it should only contain receive time, payload,
- * command and size.
+ * type and size.
*/
class CNetMessage {
public:
@@ -286,7 +286,7 @@ public:
std::chrono::microseconds m_time{0}; //!< time of message receipt
uint32_t m_message_size{0}; //!< size of the payload
uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
- std::string m_command;
+ std::string m_type;
CNetMessage(CDataStream&& recv_in) : m_recv(std::move(recv_in)) {}
@@ -444,12 +444,12 @@ public:
//! Whether this peer is an inbound onion, i.e. connected via our Tor onion service.
const bool m_inbound_onion;
std::atomic<int> nVersion{0};
- RecursiveMutex cs_SubVer;
+ Mutex m_subver_mutex;
/**
* cleanSubVer is a sanitized string of the user agent byte array we read
* from the wire. This cleaned string can safely be logged or displayed.
*/
- std::string cleanSubVer GUARDED_BY(cs_SubVer){};
+ std::string cleanSubVer GUARDED_BY(m_subver_mutex){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
bool HasPermission(NetPermissionFlags permission) const {
return NetPermissions::HasFlag(m_permissionFlags, permission);
@@ -627,9 +627,9 @@ public:
return m_greatest_common_version;
}
- CService GetAddrLocal() const;
+ CService GetAddrLocal() const LOCKS_EXCLUDED(m_addr_local_mutex);
//! May not be called more than once
- void SetAddrLocal(const CService& addrLocalIn);
+ void SetAddrLocal(const CService& addrLocalIn) LOCKS_EXCLUDED(m_addr_local_mutex);
CNode* AddRef()
{
@@ -702,8 +702,8 @@ private:
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
// Our address, as reported by the peer
- CService addrLocal GUARDED_BY(cs_addrLocal);
- mutable RecursiveMutex cs_addrLocal;
+ CService addrLocal GUARDED_BY(m_addr_local_mutex);
+ mutable Mutex m_addr_local_mutex;
mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend);
mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
@@ -945,12 +945,6 @@ public:
void WakeMessageHandler();
- /** Attempts to obfuscate tx time through exponentially distributed emitting.
- Works assuming that a single interval is used.
- Variable intervals will result in privacy decrease.
- */
- std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
-
/** Return true if we should disconnect the peer for failing an inactivity check. */
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
@@ -1230,8 +1224,6 @@ private:
*/
std::atomic_bool m_start_extra_block_relay_peers{false};
- std::atomic<std::chrono::microseconds> m_next_send_inv_to_incoming{0us};
-
/**
* A vector of -bind=<address>:<port>=onion arguments each of which is
* an address and port that are designated for incoming Tor connections.
@@ -1279,9 +1271,6 @@ private:
friend struct ConnmanTestMsg;
};
-/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
-std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval);
-
/** Dump binary message to file, with timestamp */
void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 7ef0054630..3cebca1a77 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -45,6 +45,12 @@
#include <optional>
#include <typeinfo>
+using node::ReadBlockFromDisk;
+using node::ReadRawBlockFromDisk;
+using node::fImporting;
+using node::fPruneMode;
+using node::fReindex;
+
/** How long to cache transactions in mapRelay for normal relay */
static constexpr auto RELAY_TX_CACHE_TIME = 15min;
/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
@@ -57,14 +63,14 @@ static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
* behind headers chain.
*/
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
-/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
-static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** Timeout for (unprotected) outbound peers to sync to our chainwork */
+static constexpr auto CHAIN_SYNC_TIMEOUT{20min};
/** How frequently to check for stale tips */
static constexpr auto STALE_CHECK_INTERVAL{10min};
/** How frequently to check for extra outbound peers and disconnect */
static constexpr auto EXTRA_PEER_CHECK_INTERVAL{45s};
/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict */
-static constexpr std::chrono::seconds MINIMUM_CONNECT_TIME{30};
+static constexpr auto MINIMUM_CONNECT_TIME{30s};
/** SHA256("main address relay")[0:8] */
static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
/// Age after which a stale block will no longer be served if requested as
@@ -74,7 +80,7 @@ static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
/// limiting block relay. Set to one week, denominated in seconds.
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
/** Time between pings automatically sent out for latency probing and keepalive */
-static constexpr std::chrono::minutes PING_INTERVAL{2};
+static constexpr auto PING_INTERVAL{2min};
/** The maximum number of entries in a locator */
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of entries in an 'inv' protocol message */
@@ -88,19 +94,19 @@ static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
* the actual transaction (from any peer) in response to requests for them. */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
-static constexpr auto TXID_RELAY_DELAY = std::chrono::seconds{2};
+static constexpr auto TXID_RELAY_DELAY{2s};
/** How long to delay requesting transactions from non-preferred peers */
-static constexpr auto NONPREF_PEER_TX_DELAY = std::chrono::seconds{2};
+static constexpr auto NONPREF_PEER_TX_DELAY{2s};
/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
-static constexpr auto OVERLOADED_PEER_TX_DELAY = std::chrono::seconds{2};
-/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
-static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}};
+static constexpr auto OVERLOADED_PEER_TX_DELAY{2s};
+/** How long to wait before downloading a transaction from an additional peer */
+static constexpr auto GETDATA_TX_INTERVAL{60s};
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
static const unsigned int MAX_GETDATA_SZ = 1000;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
/** Time during which a peer must stall block download progress before being disconnected. */
-static constexpr auto BLOCK_STALLING_TIMEOUT = 2s;
+static constexpr auto BLOCK_STALLING_TIMEOUT{2s};
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
@@ -125,16 +131,16 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
/** Average delay between local address broadcasts */
-static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h;
+static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24h};
/** Average delay between peer address broadcasts */
-static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL = 30s;
+static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL{30s};
/** Average delay between trickled inventory transmissions for inbound peers.
* Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */
-static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL = 5s;
+static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s};
/** Average delay between trickled inventory transmissions for outbound peers.
* Use a smaller delay as there is less privacy concern for them.
* Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */
-static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL = 2s;
+static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
@@ -148,9 +154,9 @@ static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
* peers, and random variations in the broadcast mechanism. */
static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
-static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL = 10min;
+static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL{10min};
/** Maximum feefilter broadcast delay after significant change. */
-static constexpr auto MAX_FEEFILTER_CHANGE_DELAY = 5min;
+static constexpr auto MAX_FEEFILTER_CHANGE_DELAY{5min};
/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
@@ -314,7 +320,7 @@ public:
/** Implement PeerManager */
void StartScheduledTasks(CScheduler& scheduler) override;
void CheckForStaleTipAndEvictPeers() override;
- bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index) override;
+ std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override;
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
void SendPings() override;
@@ -329,7 +335,7 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** 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);
@@ -444,6 +450,8 @@ private:
*/
std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
+ std::atomic<std::chrono::microseconds> m_next_inv_to_inbounds{0us};
+
/** Number of nodes with fSyncStarted. */
int nSyncStarted GUARDED_BY(cs_main) = 0;
@@ -518,6 +526,15 @@ private:
Mutex m_recent_confirmed_transactions_mutex;
CRollingBloomFilter m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex){48'000, 0.000'001};
+ /**
+ * For sending `inv`s to inbound peers, we use a single (exponentially
+ * distributed) timer for all peers. If we used a separate timer for each
+ * peer, a spy node could make multiple inbound connections to us to
+ * accurately determine when we received the transaction (and potentially
+ * determine the transaction's origin). */
+ std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
+ std::chrono::seconds average_interval);
+
/** Have we requested this block from a peer */
bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -727,7 +744,7 @@ struct CNodeState {
* - its chain tip has at least as much work as ours
*
* CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip,
- * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
+ * set a timeout CHAIN_SYNC_TIMEOUT in the future:
* - If at timeout their best known block now has more work than our tip
* when the timeout was set, then either reset the timeout or clear it
* (after comparing against our current tip's work)
@@ -742,7 +759,7 @@ struct CNodeState {
*/
struct ChainSyncTimeoutState {
//! A timeout used for checking whether our peer has sufficiently synced
- int64_t m_timeout{0};
+ std::chrono::seconds m_timeout{0s};
//! A header with the work we require on our peer's chain
const CBlockIndex* m_work_header{nullptr};
//! After timeout is reached, set to true after sending getheaders
@@ -819,6 +836,18 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
+std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
+ std::chrono::seconds average_interval)
+{
+ if (m_next_inv_to_inbounds.load() < now) {
+ // If this function were called from multiple threads simultaneously
+ // it would possible that both update the next send variable, and return a different result to their caller.
+ // This is not possible in practice as only the net processing thread invokes this function.
+ m_next_inv_to_inbounds = GetExponentialRand(now, average_interval);
+ }
+ return m_next_inv_to_inbounds;
+}
+
bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{
return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
@@ -949,10 +978,10 @@ bool PeerManagerImpl::TipMayBeStale()
{
AssertLockHeld(cs_main);
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
- if (count_seconds(m_last_tip_update) == 0) {
+ if (m_last_tip_update.load() == 0s) {
m_last_tip_update = GetTime<std::chrono::seconds>();
}
- return count_seconds(m_last_tip_update) < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+ return m_last_tip_update.load() < GetTime<std::chrono::seconds>() - std::chrono::seconds{consensusParams.nPowTargetSpacing * 3} && mapBlocksInFlight.empty();
}
bool PeerManagerImpl::CanDirectFetch()
@@ -1137,7 +1166,7 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
- auto delay = std::chrono::microseconds{0};
+ auto delay{0us};
const bool preferred = state->fPreferredDownload;
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
@@ -1191,7 +1220,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
// Schedule next run for 10-15 minutes in the future.
// We add randomness on every cycle to avoid the possibility of P2P fingerprinting.
- const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
+ const std::chrono::milliseconds delta = 10min + GetRandMillis(5min);
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1296,7 +1325,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
- std::chrono::microseconds ping_wait{0};
+ auto ping_wait{0us};
if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) {
ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
}
@@ -1431,39 +1460,39 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
}
-bool PeerManagerImpl::FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index)
+std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBlockIndex& block_index)
{
- if (fImporting || fReindex) return false;
+ if (fImporting) return "Importing...";
+ if (fReindex) return "Reindexing...";
LOCK(cs_main);
// Ensure this peer exists and hasn't been disconnected
- CNodeState* state = State(id);
- if (state == nullptr) return false;
+ CNodeState* state = State(peer_id);
+ if (state == nullptr) return "Peer does not exist";
// Ignore pre-segwit peers
- if (!state->fHaveWitness) return false;
+ if (!state->fHaveWitness) return "Pre-SegWit peer";
- // Mark block as in-flight unless it already is
- if (!BlockRequested(id, index)) return false;
+ // Mark block as in-flight unless it already is (for this peer).
+ // If a block was already in-flight for a different peer, its BLOCKTXN
+ // response will be dropped.
+ if (!BlockRequested(peer_id, block_index)) return "Already requested from this peer";
// Construct message to request the block
+ const uint256& hash{block_index.GetBlockHash()};
std::vector<CInv> invs{CInv(MSG_BLOCK | MSG_WITNESS_FLAG, hash)};
// Send block request message to the peer
- bool success = m_connman.ForNode(id, [this, &invs](CNode* node) {
+ bool success = m_connman.ForNode(peer_id, [this, &invs](CNode* node) {
const CNetMsgMaker msgMaker(node->GetCommonVersion());
this->m_connman.PushMessage(node, msgMaker.Make(NetMsgType::GETDATA, invs));
return true;
});
- if (success) {
- LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- hash.ToString(), id);
- } else {
- RemoveBlockRequest(hash);
- LogPrint(BCLog::NET, "Failed to request block %s from peer=%d\n",
- hash.ToString(), id);
- }
- return success;
+ if (!success) return "Peer not fully connected";
+
+ LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
+ hash.ToString(), peer_id);
+ return std::nullopt;
}
std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
@@ -1496,7 +1525,7 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
// schedule next run for 10-15 minutes in the future
- const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
+ const std::chrono::milliseconds delta = 10min + GetRandMillis(5min);
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1851,7 +1880,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
// Fast-path: in this case it is possible to serve the block directly from disk,
// as the network format matches the format on disk
std::vector<uint8_t> block_data;
- if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) {
+ if (!ReadRawBlockFromDisk(block_data, pindex->GetBlockPos(), m_chainparams.MessageStart())) {
assert(!"cannot load block from disk");
}
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data}));
@@ -1963,10 +1992,9 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
- const std::chrono::seconds now = GetTime<std::chrono::seconds>();
+ const auto now{GetTime<std::chrono::seconds>()};
// Get last mempool request time
- const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
- : std::chrono::seconds::min();
+ const auto mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min();
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
@@ -2631,7 +2659,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.nServices = nServices;
pfrom.SetAddrLocal(addrMe);
{
- LOCK(pfrom.cs_SubVer);
+ LOCK(pfrom.m_subver_mutex);
pfrom.cleanSubVer = cleanSubVer;
}
peer->m_starting_height = starting_height;
@@ -2890,7 +2918,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
int64_t nSince = nNow - 10 * 60;
// Update/increment addr rate limiting bucket.
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
// Don't increment bucket if it's already full
const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us);
@@ -2976,7 +3004,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
uint256* best_block{nullptr};
for (CInv& inv : vInv) {
@@ -3354,7 +3382,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
}
if (!fRejectedParents) {
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
for (const uint256& parent_txid : unique_parents) {
// Here, we only have the txid (and not wtxid) of the
@@ -4149,38 +4177,34 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
pfrom->GetId(),
pfrom->m_addr_name.c_str(),
pfrom->ConnectionTypeAsString().c_str(),
- msg.m_command.c_str(),
+ msg.m_type.c_str(),
msg.m_recv.size(),
msg.m_recv.data()
);
if (gArgs.GetBoolArg("-capturemessages", false)) {
- CaptureMessage(pfrom->addr, msg.m_command, MakeUCharSpan(msg.m_recv), /*is_incoming=*/true);
+ CaptureMessage(pfrom->addr, msg.m_type, MakeUCharSpan(msg.m_recv), /*is_incoming=*/true);
}
msg.SetVersion(pfrom->GetCommonVersion());
- const std::string& msg_type = msg.m_command;
-
- // Message size
- unsigned int nMessageSize = msg.m_message_size;
try {
- ProcessMessage(*pfrom, msg_type, msg.m_recv, msg.m_time, interruptMsgProc);
+ ProcessMessage(*pfrom, msg.m_type, msg.m_recv, msg.m_time, interruptMsgProc);
if (interruptMsgProc) return false;
{
LOCK(peer->m_getdata_requests_mutex);
if (!peer->m_getdata_requests.empty()) fMoreWork = true;
}
} catch (const std::exception& e) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg_type), nMessageSize, e.what(), typeid(e).name());
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg.m_type), msg.m_message_size, e.what(), typeid(e).name());
} catch (...) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(msg_type), nMessageSize);
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(msg.m_type), msg.m_message_size);
}
return fMoreWork;
}
-void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
+void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds)
{
AssertLockHeld(cs_main);
@@ -4195,12 +4219,12 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {
- if (state.m_chain_sync.m_timeout != 0) {
- state.m_chain_sync.m_timeout = 0;
+ if (state.m_chain_sync.m_timeout != 0s) {
+ state.m_chain_sync.m_timeout = 0s;
state.m_chain_sync.m_work_header = nullptr;
state.m_chain_sync.m_sent_getheaders = false;
}
- } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
+ } else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
// Our best block known by this peer is behind our tip, and we're either noticing
// that for the first time, OR this peer was able to catch up to some earlier point
// where we checked against our tip.
@@ -4208,7 +4232,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();
state.m_chain_sync.m_sent_getheaders = false;
- } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
+ } else if (state.m_chain_sync.m_timeout > 0s && time_in_seconds > state.m_chain_sync.m_timeout) {
// No evidence yet that our peer has synced to a chain with work equal to that
// of our tip, when we first detected it was behind. Send a single getheaders
// message to give the peer a chance to update us.
@@ -4221,7 +4245,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
- constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
+ constexpr auto HEADERS_RESPONSE_TIME{2min};
// Bump the timeout to allow a response, which could clear the timeout
// (if the response shows the peer has synced), reset the timeout (if
// the peer syncs to the required work but not to our tip), or result
@@ -4348,7 +4372,8 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) {
- LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", count_seconds(now) - count_seconds(m_last_tip_update));
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n",
+ count_seconds(now - m_last_tip_update.load()));
m_connman.SetTryNewOutboundPeer(true);
} else if (m_connman.GetTryNewOutboundPeer()) {
m_connman.SetTryNewOutboundPeer(false);
@@ -4428,13 +4453,13 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
FastRandomContext insecure_rand;
PushAddress(peer, *local_addr, insecure_rand);
}
- peer.m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
+ peer.m_next_local_addr_send = GetExponentialRand(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
// We sent an `addr` message to this peer recently. Nothing more to do.
if (current_time <= peer.m_next_addr_send) return;
- peer.m_next_addr_send = PoissonNextSend(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
+ peer.m_next_addr_send = GetExponentialRand(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
if (!Assume(peer.m_addrs_to_send.size() <= MAX_ADDR_TO_SEND)) {
// Should be impossible since we always check size before adding to
@@ -4506,7 +4531,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds c
m_connman.PushMessage(&pto, CNetMsgMaker(pto.GetCommonVersion()).Make(NetMsgType::FEEFILTER, filterToSend));
pto.m_tx_relay->lastSentFeeFilter = filterToSend;
}
- pto.m_tx_relay->m_next_send_feefilter = PoissonNextSend(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ pto.m_tx_relay->m_next_send_feefilter = GetExponentialRand(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
}
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
@@ -4570,7 +4595,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// If we get here, the outgoing message serialization version is set and can't change.
const CNetMsgMaker msgMaker(pto->GetCommonVersion());
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
if (pto->IsAddrFetchConn() && current_time - pto->m_connected > 10 * AVG_ADDRESS_BROADCAST_INTERVAL) {
LogPrint(BCLog::NET, "addrfetch connection timeout; disconnecting peer=%d\n", pto->GetId());
@@ -4786,9 +4811,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->IsInboundConn()) {
- pto->m_tx_relay->nNextInvSend = m_connman.PoissonNextSendInbound(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nNextInvSend = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
} else {
- pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nNextInvSend = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
}
}
@@ -4969,7 +4994,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Check that outbound peers have reasonable chains
// GetTime() is used by this anti-DoS logic so we can test this using mocktime
- ConsiderEviction(*pto, GetTime());
+ ConsiderEviction(*pto, GetTime<std::chrono::seconds>());
//
// Message: getdata (blocks)
diff --git a/src/net_processing.h b/src/net_processing.h
index 27775cea97..e30f9f516c 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -45,12 +45,11 @@ public:
/**
* Attempt to manually fetch block from a given peer. We must already have the header.
*
- * @param[in] id The peer id
- * @param[in] hash The block hash
- * @param[in] pindex The blockindex
- * @returns Whether a request was successfully made
+ * @param[in] peer_id The peer id
+ * @param[in] block_index The blockindex
+ * @returns std::nullopt if a request was successfully made, otherwise an error message
*/
- virtual bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& pindex) = 0;
+ virtual std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) = 0;
/** Begin running background tasks, should only be called once */
virtual void StartScheduledTasks(CScheduler& scheduler) = 0;
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index e9f78c2870..7691c9a5ce 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -21,29 +21,13 @@
#include <util/system.h>
#include <validation.h>
+namespace node {
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
bool fPruneMode = false;
uint64_t nPruneTarget = 0;
-// TODO make namespace {
-RecursiveMutex cs_LastBlockFile;
-std::vector<CBlockFileInfo> vinfoBlockFile;
-int nLastBlockFile = 0;
-/** Global flag to indicate we should check to see if there are
-* block/undo files that should be deleted. Set on startup
-* or if we allocate more file space when we're in prune mode
-*/
-bool fCheckForPruning = false;
-
-/** Dirty block index entries. */
-std::set<CBlockIndex*> setDirtyBlockIndex;
-
-/** Dirty block file entries. */
-std::set<int> setDirtyFileInfo;
-// } // namespace
-
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();
@@ -86,7 +70,7 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork)
pindexBestHeader = pindexNew;
- setDirtyBlockIndex.insert(pindexNew);
+ m_dirty_blockindex.insert(pindexNew);
return pindexNew;
}
@@ -104,7 +88,7 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
pindex->nFile = 0;
pindex->nDataPos = 0;
pindex->nUndoPos = 0;
- setDirtyBlockIndex.insert(pindex);
+ m_dirty_blockindex.insert(pindex);
// Prune from m_blocks_unlinked -- any block we prune would have
// to be downloaded again in order to consider its chain, at which
@@ -121,8 +105,8 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
}
}
- vinfoBlockFile[fileNumber].SetNull();
- setDirtyFileInfo.insert(fileNumber);
+ m_blockfile_info[fileNumber].SetNull();
+ m_dirty_fileinfo.insert(fileNumber);
}
void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
@@ -137,8 +121,8 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
int count = 0;
- for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
+ for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
+ if (m_blockfile_info[fileNumber].nSize == 0 || m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
}
PruneOneBlockFile(fileNumber);
@@ -177,10 +161,10 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
nBuffer += nPruneTarget / 10;
}
- for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
+ for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
+ nBytesToPrune = m_blockfile_info[fileNumber].nSize + m_blockfile_info[fileNumber].nUndoSize;
- if (vinfoBlockFile[fileNumber].nSize == 0) {
+ if (m_blockfile_info[fileNumber].nSize == 0) {
continue;
}
@@ -189,7 +173,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
}
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
+ if (m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
}
@@ -290,7 +274,7 @@ bool BlockManager::LoadBlockIndex(
}
if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
pindex->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindex);
+ m_dirty_blockindex.insert(pindex);
}
if (pindex->IsAssumedValid() ||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
@@ -348,6 +332,32 @@ void BlockManager::Unload()
}
m_block_index.clear();
+
+ m_blockfile_info.clear();
+ m_last_blockfile = 0;
+ m_dirty_blockindex.clear();
+ m_dirty_fileinfo.clear();
+}
+
+bool BlockManager::WriteBlockIndexDB()
+{
+ AssertLockHeld(::cs_main);
+ std::vector<std::pair<int, const CBlockFileInfo*>> vFiles;
+ vFiles.reserve(m_dirty_fileinfo.size());
+ for (std::set<int>::iterator it = m_dirty_fileinfo.begin(); it != m_dirty_fileinfo.end();) {
+ vFiles.push_back(std::make_pair(*it, &m_blockfile_info[*it]));
+ m_dirty_fileinfo.erase(it++);
+ }
+ std::vector<const CBlockIndex*> vBlocks;
+ vBlocks.reserve(m_dirty_blockindex.size());
+ for (std::set<CBlockIndex*>::iterator it = m_dirty_blockindex.begin(); it != m_dirty_blockindex.end();) {
+ vBlocks.push_back(*it);
+ m_dirty_blockindex.erase(it++);
+ }
+ if (!m_block_tree_db->WriteBatchSync(vFiles, m_last_blockfile, vBlocks)) {
+ return false;
+ }
+ return true;
}
bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
@@ -357,17 +367,17 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
}
// Load block file info
- m_block_tree_db->ReadLastBlockFile(nLastBlockFile);
- vinfoBlockFile.resize(nLastBlockFile + 1);
- LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
- for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
- m_block_tree_db->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
- }
- LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
- for (int nFile = nLastBlockFile + 1; true; nFile++) {
+ m_block_tree_db->ReadLastBlockFile(m_last_blockfile);
+ m_blockfile_info.resize(m_last_blockfile + 1);
+ LogPrintf("%s: last block file = %i\n", __func__, m_last_blockfile);
+ for (int nFile = 0; nFile <= m_last_blockfile; nFile++) {
+ m_block_tree_db->ReadBlockFileInfo(nFile, m_blockfile_info[nFile]);
+ }
+ LogPrintf("%s: last block file info: %s\n", __func__, m_blockfile_info[m_last_blockfile].ToString());
+ for (int nFile = m_last_blockfile + 1; true; nFile++) {
CBlockFileInfo info;
if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
- vinfoBlockFile.push_back(info);
+ m_blockfile_info.push_back(info);
} else {
break;
}
@@ -419,13 +429,14 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
bool IsBlockPruned(const CBlockIndex* pblockindex)
{
+ AssertLockHeld(::cs_main);
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
// If we're using -prune with -reindex, then delete block files that will be ignored by the
// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
// is missing, do the same here to delete any later block files after a gap. Also delete all
-// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile
+// rev files since they'll be rewritten by the reindex anyway. This ensures that m_blockfile_info
// is in sync with what's actually on disk by the time we start downloading, so that pruning
// works correctly.
void CleanupBlockRevFiles()
@@ -465,16 +476,11 @@ void CleanupBlockRevFiles()
}
}
-std::string CBlockFileInfo::ToString() const
-{
- return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
-}
-
-CBlockFileInfo* GetBlockFileInfo(size_t n)
+CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
{
LOCK(cs_LastBlockFile);
- return &vinfoBlockFile.at(n);
+ return &m_blockfile_info.at(n);
}
static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
@@ -508,7 +514,8 @@ static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
- FlatFilePos pos = pindex->GetUndoPos();
+ const FlatFilePos pos{WITH_LOCK(::cs_main, return pindex->GetUndoPos())};
+
if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
@@ -538,32 +545,32 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
return true;
}
-static void FlushUndoFile(int block_file, bool finalize = false)
+void BlockManager::FlushUndoFile(int block_file, bool finalize)
{
- FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
+ FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize);
if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
}
}
-void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
+void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
{
LOCK(cs_LastBlockFile);
- FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
+ 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.");
}
// we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
// e.g. during IBD or a sync after a node going offline
- if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
+ if (!fFinalize || finalize_undo) FlushUndoFile(m_last_blockfile, finalize_undo);
}
-uint64_t CalculateCurrentUsage()
+uint64_t BlockManager::CalculateCurrentUsage()
{
LOCK(cs_LastBlockFile);
uint64_t retval = 0;
- for (const CBlockFileInfo& file : vinfoBlockFile) {
+ for (const CBlockFileInfo& file : m_blockfile_info) {
retval += file.nSize + file.nUndoSize;
}
return retval;
@@ -605,44 +612,44 @@ fs::path GetBlockPosFilename(const FlatFilePos& pos)
return BlockFileSeq().FileName(pos);
}
-bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
+bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown)
{
LOCK(cs_LastBlockFile);
- unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
- if (vinfoBlockFile.size() <= nFile) {
- vinfoBlockFile.resize(nFile + 1);
+ unsigned int nFile = fKnown ? pos.nFile : m_last_blockfile;
+ if (m_blockfile_info.size() <= nFile) {
+ m_blockfile_info.resize(nFile + 1);
}
bool finalize_undo = false;
if (!fKnown) {
- while (vinfoBlockFile[nFile].nSize + nAddSize >= (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) {
+ while (m_blockfile_info[nFile].nSize + nAddSize >= (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) {
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
+ finalize_undo = (m_blockfile_info[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
- if (vinfoBlockFile.size() <= nFile) {
- vinfoBlockFile.resize(nFile + 1);
+ if (m_blockfile_info.size() <= nFile) {
+ m_blockfile_info.resize(nFile + 1);
}
}
pos.nFile = nFile;
- pos.nPos = vinfoBlockFile[nFile].nSize;
+ pos.nPos = m_blockfile_info[nFile].nSize;
}
- if ((int)nFile != nLastBlockFile) {
+ if ((int)nFile != m_last_blockfile) {
if (!fKnown) {
- LogPrint(BCLog::BLOCKSTORE, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ LogPrint(BCLog::BLOCKSTORE, "Leaving block file %i: %s\n", m_last_blockfile, m_blockfile_info[m_last_blockfile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);
- nLastBlockFile = nFile;
+ m_last_blockfile = nFile;
}
- vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
+ m_blockfile_info[nFile].AddBlock(nHeight, nTime);
if (fKnown) {
- vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize);
+ m_blockfile_info[nFile].nSize = std::max(pos.nPos + nAddSize, m_blockfile_info[nFile].nSize);
} else {
- vinfoBlockFile[nFile].nSize += nAddSize;
+ m_blockfile_info[nFile].nSize += nAddSize;
}
if (!fKnown) {
@@ -652,23 +659,23 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
return AbortNode("Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
- fCheckForPruning = true;
+ m_check_for_pruning = true;
}
}
- setDirtyFileInfo.insert(nFile);
+ m_dirty_fileinfo.insert(nFile);
return true;
}
-static bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize)
+bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
- pos.nPos = vinfoBlockFile[nFile].nUndoSize;
- vinfoBlockFile[nFile].nUndoSize += nAddSize;
- setDirtyFileInfo.insert(nFile);
+ pos.nPos = m_blockfile_info[nFile].nUndoSize;
+ m_blockfile_info[nFile].nUndoSize += nAddSize;
+ m_dirty_fileinfo.insert(nFile);
bool out_of_space;
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
@@ -676,7 +683,7 @@ static bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
- fCheckForPruning = true;
+ m_check_for_pruning = true;
}
return true;
@@ -705,8 +712,9 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa
return true;
}
-bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
+bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
{
+ AssertLockHeld(::cs_main);
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
FlatFilePos _pos;
@@ -721,14 +729,14 @@ bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& st
// in the block file info as below; note that this does not catch the case where the undo writes are keeping up
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
// the FindBlockPos function
- if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
+ if (_pos.nFile < m_last_blockfile && static_cast<uint32_t>(pindex->nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) {
FlushUndoFile(_pos.nFile, true);
}
// update nUndoPos in block index
pindex->nUndoPos = _pos.nPos;
pindex->nStatus |= BLOCK_HAVE_UNDO;
- setDirtyBlockIndex.insert(pindex);
+ m_dirty_blockindex.insert(pindex);
}
return true;
@@ -805,7 +813,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
}
block.resize(blk_size); // Zeroing of memory is intentional here
- filein.read((char*)block.data(), blk_size);
+ filein.read(MakeWritableByteSpan(block));
} catch (const std::exception& e) {
return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
}
@@ -813,19 +821,8 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
return true;
}
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
-{
- FlatFilePos block_pos;
- {
- LOCK(cs_main);
- block_pos = pindex->GetBlockPos();
- }
-
- return ReadRawBlockFromDisk(block, block_pos, message_start);
-}
-
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp)
+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;
@@ -931,3 +928,4 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
} // End scope of CImportingNow
chainman.ActiveChainstate().LoadMempool(args);
}
+} // namespace node
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index a18203f48d..42e46797d2 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -7,12 +7,15 @@
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <sync.h>
#include <txdb.h>
#include <atomic>
#include <cstdint>
#include <vector>
+extern RecursiveMutex cs_main;
+
class ArgsManager;
class BlockValidationState;
class CBlock;
@@ -29,6 +32,7 @@ namespace Consensus {
struct Params;
}
+namespace node {
static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
@@ -64,8 +68,14 @@ struct CBlockIndexWorkComparator {
class BlockManager
{
friend CChainState;
+ friend ChainstateManager;
private:
+ void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
+ void FlushUndoFile(int block_file, bool finalize = false);
+ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown);
+ bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);
+
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
@@ -75,9 +85,9 @@ private:
* space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
* (which in this case means the blockchain must be re-downloaded.)
*
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Pruning functions are called from FlushStateToDisk when the m_check_for_pruning flag has been set.
* Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning cannot take place until the longest chain is at least a certain length (CChainParams::nPruneAfterHeight).
* Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
* The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
* A db flag records the fact that at least some block files have been pruned.
@@ -86,6 +96,21 @@ private:
*/
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
+ RecursiveMutex cs_LastBlockFile;
+ std::vector<CBlockFileInfo> m_blockfile_info;
+ int m_last_blockfile = 0;
+ /** Global flag to indicate we should check to see if there are
+ * block/undo files that should be deleted. Set on startup
+ * or if we allocate more file space when we're in prune mode
+ */
+ bool m_check_for_pruning = false;
+
+ /** Dirty block index entries. */
+ std::set<CBlockIndex*> m_dirty_blockindex;
+
+ /** Dirty block file entries. */
+ std::set<int> m_dirty_fileinfo;
+
public:
BlockMap m_block_index GUARDED_BY(cs_main);
@@ -97,12 +122,13 @@ public:
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
+ bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/**
* Load the blocktree off disk and into memory. Populate certain metadata
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
- * collections like setDirtyBlockIndex.
+ * collections like m_dirty_blockindex.
*/
bool LoadBlockIndex(
const Consensus::Params& consensus_params,
@@ -120,6 +146,17 @@ public:
CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Get block file info entry for one block file */
+ CBlockFileInfo* GetBlockFileInfo(size_t n);
+
+ bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ 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 */
+ uint64_t CalculateCurrentUsage();
+
//! Returns last CBlockIndex* that is a checkpoint
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -130,7 +167,7 @@ public:
};
//! Check whether the block associated with this index entry is pruned or not.
-bool IsBlockPruned(const CBlockIndex* pblockindex);
+bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
void CleanupBlockRevFiles();
@@ -139,12 +176,6 @@ FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly = false);
/** Translation to a filesystem path */
fs::path GetBlockPosFilename(const FlatFilePos& pos);
-/** Get block file info entry for one block file */
-CBlockFileInfo* GetBlockFileInfo(size_t n);
-
-/** Calculate the amount of disk space the block & undo files currently use */
-uint64_t CalculateCurrentUsage();
-
/**
* Actually unlink the specified files
*/
@@ -154,13 +185,10 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
-bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams);
-
-FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args);
+} // namespace node
#endif // BITCOIN_NODE_BLOCKSTORAGE_H
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index 36254dc714..f168332ee6 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -8,6 +8,7 @@
#include <util/system.h>
#include <validation.h>
+namespace node {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
{
int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
@@ -30,3 +31,4 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
sizes.coins = nTotalCache; // the rest goes to in-memory cache
return sizes;
}
+} // namespace node
diff --git a/src/node/caches.h b/src/node/caches.h
index 200f0b85b8..67388b91fd 100644
--- a/src/node/caches.h
+++ b/src/node/caches.h
@@ -10,6 +10,7 @@
class ArgsManager;
+namespace node {
struct CacheSizes {
int64_t block_tree_db;
int64_t coins_db;
@@ -18,5 +19,6 @@ struct CacheSizes {
int64_t filter_index;
};
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
+} // namespace node
#endif // BITCOIN_NODE_CACHES_H
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index abeebad1a6..e21116dd7e 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -8,6 +8,7 @@
#include <node/blockstorage.h>
#include <validation.h>
+namespace node {
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
ChainstateManager& chainman,
CTxMemPool* mempool,
@@ -141,7 +142,7 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
const CBlockIndex* tip = chainstate->m_chain.Tip();
- if (tip && tip->nTime > get_unix_time_seconds() + 2 * 60 * 60) {
+ if (tip && tip->nTime > get_unix_time_seconds() + MAX_FUTURE_BLOCK_TIME) {
return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE;
}
@@ -156,3 +157,4 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage
return std::nullopt;
}
+} // namespace node
diff --git a/src/node/chainstate.h b/src/node/chainstate.h
index 11278a0991..279f187642 100644
--- a/src/node/chainstate.h
+++ b/src/node/chainstate.h
@@ -10,11 +10,12 @@
#include <optional>
class ChainstateManager;
-namespace Consensus {
- struct Params;
-}
class CTxMemPool;
+namespace Consensus {
+struct Params;
+} // namespace Consensus
+namespace node {
enum class ChainstateLoadingError {
ERROR_LOADING_BLOCK_DB,
ERROR_BAD_GENESIS_BLOCK,
@@ -50,7 +51,7 @@ enum class ChainstateLoadingError {
* this sequence, when we explicitly checked shutdown_requested() at
* arbitrary points, one of those calls returned true". Therefore, a
* return value other than SHUTDOWN_PROBED does not guarantee that
- * shutdown_requested() hasn't been called indirectly.
+ * shutdown hasn't been called indirectly.
* - else
* - Success!
*/
@@ -81,5 +82,6 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage
unsigned int check_blocks,
unsigned int check_level,
std::function<int64_t()> get_unix_time_seconds);
+} // namespace node
#endif // BITCOIN_NODE_CHAINSTATE_H
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index 78ab46f2e8..221854c5f6 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -8,6 +8,7 @@
#include <txmempool.h>
#include <validation.h>
+namespace node {
void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
{
assert(node.mempool);
@@ -22,3 +23,4 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
}
}
}
+} // namespace node
diff --git a/src/node/coin.h b/src/node/coin.h
index 908850e2a5..3d534463e8 100644
--- a/src/node/coin.h
+++ b/src/node/coin.h
@@ -9,6 +9,8 @@
class COutPoint;
class Coin;
+
+namespace node {
struct NodeContext;
/**
@@ -19,6 +21,7 @@ struct NodeContext;
* @param[in] node The node context to use for lookup
* @param[in,out] coins map to fill
*/
-void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins);
+void FindCoins(const node::NodeContext& node, std::map<COutPoint, Coin>& coins);
+} // namespace node
#endif // BITCOIN_NODE_COIN_H
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 68cc65d3ed..eed43a1bc7 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -17,6 +17,7 @@
#include <map>
+namespace node {
// Database-independent metric indicating the UTXO set size
uint64_t GetBogoSize(const CScript& script_pub_key)
{
@@ -181,3 +182,4 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
stats.hashSerialized = out;
}
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
+} // namespace node
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 3b641200ad..aa771b18b0 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -15,9 +15,12 @@
#include <cstdint>
#include <functional>
-class BlockManager;
class CCoinsView;
+namespace node {
+class BlockManager;
+} // namespace node
+namespace node {
enum class CoinStatsHashType {
HASH_SERIALIZED,
MUHASH,
@@ -71,10 +74,11 @@ struct CCoinsStats {
};
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {}, const CBlockIndex* pindex = nullptr);
+bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {}, const CBlockIndex* pindex = nullptr);
uint64_t GetBogoSize(const CScript& script_pub_key);
CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
+} // namespace node
#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 9afadd09a9..893c32f1bc 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -14,5 +14,7 @@
#include <txmempool.h>
#include <validation.h>
+namespace node {
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
+} // namespace node
diff --git a/src/node/context.h b/src/node/context.h
index 8f5c50377d..644c997531 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -26,6 +26,7 @@ class Init;
class WalletLoader;
} // namespace interfaces
+namespace node {
//! NodeContext struct containing references to chain state and connection
//! state.
//!
@@ -62,5 +63,6 @@ struct NodeContext {
NodeContext();
~NodeContext();
};
+} // namespace node
#endif // BITCOIN_NODE_CONTEXT_H
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index a0ee52f8e6..ffad289fa9 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -249,8 +249,8 @@ public:
bool isInitialBlockDownload() override {
return chainman().ActiveChainstate().IsInitialBlockDownload();
}
- bool getReindex() override { return ::fReindex; }
- bool getImporting() override { return ::fImporting; }
+ bool getReindex() override { return node::fReindex; }
+ bool getImporting() override { return node::fImporting; }
void setNetworkActive(bool active) override
{
if (m_context->connman) {
@@ -486,11 +486,6 @@ public:
const CChain& active = Assert(m_node.chainman)->ActiveChain();
return active.GetLocator();
}
- bool checkFinalTx(const CTransaction& tx) override
- {
- LOCK(cs_main);
- return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
- }
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
@@ -649,9 +644,9 @@ public:
bool havePruned() override
{
LOCK(cs_main);
- return ::fHavePruned;
+ return node::fHavePruned;
}
- bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
+ bool isReadyToBroadcast() override { return !node::fImporting && !node::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
return chainman().ActiveChainstate().IsInitialBlockDownload();
}
@@ -729,6 +724,6 @@ public:
} // namespace node
namespace interfaces {
-std::unique_ptr<Node> MakeNode(NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
-std::unique_ptr<Chain> MakeChain(NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
+std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
+std::unique_ptr<Chain> MakeChain(node::NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
} // namespace interfaces
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 8c3e7b7b65..7fe10ecabc 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -26,6 +26,7 @@
#include <algorithm>
#include <utility>
+namespace node {
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
int64_t nOldTime = pblock->nTime;
@@ -464,3 +465,4 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}
+} // namespace node
diff --git a/src/node/miner.h b/src/node/miner.h
index 38b7b4546b..c96da874a7 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -23,6 +23,7 @@ class CScript;
namespace Consensus { struct Params; };
+namespace node {
static const bool DEFAULT_PRINTPRIORITY = false;
struct CBlockTemplate
@@ -206,5 +207,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
+} // namespace node
#endif // BITCOIN_NODE_MINER_H
diff --git a/src/node/minisketchwrapper.cpp b/src/node/minisketchwrapper.cpp
index 572df63463..67e823cb68 100644
--- a/src/node/minisketchwrapper.cpp
+++ b/src/node/minisketchwrapper.cpp
@@ -16,6 +16,7 @@
#include <utility>
#include <vector>
+namespace node {
namespace {
static constexpr uint32_t BITS = 32;
@@ -75,3 +76,4 @@ Minisketch MakeMinisketch32FP(size_t max_elements, uint32_t fpbits)
{
return Minisketch::CreateFP(BITS, Minisketch32Implementation(), max_elements, fpbits);
}
+} // namespace node
diff --git a/src/node/minisketchwrapper.h b/src/node/minisketchwrapper.h
index a8aef68d01..a92912d9ed 100644
--- a/src/node/minisketchwrapper.h
+++ b/src/node/minisketchwrapper.h
@@ -10,9 +10,11 @@
#include <cstddef>
#include <cstdint>
+namespace node {
/** Wrapper around Minisketch::Minisketch(32, implementation, capacity). */
Minisketch MakeMinisketch32(size_t capacity);
/** Wrapper around Minisketch::CreateFP. */
Minisketch MakeMinisketch32FP(size_t max_elements, uint32_t fpbits);
+} // namespace node
#endif // BITCOIN_NODE_MINISKETCHWRAPPER_H
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index 26023d5a4c..5a932f435d 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -12,6 +12,7 @@
#include <numeric>
+namespace node {
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
{
// Go through each input and build status
@@ -147,3 +148,4 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
return result;
}
+} // namespace node
diff --git a/src/node/psbt.h b/src/node/psbt.h
index 43fe293ad0..cbb3bd8201 100644
--- a/src/node/psbt.h
+++ b/src/node/psbt.h
@@ -9,6 +9,7 @@
#include <optional>
+namespace node {
/**
* Holds an analysis of one input from a PSBT
*/
@@ -52,5 +53,6 @@ struct PSBTAnalysis {
* @return A PSBTAnalysis with information about the provided PSBT.
*/
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
+} // namespace node
#endif // BITCOIN_NODE_PSBT_H
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index d5681a0663..c7c8493f0c 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -16,6 +16,7 @@
#include <future>
+namespace node {
static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out)
{
err_string_out = state.ToString();
@@ -153,3 +154,4 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
}
return nullptr;
}
+} // namespace node
diff --git a/src/node/transaction.h b/src/node/transaction.h
index 79f02958da..b7cf225636 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -12,11 +12,13 @@
class CBlockIndex;
class CTxMemPool;
-struct NodeContext;
namespace Consensus {
struct Params;
}
+namespace node {
+struct NodeContext;
+
/** Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls.
* Also used by the GUI when broadcasting a completed PSBT.
* By default, a transaction with a fee rate higher than this will be rejected
@@ -57,5 +59,6 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
* @returns The tx if found, otherwise nullptr
*/
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock);
+} // namespace node
#endif // BITCOIN_NODE_TRANSACTION_H
diff --git a/src/node/ui_interface.h b/src/node/ui_interface.h
index 5c7c3e7074..d02238b549 100644
--- a/src/node/ui_interface.h
+++ b/src/node/ui_interface.h
@@ -25,8 +25,7 @@ class CClientUIInterface
{
public:
/** Flags for CClientUIInterface::ThreadSafeMessageBox */
- enum MessageBoxFlags
- {
+ enum MessageBoxFlags : uint32_t {
ICON_INFORMATION = 0,
ICON_WARNING = (1U << 0),
ICON_ERROR = (1U << 1),
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
index 9b0df96a75..401d4baaeb 100644
--- a/src/node/utxo_snapshot.h
+++ b/src/node/utxo_snapshot.h
@@ -9,6 +9,7 @@
#include <uint256.h>
#include <serialize.h>
+namespace node {
//! Metadata describing a serialized version of a UTXO set from which an
//! assumeutxo CChainState can be constructed.
class SnapshotMetadata
@@ -32,5 +33,6 @@ public:
SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); }
};
+} // namespace node
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
diff --git a/src/policy/packages.h b/src/policy/packages.h
index d2744f1265..9f274f6b7d 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -25,6 +25,7 @@ enum class PackageValidationResult {
PCKG_RESULT_UNSET = 0, //!< Initial value. The package has not yet been rejected.
PCKG_POLICY, //!< The package itself is invalid (e.g. too many transactions).
PCKG_TX, //!< At least one tx is invalid.
+ PCKG_MEMPOOL_ERROR, //!< Mempool logic error.
};
/** A package is an ordered list of transactions. The transactions cannot conflict with (spend the
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 67ea4a3747..1fcbc45c72 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -70,25 +70,45 @@ public:
uint32_t nSequence;
CScriptWitness scriptWitness; //!< Only serialized through CTransaction
- /* Setting nSequence to this value for every input in a transaction
- * disables nLockTime. */
+ /**
+ * Setting nSequence to this value for every input in a transaction
+ * disables nLockTime/IsFinalTx().
+ * It fails OP_CHECKLOCKTIMEVERIFY/CheckLockTime() for any input that has
+ * it set (BIP 65).
+ * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112).
+ */
static const uint32_t SEQUENCE_FINAL = 0xffffffff;
+ /**
+ * This is the maximum sequence number that enables both nLockTime and
+ * OP_CHECKLOCKTIMEVERIFY (BIP 65).
+ * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112).
+ */
+ static const uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1};
- /* Below flags apply in the context of BIP 68*/
- /* If this flag set, CTxIn::nSequence is NOT interpreted as a
- * relative lock-time. */
+ // Below flags apply in the context of BIP 68. BIP 68 requires the tx
+ // version to be set to 2, or higher.
+ /**
+ * If this flag is set, CTxIn::nSequence is NOT interpreted as a
+ * relative lock-time.
+ * It skips SequenceLocks() for any input that has it set (BIP 68).
+ * It fails OP_CHECKSEQUENCEVERIFY/CheckSequence() for any input that has
+ * it set (BIP 112).
+ */
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31);
- /* If CTxIn::nSequence encodes a relative lock-time and this flag
+ /**
+ * If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1. */
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
- /* If CTxIn::nSequence encodes a relative lock-time, this mask is
+ /**
+ * If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field. */
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
- /* In order to use the same number of bits to encode roughly the
+ /**
+ * In order to use the same number of bits to encode roughly the
* same wall-clock duration, and because blocks are naturally
* limited to occur every 600s on average, the minimum granularity
* for time-based relative lock-time is fixed at 512 seconds.
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 8248609ba6..c8c73e130b 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -399,7 +399,7 @@ bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base6
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
{
- CDataStream ss_data(MakeUCharSpan(tx_data), SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ss_data(MakeByteSpan(tx_data), SER_NETWORK, PROTOCOL_VERSION);
try {
ss_data >> psbt;
if (!ss_data.empty()) {
diff --git a/src/pubkey.h b/src/pubkey.h
index 349081205b..dfe06f834c 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -142,14 +142,14 @@ public:
{
unsigned int len = size();
::WriteCompactSize(s, len);
- s.write((char*)vch, len);
+ s.write(AsBytes(Span{vch, len}));
}
template <typename Stream>
void Unserialize(Stream& s)
{
const unsigned int len(::ReadCompactSize(s));
if (len <= SIZE) {
- s.read((char*)vch, len);
+ s.read(AsWritableBytes(Span{vch, len}));
if (len != size()) {
Invalidate();
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 3002e0fe88..6a2781079c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -41,6 +41,7 @@
#endif // ENABLE_WALLET
#include <boost/signals2/connection.hpp>
+#include <chrono>
#include <memory>
#include <QApplication>
@@ -76,6 +77,8 @@ Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(SynchronizationState)
Q_DECLARE_METATYPE(uint256)
+using node::NodeContext;
+
static void RegisterMetaTypes()
{
// Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
@@ -410,10 +413,10 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) {
window->message(title, message, style);
});
- QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady);
+ QTimer::singleShot(100ms, paymentServer, &PaymentServer::uiReady);
}
#endif
- pollShutdownTimer->start(200);
+ pollShutdownTimer->start(SHUTDOWN_POLLING_DELAY);
} else {
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
requestShutdown();
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index d3519edf7f..a28329082a 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -17,6 +17,7 @@
#include <netbase.h>
#include <util/system.h>
#include <util/threadnames.h>
+#include <util/time.h>
#include <validation.h>
#include <stdint.h>
@@ -221,12 +222,12 @@ QString ClientModel::formatClientStartupTime() const
QString ClientModel::dataDir() const
{
- return GUIUtil::boostPathToQString(gArgs.GetDataDirNet());
+ return GUIUtil::PathToQString(gArgs.GetDataDirNet());
}
QString ClientModel::blocksDir() const
{
- return GUIUtil::boostPathToQString(gArgs.GetBlocksDirPath());
+ return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
}
void ClientModel::updateBanlist()
@@ -288,7 +289,7 @@ static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_
const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
const int64_t now = throttle ? GetTimeMillis() : 0;
int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
- if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
+ if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
return;
}
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 71d1ca0a33..d7a2aaaf19 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -31,6 +31,9 @@
#include <QSettings>
#include <QTreeWidget>
+using wallet::CCoinControl;
+using wallet::MIN_CHANGE;
+
QList<CAmount> CoinControlDialog::payAmounts;
bool CoinControlDialog::fSubtractFeeFromAmount = false;
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 92fae45110..ccceff10e8 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -19,7 +19,9 @@
class PlatformStyle;
class WalletModel;
+namespace wallet {
class CCoinControl;
+} // namespace wallet
namespace Ui {
class CoinControlDialog;
@@ -42,11 +44,11 @@ class CoinControlDialog : public QDialog
Q_OBJECT
public:
- explicit CoinControlDialog(CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr);
+ explicit CoinControlDialog(wallet::CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr);
~CoinControlDialog();
// static because also called from sendcoinsdialog
- static void updateLabels(CCoinControl& m_coin_control, WalletModel*, QDialog*);
+ static void updateLabels(wallet::CCoinControl& m_coin_control, WalletModel*, QDialog*);
static QList<CAmount> payAmounts;
static bool fSubtractFeeFromAmount;
@@ -56,7 +58,7 @@ protected:
private:
Ui::CoinControlDialog *ui;
- CCoinControl& m_coin_control;
+ wallet::CCoinControl& m_coin_control;
WalletModel *model;
int sortColumn;
Qt::SortOrder sortOrder;
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 15e0d3fad9..2196801023 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1355,10 +1355,10 @@
<item row="13" column="0">
<widget class="QLabel" name="peerLastTxLabel">
<property name="toolTip">
- <string>Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
+ <string extracomment="Tooltip text for the Last Transaction field in the peer details area.">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
</property>
<property name="text">
- <string>Last Tx</string>
+ <string>Last Transaction</string>
</property>
</widget>
</item>
@@ -1592,6 +1592,84 @@
</widget>
</item>
<item row="23" column="0">
+ <widget class="QLabel" name="peerAddrRelayEnabledLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Address Relay field in the peer details area.">Whether we relay addresses to this peer.</string>
+ </property>
+ <property name="text">
+ <string>Address Relay</string>
+ </property>
+ </widget>
+ </item>
+ <item row="23" column="1">
+ <widget class="QLabel" name="peerAddrRelayEnabled">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="24" column="0">
+ <widget class="QLabel" name="peerAddrProcessedLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Addresses Processed field in the peer details area.">Total number of addresses processed, excluding those dropped due to rate-limiting.</string>
+ </property>
+ <property name="text">
+ <string>Addresses Processed</string>
+ </property>
+ </widget>
+ </item>
+ <item row="24" column="1">
+ <widget class="QLabel" name="peerAddrProcessed">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="25" column="0">
+ <widget class="QLabel" name="peerAddrRateLimitedLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Addresses Rate-Limited field in the peer details area.">Total number of addresses dropped due to rate-limiting.</string>
+ </property>
+ <property name="text">
+ <string>Addresses Rate-Limited</string>
+ </property>
+ </widget>
+ </item>
+ <item row="25" column="1">
+ <widget class="QLabel" name="peerAddrRateLimited">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="26" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 6b3a4630a3..5438811aff 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -252,6 +252,16 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="m_enable_psbt_controls">
+ <property name="text">
+ <string extracomment="An options window setting to enable PSBT controls.">Enable &amp;PSBT controls</string>
+ </property>
+ <property name="toolTip">
+ <string extracomment="Tooltip text for options window setting that enables PSBT controls.">Whether to show PSBT controls.</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 882d2c8f52..fcdf6056c9 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -5,10 +5,16 @@
#ifndef BITCOIN_QT_GUICONSTANTS_H
#define BITCOIN_QT_GUICONSTANTS_H
+#include <chrono>
#include <cstdint>
-/* Milliseconds between model updates */
-static const int MODEL_UPDATE_DELAY = 250;
+using namespace std::chrono_literals;
+
+/* A delay between model updates */
+static constexpr auto MODEL_UPDATE_DELAY{250ms};
+
+/* A delay between shutdown pollings */
+static constexpr auto SHUTDOWN_POLLING_DELAY{200ms};
/* AskPassphraseDialog -- Maximum passphrase length */
static const int MAX_PASSPHRASE_SIZE = 1024;
@@ -27,8 +33,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_NEGATIVE QColor(255, 0, 0)
/* Transaction list -- bare address (without label) */
#define COLOR_BAREADDRESS QColor(140, 140, 140)
-/* Transaction list -- TX status decoration - open until date */
-#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
/* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index f9f0ea81ba..dc73bcd911 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -12,6 +12,7 @@
#include <base58.h>
#include <chainparams.h>
+#include <fs.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <policy/policy.h>
@@ -66,6 +67,10 @@
#include <cassert>
#include <chrono>
+#include <exception>
+#include <fstream>
+#include <string>
+#include <vector>
#if defined(Q_OS_MAC)
@@ -281,7 +286,7 @@ void LoadFont(const QString& file_name)
QString getDefaultDataDirectory()
{
- return boostPathToQString(GetDefaultDataDir());
+ return PathToQString(GetDefaultDataDir());
}
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
@@ -418,7 +423,7 @@ void openDebugLogfile()
/* Open debug.log with the associated application */
if (fs::exists(pathDebug))
- QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
+ QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathDebug)));
}
bool openBitcoinConf()
@@ -426,7 +431,7 @@ bool openBitcoinConf()
fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
/* Create the file */
- fsbridge::ofstream configFile(pathConfig, std::ios_base::app);
+ std::ofstream configFile{pathConfig, std::ios_base::app};
if (!configFile.good())
return false;
@@ -434,11 +439,11 @@ bool openBitcoinConf()
configFile.close();
/* Open bitcoin.conf with the associated application */
- bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+ bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathConfig)));
#ifdef Q_OS_MAC
// Workaround for macOS-specific behavior; see #15409.
if (!res) {
- res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
+ res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", PathToQString(pathConfig)});
}
#endif
@@ -586,7 +591,7 @@ fs::path static GetAutostartFilePath()
bool GetStartOnSystemStartup()
{
- fsbridge::ifstream optionFile(GetAutostartFilePath());
+ std::ifstream optionFile{GetAutostartFilePath()};
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
@@ -617,7 +622,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
fs::create_directories(GetAutostartDir());
- fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
+ std::ofstream optionFile{GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc};
if (!optionFile.good())
return false;
std::string chain = gArgs.GetChainName();
@@ -652,12 +657,12 @@ void setClipboard(const QString& str)
}
}
-fs::path qstringToBoostPath(const QString &path)
+fs::path QStringToPath(const QString &path)
{
return fs::u8path(path.toStdString());
}
-QString boostPathToQString(const fs::path &path)
+QString PathToQString(const fs::path &path)
{
return QString::fromStdString(path.u8string());
}
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index aaed0ea690..9b25b77325 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -209,10 +209,10 @@ namespace GUIUtil
bool SetStartOnSystemStartup(bool fAutoStart);
/** Convert QString to OS specific boost path through UTF-8 */
- fs::path qstringToBoostPath(const QString &path);
+ fs::path QStringToPath(const QString &path);
/** Convert OS specific boost path to QString through UTF-8 */
- QString boostPathToQString(const fs::path &path);
+ QString PathToQString(const fs::path &path);
/** Convert enum Network to QString */
QString NetworkToQString(Network net);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index c2d6104658..e9a4034e62 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -67,7 +67,7 @@ FreespaceChecker::FreespaceChecker(Intro *_intro)
void FreespaceChecker::check()
{
QString dataDirStr = intro->getPathToCheck();
- fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
+ fs::path dataDir = GUIUtil::QStringToPath(dataDirStr);
uint64_t freeBytesAvailable = 0;
int replyStatus = ST_OK;
QString replyMessage = tr("A new data directory will be created.");
@@ -216,7 +216,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
- if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
+ if(!fs::exists(GUIUtil::QStringToPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
{
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
@@ -240,9 +240,9 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
}
dataDir = intro.getDataDirectory();
try {
- if (TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir))) {
+ if (TryCreateDirectories(GUIUtil::QStringToPath(dataDir))) {
// If a new data directory has been created, make wallets subdirectory too
- TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir) / "wallets");
+ TryCreateDirectories(GUIUtil::QStringToPath(dataDir) / "wallets");
}
break;
} catch (const fs::filesystem_error&) {
@@ -263,7 +263,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
* (to be consistent with bitcoind behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
- gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::qstringToBoostPath(dataDir))); // use OS locale for path setting
+ gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(dataDir))); // use OS locale for path setting
}
return true;
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index a851f99a3a..c05571677c 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -19,6 +19,8 @@
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
+#include <chrono>
+
#include <QDataWidgetMapper>
#include <QDir>
#include <QIntValidator>
@@ -242,6 +244,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
mapper->addMapping(ui->subFeeFromAmount, OptionsModel::SubFeeFromAmount);
mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath);
+ mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls);
/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
@@ -361,7 +364,7 @@ void OptionsDialog::showRestartWarning(bool fPersistent)
ui->statusLabel->setText(tr("This change would require a client restart."));
// clear non-persistent status label after 10 seconds
// Todo: should perhaps be a class attribute, if we extend the use of statusLabel
- QTimer::singleShot(10000, this, &OptionsDialog::clearStatusLabel);
+ QTimer::singleShot(10s, this, &OptionsDialog::clearStatusLabel);
}
}
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index fe40c9ef3c..5d9ed5bf23 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -83,6 +83,11 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("fCoinControlFeatures", false);
fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
+ if (!settings.contains("enable_psbt_controls")) {
+ settings.setValue("enable_psbt_controls", false);
+ }
+ m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool();
+
// These are shared with the core or have a command-line parameter
// and we want command-line parameters to overwrite the GUI settings.
//
@@ -204,8 +209,8 @@ static void CopySettings(QSettings& dst, const QSettings& src)
/** Back up a QSettings to an ini-formatted file. */
static void BackupSettings(const fs::path& filename, const QSettings& src)
{
- qInfo() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
- QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
+ qInfo() << "Backing up GUI settings to" << GUIUtil::PathToQString(filename);
+ QSettings dst(GUIUtil::PathToQString(filename), QSettings::IniFormat);
dst.clear();
CopySettings(dst, src);
}
@@ -360,6 +365,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return m_use_embedded_monospaced_font;
case CoinControlFeatures:
return fCoinControlFeatures;
+ case EnablePSBTControls:
+ return settings.value("enable_psbt_controls");
case Prune:
return settings.value("bPrune");
case PruneSize:
@@ -507,6 +514,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
break;
+ case EnablePSBTControls:
+ m_enable_psbt_controls = value.toBool();
+ settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
+ break;
case Prune:
if (settings.value("bPrune") != value) {
settings.setValue("bPrune", value);
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 059fe2381f..bb9a8c1f8c 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -69,6 +69,7 @@ public:
SpendZeroConfChange, // bool
Listen, // bool
Server, // bool
+ EnablePSBTControls, // bool
OptionIDRowCount,
};
@@ -90,6 +91,7 @@ public:
bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; }
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; }
+ bool getEnablePSBTControls() const { return m_enable_psbt_controls; }
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
/* Explicit setters */
@@ -115,6 +117,7 @@ private:
bool m_use_embedded_monospaced_font;
bool fCoinControlFeatures;
bool m_sub_fee_from_amount;
+ bool m_enable_psbt_controls;
/* settings that were overridden by command-line */
QString strOverriddenByCommandLine;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 0cf311a360..cb09035b45 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -51,7 +51,7 @@ static QString ipcServerName()
// Append a simple hash of the datadir
// Note that gArgs.GetDataDirNet() returns a different path
// for -testnet versus main net
- QString ddir(GUIUtil::boostPathToQString(gArgs.GetDataDirNet()));
+ QString ddir(GUIUtil::PathToQString(gArgs.GetDataDirNet()));
name.append(QString::number(qHash(ddir)));
return name;
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 800c493a05..6880c157c0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -5,6 +5,7 @@
#include <qt/psbtoperationsdialog.h>
#include <core_io.h>
+#include <fs.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <node/psbt.h>
@@ -15,8 +16,13 @@
#include <qt/optionsmodel.h>
#include <util/strencodings.h>
+#include <fstream>
#include <iostream>
+#include <string>
+using node::AnalyzePSBT;
+using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
+using node::PSBTAnalysis;
PSBTOperationsDialog::PSBTOperationsDialog(
QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent, GUIUtil::dialog_flags),
@@ -155,7 +161,7 @@ void PSBTOperationsDialog::saveTransaction() {
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
+ std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
out << ssTx.str();
out.close();
showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 1cadfaeeb9..08729a7722 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1215,6 +1215,9 @@ void RPCConsole::updateDetailWidget()
}
ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait));
+ ui->peerAddrRelayEnabled->setText(stats->nodeStateStats.m_addr_relay_enabled ? ts.yes : ts.no);
+ ui->peerAddrProcessed->setText(QString::number(stats->nodeStateStats.m_addr_processed));
+ ui->peerAddrRateLimited->setText(QString::number(stats->nodeStateStats.m_addr_rate_limited));
}
ui->peersTabRightPanel->show();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index d76f9fade0..e37168830e 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -24,17 +24,24 @@
#include <node/ui_interface.h>
#include <policy/fees.h>
#include <txmempool.h>
+#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
#include <wallet/wallet.h>
-#include <validation.h>
+#include <array>
+#include <chrono>
+#include <fstream>
+#include <memory>
#include <QFontMetrics>
#include <QScrollBar>
#include <QSettings>
#include <QTextDocument>
+using wallet::CCoinControl;
+using wallet::DEFAULT_PAY_TX_FEE;
+
static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
int getConfTargetForIndex(int index) {
if (index+1 > static_cast<int>(confTargets.size())) {
@@ -324,16 +331,22 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
formatted.append(recipientElement);
}
- if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
- question_string.append(tr("Do you want to draft this transaction?"));
- } else {
- question_string.append(tr("Are you sure you want to send?"));
- }
-
+ /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
+ that the displayed transaction details represent the transaction the user intends to create. */
+ question_string.append(tr("Do you want to create this transaction?"));
question_string.append("<br /><span style='font-size:10pt;'>");
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
+ /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
+ a user can only create a PSBT. This string is displayed when private keys are disabled and an external
+ signer is not available. */
question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
+ } else if (model->getOptionsModel()->getEnablePSBTControls()) {
+ /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
+ a user can send their transaction or create a PSBT. This string is displayed when both private keys
+ and PSBT controls are enabled. */
+ question_string.append(tr("Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
} else {
+ /*: Text to prompt a user to review the details of the transaction they are attempting to send. */
question_string.append(tr("Please, review your transaction."));
}
question_string.append("</span>%1");
@@ -397,21 +410,20 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
assert(m_current_transaction);
- const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
- const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
- auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
+ const QString confirmation = tr("Confirm send coins");
+ auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this);
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
// TODO: Replace QDialog::exec() with safer QDialog::show().
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
- if(retval != QMessageBox::Yes)
+ if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
{
fNewRecipientAllowed = true;
return;
}
bool send_failure = false;
- if (model->wallet().privateKeysDisabled()) {
+ if (retval == QMessageBox::Save) {
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
@@ -500,7 +512,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
+ std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
out << ssTx.str();
out.close();
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
@@ -512,6 +524,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
assert(false);
} // msgBox.exec()
} else {
+ assert(!model->wallet().privateKeysDisabled());
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
// process sendStatus and on error generate message shown to user
@@ -1031,8 +1044,8 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
- : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
+SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, bool enable_send, bool always_show_unsigned, QWidget* parent)
+ : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
{
setIcon(QMessageBox::Question);
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
@@ -1040,26 +1053,28 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri
setInformativeText(informative_text);
setDetailedText(detailed_text);
setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
+ if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
if (confirmButtonText.isEmpty()) {
confirmButtonText = yesButton->text();
}
- updateYesButton();
+ m_psbt_button = button(QMessageBox::Save);
+ updateButtons();
connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
}
int SendConfirmationDialog::exec()
{
- updateYesButton();
- countDownTimer.start(1000);
+ updateButtons();
+ countDownTimer.start(1s);
return QMessageBox::exec();
}
void SendConfirmationDialog::countDown()
{
secDelay--;
- updateYesButton();
+ updateButtons();
if(secDelay <= 0)
{
@@ -1067,16 +1082,24 @@ void SendConfirmationDialog::countDown()
}
}
-void SendConfirmationDialog::updateYesButton()
+void SendConfirmationDialog::updateButtons()
{
if(secDelay > 0)
{
yesButton->setEnabled(false);
- yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
+ yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
+ if (m_psbt_button) {
+ m_psbt_button->setEnabled(false);
+ m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
+ }
}
else
{
- yesButton->setEnabled(true);
+ yesButton->setEnabled(m_enable_send);
yesButton->setText(confirmButtonText);
+ if (m_psbt_button) {
+ m_psbt_button->setEnabled(true);
+ m_psbt_button->setText(m_psbt_button_text);
+ }
}
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 4e43697f78..4a16702756 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -12,12 +12,14 @@
#include <QString>
#include <QTimer>
-class CCoinControl;
class ClientModel;
class PlatformStyle;
class SendCoinsEntry;
class SendCoinsRecipient;
enum class SynchronizationState;
+namespace wallet {
+class CCoinControl;
+} // namespace wallet
namespace Ui {
class SendCoinsDialog;
@@ -62,7 +64,7 @@ private:
Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
WalletModel *model;
- std::unique_ptr<CCoinControl> m_coin_control;
+ std::unique_ptr<wallet::CCoinControl> m_coin_control;
std::unique_ptr<WalletModelTransaction> m_current_transaction;
bool fNewRecipientAllowed;
bool fFeeMinimized;
@@ -114,18 +116,21 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
- SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "", QWidget* parent = nullptr);
+ SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, bool enable_send = true, bool always_show_unsigned = true, QWidget* parent = nullptr);
int exec() override;
private Q_SLOTS:
void countDown();
- void updateYesButton();
+ void updateButtons();
private:
QAbstractButton *yesButton;
+ QAbstractButton *m_psbt_button;
QTimer countDownTimer;
int secDelay;
- QString confirmButtonText;
+ QString confirmButtonText{tr("Send")};
+ bool m_enable_send;
+ QString m_psbt_button_text{tr("Create Unsigned")};
};
#endif // BITCOIN_QT_SENDCOINSDIALOG_H
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 9eabecbfc9..66637a5dcf 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -20,10 +20,19 @@
#include <wallet/wallet.h>
#include <walletinitinterface.h>
+#include <chrono>
+
#include <QApplication>
#include <QTimer>
#include <QMessageBox>
+using wallet::AddWallet;
+using wallet::CWallet;
+using wallet::CreateMockWalletDatabase;
+using wallet::RemoveWallet;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+using wallet::WalletContext;
+
namespace
{
@@ -40,7 +49,7 @@ void EditAddressAndSubmit(
dialog->findChild<QLineEdit*>("labelEdit")->setText(label);
dialog->findChild<QValidatedLineEdit*>("addressEdit")->setText(address);
- ConfirmMessage(&warning_text, 5);
+ ConfirmMessage(&warning_text, 5ms);
dialog->accept();
QCOMPARE(warning_text, expected_msg);
}
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 5f7b5e429d..10b7e2ffe7 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -22,6 +22,7 @@
#include <QApplication>
#include <QObject>
#include <QTest>
+#include <functional>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -39,8 +40,12 @@ Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin)
#endif
#endif
+using node::NodeContext;
+
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
+const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS{};
+
// This is all you need to run all the tests
int main(int argc, char* argv[])
{
diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp
index 987d921f03..635dbcd1c5 100644
--- a/src/qt/test/util.cpp
+++ b/src/qt/test/util.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chrono>
+
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
@@ -9,7 +11,7 @@
#include <QTimer>
#include <QWidget>
-void ConfirmMessage(QString* text, int msec)
+void ConfirmMessage(QString* text, std::chrono::milliseconds msec)
{
QTimer::singleShot(msec, [text]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
diff --git a/src/qt/test/util.h b/src/qt/test/util.h
index df5931a032..f50a6b6c61 100644
--- a/src/qt/test/util.h
+++ b/src/qt/test/util.h
@@ -5,7 +5,11 @@
#ifndef BITCOIN_QT_TEST_UTIL_H
#define BITCOIN_QT_TEST_UTIL_H
-#include <QString>
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
/**
* Press "Ok" button in message box dialog.
@@ -13,6 +17,6 @@
* @param text - Optionally store dialog text.
* @param msec - Number of milliseconds to pause before triggering the callback.
*/
-void ConfirmMessage(QString* text = nullptr, int msec = 0);
+void ConfirmMessage(QString* text, std::chrono::milliseconds msec);
#endif // BITCOIN_QT_TEST_UTIL_H
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 147a8a076b..6ab534764b 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -26,6 +26,7 @@
#include <qt/recentrequeststablemodel.h>
#include <qt/receiverequestdialog.h>
+#include <chrono>
#include <memory>
#include <QAbstractButton>
@@ -39,6 +40,15 @@
#include <QListView>
#include <QDialogButtonBox>
+using wallet::AddWallet;
+using wallet::CWallet;
+using wallet::CreateMockWalletDatabase;
+using wallet::RemoveWallet;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+using wallet::WalletContext;
+using wallet::WalletDescriptor;
+using wallet::WalletRescanReserver;
+
namespace
{
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
@@ -112,7 +122,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
if (expectError.empty()) {
ConfirmSend(&text, cancel);
} else {
- ConfirmMessage(&text);
+ ConfirmMessage(&text, 0ms);
}
action->trigger();
QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 260e9eeb33..be5851d627 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -18,7 +18,6 @@
#include <interfaces/wallet.h>
#include <key_io.h>
#include <policy/policy.h>
-#include <script/script.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/ismine.h>
@@ -28,16 +27,13 @@
#include <QLatin1String>
+using wallet::ISMINE_ALL;
+using wallet::ISMINE_SPENDABLE;
+using wallet::ISMINE_WATCH_ONLY;
+using wallet::isminetype;
+
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
{
- if (!status.is_final)
- {
- if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks);
- else
- return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
- }
- else
{
int nDepth = status.depth_in_main_chain;
if (nDepth < 0) {
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 44714de146..26144ba197 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -13,6 +13,10 @@
#include <QDateTime>
+using wallet::ISMINE_SPENDABLE;
+using wallet::ISMINE_WATCH_ONLY;
+using wallet::isminetype;
+
/* Return positive answer if transaction should be shown in list.
*/
bool TransactionRecord::showTransaction()
@@ -175,21 +179,8 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
status.depth = wtx.depth_in_main_chain;
status.m_cur_block_hash = block_hash;
- const bool up_to_date = ((int64_t)QDateTime::currentMSecsSinceEpoch() / 1000 - block_time < MAX_BLOCK_TIME_GAP);
- if (up_to_date && !wtx.is_final) {
- if (wtx.lock_time < LOCKTIME_THRESHOLD) {
- status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = wtx.lock_time - numBlocks;
- }
- else
- {
- status.status = TransactionStatus::OpenUntilDate;
- status.open_for = wtx.lock_time;
- }
- }
// For generated transactions, determine maturity
- else if(type == TransactionRecord::Generated)
- {
+ if (type == TransactionRecord::Generated) {
if (wtx.blocks_to_maturity > 0)
{
status.status = TransactionStatus::Immature;
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 1c139efabc..dd34656d5f 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -30,8 +30,6 @@ public:
enum Status {
Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/
/// Normal (sent/received) transactions
- OpenUntilDate, /**< Transaction not yet final, waiting for date */
- OpenUntilBlock, /**< Transaction not yet final, waiting for block */
Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index b42c3f8c24..44b4fee2e7 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -316,12 +316,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
switch(wtx->status.status)
{
- case TransactionStatus::OpenUntilBlock:
- status = tr("Open for %n more block(s)","",wtx->status.open_for);
- break;
- case TransactionStatus::OpenUntilDate:
- status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
- break;
case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed");
break;
@@ -475,9 +469,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
{
switch(wtx->status.status)
{
- case TransactionStatus::OpenUntilBlock:
- case TransactionStatus::OpenUntilDate:
- return COLOR_TX_STATUS_OPENUNTILDATE;
case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned:
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 6f6e40fcf7..1ab1333b72 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -19,6 +19,7 @@
#include <node/ui_interface.h>
+#include <chrono>
#include <optional>
#include <QApplication>
@@ -114,8 +115,8 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
amountWidget->setValidator(amountValidator);
hlayout->addWidget(amountWidget);
- // Delay before filtering transactions in ms
- static const int input_filter_delay = 200;
+ // Delay before filtering transactions
+ static constexpr auto input_filter_delay{200ms};
QTimer* amount_typing_delay = new QTimer(this);
amount_typing_delay->setSingleShot(true);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index fa561e05db..b025bb367c 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -20,6 +20,7 @@
#include <wallet/wallet.h>
#include <algorithm>
+#include <chrono>
#include <QApplication>
#include <QMessageBox>
@@ -28,6 +29,11 @@
#include <QTimer>
#include <QWindow>
+using wallet::WALLET_FLAG_BLANK_WALLET;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;
+using wallet::WALLET_FLAG_EXTERNAL_SIGNER;
+
WalletController::WalletController(ClientModel& client_model, const PlatformStyle* platform_style, QObject* parent)
: QObject(parent)
, m_activity_thread(new QThread(this))
@@ -254,12 +260,12 @@ void CreateWalletActivity::createWallet()
flags |= WALLET_FLAG_EXTERNAL_SIGNER;
}
- QTimer::singleShot(500, worker(), [this, name, flags] {
+ QTimer::singleShot(500ms, worker(), [this, name, flags] {
std::unique_ptr<interfaces::Wallet> wallet = node().walletLoader().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
- QTimer::singleShot(500, this, &CreateWalletActivity::finish);
+ QTimer::singleShot(500ms, this, &CreateWalletActivity::finish);
});
}
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 98f5ebce99..08190f0b9f 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -4,6 +4,7 @@
#include <qt/walletframe.h>
+#include <fs.h>
#include <node/ui_interface.h>
#include <psbt.h>
#include <qt/guiutil.h>
@@ -14,6 +15,8 @@
#include <util/system.h>
#include <cassert>
+#include <fstream>
+#include <string>
#include <QApplication>
#include <QClipboard>
@@ -210,7 +213,7 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
return;
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
data = std::string(std::istreambuf_iterator<char>{in}, {});
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index cfc255c843..5ee32e79d5 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -36,6 +36,9 @@
#include <QSet>
#include <QTimer>
+using wallet::CCoinControl;
+using wallet::CRecipient;
+using wallet::DEFAULT_DISABLE_WALLET;
WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel& client_model, const PlatformStyle *platformStyle, QObject *parent) :
QObject(parent),
@@ -480,10 +483,9 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return false;
}
- const bool create_psbt = m_wallet->privateKeysDisabled();
-
// allow a user based fee verification
- QString questionString = create_psbt ? tr("Do you want to draft a transaction with fee increase?") : tr("Do you want to increase the fee?");
+ /*: Asks a user if they would like to manually increase the fee of a transaction that has already been created. */
+ QString questionString = tr("Do you want to increase the fee?");
questionString.append("<br />");
questionString.append("<table style=\"text-align: left;\">");
questionString.append("<tr><td>");
@@ -506,13 +508,13 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy."));
}
- auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString);
+ auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString, "", "", SEND_CONFIRM_DELAY, !m_wallet->privateKeysDisabled(), getOptionsModel()->getEnablePSBTControls(), nullptr);
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
// TODO: Replace QDialog::exec() with safer QDialog::show().
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
// cancel sign&broadcast if user doesn't want to bump the fee
- if (retval != QMessageBox::Yes) {
+ if (retval != QMessageBox::Yes && retval != QMessageBox::Save) {
return false;
}
@@ -523,7 +525,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
}
// Short-circuit if we are returning a bumped transaction PSBT to clipboard
- if (create_psbt) {
+ if (retval == QMessageBox::Save) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
@@ -539,6 +541,8 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return true;
}
+ assert(!m_wallet->privateKeysDisabled());
+
// sign bumped transaction
if (!m_wallet->signBumpTransaction(mtx)) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction."));
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 3097a4f78f..ad1239ccdc 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -32,16 +32,17 @@ class SendCoinsRecipient;
class TransactionTableModel;
class WalletModelTransaction;
-class CCoinControl;
class CKeyID;
class COutPoint;
-class COutput;
class CPubKey;
class uint256;
namespace interfaces {
class Node;
} // namespace interfaces
+namespace wallet {
+class CCoinControl;
+} // namespace wallet
QT_BEGIN_NAMESPACE
class QTimer;
@@ -99,7 +100,7 @@ public:
};
// prepare transaction for getting txfee before sending coins
- SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl);
+ SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl& coinControl);
// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
diff --git a/src/random.cpp b/src/random.cpp
index 6eb06c5d47..5dae80fe31 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -19,6 +19,7 @@
#include <sync.h> // for Mutex
#include <util/time.h> // for GetTimeMicros()
+#include <cmath>
#include <stdlib.h>
#include <thread>
@@ -714,3 +715,9 @@ void RandomInit()
ReportHardwareRand();
}
+
+std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval)
+{
+ double unscaled = -std::log1p(GetRand(uint64_t{1} << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
+ return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
+}
diff --git a/src/random.h b/src/random.h
index 0c6dc24983..97302d61ab 100644
--- a/src/random.h
+++ b/src/random.h
@@ -10,7 +10,7 @@
#include <crypto/common.h>
#include <uint256.h>
-#include <chrono> // For std::chrono::microseconds
+#include <chrono>
#include <cstdint>
#include <limits>
@@ -82,6 +82,18 @@ D GetRandomDuration(typename std::common_type<D>::type max) noexcept
};
constexpr auto GetRandMicros = GetRandomDuration<std::chrono::microseconds>;
constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
+
+/**
+ * Return a timestamp in the future sampled from an exponential distribution
+ * (https://en.wikipedia.org/wiki/Exponential_distribution). This distribution
+ * is memoryless and should be used for repeated network events (e.g. sending a
+ * certain type of message) to minimize leaking information to observers.
+ *
+ * The probability of an event occurring before time x is 1 - e^-(x/a) where a
+ * is the average interval between events.
+ * */
+std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval);
+
int GetRandInt(int nMax) noexcept;
uint256 GetRandHash() noexcept;
diff --git a/src/rest.cpp b/src/rest.cpp
index addce6b0ea..063872b47a 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -32,6 +32,11 @@
#include <univalue.h>
+using node::GetTransaction;
+using node::IsBlockPruned;
+using node::NodeContext;
+using node::ReadBlockFromDisk;
+
static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 073c28b5e8..7cbe7e6159 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -56,6 +56,16 @@
#include <memory>
#include <mutex>
+using node::BlockManager;
+using node::CCoinsStats;
+using node::CoinStatsHashType;
+using node::GetUTXOStats;
+using node::IsBlockPruned;
+using node::NodeContext;
+using node::ReadBlockFromDisk;
+using node::SnapshotMetadata;
+using node::UndoReadFromDisk;
+
struct CUpdatedBlock
{
uint256 hash;
@@ -175,7 +185,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
- const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
+ const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -780,17 +790,15 @@ static RPCHelpMan getblockfrompeer()
{
return RPCHelpMan{
"getblockfrompeer",
- "\nAttempt to fetch block from a given peer.\n"
- "\nWe must have the header for this block, e.g. using submitheader.\n"
- "\nReturns {} if a block-request was successfully scheduled\n",
+ "Attempt to fetch block from a given peer.\n\n"
+ "We must have the header for this block, e.g. using submitheader.\n"
+ "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n\n"
+ "Returns an empty JSON object if the request was successfully scheduled.",
{
- {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"nodeid", RPCArg::Type::NUM, RPCArg::Optional::NO, "The node ID (see getpeerinfo for node IDs)"},
+ {"block_hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
+ {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
},
- RPCResult{RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR, "warnings", /*optional=*/true, "any warnings"},
- }},
+ RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
RPCExamples{
HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
+ HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
@@ -800,31 +808,25 @@ static RPCHelpMan getblockfrompeer()
const NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
PeerManager& peerman = EnsurePeerman(node);
- CConnman& connman = EnsureConnman(node);
- uint256 hash(ParseHashV(request.params[0], "hash"));
+ const uint256& block_hash{ParseHashV(request.params[0], "block_hash")};
+ const NodeId peer_id{request.params[1].get_int64()};
- const NodeId nodeid = static_cast<NodeId>(request.params[1].get_int64());
-
- // Check that the peer with nodeid exists
- if (!connman.ForNode(nodeid, [](CNode* node) {return true;})) {
- throw JSONRPCError(RPC_MISC_ERROR, strprintf("Peer nodeid %d does not exist", nodeid));
- }
-
- const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash););
+ const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
if (!index) {
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
}
- UniValue result = UniValue::VOBJ;
+ 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");
+ }
- if (index->nStatus & BLOCK_HAVE_DATA) {
- result.pushKV("warnings", "Block already downloaded");
- } else if (!peerman.FetchBlock(nodeid, hash, *index)) {
- throw JSONRPCError(RPC_MISC_ERROR, "Failed to fetch block from peer");
+ if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
+ throw JSONRPCError(RPC_MISC_ERROR, err.value());
}
- return result;
+ return UniValue::VOBJ;
},
};
}
@@ -928,8 +930,9 @@ static RPCHelpMan getblockheader()
};
}
-static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
+static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
CBlock block;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
@@ -945,8 +948,9 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}
-static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
+ AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
@@ -1112,7 +1116,7 @@ static RPCHelpMan pruneblockchain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- if (!fPruneMode)
+ if (!node::fPruneMode)
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
ChainstateManager& chainman = EnsureAnyChainman(request.context);
@@ -1330,6 +1334,7 @@ static RPCHelpMan gettxout()
{RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
{RPCResult::Type::OBJ, "scriptPubKey", "", {
{RPCResult::Type::STR, "asm", ""},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR_HEX, "hex", ""},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -1433,7 +1438,7 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
- // getblockchaininfo reports the softfork as active from when the chain height is
+ // getdeploymentinfo reports the softfork as active from when the chain height is
// one below the activation height
rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep));
rv.pushKV("height", params.DeploymentHeight(dep));
@@ -1445,51 +1450,82 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
// For BIP9 deployments.
if (!DeploymentEnabled(consensusParams, id)) return;
+ if (active_chain_tip == nullptr) return;
+
+ auto get_state_name = [](const ThresholdState state) -> std::string {
+ switch (state) {
+ case ThresholdState::DEFINED: return "defined";
+ case ThresholdState::STARTED: return "started";
+ case ThresholdState::LOCKED_IN: return "locked_in";
+ case ThresholdState::ACTIVE: return "active";
+ case ThresholdState::FAILED: return "failed";
+ }
+ return "invalid";
+ };
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id);
- switch (thresholdState) {
- case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
- case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
- case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
- case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
- case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
- }
- const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
+
+ const ThresholdState next_state = g_versionbitscache.State(active_chain_tip, consensusParams, id);
+ const ThresholdState current_state = g_versionbitscache.State(active_chain_tip->pprev, consensusParams, id);
+
+ const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state);
+
+ // BIP9 parameters
if (has_signal) {
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
- bip9.pushKV("since", since_height);
+ bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
+
+ // BIP9 status
+ bip9.pushKV("status", get_state_name(current_state));
+ bip9.pushKV("since", g_versionbitscache.StateSinceHeight(active_chain_tip->pprev, consensusParams, id));
+ bip9.pushKV("status-next", get_state_name(next_state));
+
+ // BIP9 signalling status, if applicable
if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
+ std::vector<bool> signals;
+ BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id, &signals);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
- if (ThresholdState::LOCKED_IN != thresholdState) {
+ if (ThresholdState::LOCKED_IN != current_state) {
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("possible", statsStruct.possible);
}
bip9.pushKV("statistics", statsUV);
+
+ std::string sig;
+ sig.reserve(signals.size());
+ for (const bool s : signals) {
+ sig.push_back(s ? '#' : '-');
+ }
+ bip9.pushKV("signalling", sig);
}
- bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
- rv.pushKV("bip9", bip9);
- if (ThresholdState::ACTIVE == thresholdState) {
- rv.pushKV("height", since_height);
+ if (ThresholdState::ACTIVE == next_state) {
+ rv.pushKV("height", g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id));
}
- rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
+ rv.pushKV("active", ThresholdState::ACTIVE == next_state);
+ rv.pushKV("bip9", bip9);
softforks.pushKV(DeploymentName(id), rv);
}
+namespace {
+/* TODO: when -dprecatedrpc=softforks is removed, drop these */
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams);
+extern const std::vector<RPCResult> RPCHelpForDeployment;
+}
+
+// used by rest.cpp:rest_chaininfo, so cannot be static
RPCHelpMan getblockchaininfo()
{
+ /* TODO: from v24, remove -deprecatedrpc=softforks */
return RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
@@ -1511,31 +1547,11 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"},
{RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
{RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
- {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks",
+ {RPCResult::Type::OBJ_DYN, "softforks", "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks",
{
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
- {
- {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
- {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
- {
- {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
- {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
- {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
- {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
- {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
- {
- {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
- {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
- {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
- {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
- }},
- }},
- {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
- {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
- }},
+ RPCHelpForDeployment
+ },
}},
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
}},
@@ -1564,9 +1580,9 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
- obj.pushKV("size_on_disk", CalculateCurrentUsage());
- obj.pushKV("pruned", fPruneMode);
- if (fPruneMode) {
+ obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
+ obj.pushKV("pruned", node::fPruneMode);
+ if (node::fPruneMode) {
const CBlockIndex* block = tip;
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
@@ -1579,11 +1595,49 @@ RPCHelpMan getblockchaininfo()
bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
- obj.pushKV("prune_target_size", nPruneTarget);
+ obj.pushKV("prune_target_size", node::nPruneTarget);
}
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ if (IsDeprecatedRPCEnabled("softforks")) {
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ obj.pushKV("softforks", DeploymentInfo(tip, consensusParams));
+ }
+
+ obj.pushKV("warnings", GetWarnings(false).original);
+ return obj;
+},
+ };
+}
+
+namespace {
+const std::vector<RPCResult> RPCHelpForDeployment{
+ {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
+ {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
+ {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
+ {
+ {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
+ {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
+ {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
+ {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
+ {RPCResult::Type::STR, "status", "bip9 status of specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
+ {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
+ {RPCResult::Type::STR, "status-next", "bip9 status of next block"},
+ {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
+ {
+ {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
+ {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
+ {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
+ {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
+ }},
+ {RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"},
+ }},
+};
+
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams)
+{
UniValue softforks(UniValue::VOBJ);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
@@ -1592,11 +1646,53 @@ RPCHelpMan getblockchaininfo()
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
- obj.pushKV("softforks", softforks);
+ return softforks;
+}
+} // anon namespace
- obj.pushKV("warnings", GetWarnings(false).original);
- return obj;
-},
+static RPCHelpMan getdeploymentinfo()
+{
+ return RPCHelpMan{"getdeploymentinfo",
+ "Returns an object containing various state info regarding soft-forks.",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"chain tip"}, "The block hash at which to query fork state"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
+ {RPCResult::Type::NUM, "height", "requested block height (or tip)"},
+ {RPCResult::Type::OBJ, "deployments", "", {
+ {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
+ }},
+ }
+ },
+ RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
+ LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+
+ const CBlockIndex* tip;
+ if (request.params[0].isNull()) {
+ tip = active_chainstate.m_chain.Tip();
+ CHECK_NONFATAL(tip);
+ } else {
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
+ tip = chainman.m_blockman.LookupBlockIndex(hash);
+ if (!tip) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ }
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+
+ UniValue deploymentinfo(UniValue::VOBJ);
+ deploymentinfo.pushKV("hash", tip->GetBlockHash().ToString());
+ deploymentinfo.pushKV("height", tip->nHeight);
+ deploymentinfo.pushKV("deployments", DeploymentInfo(tip, consensusParams));
+ return deploymentinfo;
+ },
};
}
@@ -2746,6 +2842,7 @@ static const CRPCCommand commands[] =
{ "blockchain", &getblockheader, },
{ "blockchain", &getchaintips, },
{ "blockchain", &getdifficulty, },
+ { "blockchain", &getdeploymentinfo, },
{ "blockchain", &getmempoolancestors, },
{ "blockchain", &getmempooldescendants, },
{ "blockchain", &getmempoolentry, },
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 2176b5997e..1f51d7c1ad 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -22,7 +22,9 @@ class CBlockIndex;
class CChainState;
class CTxMemPool;
class UniValue;
+namespace node {
struct NodeContext;
+} // namespace node
static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
@@ -57,7 +59,7 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
* @return a UniValue map containing metadata about the snapshot.
*/
UniValue CreateUTXOSnapshot(
- NodeContext& node,
+ node::NodeContext& node,
CChainState& chainstate,
CAutoFile& afile,
const fs::path& path,
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 003ba8bb20..c480a093a4 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -60,7 +60,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getbalance", 3, "avoid_reuse" },
- { "getblockfrompeer", 1, "nodeid" },
+ { "getblockfrompeer", 1, "peer_id" },
{ "getblockhash", 0, "height" },
{ "waitforblockheight", 0, "height" },
{ "waitforblockheight", 1, "timeout" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 3042f1657b..0554367672 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -41,6 +41,13 @@
#include <memory>
#include <stdint.h>
+using node::BlockAssembler;
+using node::CBlockTemplate;
+using node::IncrementExtraNonce;
+using node::NodeContext;
+using node::RegenerateCommitments;
+using node::UpdateTime;
+
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive.
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 0e33bd6f28..8d574e0359 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -35,6 +35,8 @@
#include <univalue.h>
+using node::NodeContext;
+
static RPCHelpMan validateaddress()
{
return RPCHelpMan{
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 71c5ceadd6..3d7c00edfc 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -32,6 +32,8 @@
#include <univalue.h>
+using node::NodeContext;
+
const std::vector<std::string> CONNECTION_TYPE_DOC{
"outbound-full-relay (default automatic connections)",
"block-relay-only (does not relay transactions or addresses)",
@@ -646,7 +648,7 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()));
UniValue localAddresses(UniValue::VARR);
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
{
UniValue rec(UniValue::VOBJ);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index f4790b007c..ff0d8a4e0f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -45,6 +45,15 @@
#include <univalue.h>
+using node::AnalyzePSBT;
+using node::BroadcastTransaction;
+using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
+using node::FindCoins;
+using node::GetTransaction;
+using node::NodeContext;
+using node::PSBTAnalysis;
+using node::ReadBlockFromDisk;
+
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, CChainState& active_chainstate)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
@@ -170,6 +179,7 @@ static RPCHelpMan getrawtransaction()
{RPCResult::Type::OBJ, "scriptPubKey", "",
{
{RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR, "hex", "the hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -231,7 +241,8 @@ static RPCHelpMan getrawtransaction()
if (!tx) {
std::string errmsg;
if (blockindex) {
- if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
+ const bool block_has_data = WITH_LOCK(::cs_main, return blockindex->nStatus & BLOCK_HAVE_DATA);
+ if (!block_has_data) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
@@ -497,6 +508,7 @@ static RPCHelpMan decoderawtransaction()
{RPCResult::Type::OBJ, "scriptPubKey", "",
{
{RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR_HEX, "hex", "the hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -552,6 +564,7 @@ static RPCHelpMan decodescript()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "asm", "Script public key"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"},
{RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
{RPCResult::Type::STR, "p2sh", /*optional=*/true,
@@ -563,6 +576,7 @@ static RPCHelpMan decodescript()
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
{RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"},
{RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"},
}},
},
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 3459897fe5..e23fe34480 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -63,7 +63,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
if (rbf) {
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
} else if (rawTx.nLockTime) {
- nSequence = CTxIn::SEQUENCE_FINAL - 1;
+ nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */
} else {
nSequence = CTxIn::SEQUENCE_FINAL;
}
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index fbb4e5ddd0..95a7c25b93 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -12,6 +12,11 @@
#include <util/system.h>
#include <util/strencodings.h>
+#include <fstream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
@@ -83,7 +88,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
- fsbridge::ofstream file;
+ std::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true);
file.open(filepath_tmp);
if (!file.is_open()) {
@@ -107,7 +112,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
bool GetAuthCookie(std::string *cookie_out)
{
- fsbridge::ifstream file;
+ std::ifstream file;
std::string cookie;
fs::path filepath = GetAuthCookieFile();
file.open(filepath);
diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp
index 2978051574..6a1b41f066 100644
--- a/src/rpc/server_util.cpp
+++ b/src/rpc/server_util.cpp
@@ -15,6 +15,8 @@
#include <any>
+using node::NodeContext;
+
NodeContext& EnsureAnyNodeContext(const std::any& context)
{
auto node_context = util::AnyPtr<NodeContext>(context);
diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h
index 3b0df2d651..2cc710a803 100644
--- a/src/rpc/server_util.h
+++ b/src/rpc/server_util.h
@@ -13,18 +13,20 @@ class CConnman;
class CTxMemPool;
class ChainstateManager;
class PeerManager;
+namespace node {
struct NodeContext;
+} // namespace node
-NodeContext& EnsureAnyNodeContext(const std::any& context);
-CTxMemPool& EnsureMemPool(const NodeContext& node);
+node::NodeContext& EnsureAnyNodeContext(const std::any& context);
+CTxMemPool& EnsureMemPool(const node::NodeContext& node);
CTxMemPool& EnsureAnyMemPool(const std::any& context);
-ArgsManager& EnsureArgsman(const NodeContext& node);
+ArgsManager& EnsureArgsman(const node::NodeContext& node);
ArgsManager& EnsureAnyArgsman(const std::any& context);
-ChainstateManager& EnsureChainman(const NodeContext& node);
+ChainstateManager& EnsureChainman(const node::NodeContext& node);
ChainstateManager& EnsureAnyChainman(const std::any& context);
-CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node);
+CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node);
CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context);
-CConnman& EnsureConnman(const NodeContext& node);
-PeerManager& EnsurePeerman(const NodeContext& node);
+CConnman& EnsureConnman(const node::NodeContext& node);
+PeerManager& EnsurePeerman(const node::NodeContext& node);
#endif // BITCOIN_RPC_SERVER_UTIL_H
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 57e3da0351..5ef7e26ce8 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -831,11 +831,14 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
}
case Type::OBJ_DYN:
case Type::OBJ: {
+ if (m_inner.empty()) {
+ sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});
+ return;
+ }
sections.PushSection({indent + maybe_key + "{", Description("json object")});
for (const auto& i : m_inner) {
i.ToSections(sections, OuterType::OBJ, current_indent + 2);
}
- CHECK_NONFATAL(!m_inner.empty());
if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) {
// If the dictionary keys are dynamic, use three dots for continuation
sections.PushSection({indent_next + "...", ""});
@@ -886,6 +889,17 @@ bool RPCResult::MatchesType(const UniValue& result) const
CHECK_NONFATAL(false);
}
+void RPCResult::CheckInnerDoc() const
+{
+ if (m_type == Type::OBJ) {
+ // May or may not be empty
+ return;
+ }
+ // Everything else must either be empty or not
+ const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};
+ CHECK_NONFATAL(inner_needed != m_inner.empty());
+}
+
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index d43ee33b0f..25ebf78fa1 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -267,8 +267,7 @@ struct RPCResult {
m_cond{std::move(cond)}
{
CHECK_NONFATAL(!m_cond.empty());
- const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
- CHECK_NONFATAL(inner_needed != inner.empty());
+ CheckInnerDoc();
}
RPCResult(
@@ -292,8 +291,7 @@ struct RPCResult {
m_description{std::move(description)},
m_cond{}
{
- const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
- CHECK_NONFATAL(inner_needed != inner.empty());
+ CheckInnerDoc();
}
RPCResult(
@@ -311,6 +309,9 @@ struct RPCResult {
std::string ToDescriptionString() const;
/** Check whether the result JSON type matches. */
bool MatchesType(const UniValue& result) const;
+
+private:
+ void CheckInnerDoc() const;
};
struct RPCResults {
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 378f866e09..0b2ad3c553 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -136,7 +136,7 @@ bool CScheduler::AreThreadsServicingQueue() const
void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue()
{
{
- LOCK(m_cs_callbacks_pending);
+ LOCK(m_callbacks_mutex);
// Try to avoid scheduling too many copies here, but if we
// accidentally have two ProcessQueue's scheduled at once its
// not a big deal.
@@ -150,7 +150,7 @@ void SingleThreadedSchedulerClient::ProcessQueue()
{
std::function<void()> callback;
{
- LOCK(m_cs_callbacks_pending);
+ LOCK(m_callbacks_mutex);
if (m_are_callbacks_running) return;
if (m_callbacks_pending.empty()) return;
m_are_callbacks_running = true;
@@ -167,7 +167,7 @@ void SingleThreadedSchedulerClient::ProcessQueue()
~RAIICallbacksRunning()
{
{
- LOCK(instance->m_cs_callbacks_pending);
+ LOCK(instance->m_callbacks_mutex);
instance->m_are_callbacks_running = false;
}
instance->MaybeScheduleProcessQueue();
@@ -182,7 +182,7 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func
assert(m_pscheduler);
{
- LOCK(m_cs_callbacks_pending);
+ LOCK(m_callbacks_mutex);
m_callbacks_pending.emplace_back(std::move(func));
}
MaybeScheduleProcessQueue();
@@ -194,13 +194,13 @@ void SingleThreadedSchedulerClient::EmptyQueue()
bool should_continue = true;
while (should_continue) {
ProcessQueue();
- LOCK(m_cs_callbacks_pending);
+ LOCK(m_callbacks_mutex);
should_continue = !m_callbacks_pending.empty();
}
}
size_t SingleThreadedSchedulerClient::CallbacksPending()
{
- LOCK(m_cs_callbacks_pending);
+ LOCK(m_callbacks_mutex);
return m_callbacks_pending.size();
}
diff --git a/src/scheduler.h b/src/scheduler.h
index 5366a5989c..bb0abfbf7a 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -119,9 +119,9 @@ class SingleThreadedSchedulerClient
private:
CScheduler* m_pscheduler;
- RecursiveMutex m_cs_callbacks_pending;
- std::list<std::function<void()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
- bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false;
+ Mutex m_callbacks_mutex;
+ std::list<std::function<void()>> m_callbacks_pending GUARDED_BY(m_callbacks_mutex);
+ bool m_are_callbacks_running GUARDED_BY(m_callbacks_mutex) = false;
void MaybeScheduleProcessQueue();
void ProcessQueue();
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index dd15e6104c..f7f9dfc262 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -22,20 +22,23 @@ public:
m_remaining(txToLen)
{}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- if (nSize > m_remaining)
+ if (dst.size() > m_remaining) {
throw std::ios_base::failure(std::string(__func__) + ": end of data");
+ }
- if (pch == nullptr)
+ if (dst.data() == nullptr) {
throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");
+ }
- if (m_data == nullptr)
+ if (m_data == nullptr) {
throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");
+ }
- memcpy(pch, m_data, nSize);
- m_remaining -= nSize;
- m_data += nSize;
+ memcpy(dst.data(), m_data, dst.size());
+ m_remaining -= dst.size();
+ m_data += dst.size();
}
template<typename T>
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 95ffe40a74..11b1a1c887 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1303,12 +1303,12 @@ public:
it = itBegin;
while (scriptCode.GetOp(it, opcode)) {
if (opcode == OP_CODESEPARATOR) {
- s.write((char*)&itBegin[0], it-itBegin-1);
+ s.write(AsBytes(Span{&itBegin[0], size_t(it - itBegin - 1)}));
itBegin = it;
}
}
if (itBegin != scriptCode.end())
- s.write((char*)&itBegin[0], it-itBegin);
+ s.write(AsBytes(Span{&itBegin[0], size_t(it - itBegin)}));
}
/** Serialize an input of txTo */
@@ -1500,7 +1500,7 @@ static bool HandleMissingData(MissingDataBehavior mdb)
}
template<typename T>
-bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
+bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
{
uint8_t ext_flag, key_version;
switch (sigversion) {
@@ -1568,9 +1568,12 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
// Data about the output (if only one).
if (output_type == SIGHASH_SINGLE) {
if (in_pos >= tx_to.vout.size()) return false;
- CHashWriter sha_single_output(SER_GETHASH, 0);
- sha_single_output << tx_to.vout[in_pos];
- ss << sha_single_output.GetSHA256();
+ if (!execdata.m_output_hash) {
+ CHashWriter sha_single_output(SER_GETHASH, 0);
+ sha_single_output << tx_to.vout[in_pos];
+ execdata.m_output_hash = sha_single_output.GetSHA256();
+ }
+ ss << execdata.m_output_hash.value();
}
// Additional data for BIP 342 signatures
@@ -1692,7 +1695,7 @@ bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vecto
}
template <class T>
-bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const
+bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
// Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 2a28f1a2d3..cf1953ad22 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -11,6 +11,7 @@
#include <span.h>
#include <primitives/transaction.h>
+#include <optional>
#include <vector>
#include <stdint.h>
@@ -215,6 +216,9 @@ struct ScriptExecutionData
bool m_validation_weight_left_init = false;
//! How much validation weight is left (decremented for every successful non-empty signature check).
int64_t m_validation_weight_left;
+
+ //! The hash of the corresponding output
+ std::optional<uint256> m_output_hash;
};
/** Signature hash sizes */
@@ -244,7 +248,7 @@ public:
return false;
}
- virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
+ virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
{
return false;
}
@@ -272,7 +276,7 @@ enum class MissingDataBehavior
};
template<typename T>
-bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
+bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
@@ -292,7 +296,7 @@ public:
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
- bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
};
@@ -313,7 +317,7 @@ public:
return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion);
}
- bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror);
}
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 8e08448480..371a937bc8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -542,7 +542,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
public:
DummySignatureChecker() {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
- bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
const DummySignatureChecker DUMMY_CHECKER;
diff --git a/src/serialize.h b/src/serialize.h
index 4cc4b0338c..44bb471f25 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -47,79 +47,72 @@ static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
struct deserialize_type {};
constexpr deserialize_type deserialize {};
-//! Safely convert odd char pointer types to standard ones.
-inline char* CharCast(char* c) { return c; }
-inline char* CharCast(unsigned char* c) { return (char*)c; }
-inline const char* CharCast(const char* c) { return c; }
-inline const char* CharCast(const unsigned char* c) { return (const char*)c; }
-
/*
* Lowest-level serialization and conversion.
- * @note Sizes of these types are verified in the tests
*/
template<typename Stream> inline void ser_writedata8(Stream &s, uint8_t obj)
{
- s.write((char*)&obj, 1);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj)
{
obj = htole16(obj);
- s.write((char*)&obj, 2);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj)
{
obj = htobe16(obj);
- s.write((char*)&obj, 2);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
{
obj = htole32(obj);
- s.write((char*)&obj, 4);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
{
obj = htobe32(obj);
- s.write((char*)&obj, 4);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
{
obj = htole64(obj);
- s.write((char*)&obj, 8);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline uint8_t ser_readdata8(Stream &s)
{
uint8_t obj;
- s.read((char*)&obj, 1);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return obj;
}
template<typename Stream> inline uint16_t ser_readdata16(Stream &s)
{
uint16_t obj;
- s.read((char*)&obj, 2);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le16toh(obj);
}
template<typename Stream> inline uint16_t ser_readdata16be(Stream &s)
{
uint16_t obj;
- s.read((char*)&obj, 2);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return be16toh(obj);
}
template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
{
uint32_t obj;
- s.read((char*)&obj, 4);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le32toh(obj);
}
template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
{
uint32_t obj;
- s.read((char*)&obj, 4);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return be32toh(obj);
}
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
{
uint64_t obj;
- s.read((char*)&obj, 8);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le64toh(obj);
}
@@ -127,7 +120,7 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
/////////////////////////////////////////////////////////////////
//
// Templates for serializing to anything that looks like a stream,
-// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t)
+// i.e. anything that supports .read(Span<std::byte>) and .write(Span<const std::byte>)
//
class CSizeComputer;
@@ -196,7 +189,7 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
FORMATTER_METHODS(cls, obj)
#ifndef CHAR_EQUALS_INT8
-template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
+template <typename Stream> void Serialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t
#endif
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
@@ -206,13 +199,13 @@ template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_wri
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
-template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); }
-template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); }
-template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
-template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(MakeByteSpan(a)); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(MakeByteSpan(a)); }
+template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(AsBytes(span)); }
+template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(AsBytes(span)); }
#ifndef CHAR_EQUALS_INT8
-template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
+template <typename Stream> void Unserialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t
#endif
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
@@ -222,9 +215,9 @@ template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a =
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
-template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); }
-template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); }
-template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(MakeWritableByteSpan(a)); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(MakeWritableByteSpan(a)); }
+template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(AsWritableBytes(span)); }
template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); }
template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; }
@@ -479,10 +472,10 @@ struct CustomUintFormatter
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
if (BigEndian) {
uint64_t raw = htobe64(v);
- s.write(((const char*)&raw) + 8 - Bytes, Bytes);
+ s.write({BytePtr(&raw) + 8 - Bytes, Bytes});
} else {
uint64_t raw = htole64(v);
- s.write((const char*)&raw, Bytes);
+ s.write({BytePtr(&raw), Bytes});
}
}
@@ -492,10 +485,10 @@ struct CustomUintFormatter
static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
uint64_t raw = 0;
if (BigEndian) {
- s.read(((char*)&raw) + 8 - Bytes, Bytes);
+ s.read({BytePtr(&raw) + 8 - Bytes, Bytes});
v = static_cast<I>(be64toh(raw));
} else {
- s.read((char*)&raw, Bytes);
+ s.read({BytePtr(&raw), Bytes});
v = static_cast<I>(le64toh(raw));
}
}
@@ -551,7 +544,7 @@ struct LimitedStringFormatter
throw std::ios_base::failure("String length limit exceeded");
}
v.resize(size);
- if (size != 0) s.read((char*)v.data(), size);
+ if (size != 0) s.read(MakeWritableByteSpan(v));
}
template<typename Stream>
@@ -715,7 +708,7 @@ void Serialize(Stream& os, const std::basic_string<C>& str)
{
WriteCompactSize(os, str.size());
if (!str.empty())
- os.write((char*)str.data(), str.size() * sizeof(C));
+ os.write(MakeByteSpan(str));
}
template<typename Stream, typename C>
@@ -724,7 +717,7 @@ void Unserialize(Stream& is, std::basic_string<C>& str)
unsigned int nSize = ReadCompactSize(is);
str.resize(nSize);
if (nSize != 0)
- is.read((char*)str.data(), nSize * sizeof(C));
+ is.read(MakeWritableByteSpan(str));
}
@@ -737,7 +730,7 @@ void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&)
{
WriteCompactSize(os, v.size());
if (!v.empty())
- os.write((char*)v.data(), v.size() * sizeof(T));
+ os.write(MakeByteSpan(v));
}
template<typename Stream, unsigned int N, typename T, typename V>
@@ -764,7 +757,7 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
{
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
v.resize_uninitialized(i + blk);
- is.read((char*)&v[i], blk * sizeof(T));
+ is.read(AsWritableBytes(Span{&v[i], blk}));
i += blk;
}
}
@@ -791,7 +784,7 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&
{
WriteCompactSize(os, v.size());
if (!v.empty())
- os.write((char*)v.data(), v.size() * sizeof(T));
+ os.write(MakeByteSpan(v));
}
template<typename Stream, typename T, typename A>
@@ -830,7 +823,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&)
{
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
v.resize(i + blk);
- is.read((char*)&v[i], blk * sizeof(T));
+ is.read(AsWritableBytes(Span{&v[i], blk}));
i += blk;
}
}
@@ -995,9 +988,9 @@ protected:
public:
explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {}
- void write(const char *psz, size_t _nSize)
+ void write(Span<const std::byte> src)
{
- this->nSize += _nSize;
+ this->nSize += src.size();
}
/** Pretend _nSize bytes are written, without specifying them. */
diff --git a/src/span.h b/src/span.h
index 47390a5bb2..b627b993c2 100644
--- a/src/span.h
+++ b/src/span.h
@@ -243,16 +243,21 @@ T& SpanPopBack(Span<T>& span)
return back;
}
+//! Convert a data pointer to a std::byte data pointer.
+//! Where possible, please use the safer AsBytes helpers.
+inline const std::byte* BytePtr(const void* data) { return reinterpret_cast<const std::byte*>(data); }
+inline std::byte* BytePtr(void* data) { return reinterpret_cast<std::byte*>(data); }
+
// From C++20 as_bytes and as_writeable_bytes
template <typename T>
Span<const std::byte> AsBytes(Span<T> s) noexcept
{
- return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
+ return {BytePtr(s.data()), s.size_bytes()};
}
template <typename T>
Span<std::byte> AsWritableBytes(Span<T> s) noexcept
{
- return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
+ return {BytePtr(s.data()), s.size_bytes()};
}
template <typename V>
diff --git a/src/streams.h b/src/streams.h
index 384bb632b0..2f26be6dd8 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -49,14 +49,14 @@ public:
return (*this);
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
- stream->write(pch, nSize);
+ stream->write(src);
}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- stream->read(pch, nSize);
+ stream->read(dst);
}
int GetVersion() const { return nVersion; }
@@ -94,17 +94,17 @@ class CVectorWriter
{
::SerializeMany(*this, std::forward<Args>(args)...);
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
assert(nPos <= vchData.size());
- size_t nOverwrite = std::min(nSize, vchData.size() - nPos);
+ size_t nOverwrite = std::min(src.size(), vchData.size() - nPos);
if (nOverwrite) {
- memcpy(vchData.data() + nPos, reinterpret_cast<const unsigned char*>(pch), nOverwrite);
+ memcpy(vchData.data() + nPos, src.data(), nOverwrite);
}
- if (nOverwrite < nSize) {
- vchData.insert(vchData.end(), reinterpret_cast<const unsigned char*>(pch) + nOverwrite, reinterpret_cast<const unsigned char*>(pch) + nSize);
+ if (nOverwrite < src.size()) {
+ vchData.insert(vchData.end(), UCharCast(src.data()) + nOverwrite, UCharCast(src.end()));
}
- nPos += nSize;
+ nPos += src.size();
}
template<typename T>
CVectorWriter& operator<<(const T& obj)
@@ -161,18 +161,18 @@ public:
size_t size() const { return m_data.size(); }
bool empty() const { return m_data.empty(); }
- void read(char* dst, size_t n)
+ void read(Span<std::byte> dst)
{
- if (n == 0) {
+ if (dst.size() == 0) {
return;
}
// Read from the beginning of the buffer
- if (n > m_data.size()) {
+ if (dst.size() > m_data.size()) {
throw std::ios_base::failure("SpanReader::read(): end of data");
}
- memcpy(dst, m_data.data(), n);
- m_data = m_data.subspan(n);
+ memcpy(dst.data(), m_data.data(), dst.size());
+ m_data = m_data.subspan(dst.size());
}
};
@@ -206,6 +206,7 @@ public:
: nType{nTypeIn},
nVersion{nVersionIn} {}
+ explicit CDataStream(Span<const uint8_t> sp, int type, int version) : CDataStream{AsBytes(sp), type, version} {}
explicit CDataStream(Span<const value_type> sp, int nTypeIn, int nVersionIn)
: vch(sp.data(), sp.data() + sp.size()),
nType{nTypeIn},
@@ -221,7 +222,7 @@ public:
std::string str() const
{
- return (std::string(begin(), end()));
+ return std::string{UCharCast(data()), UCharCast(data() + size())};
}
@@ -342,16 +343,16 @@ public:
void SetVersion(int n) { nVersion = n; }
int GetVersion() const { return nVersion; }
- void read(char* pch, size_t nSize)
+ void read(Span<value_type> dst)
{
- if (nSize == 0) return;
+ if (dst.size() == 0) return;
// Read from the beginning of the buffer
- unsigned int nReadPosNext = nReadPos + nSize;
+ unsigned int nReadPosNext = nReadPos + dst.size();
if (nReadPosNext > vch.size()) {
throw std::ios_base::failure("CDataStream::read(): end of data");
}
- memcpy(pch, &vch[nReadPos], nSize);
+ memcpy(dst.data(), &vch[nReadPos], dst.size());
if (nReadPosNext == vch.size())
{
nReadPos = 0;
@@ -379,10 +380,10 @@ public:
nReadPos = nReadPosNext;
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const value_type> src)
{
// Write to the end of the buffer
- vch.insert(vch.end(), pch, pch + nSize);
+ vch.insert(vch.end(), src.begin(), src.end());
}
template<typename Stream>
@@ -390,7 +391,7 @@ public:
{
// Special case: stream << stream concatenates like stream += stream
if (!vch.empty())
- s.write((char*)vch.data(), vch.size() * sizeof(value_type));
+ s.write(MakeByteSpan(vch));
}
template<typename T>
@@ -421,7 +422,7 @@ public:
}
for (size_type i = 0, j = 0; i != size(); i++) {
- vch[i] ^= key[j++];
+ vch[i] ^= std::byte{key[j++]};
// This potentially acts on very many bytes of data, so it's
// important that we calculate `j`, i.e. the `key` index in this
@@ -594,12 +595,13 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
if (!file)
throw std::ios_base::failure("CAutoFile::read: file handle is nullptr");
- if (fread(pch, 1, nSize, file) != nSize)
+ if (fread(dst.data(), 1, dst.size(), file) != dst.size()) {
throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed");
+ }
}
void ignore(size_t nSize)
@@ -615,12 +617,13 @@ public:
}
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
if (!file)
throw std::ios_base::failure("CAutoFile::write: file handle is nullptr");
- if (fwrite(pch, 1, nSize, file) != nSize)
+ if (fwrite(src.data(), 1, src.size(), file) != src.size()) {
throw std::ios_base::failure("CAutoFile::write: write failed");
+ }
}
template<typename T>
@@ -661,7 +664,7 @@ private:
uint64_t nReadPos; //!< how many bytes have been read from this
uint64_t nReadLimit; //!< up to which position we're allowed to read
uint64_t nRewind; //!< how many bytes we guarantee to rewind
- std::vector<char> vchBuf; //!< the buffer
+ std::vector<std::byte> vchBuf; //!< the buffer
protected:
//! read data from the source to fill the buffer
@@ -682,8 +685,8 @@ protected:
}
public:
- CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
- nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0)
+ CBufferedFile(FILE* fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn)
+ : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, std::byte{0})
{
if (nRewindIn >= nBufSize)
throw std::ios_base::failure("Rewind limit must be less than buffer size");
@@ -716,22 +719,23 @@ public:
}
//! read a number of bytes
- void read(char *pch, size_t nSize) {
- if (nSize + nReadPos > nReadLimit)
+ void read(Span<std::byte> dst)
+ {
+ if (dst.size() + nReadPos > nReadLimit) {
throw std::ios_base::failure("Read attempted past buffer limit");
- while (nSize > 0) {
+ }
+ while (dst.size() > 0) {
if (nReadPos == nSrcPos)
Fill();
unsigned int pos = nReadPos % vchBuf.size();
- size_t nNow = nSize;
+ size_t nNow = dst.size();
if (nNow + pos > vchBuf.size())
nNow = vchBuf.size() - pos;
if (nNow + nReadPos > nSrcPos)
nNow = nSrcPos - nReadPos;
- memcpy(pch, &vchBuf[pos], nNow);
+ memcpy(dst.data(), &vchBuf[pos], nNow);
nReadPos += nNow;
- pch += nNow;
- nSize -= nNow;
+ dst = dst.subspan(nNow);
}
}
@@ -774,12 +778,14 @@ public:
}
//! search for a given byte in the stream, and remain positioned on it
- void FindByte(char ch) {
+ void FindByte(uint8_t ch)
+ {
while (true) {
if (nReadPos == nSrcPos)
Fill();
- if (vchBuf[nReadPos % vchBuf.size()] == ch)
+ if (vchBuf[nReadPos % vchBuf.size()] == std::byte{ch}) {
break;
+ }
nReadPos++;
}
}
diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h
index bc9c95eb53..0befe0ffcd 100644
--- a/src/support/allocators/zeroafterfree.h
+++ b/src/support/allocators/zeroafterfree.h
@@ -41,6 +41,6 @@ struct zero_after_free_allocator : public std::allocator<T> {
};
/** Byte-vector that clears its contents before deletion. */
-using SerializeData = std::vector<uint8_t, zero_after_free_allocator<uint8_t>>;
+using SerializeData = std::vector<std::byte, zero_after_free_allocator<std::byte>>;
#endif // BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H
diff --git a/src/test/README.md b/src/test/README.md
index d03411c3ed..90d0e7102d 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -33,19 +33,31 @@ the `src/qt/test/test_main.cpp` file.
### Running individual tests
-`test_bitcoin` has some built-in command-line arguments; for
-example, to run just the `getarg_tests` verbosely:
+`test_bitcoin` accepts the command line arguments from the boost framework.
+For example, to run just the `getarg_tests` suite of tests:
- test_bitcoin --log_level=all --run_test=getarg_tests -- DEBUG_LOG_OUT
+```bash
+test_bitcoin --log_level=all --run_test=getarg_tests
+```
`log_level` controls the verbosity of the test framework, which logs when a
-test case is entered, for example. The `DEBUG_LOG_OUT` after the two dashes
-redirects the debug log, which would normally go to a file in the test datadir
+test case is entered, for example. `test_bitcoin` also accepts the command
+line arguments accepted by `bitcoind`. Use `--` to separate both types of
+arguments:
+
+```bash
+test_bitcoin --log_level=all --run_test=getarg_tests -- -printtoconsole=1
+```
+
+The `-printtoconsole=1` after the two dashes redirects the debug log, which
+would normally go to a file in the test datadir
(`BasicTestingSetup::m_path_root`), to the standard terminal output.
... or to run just the doubledash test:
- test_bitcoin --run_test=getarg_tests/doubledash
+```bash
+test_bitcoin --run_test=getarg_tests/doubledash
+```
Run `test_bitcoin --help` for the full list.
@@ -68,7 +80,7 @@ on failure. For running individual tests verbosely, refer to the section
To write to logs from unit tests you need to use specific message methods
provided by Boost. The simplest is `BOOST_TEST_MESSAGE`.
-For debugging you can launch the `test_bitcoin` executable with `gdb`or `lldb` and
+For debugging you can launch the `test_bitcoin` executable with `gdb` or `lldb` and
start debugging, just like you would with any other program:
```bash
@@ -95,7 +107,7 @@ Running the tests and hitting a segmentation fault should now produce a file cal
`/proc/sys/kernel/core_pattern`).
You can then explore the core dump using
-``` bash
+```bash
gdb src/test/test_bitcoin core
(gbd) bt # produce a backtrace for where a segfault occurred
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 752bd0af9e..efc30b6822 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -21,6 +21,15 @@
#include <string>
using namespace std::literals;
+using node::NodeContext;
+
+static const std::vector<bool> EMPTY_ASMAP;
+static const bool DETERMINISTIC{true};
+
+static int32_t GetCheckRatio(const NodeContext& node_ctx)
+{
+ return std::clamp<int32_t>(node_ctx.args->GetIntArg("-checkaddrman", 100), 0, 1000000);
+}
static CNetAddr ResolveIP(const std::string& ip)
{
@@ -49,17 +58,11 @@ static std::vector<bool> FromBytes(const unsigned char* source, int vector_size)
return result;
}
-/* Utility function to create a deterministic addrman, as used in most tests */
-static std::unique_ptr<AddrMan> TestAddrMan(std::vector<bool> asmap = std::vector<bool>())
-{
- return std::make_unique<AddrMan>(asmap, /*deterministic=*/true, /*consistency_check_ratio=*/100);
-}
-
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2");
@@ -93,7 +96,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_CHECK(addrman->size() >= 1);
// Test: reset addrman and test AddrMan::Add multiple addresses works as expected
- addrman = TestAddrMan();
+ addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
std::vector<CAddress> vAddr;
vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
@@ -103,7 +106,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_AUTO_TEST_CASE(addrman_ports)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2");
@@ -132,7 +135,7 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_AUTO_TEST_CASE(addrman_select)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2");
@@ -191,7 +194,7 @@ BOOST_AUTO_TEST_CASE(addrman_select)
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2");
@@ -220,7 +223,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
int64_t start_time{GetAdjustedTime()};
addr.nTime = start_time;
@@ -252,7 +255,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source = ResolveIP("252.2.2.2");
@@ -283,7 +286,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_AUTO_TEST_CASE(addrman_getaddr)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
// Test: Sanity check, GetAddr should never return anything if addrman
// is empty.
@@ -604,9 +607,11 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
{
std::vector<bool> asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
- auto addrman_asmap1 = TestAddrMan(asmap1);
- auto addrman_asmap1_dup = TestAddrMan(asmap1);
- auto addrman_noasmap = TestAddrMan();
+ const auto ratio = GetCheckRatio(m_node);
+ auto addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio);
+ auto addrman_asmap1_dup = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio);
+ auto addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio);
+
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
@@ -634,8 +639,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(addr_pos1.position != addr_pos3.position);
// deserializing non-asmaped peers.dat to asmaped addrman
- addrman_asmap1 = TestAddrMan(asmap1);
- addrman_noasmap = TestAddrMan();
+ addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio);
+ addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio);
addrman_noasmap->Add({addr}, default_source);
stream << *addrman_noasmap;
stream >> *addrman_asmap1;
@@ -646,8 +651,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(addr_pos4 == addr_pos2);
// used to map to different buckets, now maps to the same bucket.
- addrman_asmap1 = TestAddrMan(asmap1);
- addrman_noasmap = TestAddrMan();
+ addrman_asmap1 = std::make_unique<AddrMan>(asmap1, DETERMINISTIC, ratio);
+ addrman_noasmap = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, ratio);
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
addrman_noasmap->Add({addr, addr2}, default_source);
@@ -666,7 +671,7 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
{
// Confirm that invalid addresses are ignored in unserialization.
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
@@ -698,14 +703,14 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
- addrman = TestAddrMan();
+ addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
stream >> *addrman;
BOOST_CHECK_EQUAL(addrman->size(), 2);
}
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
BOOST_CHECK(addrman->size() == 0);
@@ -738,7 +743,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
BOOST_AUTO_TEST_CASE(addrman_noevict)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
// Add 35 addresses.
CNetAddr source = ResolveIP("252.2.2.2");
@@ -790,7 +795,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
BOOST_CHECK(addrman->size() == 0);
@@ -860,8 +865,7 @@ static CDataStream AddrmanToStream(const AddrMan& addrman)
BOOST_AUTO_TEST_CASE(load_addrman)
{
- AddrMan addrman{/*asmap=*/ std::vector<bool>(), /*deterministic=*/ true,
- /*consistency_check_ratio=*/ 100};
+ AddrMan addrman{EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node)};
CService addr1, addr2, addr3;
BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
@@ -880,7 +884,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrman);
bool exceptionThrown = false;
- AddrMan addrman1(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/100);
+ AddrMan addrman1{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman1.size() == 0);
try {
@@ -897,7 +901,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that ReadFromStream creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrman);
- AddrMan addrman2(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/100);
+ AddrMan addrman2{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman2.size() == 0);
ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
@@ -935,7 +939,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
// Test that the de-serialization of corrupted peers.dat throws an exception.
CDataStream ssPeers1 = MakeCorruptPeersDat();
bool exceptionThrown = false;
- AddrMan addrman1(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/100);
+ AddrMan addrman1{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
@@ -951,7 +955,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
// Test that ReadFromStream fails if peers.dat is corrupt
CDataStream ssPeers2 = MakeCorruptPeersDat();
- AddrMan addrman2(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/100);
+ AddrMan addrman2{EMPTY_ASMAP, !DETERMINISTIC, GetCheckRatio(m_node)};
BOOST_CHECK(addrman2.size() == 0);
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
@@ -959,7 +963,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
BOOST_AUTO_TEST_CASE(addrman_update_address)
{
// Tests updating nTime via Connected() and nServices via SetServices()
- auto addrman = TestAddrMan();
+ auto addrman = std::make_unique<AddrMan>(EMPTY_ASMAP, DETERMINISTIC, GetCheckRatio(m_node));
CNetAddr source{ResolveIP("252.2.2.2")};
CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index a7494be882..a923d38467 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -129,11 +129,11 @@ static void shiftArrayRight(unsigned char* to, const unsigned char* from, unsign
{
unsigned int F = (T+bitsToShift/8);
if (F < arrayLength)
- to[T] = from[F] >> (bitsToShift%8);
+ to[T] = uint8_t(from[F] >> (bitsToShift % 8));
else
to[T] = 0;
if (F + 1 < arrayLength)
- to[T] |= from[(F+1)] << (8-bitsToShift%8);
+ to[T] |= uint8_t(from[(F + 1)] << (8 - bitsToShift % 8));
}
}
@@ -144,9 +144,9 @@ static void shiftArrayLeft(unsigned char* to, const unsigned char* from, unsigne
if (T >= bitsToShift/8)
{
unsigned int F = T-bitsToShift/8;
- to[T] = from[F] << (bitsToShift%8);
+ to[T] = uint8_t(from[F] << (bitsToShift % 8));
if (T >= bitsToShift/8+1)
- to[T] |= from[F-1] >> (8-bitsToShift%8);
+ to[T] |= uint8_t(from[F - 1] >> (8 - bitsToShift % 8));
}
else {
to[T] = 0;
@@ -202,7 +202,7 @@ BOOST_AUTO_TEST_CASE( unaryOperators ) // ! ~ -
BOOST_CHECK(~ZeroL == MaxL);
unsigned char TmpArray[32];
- for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = ~R1Array[i]; }
+ for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = uint8_t(~R1Array[i]); }
BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (~R1L));
BOOST_CHECK(-ZeroL == ZeroL);
@@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE( unaryOperators ) // ! ~ -
// Check if doing _A_ _OP_ _B_ results in the same as applying _OP_ onto each
// element of Aarray and Barray, and then converting the result into an arith_uint256.
#define CHECKBITWISEOPERATOR(_A_,_B_,_OP_) \
- for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = _A_##Array[i] _OP_ _B_##Array[i]; } \
+ for (unsigned int i = 0; i < 32; ++i) { TmpArray[i] = uint8_t(_A_##Array[i] _OP_ _B_##Array[i]); } \
BOOST_CHECK(arith_uint256V(std::vector<unsigned char>(TmpArray,TmpArray+32)) == (_A_##L _OP_ _B_##L));
#define CHECKASSIGNMENTOPERATOR(_A_,_B_,_OP_) \
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index a9b08ddc9c..7c502349b3 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -16,6 +16,10 @@
#include <boost/test/unit_test.hpp>
+using node::BlockAssembler;
+using node::CBlockTemplate;
+using node::IncrementExtraNonce;
+
BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
struct BuildChainTestingSetup : public TestChain100Setup {
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index bd579db205..35c4108caa 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -43,8 +43,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
stream << filter;
std::vector<uint8_t> expected = ParseHex("03614e9b050000000000000001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
}
@@ -69,8 +70,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
stream << filter;
std::vector<uint8_t> expected = ParseHex("03ce4299050000000100008001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
@@ -89,8 +91,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
stream << filter;
std::vector<unsigned char> expected = ParseHex("038fc16b080000000000000001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_match)
@@ -341,8 +344,9 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
merkleStream << merkleBlock;
std::vector<uint8_t> expected = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101");
+ auto result{MakeUCharSpan(merkleStream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), merkleStream.begin(), merkleStream.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), result.begin(), result.end());
}
BOOST_AUTO_TEST_CASE(merkle_block_4)
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 3921a9d2d1..922fd8e513 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -393,11 +393,11 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// Update the expected result to know about the new output coins
assert(tx.vout.size() == 1);
const COutPoint outpoint(tx.GetHash(), 0);
- result[outpoint] = Coin(tx.vout[0], height, CTransaction(tx).IsCoinBase());
+ result[outpoint] = Coin{tx.vout[0], int(height), CTransaction(tx).IsCoinBase()};
// Call UpdateCoins on the top cache
CTxUndo undo;
- UpdateCoins(CTransaction(tx), *(stack.back()), undo, height);
+ UpdateCoins(CTransaction(tx), *(stack.back()), undo, int(height));
// Update the utxo set for future spends
utxoset.insert(outpoint);
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index c2911e5420..92de4ec7ba 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -11,6 +11,8 @@
#include <chrono>
+using node::CCoinsStats;
+using node::CoinStatsHashType;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 9b58711eb8..5a3e382c3f 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -42,6 +42,7 @@ constexpr int HARDENED = 2; // Derivation needs access to private keys
constexpr int UNSOLVABLE = 4; // This descriptor is not expected to be solvable
constexpr int SIGNABLE = 8; // We can sign with this descriptor (this is not true when actual BIP32 derivation is used, as that's not integrated in our signing code)
constexpr int DERIVE_HARDENED = 16; // The final derivation is hardened, i.e. ends with *' or *h
+constexpr int MIXED_PUBKEYS = 32;
/** Compare two descriptors. If only one of them has a checksum, the checksum is ignored. */
bool EqualDescriptor(std::string a, std::string b)
@@ -73,6 +74,18 @@ std::string UseHInsteadOfApostrophe(const std::string& desc)
return ret;
}
+// Count the number of times the string "xpub" appears in a descriptor string
+static size_t CountXpubs(const std::string& desc)
+{
+ size_t count = 0;
+ size_t p = desc.find("xpub", 0);
+ while (p != std::string::npos) {
+ count++;
+ p = desc.find("xpub", p + 1);
+ }
+ return count;
+}
+
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
@@ -171,7 +184,8 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
// Check whether keys are in the cache
const auto& der_xpub_cache = desc_cache.GetCachedDerivedExtPubKeys();
const auto& parent_xpub_cache = desc_cache.GetCachedParentExtPubKeys();
- if ((flags & RANGE) && !(flags & DERIVE_HARDENED)) {
+ const size_t num_xpubs = CountXpubs(pub1);
+ if ((flags & RANGE) && !(flags & (DERIVE_HARDENED))) {
// For ranged, unhardened derivation, None of the keys in origins should appear in the cache but the cache should have parent keys
// But we can derive one level from each of those parent keys and find them all
BOOST_CHECK(der_xpub_cache.empty());
@@ -183,13 +197,22 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
xpub.Derive(der, i);
pubkeys.insert(der.pubkey);
}
+ int count_pks = 0;
for (const auto& origin_pair : script_provider_cached.origins) {
const CPubKey& pk = origin_pair.second.first;
- BOOST_CHECK(pubkeys.count(pk) > 0);
+ count_pks += pubkeys.count(pk);
}
- } else if (pub1.find("xpub") != std::string::npos) {
+ if (flags & MIXED_PUBKEYS) {
+ BOOST_CHECK_EQUAL(num_xpubs, count_pks);
+ } else {
+ BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), count_pks);
+ }
+ } else if (num_xpubs > 0) {
// For ranged, hardened derivation, or not ranged, but has an xpub, all of the keys should appear in the cache
- BOOST_CHECK(der_xpub_cache.size() + parent_xpub_cache.size() == script_provider_cached.origins.size());
+ BOOST_CHECK(der_xpub_cache.size() + parent_xpub_cache.size() == num_xpubs);
+ if (!(flags & MIXED_PUBKEYS)) {
+ BOOST_CHECK(num_xpubs == script_provider_cached.origins.size());
+ }
// Get all of the derived pubkeys
std::set<CPubKey> pubkeys;
for (const auto& xpub_map_pair : der_xpub_cache) {
@@ -206,12 +229,18 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
xpub.Derive(der, i);
pubkeys.insert(der.pubkey);
}
+ int count_pks = 0;
for (const auto& origin_pair : script_provider_cached.origins) {
const CPubKey& pk = origin_pair.second.first;
- BOOST_CHECK(pubkeys.count(pk) > 0);
+ count_pks += pubkeys.count(pk);
}
- } else {
- // No xpub, nothing should be cached
+ if (flags & MIXED_PUBKEYS) {
+ BOOST_CHECK_EQUAL(num_xpubs, count_pks);
+ } else {
+ BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), count_pks);
+ }
+ } else if (!(flags & MIXED_PUBKEYS)) {
+ // Only const pubkeys, nothing should be cached
BOOST_CHECK(der_xpub_cache.empty());
BOOST_CHECK(parent_xpub_cache.empty());
}
@@ -333,6 +362,11 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}});
+ // Mixed xpubs and const pubkeys
+ Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32,{{0},{}});
+ // Mixed range xpubs and const pubkeys
+ Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt,{{2},{1},{0},{}});
+
CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "Key path value 2147483648 is out of range"); // BIP 32 path element overflow
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "Key path value '1aa' is not a valid uint32"); // Path is not valid uint
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index d3389c30eb..1256395849 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -9,6 +9,10 @@
#include <boost/test/unit_test.hpp>
+#include <fstream>
+#include <ios>
+#include <string>
+
BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(fsbridge_pathtostring)
@@ -45,37 +49,37 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
fs::path tmpfile2 = tmpfolder / "fs_tests_₿_🏃";
{
- fsbridge::ofstream file(tmpfile1);
+ std::ofstream file{tmpfile1};
file << "bitcoin";
}
{
- fsbridge::ifstream file(tmpfile2);
+ std::ifstream file{tmpfile2};
std::string input_buffer;
file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
}
{
- fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate);
+ std::ifstream file{tmpfile1, std::ios_base::in | std::ios_base::ate};
std::string input_buffer;
file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "");
}
{
- fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app);
+ std::ofstream file{tmpfile2, std::ios_base::out | std::ios_base::app};
file << "tests";
}
{
- fsbridge::ifstream file(tmpfile1);
+ std::ifstream file{tmpfile1};
std::string input_buffer;
file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcointests");
}
{
- fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc);
+ std::ofstream file{tmpfile2, std::ios_base::out | std::ios_base::trunc};
file << "bitcoin";
}
{
- fsbridge::ifstream file(tmpfile1);
+ std::ifstream file{tmpfile1};
std::string input_buffer;
file >> input_buffer;
BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 9c85c20e2b..3699abb597 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -11,8 +11,10 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <time.h>
#include <util/asmap.h>
+#include <util/system.h>
#include <cassert>
#include <cstdint>
@@ -20,16 +22,26 @@
#include <string>
#include <vector>
+namespace {
+const BasicTestingSetup* g_setup;
+
+int32_t GetCheckRatio()
+{
+ return std::clamp<int32_t>(g_setup->m_node.args->GetIntArg("-checkaddrman", 0), 0, 1000000);
+}
+} // namespace
+
void initialize_addrman()
{
- SelectParams(CBaseChainParams::REGTEST);
+ static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::REGTEST);
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
- AddrMan addr_man(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/0);
+ AddrMan addr_man{/*asmap=*/std::vector<bool>(), /*deterministic=*/false, GetCheckRatio()};
try {
ReadFromStream(addr_man, data_stream);
} catch (const std::exception&) {
@@ -113,7 +125,7 @@ class AddrManDeterministic : public AddrMan
{
public:
explicit AddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
- : AddrMan(std::move(asmap), /*deterministic=*/true, /*consistency_check_ratio=*/0)
+ : AddrMan{std::move(asmap), /*deterministic=*/true, GetCheckRatio()}
{
WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
}
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
index 0cc2d12d29..3b410930ed 100644
--- a/src/test/fuzz/autofile.cpp
+++ b/src/test/fuzz/autofile.cpp
@@ -23,16 +23,16 @@ FUZZ_TARGET(autofile)
CallOneOf(
fuzzed_data_provider,
[&] {
- std::array<uint8_t, 4096> arr{};
+ std::array<std::byte, 4096> arr{};
try {
- auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ auto_file.read({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
[&] {
- const std::array<uint8_t, 4096> arr{};
+ const std::array<std::byte, 4096> arr{};
try {
- auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ auto_file.write({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index c3c2e4050f..a8c3318629 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -33,9 +33,9 @@ FUZZ_TARGET(buffered_file)
CallOneOf(
fuzzed_data_provider,
[&] {
- std::array<uint8_t, 4096> arr{};
+ std::array<std::byte, 4096> arr{};
try {
- opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ opt_buffered_file->read({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
@@ -53,7 +53,7 @@ FUZZ_TARGET(buffered_file)
return;
}
try {
- opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
+ opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<uint8_t>());
} catch (const std::ios_base::failure&) {
}
},
diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp
index 326904a811..8c0ed32d51 100644
--- a/src/test/fuzz/chain.cpp
+++ b/src/test/fuzz/chain.cpp
@@ -21,15 +21,18 @@ FUZZ_TARGET(chain)
const uint256 zero{};
disk_block_index->phashBlock = &zero;
- (void)disk_block_index->GetBlockHash();
- (void)disk_block_index->GetBlockPos();
- (void)disk_block_index->GetBlockTime();
- (void)disk_block_index->GetBlockTimeMax();
- (void)disk_block_index->GetMedianTimePast();
- (void)disk_block_index->GetUndoPos();
- (void)disk_block_index->HaveTxsDownloaded();
- (void)disk_block_index->IsValid();
- (void)disk_block_index->ToString();
+ {
+ LOCK(::cs_main);
+ (void)disk_block_index->GetBlockHash();
+ (void)disk_block_index->GetBlockPos();
+ (void)disk_block_index->GetBlockTime();
+ (void)disk_block_index->GetBlockTimeMax();
+ (void)disk_block_index->GetMedianTimePast();
+ (void)disk_block_index->GetUndoPos();
+ (void)disk_block_index->HaveTxsDownloaded();
+ (void)disk_block_index->IsValid();
+ (void)disk_block_index->ToString();
+ }
const CBlockHeader block_header = disk_block_index->GetBlockHeader();
(void)CDiskBlockIndex{*disk_block_index};
@@ -55,7 +58,7 @@ FUZZ_TARGET(chain)
if (block_status & ~BLOCK_VALID_MASK) {
continue;
}
- (void)disk_block_index->RaiseValidity(block_status);
+ WITH_LOCK(::cs_main, (void)disk_block_index->RaiseValidity(block_status));
}
CBlockIndex block_index{block_header};
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 2f33598348..994b4b9e49 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -26,6 +26,10 @@
#include <string>
#include <vector>
+using node::CCoinsStats;
+using node::CoinStatsHashType;
+using node::GetUTXOStats;
+
namespace {
const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
@@ -269,7 +273,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
+ (void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index f87b6f1503..a14d28f4ef 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -12,21 +12,29 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
+#include <util/system.h>
#include <util/translation.h>
#include <cstdint>
#include <vector>
+namespace {
+const BasicTestingSetup* g_setup;
+} // namespace
+
void initialize_connman()
{
static const auto testing_setup = MakeNoLogFileContext<>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(connman, initialize_connman)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- AddrMan addrman(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/0);
+ AddrMan addrman(/*asmap=*/std::vector<bool>(),
+ /*deterministic=*/false,
+ g_setup->m_node.args->GetIntArg("-checkaddrman", 0));
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman, fuzzed_data_provider.ConsumeBool()};
CNetAddr random_netaddr;
CNode random_node = ConsumeNode(fuzzed_data_provider);
@@ -90,12 +98,6 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
(void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool());
},
[&] {
- // Limit now to int32_t to avoid signed integer overflow
- (void)connman.PoissonNextSendInbound(
- std::chrono::microseconds{fuzzed_data_provider.ConsumeIntegral<int32_t>()},
- std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<int>()});
- },
- [&] {
CSerializedNetMsg serialized_net_msg;
serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE);
serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 48574d71cc..ed6f172a2a 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -22,7 +22,9 @@
#include <pubkey.h>
#include <script/keyorigin.h>
#include <streams.h>
+#include <test/util/setup_common.h>
#include <undo.h>
+#include <util/system.h>
#include <version.h>
#include <exception>
@@ -33,8 +35,17 @@
#include <test/fuzz/fuzz.h>
+using node::SnapshotMetadata;
+
+namespace {
+const BasicTestingSetup* g_setup;
+} // namespace
+
void initialize_deserialize()
{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+ g_setup = testing_setup.get();
+
// Fuzzers using pubkey must hold an ECCVerifyHandle.
static const ECCVerifyHandle verify_handle;
}
@@ -189,7 +200,9 @@ FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
BlockMerkleRoot(block, &mutated);
})
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
- AddrMan am(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/0);
+ AddrMan am(/*asmap=*/std::vector<bool>(),
+ /*deterministic=*/false,
+ g_setup->m_node.args->GetIntArg("-checkaddrman", 0));
DeserializeFromFuzzingInput(buffer, am);
})
FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index a33297e0ed..a490bbfa1d 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -4,6 +4,7 @@
#include <test/fuzz/fuzz.h>
+#include <fs.h>
#include <netaddress.h>
#include <netbase.h>
#include <test/util/setup_common.h>
@@ -12,13 +13,40 @@
#include <cstdint>
#include <exception>
+#include <fstream>
+#include <functional>
+#include <map>
#include <memory>
#include <string>
+#include <tuple>
#include <unistd.h>
#include <vector>
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
+/**
+ * A copy of the command line arguments that start with `--`.
+ * First `LLVMFuzzerInitialize()` is called, which saves the arguments to `g_args`.
+ * Later, depending on the fuzz test, `G_TEST_COMMAND_LINE_ARGUMENTS()` may be
+ * called by `BasicTestingSetup` constructor to fetch those arguments and store
+ * them in `BasicTestingSetup::m_node::args`.
+ */
+static std::vector<const char*> g_args;
+
+static void SetArgs(int argc, char** argv) {
+ for (int i = 1; i < argc; ++i) {
+ // Only take into account arguments that start with `--`. The others are for the fuzz engine:
+ // `fuzz -runs=1 fuzz_seed_corpus/address_deserialize_v2 --checkaddrman=5`
+ if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == '-') {
+ g_args.push_back(argv[i]);
+ }
+ }
+}
+
+const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
+ return g_args;
+};
+
std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets()
{
static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets;
@@ -56,7 +84,7 @@ void initialize()
}
if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
- std::ofstream out_stream(out_path, std::ios::binary);
+ std::ofstream out_stream{out_path, std::ios::binary};
for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue;
out_stream << t.first << std::endl;
@@ -95,6 +123,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
// This function is used by libFuzzer
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
{
+ SetArgs(*argc, *argv);
initialize();
return 0;
}
diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp
index d6879067b8..b4bb4c6dc6 100644
--- a/src/test/fuzz/golomb_rice.cpp
+++ b/src/test/fuzz/golomb_rice.cpp
@@ -19,27 +19,13 @@
#include <vector>
namespace {
-uint64_t MapIntoRange(const uint64_t x, const uint64_t n)
-{
- const uint64_t x_hi = x >> 32;
- const uint64_t x_lo = x & 0xFFFFFFFF;
- const uint64_t n_hi = n >> 32;
- const uint64_t n_lo = n & 0xFFFFFFFF;
- const uint64_t ac = x_hi * n_hi;
- const uint64_t ad = x_hi * n_lo;
- const uint64_t bc = x_lo * n_hi;
- const uint64_t bd = x_lo * n_lo;
- const uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
- const uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
- return upper64;
-}
uint64_t HashToRange(const std::vector<uint8_t>& element, const uint64_t f)
{
const uint64_t hash = CSipHasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL)
.Write(element.data(), element.size())
.Finalize();
- return MapIntoRange(hash, f);
+ return FastRange64(hash, f);
}
std::vector<uint64_t> BuildHashedSet(const std::unordered_set<std::vector<uint8_t>, ByteVectorHash>& elements, const uint64_t f)
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 3087f11771..72574612a2 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -206,11 +206,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
stream >> deserialized_i8;
assert(i8 == deserialized_i8 && stream.empty());
- char deserialized_ch;
- stream << ch;
- stream >> deserialized_ch;
- assert(ch == deserialized_ch && stream.empty());
-
bool deserialized_b;
stream << b;
stream >> deserialized_b;
diff --git a/src/test/fuzz/minisketch.cpp b/src/test/fuzz/minisketch.cpp
index 93954bd3cf..a17be73f6c 100644
--- a/src/test/fuzz/minisketch.cpp
+++ b/src/test/fuzz/minisketch.cpp
@@ -12,6 +12,8 @@
#include <map>
#include <numeric>
+using node::MakeMinisketch32;
+
FUZZ_TARGET(minisketch)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index a7b2b8bfc1..88c22ca305 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -70,13 +70,13 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
bool reject_message{false};
CNetMessage msg = deserializer.GetMessage(m_time, reject_message);
- assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
+ assert(msg.m_type.size() <= CMessageHeader::COMMAND_SIZE);
assert(msg.m_raw_message_size <= mutable_msg_bytes.size());
assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
assert(msg.m_time == m_time);
std::vector<unsigned char> header;
- auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_command, MakeUCharSpan(msg.m_recv));
+ auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_type, MakeUCharSpan(msg.m_recv));
serializer.prepareForTransport(msg2, header);
}
}
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index f221e2ba67..669688a80d 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -18,6 +18,10 @@
#include <string>
#include <vector>
+using node::AnalyzePSBT;
+using node::PSBTAnalysis;
+using node::PSBTInputAnalysis;
+
void initialize_psbt()
{
static const ECCVerifyHandle verify_handle;
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index b6ecf1c492..03a84b697d 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -120,6 +120,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getchaintips",
"getchaintxstats",
"getconnectioncount",
+ "getdeploymentinfo",
"getdescriptorinfo",
"getdifficulty",
"getindexinfo",
@@ -271,7 +272,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
}
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
data_stream << *opt_psbt;
- r = EncodeBase64({data_stream.begin(), data_stream.end()});
+ r = EncodeBase64(data_stream);
},
[&] {
// base58 encoded key
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index eb170aab76..14a59912db 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -102,17 +102,6 @@ FUZZ_TARGET_INIT(script, initialize_script)
(void)script.IsPushOnly();
(void)script.GetSigOpCount(/* fAccurate= */ false);
- (void)FormatScript(script);
- (void)ScriptToAsmStr(script, false);
- (void)ScriptToAsmStr(script, true);
-
- UniValue o1(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o1, true);
- UniValue o2(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o2, false);
- UniValue o3(UniValue::VOBJ);
- ScriptToUniv(script, o3);
-
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
CompressedScript compressed_script;
@@ -178,4 +167,12 @@ FUZZ_TARGET_INIT(script, initialize_script)
Assert(dest == GetScriptForDestination(tx_destination_2));
}
}
+
+ (void)FormatScript(script);
+ (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool());
+
+ UniValue o1(UniValue::VOBJ);
+ ScriptPubKeyToUniv(script, o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool());
+ UniValue o3(UniValue::VOBJ);
+ ScriptToUniv(script, o3);
}
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index deffe26b17..f6c591aca4 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -34,7 +34,7 @@ public:
return m_fuzzed_data_provider.ConsumeBool();
}
- bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_fuzzed_data_provider.ConsumeBool();
}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index ab646c68fc..8f071b71fe 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -276,20 +276,14 @@ FUZZ_TARGET(string)
}
{
- const int atoi_result = atoi(random_string_1.c_str());
const int locale_independent_atoi_result = LocaleIndependentAtoi<int>(random_string_1);
const int64_t atoi64_result = atoi64_legacy(random_string_1);
- const bool out_of_range = atoi64_result < std::numeric_limits<int>::min() || atoi64_result > std::numeric_limits<int>::max();
- if (out_of_range) {
- assert(locale_independent_atoi_result == 0);
- } else {
- assert(atoi_result == locale_independent_atoi_result);
- }
+ assert(locale_independent_atoi_result == std::clamp<int64_t>(atoi64_result, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
}
{
const int64_t atoi64_result = atoi64_legacy(random_string_1);
const int64_t locale_independent_atoi_result = LocaleIndependentAtoi<int64_t>(random_string_1);
- assert(atoi64_result == locale_independent_atoi_result || locale_independent_atoi_result == 0);
+ assert(atoi64_result == locale_independent_atoi_result);
}
}
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index fe1b9c7c0c..df5b271d06 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -14,6 +14,8 @@
#include <validation.h>
#include <validationinterface.h>
+using node::BlockAssembler;
+
namespace {
const TestingSetup* g_setup;
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index f89b597eed..2514636d6e 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -281,8 +281,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("1970-01-01T00:00:01Z");
- static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
+ static const int64_t time_min{ParseISO8601DateTime("2000-01-01T00:00:01Z")};
+ static const int64_t time_max{ParseISO8601DateTime("2100-12-31T23:59:59Z")};
return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.value_or(time_min), max.value_or(time_max));
}
@@ -408,7 +408,7 @@ uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
return fuzzed_data_provider.ConsumeBool() ?
fuzzed_data_provider.PickValueInArray({
CTxIn::SEQUENCE_FINAL,
- CTxIn::SEQUENCE_FINAL - 1,
+ CTxIn::MAX_SEQUENCE_NONFINAL,
MAX_BIP125_RBF_SEQUENCE,
}) :
fuzzed_data_provider.ConsumeIntegral<uint32_t>();
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 019c75c364..6c91844633 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -386,7 +386,6 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe
CallOneOf(
fuzzed_data_provider,
WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()),
- WRITE_TO_STREAM_CASE(char, fuzzed_data_provider.ConsumeIntegral<char>()),
WRITE_TO_STREAM_CASE(int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()),
WRITE_TO_STREAM_CASE(uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()),
WRITE_TO_STREAM_CASE(int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()),
@@ -396,7 +395,7 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe
WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()),
WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)),
- WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)));
+ WRITE_TO_STREAM_CASE(std::vector<uint8_t>, ConsumeRandomLengthIntegralVector<uint8_t>(fuzzed_data_provider)));
} catch (const std::ios_base::failure&) {
break;
}
@@ -416,7 +415,6 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
CallOneOf(
fuzzed_data_provider,
READ_FROM_STREAM_CASE(bool),
- READ_FROM_STREAM_CASE(char),
READ_FROM_STREAM_CASE(int8_t),
READ_FROM_STREAM_CASE(uint8_t),
READ_FROM_STREAM_CASE(int16_t),
@@ -426,7 +424,7 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
READ_FROM_STREAM_CASE(int64_t),
READ_FROM_STREAM_CASE(uint64_t),
READ_FROM_STREAM_CASE(std::string),
- READ_FROM_STREAM_CASE(std::vector<char>));
+ READ_FROM_STREAM_CASE(std::vector<uint8_t>));
} catch (const std::ios_base::failure&) {
break;
}
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index 1b9f0c8a02..e513f1883c 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <consensus/validation.h>
+#include <fs.h>
#include <node/utxo_snapshot.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -13,6 +14,8 @@
#include <validation.h>
#include <validationinterface.h>
+using node::SnapshotMetadata;
+
namespace {
const std::vector<std::shared_ptr<CBlock>>* g_chain;
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index cf95c0b9bf..95eb71099d 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -51,7 +51,7 @@ public:
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
- BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params); }
+ BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, std::vector<bool>* signals=nullptr) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindex, dummy_params, signals); }
bool Condition(int32_t version) const
{
@@ -220,7 +220,14 @@ FUZZ_TARGET_INIT(versionbits, initialize)
CBlockIndex* prev = blocks.tip();
const int exp_since = checker.GetStateSinceHeightFor(prev);
const ThresholdState exp_state = checker.GetStateFor(prev);
- BIP9Stats last_stats = checker.GetStateStatisticsFor(prev);
+
+ // get statistics from end of previous period, then reset
+ BIP9Stats last_stats;
+ last_stats.period = period;
+ last_stats.threshold = threshold;
+ last_stats.count = last_stats.elapsed = 0;
+ last_stats.possible = (period >= threshold);
+ std::vector<bool> last_signals{};
int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
assert(exp_since <= prev_next_height);
@@ -241,17 +248,25 @@ FUZZ_TARGET_INIT(versionbits, initialize)
assert(state == exp_state);
assert(since == exp_since);
- // GetStateStatistics may crash when state is not STARTED
- if (state != ThresholdState::STARTED) continue;
-
// check that after mining this block stats change as expected
- const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
+ std::vector<bool> signals;
+ const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals);
+ const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block);
+ assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold
+ && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count
+ && stats.possible == stats_no_signals.possible);
+
assert(stats.period == period);
assert(stats.threshold == threshold);
assert(stats.elapsed == b);
assert(stats.count == last_stats.count + (signal ? 1 : 0));
assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
last_stats = stats;
+
+ assert(signals.size() == last_signals.size() + 1);
+ assert(signals.back() == signal);
+ last_signals.push_back(signal);
+ assert(signals == last_signals);
}
if (exp_state == ThresholdState::STARTED) {
@@ -265,14 +280,12 @@ FUZZ_TARGET_INIT(versionbits, initialize)
CBlockIndex* current_block = blocks.mine_block(signal);
assert(checker.Condition(current_block) == signal);
- // GetStateStatistics is safe on a period boundary
- // and has progressed to a new period
const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
assert(stats.period == period);
assert(stats.threshold == threshold);
- assert(stats.elapsed == 0);
- assert(stats.count == 0);
- assert(stats.possible == true);
+ assert(stats.elapsed == period);
+ assert(stats.count == blocks_sig);
+ assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
// More interesting is whether the state changed.
const ThresholdState state = checker.GetStateFor(current_block);
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 88ce9648bc..d5142c8d74 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -6,6 +6,7 @@
#include <util/strencodings.h>
#include <util/system.h>
+#include <limits>
#include <string>
#include <utility>
#include <vector>
@@ -144,6 +145,11 @@ BOOST_AUTO_TEST_CASE(intarg)
BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 11), 0);
BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 0);
+ // Check under-/overflow behavior.
+ ResetArgs("-foo=-9223372036854775809 -bar=9223372036854775808");
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), std::numeric_limits<int64_t>::min());
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 0), std::numeric_limits<int64_t>::max());
+
ResetArgs("-foo=11 -bar=12");
BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), 11);
BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 12);
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index f4bf6ff8c9..49b7d2003b 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
BOOST_AUTO_TEST_CASE(hasBlocks)
{
+ LOCK(::cs_main);
auto& chain = m_node.chain;
const CChain& active = Assert(m_node.chainman)->ActiveChain();
diff --git a/src/test/main.cpp b/src/test/main.cpp
index 5885564074..1ad8fcce3a 100644
--- a/src/test/main.cpp
+++ b/src/test/main.cpp
@@ -11,6 +11,7 @@
#include <test/util/setup_common.h>
+#include <functional>
#include <iostream>
/** Redirect debug log to unit_test.log files */
@@ -24,3 +25,17 @@ const std::function<void(const std::string&)> G_TEST_LOG_FUN = [](const std::str
if (!should_log) return;
std::cout << s;
};
+
+/**
+ * Retrieve the command line arguments from boost.
+ * Allows usage like:
+ * `test_bitcoin --run_test="net_tests/cnode_listen_port" -- -checkaddrman=1 -printtoconsole=1`
+ * which would return `["-checkaddrman=1", "-printtoconsole=1"]`.
+ */
+const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
+ std::vector<const char*> args;
+ for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) {
+ args.push_back(boost::unit_test::framework::master_test_suite().argv[i]);
+ }
+ return args;
+};
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index bdc6ff6130..c453dae701 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -24,6 +24,9 @@
#include <boost/test/unit_test.hpp>
+using node::BlockAssembler;
+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);
@@ -456,7 +459,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
+ tx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL;
prevheights[0] = baseheight + 3;
tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
diff --git a/src/test/minisketch_tests.cpp b/src/test/minisketch_tests.cpp
index f7dd18923b..9c53ace633 100644
--- a/src/test/minisketch_tests.cpp
+++ b/src/test/minisketch_tests.cpp
@@ -11,6 +11,8 @@
#include <utility>
+using node::MakeMinisketch32;
+
BOOST_AUTO_TEST_SUITE(minisketch_tests)
BOOST_AUTO_TEST_CASE(minisketch_test)
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 6f850114d3..908b030eea 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -608,7 +608,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
// that a normal IPv4 address is among the entries, but if this address is
// !IsRoutable the undefined behavior is easier to trigger deterministically
{
- LOCK(cs_mapLocalHost);
+ LOCK(g_maplocalhost_mutex);
in_addr ipv4AddrLocal;
ipv4AddrLocal.s_addr = 0x0100007f;
CNetAddr addr = CNetAddr(ipv4AddrLocal);
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index d5a4d3fd80..2f43ae52f7 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
{
const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
- unsigned int nBits = ~0x00800000;
+ unsigned int nBits{~0x00800000U};
hash.SetHex("0x1");
BOOST_CHECK(!CheckProofOfWork(hash, nBits, consensus));
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 12c5848eaf..89814748fe 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
prevector_tester<8, int> test;
for (int i = 0; i < 2048; i++) {
if (InsecureRandBits(2) == 0) {
- test.insert(InsecureRandRange(test.size() + 1), InsecureRand32());
+ test.insert(InsecureRandRange(test.size() + 1), int(InsecureRand32()));
}
if (test.size() > 0 && InsecureRandBits(2) == 1) {
test.erase(InsecureRandRange(test.size()));
@@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
test.resize(new_size);
}
if (InsecureRandBits(3) == 3) {
- test.insert(InsecureRandRange(test.size() + 1), 1 + InsecureRandBool(), InsecureRand32());
+ test.insert(InsecureRandRange(test.size() + 1), 1 + InsecureRandBool(), int(InsecureRand32()));
}
if (InsecureRandBits(3) == 4) {
int del = std::min<int>(test.size(), 1 + (InsecureRandBool()));
@@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
test.erase(beg, beg + del);
}
if (InsecureRandBits(4) == 5) {
- test.push_back(InsecureRand32());
+ test.push_back(int(InsecureRand32()));
}
if (test.size() > 0 && InsecureRandBits(4) == 6) {
test.pop_back();
@@ -247,7 +247,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
int values[4];
int num = 1 + (InsecureRandBits(2));
for (int k = 0; k < num; k++) {
- values[k] = InsecureRand32();
+ values[k] = int(InsecureRand32());
}
test.insert_range(InsecureRandRange(test.size() + 1), values, values + num);
}
@@ -263,13 +263,13 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
test.shrink_to_fit();
}
if (test.size() > 0) {
- test.update(InsecureRandRange(test.size()), InsecureRand32());
+ test.update(InsecureRandRange(test.size()), int(InsecureRand32()));
}
if (InsecureRandBits(10) == 11) {
test.clear();
}
if (InsecureRandBits(9) == 12) {
- test.assign(InsecureRandBits(5), InsecureRand32());
+ test.assign(InsecureRandBits(5), int(InsecureRand32()));
}
if (InsecureRandBits(3) == 3) {
test.swap();
@@ -283,8 +283,8 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt)
if (InsecureRandBits(5) == 19) {
unsigned int num = 1 + (InsecureRandBits(4));
std::vector<int> values(num);
- for (auto &v : values) {
- v = InsecureRand32();
+ for (int& v : values) {
+ v = int(InsecureRand32());
}
test.resize_uninitialized(values);
}
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index eacd7ae894..c453f22594 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -24,7 +24,8 @@
#include <script/bitcoinconsensus.h>
#endif
-#include <stdint.h>
+#include <cstdint>
+#include <fstream>
#include <string>
#include <vector>
@@ -155,10 +156,10 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
if (libconsensus_flags == flags) {
int expectedSuccessCode = expect ? 1 : 0;
if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
} else {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
}
}
#endif
@@ -923,7 +924,7 @@ BOOST_AUTO_TEST_CASE(script_build)
}
#ifdef UPDATE_JSON_TESTS
- FILE* file = fopen("script_tests.json.gen", "w");
+ FILE* file = fsbridge::fopen("script_tests.json.gen", "w");
fputs(strGen.c_str(), file);
fclose(file);
#endif
@@ -1520,7 +1521,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 1);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
}
@@ -1543,7 +1544,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
}
@@ -1566,7 +1567,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size() * 2, nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size() * 2, nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
}
@@ -1589,7 +1590,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
stream << 0xffffffff;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
}
@@ -1612,7 +1613,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
}
@@ -1635,7 +1636,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
@@ -1727,7 +1728,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
bool exists = fs::exists(path);
BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
if (!exists) return;
- fs::ifstream file(path);
+ std::ifstream file{path};
BOOST_CHECK(file.is_open());
file.seekg(0, std::ios::end);
size_t length = file.tellg();
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index e91c203c26..8b8133b689 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -61,7 +61,7 @@ public:
BOOST_AUTO_TEST_CASE(sizes)
{
- BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
+ BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0, 0));
BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0), 0));
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(sizes)
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0), 0));
// Sanity-check GetSerializeSize and c++ type matching
- BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1U);
+ BOOST_CHECK_EQUAL(GetSerializeSize((unsigned char)0, 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2U);
@@ -186,76 +186,78 @@ BOOST_AUTO_TEST_CASE(noncanonical)
std::vector<char>::size_type n;
// zero encoded with three bytes:
- ss.write("\xfd\x00\x00", 3);
+ ss.write(MakeByteSpan("\xfd\x00\x00").first(3));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfc encoded with three bytes:
- ss.write("\xfd\xfc\x00", 3);
+ ss.write(MakeByteSpan("\xfd\xfc\x00").first(3));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfd encoded with three bytes is OK:
- ss.write("\xfd\xfd\x00", 3);
+ ss.write(MakeByteSpan("\xfd\xfd\x00").first(3));
n = ReadCompactSize(ss);
BOOST_CHECK(n == 0xfd);
// zero encoded with five bytes:
- ss.write("\xfe\x00\x00\x00\x00", 5);
+ ss.write(MakeByteSpan("\xfe\x00\x00\x00\x00").first(5));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xffff encoded with five bytes:
- ss.write("\xfe\xff\xff\x00\x00", 5);
+ ss.write(MakeByteSpan("\xfe\xff\xff\x00\x00").first(5));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// zero encoded with nine bytes:
- ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9);
+ ss.write(MakeByteSpan("\xff\x00\x00\x00\x00\x00\x00\x00\x00").first(9));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0x01ffffff encoded with nine bytes:
- ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9);
+ ss.write(MakeByteSpan("\xff\xff\xff\xff\x01\x00\x00\x00\x00").first(9));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
}
BOOST_AUTO_TEST_CASE(insert_delete)
{
+ constexpr auto B2I{[](std::byte b) { return std::to_integer<uint8_t>(b); }};
+
// Test inserting/deleting bytes.
CDataStream ss(SER_DISK, 0);
BOOST_CHECK_EQUAL(ss.size(), 0U);
- ss.write("\x00\x01\x02\xff", 4);
+ ss.write(MakeByteSpan("\x00\x01\x02\xff").first(4));
BOOST_CHECK_EQUAL(ss.size(), 4U);
- char c = (char)11;
+ uint8_t c{11};
// Inserting at beginning/end/middle:
- ss.insert(ss.begin(), c);
+ ss.insert(ss.begin(), std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 5U);
- BOOST_CHECK_EQUAL(ss[0], c);
- BOOST_CHECK_EQUAL(ss[1], 0);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), c);
+ BOOST_CHECK_EQUAL(B2I(ss[1]), 0);
- ss.insert(ss.end(), c);
+ ss.insert(ss.end(), std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 6U);
- BOOST_CHECK_EQUAL(ss[4], 0xff);
- BOOST_CHECK_EQUAL(ss[5], c);
+ BOOST_CHECK_EQUAL(B2I(ss[4]), 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[5]), c);
- ss.insert(ss.begin()+2, c);
+ ss.insert(ss.begin() + 2, std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 7U);
- BOOST_CHECK_EQUAL(ss[2], c);
+ BOOST_CHECK_EQUAL(B2I(ss[2]), c);
// Delete at beginning/end/middle
ss.erase(ss.begin());
BOOST_CHECK_EQUAL(ss.size(), 6U);
- BOOST_CHECK_EQUAL(ss[0], 0);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), 0);
ss.erase(ss.begin()+ss.size()-1);
BOOST_CHECK_EQUAL(ss.size(), 5U);
- BOOST_CHECK_EQUAL(ss[4], 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[4]), 0xff);
ss.erase(ss.begin()+1);
BOOST_CHECK_EQUAL(ss.size(), 4U);
- BOOST_CHECK_EQUAL(ss[0], 0);
- BOOST_CHECK_EQUAL(ss[1], 1);
- BOOST_CHECK_EQUAL(ss[2], 2);
- BOOST_CHECK_EQUAL(ss[3], 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), 0);
+ BOOST_CHECK_EQUAL(B2I(ss[1]), 1);
+ BOOST_CHECK_EQUAL(B2I(ss[2]), 2);
+ BOOST_CHECK_EQUAL(B2I(ss[3]), 0xff);
}
BOOST_AUTO_TEST_CASE(class_methods)
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 3c6e31d311..15ffd068c7 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -4,6 +4,7 @@
#include <util/settings.h>
+#include <fs.h>
#include <test/util/setup_common.h>
#include <test/util/str.h>
@@ -13,6 +14,11 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+
+#include <fstream>
+#include <map>
+#include <string>
+#include <system_error>
#include <vector>
inline bool operator==(const util::SettingsValue& a, const util::SettingsValue& b)
@@ -36,7 +42,7 @@ inline std::ostream& operator<<(std::ostream& os, const std::pair<std::string, u
inline void WriteText(const fs::path& path, const std::string& text)
{
- fsbridge::ofstream file;
+ std::ofstream file;
file.open(path);
file << text;
}
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index c16087bc1b..1601b02356 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -91,8 +91,9 @@ void static RandomScript(CScript &script) {
script << oplist[InsecureRandRange(std::size(oplist))];
}
-void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
- tx.nVersion = InsecureRand32();
+void static RandomTransaction(CMutableTransaction& tx, bool fSingle)
+{
+ tx.nVersion = int(InsecureRand32());
tx.vin.clear();
tx.vout.clear();
tx.nLockTime = (InsecureRandBool()) ? InsecureRand32() : 0;
@@ -126,7 +127,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
int nRandomTests = 50000;
#endif
for (int i=0; i<nRandomTests; i++) {
- int nHashType = InsecureRand32();
+ int nHashType{int(InsecureRand32())};
CMutableTransaction txTo;
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
CScript scriptCode;
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 7ede79279f..6dadf09176 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -114,8 +114,8 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
} else {
// randomly choose something in the range [MTP, MTP*2]
int64_t medianTimePast = vBlocksMain[i].GetMedianTimePast();
- int r = InsecureRandRange(medianTimePast);
- vBlocksMain[i].nTime = r + medianTimePast;
+ int r{int(InsecureRandRange(medianTimePast))};
+ vBlocksMain[i].nTime = uint32_t(r + medianTimePast);
vBlocksMain[i].nTimeMax = std::max(vBlocksMain[i].nTime, vBlocksMain[i-1].nTimeMax);
}
}
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 3571927397..6a69fe9840 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_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 <fs.h>
#include <streams.h>
#include <test/util/setup_common.h>
@@ -160,22 +161,18 @@ BOOST_AUTO_TEST_CASE(bitstream_reader_writer)
BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
{
- std::vector<uint8_t> in;
+ std::vector<std::byte> in;
std::vector<char> expected_xor;
- std::vector<unsigned char> key;
CDataStream ds(in, 0, 0);
// Degenerate case
-
- key.push_back('\x00');
- key.push_back('\x00');
- ds.Xor(key);
+ ds.Xor({0x00, 0x00});
BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()),
ds.str());
- in.push_back('\x0f');
- in.push_back('\xf0');
+ in.push_back(std::byte{0x0f});
+ in.push_back(std::byte{0xf0});
expected_xor.push_back('\xf0');
expected_xor.push_back('\x0f');
@@ -183,10 +180,8 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
ds.clear();
ds.insert(ds.begin(), in.begin(), in.end());
- key.clear();
- key.push_back('\xff');
- ds.Xor(key);
+ ds.Xor({0xff});
BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()),
ds.str());
@@ -195,19 +190,15 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
in.clear();
expected_xor.clear();
- in.push_back('\xf0');
- in.push_back('\x0f');
+ in.push_back(std::byte{0xf0});
+ in.push_back(std::byte{0x0f});
expected_xor.push_back('\x0f');
expected_xor.push_back('\x00');
ds.clear();
ds.insert(ds.begin(), in.begin(), in.end());
- key.clear();
- key.push_back('\xff');
- key.push_back('\x0f');
-
- ds.Xor(key);
+ ds.Xor({0xff, 0x0f});
BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()),
ds.str());
@@ -421,7 +412,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
size_t find = currentPos + InsecureRandRange(8);
if (find >= fileSize)
find = fileSize - 1;
- bf.FindByte(static_cast<char>(find));
+ bf.FindByte(uint8_t(find));
// The value at each offset is the offset.
BOOST_CHECK_EQUAL(bf.GetPos(), find);
currentPos = find;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 1fe51fadd4..4fb7f9c405 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
fValid = false;
break;
}
- COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int());
+ COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
@@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
fValid = false;
break;
}
- COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int());
+ COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index 6f78b43826..560efb6b42 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -327,4 +327,236 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_child->GetHash())));
}
}
+
+// Tests for packages containing transactions that have same-txid-different-witness equivalents in
+// the mempool.
+BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
+{
+ // Mine blocks to mature coinbases.
+ mineBlocks(5);
+ LOCK(cs_main);
+
+ // Transactions with a same-txid-different-witness transaction in the mempool should be ignored,
+ // and the mempool entry's wtxid returned.
+ CScript witnessScript = CScript() << OP_DROP << OP_TRUE;
+ CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
+ auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[0], /*vout=*/ 0,
+ /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
+ /*output_destination=*/ scriptPubKey,
+ /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ false);
+ CTransactionRef ptx_parent = MakeTransactionRef(mtx_parent);
+
+ // Make two children with the same txid but different witnesses.
+ CScriptWitness witness1;
+ witness1.stack.push_back(std::vector<unsigned char>(1));
+ witness1.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
+
+ CScriptWitness witness2(witness1);
+ witness2.stack.push_back(std::vector<unsigned char>(2));
+ witness2.stack.push_back(std::vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
+
+ CKey child_key;
+ child_key.MakeNewKey(true);
+ CScript child_locking_script = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));
+ CMutableTransaction mtx_child1;
+ mtx_child1.nVersion = 1;
+ mtx_child1.vin.resize(1);
+ mtx_child1.vin[0].prevout.hash = ptx_parent->GetHash();
+ mtx_child1.vin[0].prevout.n = 0;
+ mtx_child1.vin[0].scriptSig = CScript();
+ mtx_child1.vin[0].scriptWitness = witness1;
+ mtx_child1.vout.resize(1);
+ mtx_child1.vout[0].nValue = CAmount(48 * COIN);
+ mtx_child1.vout[0].scriptPubKey = child_locking_script;
+
+ CMutableTransaction mtx_child2{mtx_child1};
+ mtx_child2.vin[0].scriptWitness = witness2;
+
+ CTransactionRef ptx_child1 = MakeTransactionRef(mtx_child1);
+ CTransactionRef ptx_child2 = MakeTransactionRef(mtx_child2);
+
+ // child1 and child2 have the same txid
+ BOOST_CHECK_EQUAL(ptx_child1->GetHash(), ptx_child2->GetHash());
+ // child1 and child2 have different wtxids
+ BOOST_CHECK(ptx_child1->GetWitnessHash() != ptx_child2->GetWitnessHash());
+
+ // Try submitting Package1{parent, child1} and Package2{parent, child2} where the children are
+ // same-txid-different-witness.
+ {
+ const auto submit_witness1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
+ {ptx_parent, ptx_child1}, /*test_accept=*/ false);
+ BOOST_CHECK_MESSAGE(submit_witness1.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << submit_witness1.m_state.GetRejectReason());
+ auto it_parent1 = submit_witness1.m_tx_results.find(ptx_parent->GetWitnessHash());
+ auto it_child1 = submit_witness1.m_tx_results.find(ptx_child1->GetWitnessHash());
+ BOOST_CHECK(it_parent1 != submit_witness1.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_parent1->second.m_state.IsValid(),
+ "Transaction unexpectedly failed: " << it_parent1->second.m_state.GetRejectReason());
+ BOOST_CHECK(it_child1 != submit_witness1.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_child1->second.m_state.IsValid(),
+ "Transaction unexpectedly failed: " << it_child1->second.m_state.GetRejectReason());
+
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent->GetHash())));
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child1->GetHash())));
+
+ const auto submit_witness2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
+ {ptx_parent, ptx_child2}, /*test_accept=*/ false);
+ BOOST_CHECK_MESSAGE(submit_witness2.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << submit_witness2.m_state.GetRejectReason());
+ auto it_parent2_deduped = submit_witness2.m_tx_results.find(ptx_parent->GetWitnessHash());
+ auto it_child2 = submit_witness2.m_tx_results.find(ptx_child2->GetWitnessHash());
+ BOOST_CHECK(it_parent2_deduped != submit_witness2.m_tx_results.end());
+ BOOST_CHECK(it_parent2_deduped->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
+ BOOST_CHECK(it_child2 != submit_witness2.m_tx_results.end());
+ BOOST_CHECK(it_child2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
+ BOOST_CHECK_EQUAL(ptx_child1->GetWitnessHash(), it_child2->second.m_other_wtxid.value());
+
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child2->GetHash())));
+ BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_child2->GetWitnessHash())));
+ }
+
+ // Try submitting Package1{child2, grandchild} where child2 is same-txid-different-witness as
+ // the in-mempool transaction, child1. Since child1 exists in the mempool and its outputs are
+ // available, child2 should be ignored and grandchild should be accepted.
+ //
+ // This tests a potential censorship vector in which an attacker broadcasts a competing package
+ // where a parent's witness is mutated. The honest package should be accepted despite the fact
+ // that we don't allow witness replacement.
+ CKey grandchild_key;
+ grandchild_key.MakeNewKey(true);
+ CScript grandchild_locking_script = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));
+ auto mtx_grandchild = CreateValidMempoolTransaction(/*input_transaction=*/ ptx_child2, /* vout=*/ 0,
+ /*input_height=*/ 0, /*input_signing_key=*/ child_key,
+ /*output_destination=*/ grandchild_locking_script,
+ /*output_amount=*/ CAmount(47 * COIN), /*submit=*/ false);
+ CTransactionRef ptx_grandchild = MakeTransactionRef(mtx_grandchild);
+
+ // We already submitted child1 above.
+ {
+ const auto submit_spend_ignored = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
+ {ptx_child2, ptx_grandchild}, /*test_accept=*/ false);
+ BOOST_CHECK_MESSAGE(submit_spend_ignored.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << submit_spend_ignored.m_state.GetRejectReason());
+ auto it_child2_ignored = submit_spend_ignored.m_tx_results.find(ptx_child2->GetWitnessHash());
+ auto it_grandchild = submit_spend_ignored.m_tx_results.find(ptx_grandchild->GetWitnessHash());
+ BOOST_CHECK(it_child2_ignored != submit_spend_ignored.m_tx_results.end());
+ BOOST_CHECK(it_child2_ignored->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
+ BOOST_CHECK(it_grandchild != submit_spend_ignored.m_tx_results.end());
+ BOOST_CHECK(it_grandchild->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
+
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_child2->GetHash())));
+ BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_child2->GetWitnessHash())));
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Wtxid(ptx_grandchild->GetWitnessHash())));
+ }
+
+ // A package Package{parent1, parent2, parent3, child} where the parents are a mixture of
+ // identical-tx-in-mempool, same-txid-different-witness-in-mempool, and new transactions.
+ Package package_mixed;
+
+ // Give all the parents anyone-can-spend scripts so we don't have to deal with signing the child.
+ CScript acs_script = CScript() << OP_TRUE;
+ CScript acs_spk = GetScriptForDestination(WitnessV0ScriptHash(acs_script));
+ CScriptWitness acs_witness;
+ acs_witness.stack.push_back(std::vector<unsigned char>(acs_script.begin(), acs_script.end()));
+
+ // parent1 will already be in the mempool
+ auto mtx_parent1 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[1], /*vout=*/ 0,
+ /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
+ /*output_destination=*/ acs_spk,
+ /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ true);
+ CTransactionRef ptx_parent1 = MakeTransactionRef(mtx_parent1);
+ package_mixed.push_back(ptx_parent1);
+
+ // parent2 will have a same-txid-different-witness tx already in the mempool
+ CScript grandparent2_script = CScript() << OP_DROP << OP_TRUE;
+ CScript grandparent2_spk = GetScriptForDestination(WitnessV0ScriptHash(grandparent2_script));
+ CScriptWitness parent2_witness1;
+ parent2_witness1.stack.push_back(std::vector<unsigned char>(1));
+ parent2_witness1.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end()));
+ CScriptWitness parent2_witness2;
+ parent2_witness2.stack.push_back(std::vector<unsigned char>(2));
+ parent2_witness2.stack.push_back(std::vector<unsigned char>(grandparent2_script.begin(), grandparent2_script.end()));
+
+ // Create grandparent2 creating an output with multiple spending paths. Submit to mempool.
+ auto mtx_grandparent2 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[2], /* vout=*/ 0,
+ /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
+ /*output_destination=*/ grandparent2_spk,
+ /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ true);
+ CTransactionRef ptx_grandparent2 = MakeTransactionRef(mtx_grandparent2);
+
+ CMutableTransaction mtx_parent2_v1;
+ mtx_parent2_v1.nVersion = 1;
+ mtx_parent2_v1.vin.resize(1);
+ mtx_parent2_v1.vin[0].prevout.hash = ptx_grandparent2->GetHash();
+ mtx_parent2_v1.vin[0].prevout.n = 0;
+ mtx_parent2_v1.vin[0].scriptSig = CScript();
+ mtx_parent2_v1.vin[0].scriptWitness = parent2_witness1;
+ mtx_parent2_v1.vout.resize(1);
+ mtx_parent2_v1.vout[0].nValue = CAmount(48 * COIN);
+ mtx_parent2_v1.vout[0].scriptPubKey = acs_spk;
+
+ CMutableTransaction mtx_parent2_v2{mtx_parent2_v1};
+ mtx_parent2_v2.vin[0].scriptWitness = parent2_witness2;
+
+ CTransactionRef ptx_parent2_v1 = MakeTransactionRef(mtx_parent2_v1);
+ CTransactionRef ptx_parent2_v2 = MakeTransactionRef(mtx_parent2_v2);
+ // Put parent2_v1 in the package, submit parent2_v2 to the mempool.
+ const MempoolAcceptResult parent2_v2_result = m_node.chainman->ProcessTransaction(ptx_parent2_v2);
+ BOOST_CHECK(parent2_v2_result.m_result_type == MempoolAcceptResult::ResultType::VALID);
+ package_mixed.push_back(ptx_parent2_v1);
+
+ // parent3 will be a new transaction
+ auto mtx_parent3 = CreateValidMempoolTransaction(/*input_transaction=*/ m_coinbase_txns[3], /*vout=*/ 0,
+ /*input_height=*/ 0, /*input_signing_key=*/ coinbaseKey,
+ /*output_destination=*/ acs_spk,
+ /*output_amount=*/ CAmount(49 * COIN), /*submit=*/ false);
+ CTransactionRef ptx_parent3 = MakeTransactionRef(mtx_parent3);
+ package_mixed.push_back(ptx_parent3);
+
+ // child spends parent1, parent2, and parent3
+ CKey mixed_grandchild_key;
+ mixed_grandchild_key.MakeNewKey(true);
+ CScript mixed_child_spk = GetScriptForDestination(WitnessV0KeyHash(mixed_grandchild_key.GetPubKey()));
+
+ CMutableTransaction mtx_mixed_child;
+ mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent1->GetHash(), 0)));
+ mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent2_v1->GetHash(), 0)));
+ mtx_mixed_child.vin.push_back(CTxIn(COutPoint(ptx_parent3->GetHash(), 0)));
+ mtx_mixed_child.vin[0].scriptWitness = acs_witness;
+ mtx_mixed_child.vin[1].scriptWitness = acs_witness;
+ mtx_mixed_child.vin[2].scriptWitness = acs_witness;
+ mtx_mixed_child.vout.push_back(CTxOut(145 * COIN, mixed_child_spk));
+ CTransactionRef ptx_mixed_child = MakeTransactionRef(mtx_mixed_child);
+ package_mixed.push_back(ptx_mixed_child);
+
+ // Submit package:
+ // parent1 should be ignored
+ // parent2_v1 should be ignored (and v2 wtxid returned)
+ // parent3 should be accepted
+ // child should be accepted
+ {
+ const auto mixed_result = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_mixed, false);
+ BOOST_CHECK_MESSAGE(mixed_result.m_state.IsValid(), mixed_result.m_state.GetRejectReason());
+ auto it_parent1 = mixed_result.m_tx_results.find(ptx_parent1->GetWitnessHash());
+ auto it_parent2 = mixed_result.m_tx_results.find(ptx_parent2_v1->GetWitnessHash());
+ auto it_parent3 = mixed_result.m_tx_results.find(ptx_parent3->GetWitnessHash());
+ auto it_child = mixed_result.m_tx_results.find(ptx_mixed_child->GetWitnessHash());
+ BOOST_CHECK(it_parent1 != mixed_result.m_tx_results.end());
+ BOOST_CHECK(it_parent2 != mixed_result.m_tx_results.end());
+ BOOST_CHECK(it_parent3 != mixed_result.m_tx_results.end());
+ BOOST_CHECK(it_child != mixed_result.m_tx_results.end());
+
+ BOOST_CHECK(it_parent1->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
+ BOOST_CHECK(it_parent2->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS);
+ BOOST_CHECK(it_parent3->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
+ BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);
+ BOOST_CHECK_EQUAL(ptx_parent2_v2->GetWitnessHash(), it_parent2->second.m_other_wtxid.value());
+
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent1->GetHash())));
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent2_v1->GetHash())));
+ BOOST_CHECK(!m_node.mempool->exists(GenTxid::Wtxid(ptx_parent2_v1->GetWitnessHash())));
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_parent3->GetHash())));
+ BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(ptx_mixed_child->GetHash())));
+ }
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp
index 4ff326340d..3ae22921b9 100644
--- a/src/test/util/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -8,9 +8,13 @@
#include <node/blockstorage.h>
#include <validation.h>
+using node::ReadBlockFromDisk;
+using node::UndoReadFromDisk;
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter)
{
+ LOCK(::cs_main);
+
CBlock block;
if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
return false;
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index a9092bd0ef..09f96a033c 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -16,7 +16,7 @@
#include <boost/test/unit_test.hpp>
-const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
+const auto NoMalleation = [](CAutoFile& file, node::SnapshotMetadata& meta){};
/**
* Create and activate a UTXO snapshot, optionally providing a function to
@@ -24,7 +24,7 @@ const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
*/
template<typename F = decltype(NoMalleation)>
static bool
-CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
+CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F malleation = NoMalleation)
{
// Write out a snapshot to the test's tempdir.
//
@@ -43,7 +43,7 @@ CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleati
//
FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
- SnapshotMetadata metadata;
+ node::SnapshotMetadata metadata;
auto_infile >> metadata;
malleation(auto_infile, metadata);
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 550cda892f..5ed8598e8e 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -16,6 +16,9 @@
#include <validation.h>
#include <versionbits.h>
+using node::BlockAssembler;
+using node::NodeContext;
+
CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
{
const auto dest = DecodeDestination(address);
diff --git a/src/test/util/mining.h b/src/test/util/mining.h
index e6cdbfd6d5..09e712cd35 100644
--- a/src/test/util/mining.h
+++ b/src/test/util/mining.h
@@ -13,18 +13,20 @@ class CBlock;
class CChainParams;
class CScript;
class CTxIn;
+namespace node {
struct NodeContext;
+} // namespace node
/** Create a blockchain, starting from genesis */
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params);
/** Returns the generated coin */
-CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey);
+CTxIn MineBlock(const node::NodeContext&, const CScript& coinbase_scriptPubKey);
/** Prepare a block to be mined */
-std::shared_ptr<CBlock> PrepareBlock(const NodeContext&, const CScript& coinbase_scriptPubKey);
+std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext&, const CScript& coinbase_scriptPubKey);
/** RPC-like helper function, returns the generated coin */
-CTxIn generatetoaddress(const NodeContext&, const std::string& address);
+CTxIn generatetoaddress(const node::NodeContext&, const std::string& address);
#endif // BITCOIN_TEST_UTIL_MINING_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 3a37aeb531..c968e4d124 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -42,6 +42,15 @@
#include <walletinitinterface.h>
#include <functional>
+#include <stdexcept>
+
+using node::BlockAssembler;
+using node::CalculateCacheSizes;
+using node::LoadChainstate;
+using node::RegenerateCommitments;
+using node::VerifyLoadedChainstate;
+using node::fPruneMode;
+using node::fReindex;
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
UrlDecodeFn* const URL_DECODE = nullptr;
@@ -80,7 +89,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
m_args{}
{
m_node.args = &gArgs;
- const std::vector<const char*> arguments = Cat(
+ std::vector<const char*> arguments = Cat(
{
"dummy",
"-printtoconsole=0",
@@ -92,6 +101,9 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
"-debugexclude=leveldb",
},
extra_args);
+ if (G_TEST_COMMAND_LINE_ARGUMENTS) {
+ arguments = Cat(arguments, G_TEST_COMMAND_LINE_ARGUMENTS());
+ }
util::ThreadRename("test");
fs::create_directories(m_path_root);
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
@@ -100,9 +112,10 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
{
SetupServerArgs(*m_node.args);
std::string error;
- const bool success{m_node.args->ParseParameters(arguments.size(), arguments.data(), error)};
- assert(success);
- assert(error.empty());
+ if (!m_node.args->ParseParameters(arguments.size(), arguments.data(), error)) {
+ m_node.args->ClearArgs();
+ throw std::runtime_error{error};
+ }
}
SelectParams(chainName);
SeedInsecureRand();
@@ -182,35 +195,37 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
- auto rv = LoadChainstate(fReindex.load(),
- *Assert(m_node.chainman.get()),
- Assert(m_node.mempool.get()),
- fPruneMode,
- chainparams.GetConsensus(),
- m_args.GetBoolArg("-reindex-chainstate", false),
- m_cache_sizes.block_tree_db,
- m_cache_sizes.coins_db,
- m_cache_sizes.coins,
- true,
- true);
- assert(!rv.has_value());
-
- auto maybe_verify_failure = VerifyLoadedChainstate(
+ auto maybe_load_error = LoadChainstate(fReindex.load(),
+ *Assert(m_node.chainman.get()),
+ Assert(m_node.mempool.get()),
+ fPruneMode,
+ chainparams.GetConsensus(),
+ m_args.GetBoolArg("-reindex-chainstate", false),
+ m_cache_sizes.block_tree_db,
+ m_cache_sizes.coins_db,
+ m_cache_sizes.coins,
+ /*block_tree_db_in_memory=*/true,
+ /*coins_db_in_memory=*/true);
+ assert(!maybe_load_error.has_value());
+
+ auto maybe_verify_error = VerifyLoadedChainstate(
*Assert(m_node.chainman),
fReindex.load(),
m_args.GetBoolArg("-reindex-chainstate", false),
chainparams.GetConsensus(),
m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS),
m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- static_cast<int64_t(*)()>(GetTime));
- assert(!maybe_verify_failure.has_value());
+ /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
+ assert(!maybe_verify_error.has_value());
BlockValidationState state;
if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
- m_node.addrman = std::make_unique<AddrMan>(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, /*consistency_check_ratio=*/0);
+ m_node.addrman = std::make_unique<AddrMan>(/*asmap=*/std::vector<bool>(),
+ /*deterministic=*/false,
+ m_node.args->GetIntArg("-checkaddrman", 0));
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 3fa75ad9ca..a1b7525cf4 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -19,18 +19,24 @@
#include <util/string.h>
#include <util/vector.h>
+#include <functional>
#include <type_traits>
#include <vector>
/** 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;
+/** Retrieve the command line arguments. */
+extern const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS;
+
// Enable BOOST_CHECK_EQUAL for enum class types
+namespace std {
template <typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
return stream << static_cast<typename std::underlying_type<T>::type>(e);
}
+} // namespace std
/**
* This global and the helpers that use it are not thread-safe.
@@ -76,7 +82,7 @@ static constexpr CAmount CENT{1000000};
*/
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
- NodeContext m_node;
+ node::NodeContext m_node;
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
~BasicTestingSetup();
@@ -90,7 +96,7 @@ struct BasicTestingSetup {
* initialization behaviour.
*/
struct ChainTestingSetup : public BasicTestingSetup {
- CacheSizes m_cache_sizes{};
+ node::CacheSizes m_cache_sizes{};
explicit ChainTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
~ChainTestingSetup();
diff --git a/src/test/util/wallet.cpp b/src/test/util/wallet.cpp
index 4273dfe719..52aaeabccf 100644
--- a/src/test/util/wallet.cpp
+++ b/src/test/util/wallet.cpp
@@ -12,6 +12,8 @@
#include <wallet/wallet.h>
#endif
+using wallet::CWallet;
+
const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
#ifdef ENABLE_WALLET
diff --git a/src/test/util/wallet.h b/src/test/util/wallet.h
index 565ef1756a..31281bf70e 100644
--- a/src/test/util/wallet.h
+++ b/src/test/util/wallet.h
@@ -7,7 +7,9 @@
#include <string>
+namespace wallet {
class CWallet;
+} // namespace wallet
// Constants //
@@ -16,9 +18,9 @@ extern const std::string ADDRESS_BCRT1_UNSPENDABLE;
// RPC-like //
/** Import the address to the wallet */
-void importaddress(CWallet& wallet, const std::string& address);
+void importaddress(wallet::CWallet& wallet, const std::string& address);
/** Returns a new address from the wallet */
-std::string getnewaddress(CWallet& w);
+std::string getnewaddress(wallet::CWallet& w);
#endif // BITCOIN_TEST_UTIL_WALLET_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 04c4524911..b6479efe7d 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -5,6 +5,7 @@
#include <util/system.h>
#include <clientversion.h>
+#include <fs.h>
#include <hash.h> // For Hash()
#include <key.h> // For CKey
#include <sync.h>
@@ -24,6 +25,8 @@
#include <array>
#include <optional>
+#include <limits>
+#include <map>
#include <stdint.h>
#include <string.h>
#include <thread>
@@ -68,9 +71,12 @@ BOOST_AUTO_TEST_CASE(util_datadir)
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
+#ifndef WIN32
+ // Windows does not consider "datadir/.//" to be a valid directory path.
args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
+#endif
}
BOOST_AUTO_TEST_CASE(util_check)
@@ -1621,6 +1627,11 @@ BOOST_AUTO_TEST_CASE(test_ToIntegral)
BOOST_CHECK(!ToIntegral<uint8_t>("256"));
}
+int64_t atoi64_legacy(const std::string& str)
+{
+ return strtoll(str.c_str(), nullptr, 10);
+}
+
BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi)
{
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1234"), 1'234);
@@ -1648,48 +1659,68 @@ BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi)
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(""), 0);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("aap"), 0);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("0x1"), 0);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-32482348723847471234"), 0);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("32482348723847471234"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-32482348723847471234"), -2'147'483'647 - 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("32482348723847471234"), 2'147'483'647);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("-9223372036854775809"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("-9223372036854775809"), -9'223'372'036'854'775'807LL - 1LL);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("-9223372036854775808"), -9'223'372'036'854'775'807LL - 1LL);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("9223372036854775807"), 9'223'372'036'854'775'807);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("9223372036854775808"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("9223372036854775808"), 9'223'372'036'854'775'807);
+
+ std::map<std::string, int64_t> atoi64_test_pairs = {
+ {"-9223372036854775809", std::numeric_limits<int64_t>::min()},
+ {"-9223372036854775808", -9'223'372'036'854'775'807LL - 1LL},
+ {"9223372036854775807", 9'223'372'036'854'775'807},
+ {"9223372036854775808", std::numeric_limits<int64_t>::max()},
+ {"+-", 0},
+ {"0x1", 0},
+ {"ox1", 0},
+ {"", 0},
+ };
+
+ for (const auto& pair : atoi64_test_pairs) {
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>(pair.first), pair.second);
+ }
+
+ // Ensure legacy compatibility with previous versions of Bitcoin Core's atoi64
+ for (const auto& pair : atoi64_test_pairs) {
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>(pair.first), atoi64_legacy(pair.first));
+ }
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("-1"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("0"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("18446744073709551615"), 18'446'744'073'709'551'615ULL);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("18446744073709551616"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("18446744073709551616"), 18'446'744'073'709'551'615ULL);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-2147483649"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-2147483649"), -2'147'483'648LL);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-2147483648"), -2'147'483'648LL);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("2147483647"), 2'147'483'647);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("2147483648"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("2147483648"), 2'147'483'647);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("-1"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("0"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("4294967295"), 4'294'967'295U);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("4294967296"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("4294967296"), 4'294'967'295U);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("-32769"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("-32769"), -32'768);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("-32768"), -32'768);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("32767"), 32'767);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("32768"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("32768"), 32'767);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("-1"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("0"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("65535"), 65'535U);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("65536"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("65536"), 65'535U);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("-129"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("-129"), -128);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("-128"), -128);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("127"), 127);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("128"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("128"), 127);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("-1"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("0"), 0U);
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("255"), 255U);
- BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("256"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("256"), 255U);
}
BOOST_AUTO_TEST_CASE(test_ParseInt64)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 3f9c28fce0..c5b1dabcb7 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -19,6 +19,8 @@
#include <thread>
+using node::BlockAssembler;
+
namespace validation_block_tests {
struct MinerTestingSetup : public RegTestingSetup {
std::shared_ptr<CBlock> Block(const uint256& prev_hash);
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 4be568ab47..1beef5cf04 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -43,6 +43,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
+ BOOST_REQUIRE(c1.LoadGenesisBlock()); // Need at least one block loaded to be able to flush caches
// Add a coin to the in-memory cache, upsize once, then downsize.
{
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 5b36f261e6..26392e690d 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -20,6 +20,8 @@
#include <boost/test/unit_test.hpp>
+using node::SnapshotMetadata;
+
BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup)
//! Basic tests for ChainstateManager.
@@ -233,7 +235,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
*chainman.SnapshotBlockhash());
// Ensure that the genesis block was not marked assumed-valid.
- BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
+ BOOST_CHECK(WITH_LOCK(::cs_main, return !chainman.ActiveChain().Genesis()->IsAssumedValid()));
const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
const CBlockIndex* tip = chainman.ActiveTip();
@@ -354,6 +356,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
// Mark some region of the chain assumed-valid.
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
+ LOCK(::cs_main);
auto index = cs1.m_chain[i];
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 23d0eacb4b..a34895d4ae 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -9,6 +9,8 @@
#include <boost/test/unit_test.hpp>
+using node::BlockManager;
+
BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, ChainTestingSetup)
//! Test utilities for detecting when we need to flush the coins cache based
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 85eea888cc..5e4379a60a 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -296,8 +296,8 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
{
+ AssertLockHeld(::cs_main);
std::unique_ptr<CDBIterator> pcursor(NewIterator());
-
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
// Load m_block_index
@@ -322,8 +322,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
- if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams))
+ if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
+ }
pcursor->Next();
} else {
diff --git a/src/txdb.h b/src/txdb.h
index d1f47719c4..e70f3cd1f2 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -86,7 +86,8 @@ public:
void ReadReindexing(bool &fReindexing);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
- bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
+ bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
};
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index dc2769b81e..fb5652d0a0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -116,10 +116,9 @@ size_t CTxMemPoolEntry::GetTxSize() const
return GetVirtualTransactionSize(nTxWeight, sigOpCost);
}
-// Update the given tx for any in-mempool descendants.
-// Assumes that CTxMemPool::m_children is correct for the given tx and all
-// descendants.
-void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
+void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
+ const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
+ uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
{
CTxMemPoolEntry::Children stageEntries, descendants;
stageEntries = updateIt->GetMemPoolChildrenConst();
@@ -156,17 +155,18 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
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()));
+ // Don't directly remove the transaction here -- doing so would
+ // invalidate iterators in cachedDescendants. Mark it for removal
+ // by inserting into descendants_to_remove.
+ if (descendant.GetCountWithAncestors() > ancestor_count_limit || descendant.GetSizeWithAncestors() > ancestor_size_limit) {
+ descendants_to_remove.insert(descendant.GetTx().GetHash());
+ }
}
}
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
}
-// vHashesToUpdate is the set of transaction hashes from a disconnected block
-// which has been re-added to the mempool.
-// for each entry, look for descendants that are outside vHashesToUpdate, and
-// add fee/size information for such descendants to the parent.
-// for each such descendant, also update the ancestor state to include the parent.
-void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
+void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate, uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
{
AssertLockHeld(cs);
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
@@ -178,6 +178,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// accounted for in the state of their ancestors)
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());
+ std::set<uint256> descendants_to_remove;
+
// 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
@@ -207,7 +209,15 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
}
}
} // release epoch guard for UpdateForDescendants
- UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded);
+ UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded, descendants_to_remove, ancestor_size_limit, ancestor_count_limit);
+ }
+
+ for (const auto& txid : descendants_to_remove) {
+ // This txid may have been removed already in a prior call to removeRecursive.
+ // Therefore we ensure it is not yet removed already.
+ if (const std::optional<txiter> txiter = GetIter(txid)) {
+ removeRecursive((*txiter)->GetTx(), MemPoolRemovalReason::SIZELIMIT);
+ }
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index b8c508fd90..e7e5a3c402 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -312,16 +312,6 @@ public:
}
};
-struct update_lock_points
-{
- explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
-
-private:
- const LockPoints& lp;
-};
-
// Multi_index tag names
struct descendant_score {};
struct entry_time {};
@@ -599,10 +589,14 @@ public:
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
- /** After reorg, check if mempool entries are now non-final, premature coinbase spends, or have
- * invalid lockpoints. Update lockpoints and remove entries (and descendants of entries) that
- * are no longer valid. */
- void removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ /** After reorg, filter the entries that would no longer be valid in the next block, and update
+ * the entries' cached LockPoints if needed. The mempool does not have any knowledge of
+ * consensus rules. It just appplies the callable function and removes the ones for which it
+ * returns true.
+ * @param[in] filter_final_and_mature Predicate that checks the relevant validation rules
+ * and updates an entry's LockPoints.
+ * */
+ void removeForReorg(CChain& chain, std::function<bool(txiter)> filter_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -642,16 +636,25 @@ public:
*/
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
- /** When adding transactions from a disconnected block back to the mempool,
- * new mempool entries may have children in the mempool (which is generally
- * not the case when otherwise adding transactions).
- * UpdateTransactionsFromBlock() will find child transactions and update the
- * descendant state for each transaction in vHashesToUpdate (excluding any
- * child transactions present in vHashesToUpdate, which are already accounted
- * for). Note: vHashesToUpdate should be the set of transactions from the
- * disconnected block that have been accepted back into the mempool.
+ /** UpdateTransactionsFromBlock is called when adding transactions from a
+ * disconnected block back to the mempool, new mempool entries may have
+ * children in the mempool (which is generally not the case when otherwise
+ * adding transactions).
+ * @post updated descendant state for descendants of each transaction in
+ * vHashesToUpdate (excluding any child transactions present in
+ * vHashesToUpdate, which are already accounted for). Updated state
+ * includes add fee/size information for such descendants to the
+ * parent and updated ancestor state to include the parent.
+ *
+ * @param[in] vHashesToUpdate The set of txids from the
+ * disconnected block that have been accepted back into the mempool.
+ * @param[in] ancestor_size_limit The maximum allowed size in virtual
+ * bytes of an entry and its ancestors
+ * @param[in] ancestor_count_limit The maximum allowed number of
+ * transactions including the entry and its ancestors.
*/
- void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
+ void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate,
+ uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) 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)
@@ -800,19 +803,38 @@ private:
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
* the descendants for a single transaction that has been added to the
* mempool but may have child transactions in the mempool, eg during a
- * chain reorg. setExclude is the set of descendant transactions in the
- * mempool that must not be accounted for (because any descendants in
- * setExclude were added to the mempool after the transaction being
- * updated and hence their state is already reflected in the parent
- * state).
+ * chain reorg.
+ *
+ * @pre CTxMemPool::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
+ * be removed for violation of ancestor limits.
+ * @post if updateIt has any non-excluded descendants, cachedDescendants has
+ * a new cache line for updateIt.
+ * @post descendants_to_remove has a new entry for any descendant which exceeded
+ * ancestor limits relative to updateIt.
*
- * cachedDescendants will be updated with the descendants of the transaction
- * being updated, so that future invocations don't need to walk the
- * same transaction again, if encountered in another transaction chain.
+ * @param[in] updateIt the entry to update for its descendants
+ * @param[in,out] cachedDescendants a cache where each line corresponds to all
+ * descendants. It will be updated with the descendants of the transaction
+ * being updated, so that future invocations don't need to walk the same
+ * transaction again, if encountered in another transaction chain.
+ * @param[in] setExclude the set of descendant transactions in the mempool
+ * that must not be accounted for (because any descendants in setExclude
+ * were added to the mempool after the transaction being updated and hence
+ * their state is already reflected in the parent state).
+ * @param[out] descendants_to_remove Populated with the txids of entries that
+ * exceed ancestor limits. It's the responsibility of the caller to
+ * removeRecursive them.
+ * @param[in] ancestor_size_limit the max number of ancestral bytes allowed
+ * for any descendant
+ * @param[in] ancestor_count_limit the max number of ancestor transactions
+ * allowed for any descendant
*/
- void UpdateForDescendants(txiter updateIt,
- cacheMap &cachedDescendants,
- const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
+ const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
+ uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Update ancestors of hash to add/remove it as a descendant transaction. */
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Set ancestor state for an entry */
diff --git a/src/uint256.h b/src/uint256.h
index 72681d09c9..5c3a2f5409 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_UINT256_H
#define BITCOIN_UINT256_H
+#include <span.h>
+
#include <assert.h>
#include <cstring>
#include <stdint.h>
@@ -96,13 +98,13 @@ public:
template<typename Stream>
void Serialize(Stream& s) const
{
- s.write((char*)m_data, sizeof(m_data));
+ s.write(MakeByteSpan(m_data));
}
template<typename Stream>
void Unserialize(Stream& s)
{
- s.read((char*)m_data, sizeof(m_data));
+ s.read(MakeWritableByteSpan(m_data));
}
};
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index ffa2755970..ceb8379c1c 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -6,6 +6,7 @@
#include <clientversion.h>
#include <crypto/common.h>
+#include <fs.h>
#include <logging.h>
#include <streams.h>
diff --git a/src/util/fastrange.h b/src/util/fastrange.h
new file mode 100644
index 0000000000..77cb883ce0
--- /dev/null
+++ b/src/util/fastrange.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2018-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_FASTRANGE_H
+#define BITCOIN_UTIL_FASTRANGE_H
+
+#include <cstdint>
+
+/* This file offers implementations of the fast range reduction technique described
+ * in https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
+ *
+ * In short, they take an integer x and a range n, and return the upper bits of
+ * (x * n). If x is uniformly distributed over its domain, the result is as close to
+ * uniformly distributed over [0, n) as (x mod n) would be, but significantly faster.
+ */
+
+/** Fast range reduction with 32-bit input and 32-bit range. */
+static inline uint32_t FastRange32(uint32_t x, uint32_t n)
+{
+ return (uint64_t{x} * n) >> 32;
+}
+
+/** Fast range reduction with 64-bit input and 64-bit range. */
+static inline uint64_t FastRange64(uint64_t x, uint64_t n)
+{
+#ifdef __SIZEOF_INT128__
+ return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
+#else
+ // To perform the calculation on 64-bit numbers without losing the
+ // result to overflow, split the numbers into the most significant and
+ // least significant 32 bits and perform multiplication piece-wise.
+ //
+ // See: https://stackoverflow.com/a/26855440
+ const uint64_t x_hi = x >> 32;
+ const uint64_t x_lo = x & 0xFFFFFFFF;
+ const uint64_t n_hi = n >> 32;
+ const uint64_t n_lo = n & 0xFFFFFFFF;
+
+ const uint64_t ac = x_hi * n_hi;
+ const uint64_t ad = x_hi * n_lo;
+ const uint64_t bc = x_lo * n_hi;
+ const uint64_t bd = x_lo * n_lo;
+
+ const uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
+ const uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
+ return upper64;
+#endif
+}
+
+#endif // BITCOIN_UTIL_FASTRANGE_H
diff --git a/src/util/golombrice.h b/src/util/golombrice.h
index 67d262406f..4ff4f6d7e5 100644
--- a/src/util/golombrice.h
+++ b/src/util/golombrice.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_UTIL_GOLOMBRICE_H
#define BITCOIN_UTIL_GOLOMBRICE_H
+#include <util/fastrange.h>
+
#include <streams.h>
#include <cstdint>
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
index 442a55ffb0..26439b010b 100644
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -2,11 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <fs.h>
#include <util/settings.h>
#include <tinyformat.h>
#include <univalue.h>
+#include <fstream>
+#include <map>
+#include <string>
+#include <vector>
+
namespace util {
namespace {
@@ -63,7 +69,7 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
// Ok for file to not exist
if (!fs::exists(path)) return true;
- fsbridge::ifstream file;
+ std::ifstream file;
file.open(path);
if (!file.is_open()) {
errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
@@ -106,13 +112,13 @@ bool WriteSettings(const fs::path& path,
for (const auto& value : values) {
out.__pushKV(value.first, value.second);
}
- fsbridge::ofstream file;
+ std::ofstream file;
file.open(path);
if (file.fail()) {
errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));
return false;
}
- file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
+ file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
file.close();
return true;
}
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 0945556b5e..1f83fa3ffa 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -16,6 +16,7 @@
#include <charconv>
#include <cstdint>
#include <iterator>
+#include <limits>
#include <optional>
#include <string>
#include <vector>
@@ -93,8 +94,12 @@ void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
// New code should use ToIntegral or the ParseInt* functions
// which provide parse error feedback.
//
-// The goal of LocaleIndependentAtoi is to replicate the exact defined behaviour
-// of atoi and atoi64 as they behave under the "C" locale.
+// The goal of LocaleIndependentAtoi is to replicate the defined behaviour of
+// std::atoi as it behaves under the "C" locale, and remove some undefined
+// behavior. If the parsed value is bigger than the integer type's maximum
+// value, or smaller than the integer type's minimum value, std::atoi has
+// undefined behavior, while this function returns the maximum or minimum
+// values, respectively.
template <typename T>
T LocaleIndependentAtoi(const std::string& str)
{
@@ -109,7 +114,15 @@ T LocaleIndependentAtoi(const std::string& str)
s = s.substr(1);
}
auto [_, error_condition] = std::from_chars(s.data(), s.data() + s.size(), result);
- if (error_condition != std::errc{}) {
+ if (error_condition == std::errc::result_out_of_range) {
+ if (s.length() >= 1 && s[0] == '-') {
+ // Saturate underflow, per strtoll's behavior.
+ return std::numeric_limits<T>::min();
+ } else {
+ // Saturate overflow, per strtoll's behavior.
+ return std::numeric_limits<T>::max();
+ }
+ } else if (error_condition != std::errc{}) {
return 0;
}
return result;
diff --git a/src/util/syscall_sandbox.cpp b/src/util/syscall_sandbox.cpp
index 3c250b7704..4157be9d9f 100644
--- a/src/util/syscall_sandbox.cpp
+++ b/src/util/syscall_sandbox.cpp
@@ -595,6 +595,7 @@ public:
allowed_syscalls.insert(__NR_readlink); // read value of a symbolic link
allowed_syscalls.insert(__NR_rename); // change the name or location of a file
allowed_syscalls.insert(__NR_rmdir); // delete a directory
+ allowed_syscalls.insert(__NR_sendfile); // transfer data between file descriptors
allowed_syscalls.insert(__NR_stat); // get file status
allowed_syscalls.insert(__NR_statfs); // get filesystem statistics
allowed_syscalls.insert(__NR_statx); // get file status (extended)
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 8f35b7b6c6..0ee63f6381 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -6,15 +6,11 @@
#include <util/system.h>
#ifdef ENABLE_EXTERNAL_SIGNER
-#if defined(WIN32) && !defined(__kernel_entry)
-// A workaround for boost 1.71 incompatibility with mingw-w64 compiler.
-// For details see https://github.com/bitcoin/bitcoin/pull/22348.
-#define __kernel_entry
-#endif
#include <boost/process.hpp>
#endif // ENABLE_EXTERNAL_SIGNER
#include <chainparamsbase.h>
+#include <fs.h>
#include <sync.h>
#include <util/check.h>
#include <util/getuniquepath.h>
@@ -71,10 +67,15 @@
#endif
#include <boost/algorithm/string/replace.hpp>
+#include <univalue.h>
+
+#include <fstream>
+#include <map>
+#include <memory>
#include <optional>
+#include <string>
#include <thread>
#include <typeinfo>
-#include <univalue.h>
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
@@ -151,7 +152,7 @@ bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
}
std::streampos GetFileSize(const char* path, std::streamsize max) {
- std::ifstream file(path, std::ios::binary);
+ std::ifstream file{path, std::ios::binary};
file.ignore(max);
return file.gcount();
}
@@ -248,7 +249,7 @@ namespace {
fs::path StripRedundantLastElementsOfPath(const fs::path& path)
{
auto result = path;
- while (fs::PathToString(result.filename()) == ".") {
+ while (result.filename().empty() || fs::PathToString(result.filename()) == ".") {
result = result.parent_path();
}
@@ -408,7 +409,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
if (!path.empty()) return path;
if (IsArgSet("-blocksdir")) {
- path = fs::system_complete(fs::PathFromString(GetArg("-blocksdir", "")));
+ path = fs::absolute(fs::PathFromString(GetArg("-blocksdir", "")));
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -420,7 +421,6 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
path /= fs::PathFromString(BaseParams().DataDir());
path /= "blocks";
fs::create_directories(path);
- path = StripRedundantLastElementsOfPath(path);
return path;
}
@@ -435,7 +435,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
std::string datadir = GetArg("-datadir", "");
if (!datadir.empty()) {
- path = fs::system_complete(fs::PathFromString(datadir));
+ path = fs::absolute(StripRedundantLastElementsOfPath(fs::PathFromString(datadir)));
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -813,7 +813,7 @@ fs::path GetDefaultDataDir()
bool CheckDataDirOption()
{
std::string datadir = gArgs.GetArg("-datadir", "");
- return datadir.empty() || fs::is_directory(fs::system_complete(fs::PathFromString(datadir)));
+ return datadir.empty() || fs::is_directory(fs::absolute(fs::PathFromString(datadir)));
}
fs::path GetConfigFile(const std::string& confPath)
@@ -903,7 +903,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
- fsbridge::ifstream stream(GetConfigFile(confPath));
+ std::ifstream stream{GetConfigFile(confPath)};
// not ok to have a config file specified that cannot be opened
if (IsArgSet("-conf") && !stream.good()) {
@@ -950,7 +950,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
const size_t default_includes = add_includes({});
for (const std::string& conf_file_name : conf_file_names) {
- fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name));
+ std::ifstream conf_file_stream{GetConfigFile(conf_file_name)};
if (conf_file_stream.good()) {
if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
return false;
@@ -1074,7 +1074,7 @@ bool RenameOver(fs::path src, fs::path dest)
}
/**
- * Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
+ * Ignores exceptions thrown by create_directories if the requested directory exists.
* Specifically handles case where path p exists, but it wasn't possible for the user to
* write to the parent directory.
*/
@@ -1318,16 +1318,6 @@ void SetupEnvironment()
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
#endif
- // The path locale is lazy initialized and to avoid deinitialization errors
- // in multithreading environments, it is set explicitly by the main thread.
- // A dummy locale is used to extract the internal default locale, used by
- // fs::path, which is then used to explicitly imbue the path.
- std::locale loc = fs::path::imbue(std::locale::classic());
-#ifndef WIN32
- fs::path::imbue(loc);
-#else
- fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
-#endif
}
bool SetupNetworking()
diff --git a/src/validation.cpp b/src/validation.cpp
index a98ffe006d..c12dc9e8b6 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -62,6 +62,25 @@
#include <boost/algorithm/string/replace.hpp>
+using node::BLOCKFILE_CHUNK_SIZE;
+using node::BlockManager;
+using node::BlockMap;
+using node::CBlockIndexWorkComparator;
+using node::CCoinsStats;
+using node::CoinStatsHashType;
+using node::GetUTXOStats;
+using node::OpenBlockFile;
+using node::ReadBlockFromDisk;
+using node::SnapshotMetadata;
+using node::UNDOFILE_CHUNK_SIZE;
+using node::UndoReadFromDisk;
+using node::UnlinkPrunedFiles;
+using node::fHavePruned;
+using node::fImporting;
+using node::fPruneMode;
+using node::fReindex;
+using node::nPruneTarget;
+
#define MICRO 0.000001
#define MILLI 0.001
@@ -133,16 +152,6 @@ arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-// Internal stuff from blockstorage ...
-extern RecursiveMutex cs_LastBlockFile;
-extern std::vector<CBlockFileInfo> vinfoBlockFile;
-extern int nLastBlockFile;
-extern bool fCheckForPruning;
-extern std::set<CBlockIndex*> setDirtyBlockIndex;
-extern std::set<int> setDirtyFileInfo;
-void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
-// ... TODO move fully to blockstorage
-
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -338,43 +347,59 @@ void CChainState::MaybeUpdateMempoolForReorg(
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
- m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
-
- const auto check_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
+ const uint64_t ancestor_count_limit = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ const uint64_t ancestor_size_limit = gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ m_mempool->UpdateTransactionsFromBlock(vHashUpdate, ancestor_size_limit, ancestor_count_limit);
+
+ // Predicate to use for filtering transactions in removeForReorg.
+ // Checks whether the transaction is still final and, if it spends a coinbase output, mature.
+ // Also updates valid entries' cached LockPoints if needed.
+ // If false, the tx is still valid and its lockpoints are updated.
+ // If true, the tx would be invalid in the next block; remove this entry and all of its descendants.
+ const auto filter_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
- bool should_remove = false;
AssertLockHeld(m_mempool->cs);
AssertLockHeld(::cs_main);
const CTransaction& tx = it->GetTx();
+
+ // The transaction must be final.
+ if (!CheckFinalTx(m_chain.Tip(), tx, flags)) return true;
LockPoints lp = it->GetLockPoints();
const bool validLP{TestLockPointValidity(m_chain, lp)};
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
- if (!CheckFinalTx(m_chain.Tip(), tx, flags)
- || !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
- // Note if CheckSequenceLocks fails the LockPoints may still be invalid
- // So it's critical that we remove the tx and not depend on the LockPoints.
- should_remove = true;
- } else if (it->GetSpendsCoinbase()) {
+ // CheckSequenceLocks checks if the transaction will be final in the next block to be
+ // created on top of the new chain. We use useExistingLockPoints=false so that, instead of
+ // using the information in lp (which might now refer to a block that no longer exists in
+ // the chain), it will update lp to contain LockPoints relevant to the new chain.
+ if (!CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
+ // If CheckSequenceLocks fails, remove the tx and don't depend on the LockPoints.
+ return true;
+ } else if (!validLP) {
+ // If CheckSequenceLocks succeeded, it also updated the LockPoints.
+ // Now update the mempool entry lockpoints as well.
+ m_mempool->mapTx.modify(it, [&lp](CTxMemPoolEntry& e) { e.UpdateLockPoints(lp); });
+ }
+
+ // If the transaction spends any coinbase outputs, it must be mature.
+ if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
if (it2 != m_mempool->mapTx.end())
continue;
- const Coin &coin = CoinsTip().AccessCoin(txin.prevout);
+ const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
- if (coin.IsSpent() || (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY)) {
- should_remove = true;
- break;
+ if (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY) {
+ return true;
}
}
}
- // CheckSequenceLocks updates lp. Update the mempool entry LockPoints.
- if (!validLP) m_mempool->mapTx.modify(it, update_lock_points(lp));
- return should_remove;
+ // Transaction is still valid and cached LockPoints are updated.
+ return false;
};
// We also need to remove any now-immature transactions
- m_mempool->removeForReorg(m_chain, check_final_and_mature);
+ m_mempool->removeForReorg(m_chain, filter_final_and_mature);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(
*m_mempool,
@@ -596,10 +621,10 @@ private:
// Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
// cache - should only be called after successful validation of all transactions in the package.
- // The package may end up partially-submitted after size limitting; returns true if all
+ // The package may end up partially-submitted after size limiting; returns true if all
// transactions are successfully added to the mempool, false otherwise.
- bool FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
- std::map<const uint256, const MempoolAcceptResult>& results)
+ bool SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
+ std::map<const uint256, const MempoolAcceptResult>& results)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Compare a package's feerate against minimum allowed.
@@ -1038,12 +1063,17 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
return true;
}
-bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,
- PackageValidationState& package_state,
- std::map<const uint256, const MempoolAcceptResult>& results)
+bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,
+ PackageValidationState& package_state,
+ std::map<const uint256, const MempoolAcceptResult>& results)
{
AssertLockHeld(cs_main);
AssertLockHeld(m_pool.cs);
+ // Sanity check: none of the transactions should be in the mempool, and none of the transactions
+ // should have a same-txid-different-witness equivalent in the mempool.
+ assert(std::all_of(workspaces.cbegin(), workspaces.cend(), [this](const auto& ws){
+ return !m_pool.exists(GenTxid::Txid(ws.m_ptx->GetHash())); }));
+
bool all_submitted = true;
// ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
// CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
@@ -1053,18 +1083,24 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
if (!ConsensusScriptChecks(args, ws)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since PolicyScriptChecks() passed, this should never fail.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! PolicyScriptChecks succeeded but ConsensusScriptChecks failed: %s",
+ ws.m_ptx->GetHash().ToString()));
}
// 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 err_string;
+ 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, err_string)) {
+ m_limit_descendant_size, 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.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! Mempool ancestors or descendants were underestimated: %s",
+ ws.m_ptx->GetHash().ToString()));
}
// If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
// the transaction's descendant feerate into account because it hasn't seen them yet. Also,
@@ -1074,7 +1110,9 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
if (!Finalize(args, ws)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since LimitMempoolSize() won't be called, this should never fail.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! Adding to mempool failed: %s", ws.m_ptx->GetHash().ToString()));
}
}
@@ -1083,7 +1121,6 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(),
gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
- if (!all_submitted) return false;
// Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
// but don't report success unless they all made it into the mempool.
@@ -1188,8 +1225,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
if (args.m_test_accept) return PackageMempoolAcceptResult(package_state, std::move(results));
- if (!FinalizePackage(args, workspaces, package_state, results)) {
- package_state.Invalid(PackageValidationResult::PCKG_TX, "submission failed");
+ if (!SubmitPackage(args, workspaces, package_state, results)) {
+ // PackageValidationState filled in by SubmitPackage().
return PackageMempoolAcceptResult(package_state, std::move(results));
}
@@ -1214,11 +1251,13 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
return PackageMempoolAcceptResult(package_state, {});
}
- const auto& child = package[package.size() - 1];
+ // IsChildWithParents() guarantees the package is > 1 transactions.
+ assert(package.size() > 1);
// The package must be 1 child with all of its unconfirmed parents. The package is expected to
// be sorted, so the last transaction is the child.
+ const auto& child = package.back();
std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
- std::transform(package.cbegin(), package.end() - 1,
+ std::transform(package.cbegin(), package.cend() - 1,
std::inserter(unconfirmed_parent_txids, unconfirmed_parent_txids.end()),
[](const auto& tx) { return tx->GetHash(); });
@@ -1250,10 +1289,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
LOCK(m_pool.cs);
std::map<const uint256, const MempoolAcceptResult> results;
- // As node operators are free to set their mempool policies however they please, it's possible
- // for package transaction(s) to already be in the mempool, and we don't want to reject the
- // entire package in that case (as that could be a censorship vector). Filter the transactions
- // that are already in mempool and add their information to results, since we already have them.
+ // Node operators are free to set their mempool policies however they please, nodes may receive
+ // transactions in different orders, and malicious counterparties may try to take advantage of
+ // policy differences to pin or delay propagation of transactions. As such, it's possible for
+ // some package transaction(s) to already be in the mempool, and we don't want to reject the
+ // entire package in that case (as that could be a censorship vector). De-duplicate the
+ // transactions that are already in the mempool, and only call AcceptMultipleTransactions() with
+ // the new transactions. This ensures we don't double-count transaction counts and sizes when
+ // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
std::vector<CTransactionRef> txns_new;
for (const auto& tx : package) {
const auto& wtxid = tx->GetWitnessHash();
@@ -1274,9 +1317,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// transaction for the mempool one. Note that we are ignoring the validity of the
// package transaction passed in.
// TODO: allow witness replacement in packages.
- auto iter = m_pool.GetIter(wtxid);
+ auto iter = m_pool.GetIter(txid);
assert(iter != std::nullopt);
- results.emplace(txid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee()));
+ // Provide the wtxid of the mempool tx so that the caller can look it up in the mempool.
+ results.emplace(wtxid, MempoolAcceptResult::MempoolTxDifferentWitness(iter.value()->GetTx().GetWitnessHash()));
} else {
// Transaction does not already exist in the mempool.
txns_new.push_back(tx);
@@ -1343,12 +1387,12 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
}();
// Uncache coins pertaining to transactions that were not submitted to the mempool.
- // Ensure the coins cache is still within limits.
if (test_accept || result.m_state.IsInvalid()) {
for (const COutPoint& hashTx : coins_to_uncache) {
active_chainstate.CoinsTip().Uncache(hashTx);
}
}
+ // Ensure the coins cache is still within limits.
BlockValidationState state_dummy;
active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
return result;
@@ -1505,7 +1549,7 @@ void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationSt
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_chainman.m_failed_blocks.insert(pindex);
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
}
@@ -2135,13 +2179,13 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
if (fJustCheck)
return true;
- if (!WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
+ if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
return false;
}
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
}
assert(pindex->phashBlock);
@@ -2214,8 +2258,8 @@ bool CChainState::FlushStateToDisk(
bool fDoFullFlush = false;
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
- LOCK(cs_LastBlockFile);
- if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
+ LOCK(m_blockman.cs_LastBlockFile);
+ if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
// make sure we don't prune above the blockfilterindexes bestblocks
// pruning is height-based
int last_prune = m_chain.Height(); // last height we can prune
@@ -2231,7 +2275,7 @@ bool CChainState::FlushStateToDisk(
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
- fCheckForPruning = false;
+ m_blockman.m_check_for_pruning = false;
}
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
@@ -2269,26 +2313,14 @@ bool CChainState::FlushStateToDisk(
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
// First make sure all block and undo data is flushed to disk.
- FlushBlockFile();
+ m_blockman.FlushBlockFile();
}
// Then update all block file information (which may refer to block and undo files).
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk", BCLog::BENCH);
- std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
- vFiles.reserve(setDirtyFileInfo.size());
- for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
- vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it]));
- setDirtyFileInfo.erase(it++);
- }
- std::vector<const CBlockIndex*> vBlocks;
- vBlocks.reserve(setDirtyBlockIndex.size());
- for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
- vBlocks.push_back(*it);
- setDirtyBlockIndex.erase(it++);
- }
- if (!m_blockman.m_block_tree_db->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
+ if (!m_blockman.WriteBlockIndexDB()) {
return AbortNode(state, "Failed to write to block index database");
}
}
@@ -2348,7 +2380,7 @@ void CChainState::ForceFlushStateToDisk()
void CChainState::PruneAndFlush()
{
BlockValidationState state;
- fCheckForPruning = true;
+ m_blockman.m_check_for_pruning = true;
if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
@@ -2815,6 +2847,8 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
// us in the middle of ProcessNewBlock - do not assume pblock is set
@@ -2824,8 +2858,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
// ABC maintains a fair degree of expensive-to-calculate internal state
// because this function periodically releases cs_main so that it does not lock up other threads for too long
// during large connects - and to allow for e.g. the callback queue to drain
- // we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
- LOCK(m_cs_chainstate);
+ // we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time
+ LOCK(m_chainstate_mutex);
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
@@ -2944,6 +2978,8 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+
// Genesis block can't be invalidated
assert(pindex);
if (pindex->nHeight == 0) return false;
@@ -2955,7 +2991,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// We do not allow ActivateBestChain() to run while InvalidateBlock() is
// running, as that could cause the tip to change while we disconnect
// blocks.
- LOCK(m_cs_chainstate);
+ LOCK(m_chainstate_mutex);
// We'll be acquiring and releasing cs_main below, to allow the validation
// callbacks to run. However, we should keep the block index in a
@@ -3018,14 +3054,14 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// are no blocks that meet the "have data and are not invalid per
// nStatus" criteria for inclusion in setBlockIndexCandidates).
invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(invalid_walk_tip);
+ m_blockman.m_dirty_blockindex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
// We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
// need to be BLOCK_FAILED_CHILD instead.
to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(to_mark_failed);
+ m_blockman.m_dirty_blockindex.insert(to_mark_failed);
}
// Add any equal or more work headers to setBlockIndexCandidates
@@ -3055,7 +3091,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// Mark pindex (or the last disconnected block) as invalid, even when it never was in the main chain
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(to_mark_failed);
+ m_blockman.m_dirty_blockindex.insert(to_mark_failed);
setBlockIndexCandidates.erase(to_mark_failed);
m_chainman.m_failed_blocks.insert(to_mark_failed);
@@ -3094,7 +3130,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
while (it != m_blockman.m_block_index.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
- setDirtyBlockIndex.insert(it->second);
+ m_blockman.m_dirty_blockindex.insert(it->second);
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
@@ -3111,7 +3147,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
while (pindex != nullptr) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
m_chainman.m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
@@ -3131,7 +3167,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
- setDirtyBlockIndex.insert(pindexNew);
+ m_blockman.m_dirty_blockindex.insert(pindexNew);
if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
@@ -3493,7 +3529,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
CBlockIndex* invalid_walk = pindexPrev;
while (invalid_walk != failedit) {
invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(invalid_walk);
+ m_blockman.m_dirty_blockindex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
@@ -3531,7 +3567,10 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
if (NotifyHeaderTip(ActiveChainstate())) {
if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) {
- LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight);
+ const CBlockIndex& last_accepted{**ppindex};
+ const int64_t blocks_left{(GetTime() - last_accepted.GetBlockTime()) / chainparams.GetConsensus().nPowTargetSpacing};
+ const double progress{100.0 * last_accepted.nHeight / (last_accepted.nHeight + blocks_left)};
+ LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", last_accepted.nHeight, progress);
}
}
return true;
@@ -3591,7 +3630,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
!ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) {
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
}
return error("%s: %s", __func__, state.ToString());
}
@@ -3604,7 +3643,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
try {
- FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp);
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp)};
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3992,10 +4031,6 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
chainman.Unload();
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
- vinfoBlockFile.clear();
- nLastBlockFile = 0;
- setDirtyBlockIndex.clear();
- setDirtyFileInfo.clear();
g_versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
@@ -4039,9 +4074,10 @@ bool CChainState::LoadGenesisBlock()
try {
const CBlock& block = m_params.GenesisBlock();
- FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, m_params, nullptr);
- if (blockPos.IsNull())
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, m_params, nullptr)};
+ if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed", __func__);
+ }
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error& e) {
@@ -4072,7 +4108,7 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
try {
// locate a header
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
- blkdat.FindByte(char(m_params.MessageStart()[0]));
+ blkdat.FindByte(m_params.MessageStart()[0]);
nRewind = blkdat.GetPos() + 1;
blkdat >> buf;
if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
@@ -4879,7 +4915,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, breakpoint_fnc)) {
+ if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
@@ -4929,7 +4965,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
index->nStatus |= BLOCK_OPT_WITNESS;
}
- setDirtyBlockIndex.insert(index);
+ m_blockman.m_dirty_blockindex.insert(index);
// Changes to the block index will be flushed to disk after this call
// returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
// called, since we've added a snapshot chainstate and therefore will
diff --git a/src/validation.h b/src/validation.h
index 16f4bfe741..fb258005f1 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -43,12 +43,14 @@ class CBlockTreeDB;
class CChainParams;
class CTxMemPool;
class ChainstateManager;
-class SnapshotMetadata;
struct ChainTxData;
struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
struct AssumeutxoData;
+namespace node {
+class SnapshotMetadata;
+} // namespace node
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
@@ -161,8 +163,12 @@ struct MempoolAcceptResult {
VALID, //!> Fully validated, valid.
INVALID, //!> Invalid.
MEMPOOL_ENTRY, //!> Valid, transaction was already in the mempool.
+ DIFFERENT_WITNESS, //!> Not validated. A same-txid-different-witness tx (see m_other_wtxid) already exists in the mempool and was not replaced.
};
+ /** Result type. Present in all MempoolAcceptResults. */
const ResultType m_result_type;
+
+ /** Contains information about why the transaction failed. */
const TxValidationState m_state;
// The following fields are only present when m_result_type = ResultType::VALID or MEMPOOL_ENTRY
@@ -173,6 +179,10 @@ struct MempoolAcceptResult {
/** Raw base fees in satoshis. */
const std::optional<CAmount> m_base_fees;
+ // The following field is only present when m_result_type = ResultType::DIFFERENT_WITNESS
+ /** The wtxid of the transaction in the mempool which has the same txid but different witness. */
+ const std::optional<uint256> m_other_wtxid;
+
static MempoolAcceptResult Failure(TxValidationState state) {
return MempoolAcceptResult(state);
}
@@ -185,6 +195,10 @@ struct MempoolAcceptResult {
return MempoolAcceptResult(vsize, fees);
}
+ static MempoolAcceptResult MempoolTxDifferentWitness(const uint256& other_wtxid) {
+ return MempoolAcceptResult(other_wtxid);
+ }
+
// Private constructors. Use static methods MempoolAcceptResult::Success, etc. to construct.
private:
/** Constructor for failure case */
@@ -201,6 +215,10 @@ private:
/** Constructor for already-in-mempool case. It wouldn't replace any transactions. */
explicit MempoolAcceptResult(int64_t vsize, CAmount fees)
: m_result_type(ResultType::MEMPOOL_ENTRY), m_vsize{vsize}, m_base_fees(fees) {}
+
+ /** Constructor for witness-swapped case. */
+ explicit MempoolAcceptResult(const uint256& other_wtxid)
+ : m_result_type(ResultType::DIFFERENT_WITNESS), m_other_wtxid(other_wtxid) {}
};
/**
@@ -210,7 +228,7 @@ struct PackageMempoolAcceptResult
{
const PackageValidationState m_state;
/**
- * Map from (w)txid to finished MempoolAcceptResults. The client is responsible
+ * Map from wtxid to finished MempoolAcceptResults. The client is responsible
* for keeping track of the transaction objects themselves. If a result is not
* present, it means validation was unfinished for that transaction. If there
* was a package-wide error (see result in m_state), m_tx_results will be empty.
@@ -453,10 +471,11 @@ protected:
arith_uint256 nLastPreciousChainwork = 0;
/**
- * the ChainState CriticalSection
- * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
+ * The ChainState Mutex
+ * A lock that must be held when modifying this ChainState - held in ActivateBestChain() and
+ * InvalidateBlock()
*/
- RecursiveMutex m_cs_chainstate;
+ Mutex m_chainstate_mutex;
/**
* Whether this chainstate is undergoing initial block download.
@@ -476,7 +495,7 @@ protected:
public:
//! Reference to a BlockManager instance which itself is shared across all
//! CChainState instances.
- BlockManager& m_blockman;
+ node::BlockManager& m_blockman;
/** Chain parameters for this chainstate */
const CChainParams& m_params;
@@ -488,7 +507,7 @@ public:
explicit CChainState(
CTxMemPool* mempool,
- BlockManager& blockman,
+ node::BlockManager& blockman,
ChainstateManager& chainman,
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
@@ -535,7 +554,7 @@ public:
* chainstates) and as good as our current tip or better. Entries may be failed,
* though, and pruning nodes may be missing the data for the block.
*/
- std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
+ std::set<CBlockIndex*, node::CBlockIndexWorkComparator> setBlockIndexCandidates;
//! @returns A reference to the in-memory cache of the UTXO set.
CCoinsViewCache& CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -620,7 +639,7 @@ public:
*/
bool ActivateBestChain(
BlockValidationState& state,
- std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(cs_main);
+ std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(m_chainstate_mutex, cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -640,7 +659,7 @@ public:
*/
bool PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
- bool InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(m_chainstate_mutex, cs_main);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -803,13 +822,13 @@ private:
bool m_snapshot_validated{false};
CBlockIndex* m_best_invalid;
- friend bool BlockManager::LoadBlockIndex(const Consensus::Params&, ChainstateManager&);
+ friend bool node::BlockManager::LoadBlockIndex(const Consensus::Params&, ChainstateManager&);
//! Internal helper for ActivateSnapshot().
[[nodiscard]] bool PopulateAndValidateSnapshot(
CChainState& snapshot_chainstate,
CAutoFile& coins_file,
- const SnapshotMetadata& metadata);
+ const node::SnapshotMetadata& metadata);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
@@ -826,7 +845,7 @@ public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
- BlockManager m_blockman GUARDED_BY(::cs_main);
+ node::BlockManager m_blockman;
/**
* In order to efficiently track invalidity of headers, we keep the set of
@@ -886,7 +905,7 @@ public:
//! - Move the new chainstate to `m_snapshot_chainstate` and make it our
//! ChainstateActive().
[[nodiscard]] bool ActivateSnapshot(
- CAutoFile& coins_file, const SnapshotMetadata& metadata, bool in_memory);
+ CAutoFile& coins_file, const node::SnapshotMetadata& metadata, bool in_memory);
//! The most-work chain.
CChainState& ActiveChainstate() const;
@@ -894,7 +913,7 @@ public:
int ActiveHeight() const { return ActiveChain().Height(); }
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
- BlockMap& BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+ node::BlockMap& BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
return m_blockman.m_block_index;
}
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index a9264e6116..36815fba17 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -98,29 +98,38 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
-BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const
+BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks) const
{
BIP9Stats stats = {};
stats.period = Period(params);
stats.threshold = Threshold(params);
- if (pindex == nullptr)
- return stats;
+ if (pindex == nullptr) return stats;
// Find beginning of period
- const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period));
- stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight;
+ int blocks_in_period = 1 + (pindex->nHeight % stats.period);
+
+ // Reset signalling_blocks
+ if (signalling_blocks) {
+ signalling_blocks->assign(blocks_in_period, false);
+ }
// Count from current block to beginning of period
+ int elapsed = 0;
int count = 0;
const CBlockIndex* currentIndex = pindex;
- while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){
- if (Condition(currentIndex, params))
- count++;
+ do {
+ ++elapsed;
+ --blocks_in_period;
+ if (Condition(currentIndex, params)) {
+ ++count;
+ if (signalling_blocks) signalling_blocks->at(blocks_in_period) = true;
+ }
currentIndex = currentIndex->pprev;
- }
+ } while(blocks_in_period > 0);
+ stats.elapsed = elapsed;
stats.count = count;
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count);
@@ -196,9 +205,9 @@ ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Cons
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
}
-BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
+BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks)
{
- return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params);
+ return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params, signalling_blocks);
}
int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
diff --git a/src/versionbits.h b/src/versionbits.h
index 25ddd6fa5d..1b3fa11e61 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -64,8 +64,10 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
- /** Returns the numerical statistics of an in-progress BIP9 softfork in the current period */
- BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
+ /** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex
+ * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
+ */
+ BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks = nullptr) const;
/** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
* Caches state from first block of period. */
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
@@ -82,8 +84,10 @@ private:
ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex);
public:
- /** Get the numerical statistics for a given deployment for the signalling period that includes the block after pindexPrev. */
- static BIP9Stats Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+ /** Get the numerical statistics for a given deployment for the signalling period that includes pindex.
+ * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
+ */
+ static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks = nullptr);
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 5b264ac4ad..49f0abf9e7 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <fs.h>
#include <wallet/bdb.h>
#include <wallet/db.h>
@@ -15,6 +16,7 @@
#include <sys/stat.h>
#endif
+namespace wallet {
namespace {
//! Make sure database has a unique fileid within the environment. If it
@@ -619,12 +621,12 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const
pathDest /= fs::PathFromString(strFile);
try {
- if (fs::equivalent(pathSrc, pathDest)) {
+ if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
return false;
}
- fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
+ fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
return true;
} catch (const fs::filesystem_error& e) {
@@ -680,10 +682,10 @@ bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool&
// Convert to streams
ssKey.SetType(SER_DISK);
ssKey.clear();
- ssKey.write((char*)datKey.get_data(), datKey.get_size());
+ ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()});
ssValue.SetType(SER_DISK);
ssValue.clear();
- ssValue.write((char*)datValue.get_data(), datValue.get_size());
+ ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()});
return true;
}
@@ -755,7 +757,7 @@ bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value)
SafeDbt datValue;
int ret = pdb->get(activeTxn, datKey, datValue, 0);
if (ret == 0 && datValue.get_data() != nullptr) {
- value.write((char*)datValue.get_data(), datValue.get_size());
+ value.write({BytePtr(datValue.get_data()), datValue.get_size()});
return true;
}
return false;
@@ -846,3 +848,4 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
status = DatabaseStatus::SUCCESS;
return db;
}
+} // namespace wallet
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 0cf9843cc8..b924890d81 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -31,6 +31,7 @@
struct bilingual_str;
+namespace wallet {
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
@@ -229,5 +230,6 @@ bool BerkeleyDatabaseSanityCheck();
//! Return object giving access to Berkeley database at specified path.
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+} // namespace wallet
#endif // BITCOIN_WALLET_BDB_H
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index bf582e1cc1..3b3c1f8da4 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -6,7 +6,9 @@
#include <util/system.h>
+namespace wallet {
CCoinControl::CCoinControl()
{
m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
}
+} // namespace wallet
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index cb30b09b87..65a5c83366 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -18,6 +18,7 @@
#include <map>
#include <set>
+namespace wallet {
const int DEFAULT_MIN_DEPTH = 0;
const int DEFAULT_MAX_DEPTH = 9999999;
@@ -114,9 +115,29 @@ public:
vOutpoints.assign(setSelected.begin(), setSelected.end());
}
+ void SetInputWeight(const COutPoint& outpoint, int64_t weight)
+ {
+ m_input_weights[outpoint] = weight;
+ }
+
+ bool HasInputWeight(const COutPoint& outpoint) const
+ {
+ return m_input_weights.count(outpoint) > 0;
+ }
+
+ int64_t GetInputWeight(const COutPoint& outpoint) const
+ {
+ auto it = m_input_weights.find(outpoint);
+ assert(it != m_input_weights.end());
+ return it->second;
+ }
+
private:
std::set<COutPoint> setSelected;
std::map<COutPoint, CTxOut> m_external_txouts;
+ //! Map of COutPoints to the maximum weight for that input
+ std::map<COutPoint, int64_t> m_input_weights;
};
+} // namespace wallet
#endif // BITCOIN_WALLET_COINCONTROL_H
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 3b48a8957e..23faad027f 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -13,6 +13,7 @@
#include <numeric>
#include <optional>
+namespace wallet {
// Descending order comparator
struct {
bool operator()(const OutputGroup& a, const OutputGroup& b) const
@@ -429,3 +430,4 @@ bool SelectionResult::operator<(SelectionResult other) const
// As this operator is only used in std::min_element, we want the result that has more inputs when waste are equal.
return *m_waste < *other.m_waste || (*m_waste == *other.m_waste && m_selected_inputs.size() > other.m_selected_inputs.size());
}
+} // namespace wallet
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index a0001351f7..496a026999 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -12,6 +12,7 @@
#include <optional>
+namespace wallet {
//! target minimum change amount
static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
@@ -249,5 +250,6 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut
// Original coin selection algorithm as a fallback
std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue);
+} // namespace wallet
#endif // BITCOIN_WALLET_COINSELECTION_H
diff --git a/src/wallet/context.cpp b/src/wallet/context.cpp
index 09b2f30467..800aa5bf9c 100644
--- a/src/wallet/context.cpp
+++ b/src/wallet/context.cpp
@@ -4,5 +4,7 @@
#include <wallet/context.h>
+namespace wallet {
WalletContext::WalletContext() {}
WalletContext::~WalletContext() {}
+} // namespace wallet
diff --git a/src/wallet/context.h b/src/wallet/context.h
index 0c11182ee6..57a6ed77f7 100644
--- a/src/wallet/context.h
+++ b/src/wallet/context.h
@@ -13,12 +13,13 @@
#include <vector>
class ArgsManager;
-class CWallet;
namespace interfaces {
class Chain;
class Wallet;
} // namespace interfaces
+namespace wallet {
+class CWallet;
using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
//! WalletContext struct containing references to state shared between CWallet
@@ -46,5 +47,6 @@ struct WalletContext {
WalletContext();
~WalletContext();
};
+} // namespace wallet
#endif // BITCOIN_WALLET_CONTEXT_H
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 683b55a408..cd414b3d44 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -10,6 +10,7 @@
#include <vector>
+namespace wallet {
int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const
{
// This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc
@@ -136,3 +137,4 @@ bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned ch
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
return key.VerifyPubKey(vchPubKey);
}
+} // namespace wallet
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index f7325541a9..4d325c7557 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -10,6 +10,7 @@
#include <script/signingprovider.h>
+namespace wallet {
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
const unsigned int WALLET_CRYPTO_IV_SIZE = 16;
@@ -105,5 +106,6 @@ public:
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext);
bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key);
+} // namespace wallet
#endif // BITCOIN_WALLET_CRYPTER_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 6437e1d7f0..0ed2658129 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -8,17 +8,22 @@
#include <logging.h>
#include <wallet/db.h>
+#include <exception>
+#include <fstream>
#include <string>
+#include <system_error>
+#include <vector>
+namespace wallet {
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{
std::vector<fs::path> paths;
- boost::system::error_code ec;
+ std::error_code ec;
for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
if (ec) {
if (fs::is_directory(*it)) {
- it.no_push();
+ it.disable_recursion_pending();
LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
} else {
LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
@@ -29,11 +34,11 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
try {
const fs::path path{it->path().lexically_relative(wallet_dir)};
- if (it->status().type() == fs::directory_file &&
+ if (it->status().type() == fs::file_type::directory &&
(IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) {
// Found a directory which contains wallet.dat btree file, add it as a wallet.
paths.emplace_back(path);
- } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBDBFile(it->path())) {
+ } else if (it.depth() == 0 && it->symlink_status().type() == fs::file_type::regular && IsBDBFile(it->path())) {
if (it->path().filename() == "wallet.dat") {
// Found top-level wallet.dat btree file, add top level directory ""
// as a wallet.
@@ -48,7 +53,7 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
}
} catch (const std::exception& e) {
LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
- it.no_push();
+ it.disable_recursion_pending();
}
}
@@ -80,12 +85,12 @@ bool IsBDBFile(const fs::path& path)
// A Berkeley DB Btree file has at least 4K.
// This check also prevents opening lock files.
- boost::system::error_code ec;
+ std::error_code ec;
auto size = fs::file_size(path, ec);
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 4096) return false;
- fsbridge::ifstream file(path, std::ios::binary);
+ std::ifstream file{path, std::ios::binary};
if (!file.is_open()) return false;
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
@@ -104,12 +109,12 @@ bool IsSQLiteFile(const fs::path& path)
if (!fs::exists(path)) return false;
// A SQLite Database file is at least 512 bytes.
- boost::system::error_code ec;
+ std::error_code ec;
auto size = fs::file_size(path, ec);
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 512) return false;
- fsbridge::ifstream file(path, std::ios::binary);
+ std::ifstream file{path, std::ios::binary};
if (!file.is_open()) return false;
// Magic is at beginning and is 16 bytes long
@@ -132,3 +137,4 @@ bool IsSQLiteFile(const fs::path& path)
// Check the application id matches our network magic
return memcmp(Params().MessageStart(), app_id, 4) == 0;
}
+} // namespace wallet
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 7acc27e2b4..5825b00e3a 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -18,6 +18,7 @@
struct bilingual_str;
+namespace wallet {
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
/** RAII class that provides access to a WalletDatabase */
@@ -232,5 +233,6 @@ fs::path BDBDataFile(const fs::path& path);
fs::path SQLiteDataFile(const fs::path& path);
bool IsBDBFile(const fs::path& path);
bool IsSQLiteFile(const fs::path& path);
+} // namespace wallet
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 9a4c201c4a..6d8508fc72 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -4,9 +4,18 @@
#include <wallet/dump.h>
+#include <fs.h>
#include <util/translation.h>
#include <wallet/wallet.h>
+#include <algorithm>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace wallet {
static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
uint32_t DUMP_VERSION = 1;
@@ -25,7 +34,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path));
return false;
}
- fsbridge::ofstream dump_file;
+ std::ofstream dump_file;
dump_file.open(path);
if (dump_file.fail()) {
error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
@@ -46,12 +55,12 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
// Write out a magic string with version
std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
// Write out the file format
line = strprintf("%s,%s\n", "format", db.Format());
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
if (ret) {
@@ -72,7 +81,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
std::string value_str = HexStr(ss_value);
line = strprintf("%s,%s\n", key_str, value_str);
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
}
}
@@ -120,7 +129,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
return false;
}
- fsbridge::ifstream dump_file(dump_path);
+ std::ifstream dump_file{dump_path};
// Compute the checksum
CHashWriter hasher(0, 0);
@@ -149,7 +158,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
return false;
}
std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
- hasher.write(magic_hasher_line.data(), magic_hasher_line.size());
+ hasher.write(MakeByteSpan(magic_hasher_line));
// Get the stored file format
std::string format_key;
@@ -180,7 +189,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format));
}
std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
- hasher.write(format_hasher_line.data(), format_hasher_line.size());
+ hasher.write(MakeByteSpan(format_hasher_line));
DatabaseOptions options;
DatabaseStatus status;
@@ -224,7 +233,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
}
std::string line = strprintf("%s,%s\n", key, value);
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
if (key.empty() || value.empty()) {
continue;
@@ -284,3 +293,4 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
return ret;
}
+} // namespace wallet
diff --git a/src/wallet/dump.h b/src/wallet/dump.h
index d0a4f5ef1d..a879c4db35 100644
--- a/src/wallet/dump.h
+++ b/src/wallet/dump.h
@@ -7,11 +7,15 @@
#include <fs.h>
-class CWallet;
+#include <string>
+#include <vector>
struct bilingual_str;
+namespace wallet {
+class CWallet;
bool DumpWallet(CWallet& wallet, bilingual_str& error);
bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings);
+} // namespace wallet
#endif // BITCOIN_WALLET_DUMP_H
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index ad89478874..9d5f58b784 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -13,6 +13,7 @@
#include <utility>
#include <vector>
+namespace wallet {
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
{
LOCK(cs_desc_man);
@@ -82,3 +83,4 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
return TransactionError::OK;
}
+} // namespace wallet
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index 92c1ccb401..9918979a81 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -9,6 +9,7 @@
#include <memory>
+namespace wallet {
class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
{
public:
@@ -30,4 +31,5 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
};
+} // namespace wallet
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 242a3c2847..3552c14160 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -16,6 +16,7 @@
#include <wallet/spend.h>
#include <wallet/wallet.h>
+namespace wallet {
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -282,3 +283,4 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
}
} // namespace feebumper
+} // namespace wallet
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 50577c9d3e..191878a137 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -7,13 +7,15 @@
#include <primitives/transaction.h>
-class CWallet;
-class CWalletTx;
class uint256;
-class CCoinControl;
enum class FeeEstimateMode;
struct bilingual_str;
+namespace wallet {
+class CCoinControl;
+class CWallet;
+class CWalletTx;
+
namespace feebumper {
enum class Result
@@ -54,5 +56,6 @@ Result CommitTransaction(CWallet& wallet,
uint256& bumped_txid);
} // namespace feebumper
+} // namespace wallet
#endif // BITCOIN_WALLET_FEEBUMPER_H
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 429101e774..6f81fa30a1 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -9,6 +9,7 @@
#include <wallet/wallet.h>
+namespace wallet {
CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
{
return GetRequiredFeeRate(wallet).GetFee(nTxBytes);
@@ -90,3 +91,4 @@ CFeeRate GetDiscardRate(const CWallet& wallet)
discard_rate = std::max(discard_rate, wallet.chain().relayDustFee());
return discard_rate;
}
+} // namespace wallet
diff --git a/src/wallet/fees.h b/src/wallet/fees.h
index ad35670d4e..af7f759553 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -8,11 +8,13 @@
#include <consensus/amount.h>
-class CCoinControl;
class CFeeRate;
-class CWallet;
struct FeeCalculation;
+namespace wallet {
+class CCoinControl;
+class CWallet;
+
/**
* Return the minimum required absolute fee for this size
* based on the required fee rate
@@ -41,5 +43,6 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
* Return the maximum feerate for discarding change.
*/
CFeeRate GetDiscardRate(const CWallet& wallet);
+} // namespace wallet
#endif // BITCOIN_WALLET_FEES_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 2f73ec3d4c..7a83dbc35d 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -23,6 +23,9 @@
#include <wallet/wallet.h>
#include <walletinitinterface.h>
+using node::NodeContext;
+
+namespace wallet {
class WalletInit : public WalletInitInterface
{
public:
@@ -39,8 +42,6 @@ public:
void Construct(NodeContext& node) const override;
};
-const WalletInitInterface& g_wallet_init_interface = WalletInit();
-
void WalletInit::AddWalletOptions(ArgsManager& argsman) const
{
argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", \"bech32\", or \"bech32m\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
@@ -137,3 +138,6 @@ void WalletInit::Construct(NodeContext& node) const
node.wallet_loader = wallet_loader.get();
node.chain_clients.emplace_back(std::move(wallet_loader));
}
+} // namespace wallet
+
+const WalletInitInterface& g_wallet_init_interface = wallet::WalletInit();
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 64775a5ddb..9083c304b2 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -90,7 +90,6 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
- result.is_final = wallet.chain().checkFinalTx(*wtx.tx);
result.is_trusted = CachedTxIsTrusted(wallet, wtx);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
@@ -557,7 +556,7 @@ public:
options.require_existing = true;
return MakeWallet(m_context, LoadWallet(m_context, name, true /* load_on_start */, options, status, error, warnings));
}
- std::unique_ptr<Wallet> restoreWallet(const std::string& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ std::unique_ptr<Wallet> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{
DatabaseStatus status;
@@ -598,7 +597,7 @@ public:
} // namespace wallet
namespace interfaces {
-std::unique_ptr<Wallet> MakeWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; }
+std::unique_ptr<Wallet> MakeWallet(wallet::WalletContext& context, const std::shared_ptr<wallet::CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; }
std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args)
{
diff --git a/src/wallet/ismine.h b/src/wallet/ismine.h
index 8527b460df..4ef4ef98ac 100644
--- a/src/wallet/ismine.h
+++ b/src/wallet/ismine.h
@@ -12,9 +12,11 @@
#include <cstdint>
#include <type_traits>
-class CWallet;
class CScript;
+namespace wallet {
+class CWallet;
+
/**
* IsMine() return codes, which depend on ScriptPubKeyMan implementation.
* Not every ScriptPubKeyMan covers all types, please refer to
@@ -66,5 +68,6 @@ struct CachableAmount
m_value[filter] = value;
}
};
+} // namespace wallet
#endif // BITCOIN_WALLET_ISMINE_H
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 4d59e17709..4949ed7dc9 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -19,6 +19,9 @@
#include <univalue.h>
+#include <system_error>
+
+namespace wallet {
bool VerifyWallets(WalletContext& context)
{
interfaces::Chain& chain = *context.chain;
@@ -26,7 +29,7 @@ bool VerifyWallets(WalletContext& context)
if (args.IsArgSet("-walletdir")) {
fs::path wallet_dir = fs::PathFromString(args.GetArg("-walletdir", ""));
- boost::system::error_code error;
+ std::error_code error;
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) {
@@ -169,3 +172,4 @@ void UnloadWallets(WalletContext& context)
UnloadWallet(std::move(wallet));
}
}
+} // namespace wallet
diff --git a/src/wallet/load.h b/src/wallet/load.h
index e207bc2e09..5c2bbdabe4 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -11,12 +11,14 @@
class ArgsManager;
class CScheduler;
-struct WalletContext;
namespace interfaces {
class Chain;
} // namespace interfaces
+namespace wallet {
+struct WalletContext;
+
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
bool VerifyWallets(WalletContext& context);
@@ -34,5 +36,6 @@ void StopWallets(WalletContext& context);
//! Close all wallets.
void UnloadWallets(WalletContext& context);
+} // namespace wallet
#endif // BITCOIN_WALLET_LOAD_H
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index 2fb274b55f..1a6f06213c 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -8,6 +8,7 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+namespace wallet {
isminetype InputIsMine(const CWallet& wallet, const CTxIn &txin)
{
AssertLockHeld(wallet.cs_wallet);
@@ -278,8 +279,6 @@ bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminef
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
{
AssertLockHeld(wallet.cs_wallet);
- // Quick answer in most cases
- if (!wallet.chain().checkFinalTx(*wtx.tx)) return false;
int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
@@ -473,3 +472,4 @@ std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
return ret;
}
+} // namespace wallet
diff --git a/src/wallet/receive.h b/src/wallet/receive.h
index f659955fc6..d7705b5262 100644
--- a/src/wallet/receive.h
+++ b/src/wallet/receive.h
@@ -10,6 +10,7 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+namespace wallet {
isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
/** Returns whether all of the inputs match the filter */
@@ -60,5 +61,6 @@ Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse =
std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet);
std::set<std::set<CTxDestination>> GetAddressGroupings(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+} // namespace wallet
#endif // BITCOIN_WALLET_RECEIVE_H
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index 2db47bc855..51587a64a3 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -13,6 +13,7 @@
#include <univalue.h>
+namespace wallet {
RPCHelpMan getnewaddress()
{
return RPCHelpMan{"getnewaddress",
@@ -802,3 +803,4 @@ RPCHelpMan walletdisplayaddress()
};
}
#endif // ENABLE_EXTERNAL_SIGNER
+} // namespace wallet
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 0029d1b09c..228564fae4 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <clientversion.h>
#include <core_io.h>
+#include <fs.h>
#include <interfaces/chain.h>
#include <key_io.h>
#include <merkleblock.h>
@@ -20,8 +21,10 @@
#include <wallet/rpc/util.h>
#include <wallet/wallet.h>
-#include <stdint.h>
+#include <cstdint>
+#include <fstream>
#include <tuple>
+#include <string>
#include <boost/algorithm/string.hpp>
@@ -31,6 +34,7 @@
using interfaces::FoundBlock;
+namespace wallet {
std::string static EncodeDumpString(const std::string &str) {
std::stringstream ret;
for (const unsigned char c : str) {
@@ -520,7 +524,7 @@ RPCHelpMan importwallet()
EnsureWalletIsUnlocked(*pwallet);
- fsbridge::ifstream file;
+ std::ifstream file;
file.open(fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate);
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -728,7 +732,7 @@ RPCHelpMan dumpwallet()
throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.u8string() + " already exists. If you are sure this is what you want, move it out of the way first");
}
- fsbridge::ofstream file;
+ std::ofstream file;
file.open(filepath);
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -1887,7 +1891,7 @@ RPCHelpMan restorewallet()
bilingual_str error;
std::vector<bilingual_str> warnings;
- const std::shared_ptr<CWallet> wallet = RestoreWallet(context, fs::PathToString(backup_file), wallet_name, load_on_start, status, error, warnings);
+ const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
HandleWalletError(wallet, status, error);
@@ -1900,3 +1904,4 @@ RPCHelpMan restorewallet()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index f3294b4570..035541babd 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -15,6 +15,7 @@
#include <univalue.h>
+namespace wallet {
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
std::set<CTxDestination> address_set;
@@ -59,8 +60,8 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
if (depth < min_depth
// Coinbase with less than 1 confirmation is no longer in the main chain
|| (wtx.IsCoinBase() && (depth < 1 || !include_coinbase))
- || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
- || !wallet.chain().checkFinalTx(*wtx.tx)) {
+ || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
+ {
continue;
}
@@ -731,3 +732,4 @@ RPCHelpMan listunspent()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp
index e659f434a3..2b6a2a198d 100644
--- a/src/wallet/rpc/encrypt.cpp
+++ b/src/wallet/rpc/encrypt.cpp
@@ -7,6 +7,7 @@
#include <wallet/wallet.h>
+namespace wallet {
RPCHelpMan walletpassphrase()
{
return RPCHelpMan{"walletpassphrase",
@@ -246,3 +247,4 @@ RPCHelpMan encryptwallet()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/signmessage.cpp b/src/wallet/rpc/signmessage.cpp
index bb8d7fc13f..438d290030 100644
--- a/src/wallet/rpc/signmessage.cpp
+++ b/src/wallet/rpc/signmessage.cpp
@@ -10,6 +10,7 @@
#include <univalue.h>
+namespace wallet {
RPCHelpMan signmessage()
{
return RPCHelpMan{"signmessage",
@@ -66,3 +67,4 @@ RPCHelpMan signmessage()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index 978174b340..433b5a1815 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.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 <consensus/validation.h>
#include <core_io.h>
#include <key_io.h>
#include <policy/policy.h>
@@ -19,6 +20,7 @@
#include <univalue.h>
+namespace wallet {
static void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
std::set<CTxDestination> destinations;
int i = 0;
@@ -428,6 +430,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
{"replaceable", UniValueType(UniValue::VBOOL)},
{"conf_target", UniValueType(UniValue::VNUM)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
+ {"input_weights", UniValueType(UniValue::VARR)},
},
true, true);
@@ -547,6 +550,37 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
}
}
+ if (options.exists("input_weights")) {
+ for (const UniValue& input : options["input_weights"].get_array().getValues()) {
+ uint256 txid = ParseHashO(input, "txid");
+
+ const UniValue& vout_v = find_value(input, "vout");
+ if (!vout_v.isNum()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ }
+ int vout = vout_v.get_int();
+ if (vout < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
+ }
+
+ const UniValue& weight_v = find_value(input, "weight");
+ if (!weight_v.isNum()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key");
+ }
+ int64_t weight = weight_v.get_int64();
+ const int64_t min_input_weight = GetTransactionInputWeight(CTxIn());
+ CHECK_NONFATAL(min_input_weight == 165);
+ if (weight < min_input_weight) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, weight cannot be less than 165 (41 bytes (size of outpoint + sequence + empty scriptSig) * 4 (witness scaling factor)) + 1 (empty witness)");
+ }
+ if (weight > MAX_STANDARD_TX_WEIGHT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, weight cannot be greater than the maximum standard tx weight of %d", MAX_STANDARD_TX_WEIGHT));
+ }
+
+ coinControl.SetInputWeight(COutPoint(txid, vout), weight);
+ }
+ }
+
if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
@@ -584,6 +618,23 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
}
}
+static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options)
+{
+ if (options.exists("input_weights")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Input weights should be specified in inputs rather than in options.");
+ }
+ if (inputs.size() == 0) {
+ return;
+ }
+ UniValue weights(UniValue::VARR);
+ for (const UniValue& input : inputs.getValues()) {
+ if (input.exists("weight")) {
+ weights.push_back(input);
+ }
+ }
+ options.pushKV("input_weights", weights);
+}
+
RPCHelpMan fundrawtransaction()
{
return RPCHelpMan{"fundrawtransaction",
@@ -625,6 +676,17 @@ RPCHelpMan fundrawtransaction()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
+ {"input_weights", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding weights",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
+ {"weight", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that serialized signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
+ },
+ },
},
FundTxDoc()),
"options"},
@@ -1006,6 +1068,11 @@ RPCHelpMan send()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
+ {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
},
},
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
@@ -1109,6 +1176,7 @@ RPCHelpMan send()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ SetOptionsInputWeights(options["inputs"], options);
FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
bool add_to_wallet = true;
@@ -1249,6 +1317,11 @@ RPCHelpMan walletcreatefundedpsbt()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"},
+ {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
},
},
},
@@ -1329,10 +1402,12 @@ RPCHelpMan walletcreatefundedpsbt()
}, true
);
+ UniValue options = request.params[3];
+
CAmount fee;
int change_position;
bool rbf{wallet.m_signal_rbf};
- const UniValue &replaceable_arg = request.params[3]["replaceable"];
+ const UniValue &replaceable_arg = options["replaceable"];
if (!replaceable_arg.isNull()) {
RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
rbf = replaceable_arg.isTrue();
@@ -1342,7 +1417,8 @@ RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
+ SetOptionsInputWeights(request.params[0], options);
+ FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ true);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
@@ -1367,3 +1443,4 @@ RPCHelpMan walletcreatefundedpsbt()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 8a1c0885ac..eef2c13ee1 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -13,6 +13,7 @@
using interfaces::FoundBlock;
+namespace wallet {
static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
{
interfaces::Chain& chain = wallet.chain();
@@ -113,8 +114,8 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
// Coinbase with less than 1 confirmation is no longer in the main chain
if ((wtx.IsCoinBase() && (nDepth < 1 || !include_coinbase))
- || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
- || !wallet.chain().checkFinalTx(*wtx.tx)) {
+ || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
+ {
continue;
}
@@ -958,3 +959,4 @@ RPCHelpMan abortrescan()
},
};
}
+} // namespace wallet
diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp
index 9a40a67ee5..59683c5fd8 100644
--- a/src/wallet/rpc/util.cpp
+++ b/src/wallet/rpc/util.cpp
@@ -12,6 +12,7 @@
#include <univalue.h>
+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"};
@@ -147,4 +148,5 @@ void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& st
}
throw JSONRPCError(code, error.original);
}
-} \ No newline at end of file
+}
+} // namespace wallet
diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h
index 5b00d2abcb..7b810eb06e 100644
--- a/src/wallet/rpc/util.h
+++ b/src/wallet/rpc/util.h
@@ -10,12 +10,14 @@
#include <string>
#include <vector>
+class JSONRPCRequest;
+class UniValue;
struct bilingual_str;
+
+namespace wallet {
class CWallet;
-enum class DatabaseStatus;
-class JSONRPCRequest;
class LegacyScriptPubKeyMan;
-class UniValue;
+enum class DatabaseStatus;
struct WalletContext;
extern const std::string HELP_REQUIRING_PASSPHRASE;
@@ -39,5 +41,6 @@ bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wal
std::string LabelFromValue(const UniValue& value);
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error);
+} // namespace wallet
#endif // BITCOIN_WALLET_RPC_UTIL_H
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index bbe40feec0..33ec715b51 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -18,6 +18,7 @@
#include <univalue.h>
+namespace wallet {
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
bool HaveKey(const SigningProvider& wallet, const CKey& key)
{
@@ -729,3 +730,4 @@ static const CRPCCommand commands[] =
// clang-format on
return commands;
}
+} // namespace wallet
diff --git a/src/wallet/rpc/wallet.h b/src/wallet/rpc/wallet.h
index 537d9b2358..423fc892b2 100644
--- a/src/wallet/rpc/wallet.h
+++ b/src/wallet/rpc/wallet.h
@@ -9,6 +9,8 @@
class CRPCCommand;
+namespace wallet {
Span<const CRPCCommand> GetWalletRPCCommands();
+} // namespace wallet
#endif // BITCOIN_WALLET_RPC_WALLET_H
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index 3859d67b6d..1ecc96fe0e 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -11,6 +11,7 @@
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
+namespace wallet {
/* End of headers, beginning of key/value data */
static const char *HEADER_END = "HEADER=END";
/* End of key/value data */
@@ -165,3 +166,4 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
return fSuccess;
}
+} // namespace wallet
diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h
index 5a8538f942..332aceb262 100644
--- a/src/wallet/salvage.h
+++ b/src/wallet/salvage.h
@@ -11,6 +11,8 @@
struct bilingual_str;
+namespace wallet {
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings);
+} // namespace wallet
#endif // BITCOIN_WALLET_SALVAGE_H
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 04a52bc86f..7218ed11dc 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -17,6 +17,7 @@
#include <optional>
+namespace wallet {
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
@@ -2359,3 +2360,4 @@ bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescript
return true;
}
+} // namespace wallet
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 8f511b68b5..6eda133771 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -25,6 +25,7 @@
enum class OutputType;
struct bilingual_str;
+namespace wallet {
// Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database.
// It provides access to things that are part of the entire wallet and not specific to a ScriptPubKeyMan such as
// wallet flags, wallet version, encryption keys, encryption status, and the database itself. This allows a
@@ -631,5 +632,6 @@ public:
void UpgradeDescriptorCache();
};
+} // namespace wallet
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 5d9cc7bf6b..3d8ae2da69 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -21,6 +21,7 @@
using interfaces::FoundBlock;
+namespace wallet {
static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100};
int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig)
@@ -104,10 +105,6 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
const uint256& wtxid = entry.first;
const CWalletTx& wtx = entry.second;
- if (!wallet.chain().checkFinalTx(*wtx.tx)) {
- continue;
- }
-
if (wallet.IsTxImmatureCoinBase(wtx))
continue;
@@ -454,15 +451,17 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
txout = wtx.tx->vout.at(outpoint.n);
- }
- if (input_bytes == -1) {
- // The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data
+ } else {
+ // The input is external. We did not find the tx in mapWallet.
if (!coin_control.GetExternalOutput(outpoint, txout)) {
- // Not ours, and we don't have solving data.
return std::nullopt;
}
input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
}
+ // If available, override calculated size with coin control specified size
+ if (coin_control.HasInputWeight(outpoint)) {
+ input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0);
+ }
CInputCoin coin(outpoint, txout, input_bytes);
if (coin.m_input_bytes == -1) {
@@ -665,8 +664,6 @@ static bool CreateTransactionInternal(
}
// Create change script that will be used if we need change
- // TODO: pass in scriptChange instead of reservedest so
- // change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
// coin control: send change to custom address
@@ -799,7 +796,7 @@ static bool CreateTransactionInternal(
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior
// behavior."
- const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ const uint32_t nSequence{coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : CTxIn::MAX_SEQUENCE_NONFINAL};
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
@@ -1030,3 +1027,4 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
return true;
}
+} // namespace wallet
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index 268bcfc033..4453fb2762 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -10,6 +10,7 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+namespace wallet {
/** Get the marginal bytes if spending the specified output from this transaction */
int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig = false);
@@ -142,5 +143,6 @@ bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend,
* calling CreateTransaction();
*/
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
+} // namespace wallet
#endif // BITCOIN_WALLET_SPEND_H
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 3bea0de99f..2b2181e70b 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
+namespace wallet {
static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
static Mutex g_sqlite_mutex;
@@ -394,9 +395,9 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
return false;
}
// Leftmost column in result is index 0
- const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0));
- int data_size = sqlite3_column_bytes(m_read_stmt, 0);
- value.write(data, data_size);
+ const std::byte* data{BytePtr(sqlite3_column_blob(m_read_stmt, 0))};
+ size_t data_size(sqlite3_column_bytes(m_read_stmt, 0));
+ value.write({data, data_size});
sqlite3_clear_bindings(m_read_stmt);
sqlite3_reset(m_read_stmt);
@@ -511,12 +512,12 @@ bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& compl
}
// Leftmost column in result is index 0
- const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0));
- int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0);
- key.write(key_data, key_data_size);
- const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1));
- int value_data_size = sqlite3_column_bytes(m_cursor_stmt, 1);
- value.write(value_data, value_data_size);
+ const std::byte* key_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 0))};
+ size_t key_data_size(sqlite3_column_bytes(m_cursor_stmt, 0));
+ key.write({key_data, key_data_size});
+ const std::byte* value_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 1))};
+ size_t value_data_size(sqlite3_column_bytes(m_cursor_stmt, 1));
+ value.write({value_data, value_data_size});
return true;
}
@@ -578,3 +579,4 @@ std::string SQLiteDatabaseVersion()
{
return std::string(sqlite3_libversion());
}
+} // namespace wallet
diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h
index 70ab4f797a..3ed598d0d2 100644
--- a/src/wallet/sqlite.h
+++ b/src/wallet/sqlite.h
@@ -10,6 +10,8 @@
#include <sqlite3.h>
struct bilingual_str;
+
+namespace wallet {
class SQLiteDatabase;
/** RAII class that provides access to a WalletDatabase */
@@ -116,5 +118,6 @@ public:
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
std::string SQLiteDatabaseVersion();
+} // namespace wallet
#endif // BITCOIN_WALLET_SQLITE_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 9ab3c81078..b9f12158ca 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -18,6 +18,7 @@
#include <boost/test/unit_test.hpp>
#include <random>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
@@ -807,3 +808,4 @@ BOOST_AUTO_TEST_CASE(waste_test)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index d9359bc171..35ae3707f8 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -2,15 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <memory>
-
#include <boost/test/unit_test.hpp>
#include <fs.h>
#include <test/util/setup_common.h>
#include <wallet/bdb.h>
+#include <fstream>
+#include <memory>
+#include <string>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, std::string& database_filename)
@@ -25,7 +27,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
std::string test_name = "test_name.dat";
const fs::path datadir = gArgs.GetDataDirNet();
fs::path file_path = datadir / test_name;
- fs::ofstream f(file_path);
+ std::ofstream f{file_path};
f.close();
std::string filename;
@@ -77,3 +79,4 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp
index 0601c492cd..1c16da25bd 100644
--- a/src/wallet/test/fuzz/notifications.cpp
+++ b/src/wallet/test/fuzz/notifications.cpp
@@ -18,6 +18,7 @@
#include <string>
#include <vector>
+namespace wallet {
namespace {
const TestingSetup* g_setup;
@@ -168,3 +169,4 @@ FUZZ_TARGET_INIT(wallet_notifications, initialize_setup)
}
}
} // namespace
+} // namespace wallet
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 439489ab59..be38cebafd 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -7,8 +7,12 @@
#include <util/check.h>
#include <util/system.h>
+#include <fstream>
+#include <string>
+
#include <wallet/test/init_test_fixture.h>
+namespace wallet {
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
m_wallet_loader = MakeWalletLoader(*m_node.chain, *Assert(m_node.args));
@@ -23,8 +27,8 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
m_walletdir_path_cases["custom"] = m_datadir / "my_wallets";
m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist";
m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat";
- m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep;
- m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep;
+ m_walletdir_path_cases["trailing"] = m_datadir / ("wallets" + sep);
+ m_walletdir_path_cases["trailing2"] = m_datadir / ("wallets" + sep + sep);
fs::current_path(m_datadir);
m_walletdir_path_cases["relative"] = "wallets";
@@ -32,7 +36,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
fs::create_directories(m_walletdir_path_cases["default"]);
fs::create_directories(m_walletdir_path_cases["custom"]);
fs::create_directories(m_walletdir_path_cases["relative"]);
- fs::ofstream f(m_walletdir_path_cases["file"]);
+ std::ofstream f{m_walletdir_path_cases["file"]};
f.close();
}
@@ -48,3 +52,4 @@ void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
{
gArgs.ForceSetArg("-walletdir", fs::PathToString(walletdir_path));
}
+} // namespace wallet
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index ccad629543..df5819fd1d 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -11,6 +11,7 @@
#include <test/util/setup_common.h>
+namespace wallet {
struct InitWalletDirTestingSetup: public BasicTestingSetup {
explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~InitWalletDirTestingSetup();
@@ -23,3 +24,4 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
};
#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
+} // namespace wallet
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 439b17fe13..d1df48a312 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -10,6 +10,7 @@
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
@@ -72,6 +73,8 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
+#ifndef WIN32
+// Windows does not consider "datadir/wallets//" to be a valid directory path.
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{
SetWalletDir(m_walletdir_path_cases["trailing2"]);
@@ -81,5 +84,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
+#endif
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index a9b3f1cbfd..dd5cd0af46 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -13,6 +13,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(ismine_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(ismine_standard)
@@ -417,3 +418,4 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 4c3c23599e..b953f402a2 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -11,6 +11,7 @@
#include <test/util/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
static void import_descriptor(CWallet& wallet, const std::string& descriptor)
@@ -145,3 +146,4 @@ BOOST_AUTO_TEST_CASE(parse_hd_keypath)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index f51648ec3b..a524b85ccb 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -10,6 +10,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
// Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true
@@ -39,3 +40,4 @@ BOOST_AUTO_TEST_CASE(CanProvide)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index 926f28686d..334bd5b8bc 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -12,6 +12,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)
BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
@@ -62,4 +63,56 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));
}
+static void TestFillInputToWeight(int64_t additional_weight, std::vector<int64_t> expected_stack_sizes)
+{
+ static const int64_t EMPTY_INPUT_WEIGHT = GetTransactionInputWeight(CTxIn());
+
+ CTxIn input;
+ int64_t target_weight = EMPTY_INPUT_WEIGHT + additional_weight;
+ BOOST_CHECK(FillInputToWeight(input, target_weight));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), target_weight);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), expected_stack_sizes.size());
+ for (unsigned int i = 0; i < expected_stack_sizes.size(); ++i) {
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack[i].size(), expected_stack_sizes[i]);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup)
+{
+ {
+ // Less than or equal minimum of 165 should not add any witness data
+ CTxIn input;
+ BOOST_CHECK(!FillInputToWeight(input, -1));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(!FillInputToWeight(input, 0));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(!FillInputToWeight(input, 164));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(FillInputToWeight(input, 165));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ }
+
+ // Make sure we can add at least one weight
+ TestFillInputToWeight(1, {0});
+
+ // 1 byte compact size uint boundary
+ TestFillInputToWeight(252, {251});
+ TestFillInputToWeight(253, {83, 168});
+ TestFillInputToWeight(262, {86, 174});
+ TestFillInputToWeight(263, {260});
+
+ // 3 byte compact size uint boundary
+ TestFillInputToWeight(65535, {65532});
+ TestFillInputToWeight(65536, {21842, 43688});
+ TestFillInputToWeight(65545, {21845, 43694});
+ TestFillInputToWeight(65546, {65541});
+
+ // Note: We don't test the next boundary because of memory allocation constraints.
+}
+
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index 93a3404d2c..aa3121511d 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -15,6 +15,7 @@
#include <memory>
+namespace wallet {
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key)
{
auto wallet = std::make_unique<CWallet>(&chain, "", args, CreateMockWalletDatabase());
@@ -44,3 +45,4 @@ std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cc
BOOST_CHECK(result.last_failed_block.IsNull());
return wallet;
}
+} // namespace wallet
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
index 3adb82b85f..712d0251cd 100644
--- a/src/wallet/test/util.h
+++ b/src/wallet/test/util.h
@@ -10,11 +10,14 @@
class ArgsManager;
class CChain;
class CKey;
-class CWallet;
namespace interfaces {
class Chain;
} // namespace interfaces
+namespace wallet {
+class CWallet;
+
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key);
+} // namespace wallet
#endif // BITCOIN_WALLET_TEST_UTIL_H
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index 5b421840e0..166e27bab9 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -10,6 +10,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup)
class TestCrypter
@@ -124,3 +125,4 @@ BOOST_AUTO_TEST_CASE(decrypt) {
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index ad24662fb6..cb006dea3a 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,6 +6,7 @@
#include <scheduler.h>
+namespace wallet {
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase())
@@ -19,3 +20,4 @@ WalletTestingSetup::~WalletTestingSetup()
{
if (m_node.scheduler) m_node.scheduler->stop();
}
+} // namespace wallet
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index cb6a8402dd..d4b855b145 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -15,6 +15,7 @@
#include <memory>
+namespace wallet {
/** Testing setup and teardown for wallet.
*/
struct WalletTestingSetup : public TestingSetup {
@@ -25,5 +26,6 @@ struct WalletTestingSetup : public TestingSetup {
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
+} // namespace wallet
#endif // BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 48998594f1..8ef0d46c4f 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -30,6 +30,10 @@
#include <boost/test/unit_test.hpp>
#include <univalue.h>
+using node::MAX_BLOCKFILE_SIZE;
+using node::UnlinkPrunedFiles;
+
+namespace wallet {
RPCHelpMan importmulti();
RPCHelpMan dumpwallet();
RPCHelpMan importwallet();
@@ -92,7 +96,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
- GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
+ WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
@@ -136,11 +140,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
}
// Prune the older block file.
+ int file_number;
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ file_number = oldTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
@@ -165,9 +171,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ file_number = newTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify ScanForWalletTransactions scans no blocks.
{
@@ -193,16 +200,18 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
- GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
+ WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
+ int file_number;
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ file_number = oldTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify importmulti RPC returns failure for a key whose creation time is
// before the missing block, and success for a key whose creation time is
@@ -289,7 +298,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
- ::dumpwallet().HandleRequest(request);
+ wallet::dumpwallet().HandleRequest(request);
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
@@ -308,7 +317,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
AddWallet(context, wallet);
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- ::importwallet().HandleRequest(request);
+ wallet::importwallet().HandleRequest(request);
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
@@ -816,38 +825,34 @@ BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
context.args = &gArgs;
context.chain = m_node.chain.get();
auto wallet = TestLoadWallet(context);
- AddKey(*wallet, coinbaseKey);
+ CKey key;
+ key.MakeNewKey(true);
+ AddKey(*wallet, key);
- // rescan to ensure coinbase transactions from test fixture are picked up by the wallet
- {
- WalletRescanReserver reserver(*wallet);
- reserver.reserve();
- wallet->ScanForWalletTransactions(m_node.chain->getBlockHash(0), 0, /* max height= */ {}, reserver, /* update= */ true);
- }
- // create one more block to get the first block coinbase to maturity
+ std::string error;
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
- // spend first coinbase tx
- auto spend_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CreateAndProcessBlock({spend_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+ auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
+ CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
SyncWithValidationInterfaceQueue();
{
- auto spend_tx_hash = spend_tx.GetHash();
+ auto block_hash = block_tx.GetHash();
auto prev_hash = m_coinbase_txns[0]->GetHash();
LOCK(wallet->cs_wallet);
BOOST_CHECK(wallet->HasWalletSpend(prev_hash));
- BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 1u);
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u);
- std::vector<uint256> vHashIn{spend_tx_hash}, vHashOut;
+ std::vector<uint256> vHashIn{ block_hash }, vHashOut;
BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK);
BOOST_CHECK(!wallet->HasWalletSpend(prev_hash));
- BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 0u);
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u);
}
TestUnloadWallet(std::move(wallet));
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/wallet_transaction_tests.cpp b/src/wallet/test/wallet_transaction_tests.cpp
index 5ef2904f66..9f56248614 100644
--- a/src/wallet/test/wallet_transaction_tests.cpp
+++ b/src/wallet/test/wallet_transaction_tests.cpp
@@ -8,6 +8,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(wallet_transaction_tests, WalletTestingSetup)
BOOST_AUTO_TEST_CASE(roundtrip)
@@ -22,3 +23,4 @@ BOOST_AUTO_TEST_CASE(roundtrip)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp
index 558121ae42..e251a3a0e4 100644
--- a/src/wallet/test/walletdb_tests.cpp
+++ b/src/wallet/test/walletdb_tests.cpp
@@ -9,6 +9,7 @@
#include <boost/test/unit_test.hpp>
+namespace wallet {
BOOST_FIXTURE_TEST_SUITE(walletdb_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue)
@@ -27,3 +28,4 @@ BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue)
}
BOOST_AUTO_TEST_SUITE_END()
+} // namespace wallet
diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp
index a926c0ecc1..a46846c1d4 100644
--- a/src/wallet/transaction.cpp
+++ b/src/wallet/transaction.cpp
@@ -4,6 +4,7 @@
#include <wallet/transaction.h>
+namespace wallet {
bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
{
CMutableTransaction tx1 {*this->tx};
@@ -23,3 +24,4 @@ int64_t CWalletTx::GetTxTime() const
int64_t n = nTimeSmart;
return n ? n : nTimeReceived;
}
+} // namespace wallet
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 52d72cccf3..00f9c9f154 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -19,6 +19,7 @@
#include <variant>
#include <vector>
+namespace wallet {
//! State of transaction confirmed in a block.
struct TxStateConfirmed {
uint256 confirmed_block_hash;
@@ -303,5 +304,6 @@ public:
CWalletTx(CWalletTx const &) = delete;
void operator=(CWalletTx const &x) = delete;
};
+} // namespace wallet
#endif // BITCOIN_WALLET_TRANSACTION_H
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 267710e8c7..7e694d1987 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -48,6 +48,7 @@
using interfaces::FoundBlock;
+namespace wallet {
const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
{WALLET_FLAG_AVOID_REUSE,
"You need to rescan the blockchain in order to correctly mark used "
@@ -357,12 +358,12 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
return wallet;
}
-std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
DatabaseOptions options;
options.require_existing = true;
- if (!fs::exists(fs::u8path(backup_file))) {
+ if (!fs::exists(backup_file)) {
error = Untranslated("Backup file does not exist");
status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
return nullptr;
@@ -377,7 +378,7 @@ std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string
}
auto wallet_file = wallet_path / "wallet.dat";
- fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists);
+ fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
auto wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
@@ -951,9 +952,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
- if (IsFromMe(*tx.get())) {
- AddToSpends(hash);
- }
+ AddToSpends(hash, &batch);
}
if (!fInsertedNew)
@@ -1506,6 +1505,49 @@ bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut
return true;
}
+bool FillInputToWeight(CTxIn& txin, int64_t target_weight)
+{
+ assert(txin.scriptSig.empty());
+ assert(txin.scriptWitness.IsNull());
+
+ int64_t txin_weight = GetTransactionInputWeight(txin);
+
+ // Do nothing if the weight that should be added is less than the weight that already exists
+ if (target_weight < txin_weight) {
+ return false;
+ }
+ if (target_weight == txin_weight) {
+ return true;
+ }
+
+ // Subtract current txin weight, which should include empty witness stack
+ int64_t add_weight = target_weight - txin_weight;
+ assert(add_weight > 0);
+
+ // We will want to subtract the size of the Compact Size UInt that will also be serialized.
+ // However doing so when the size is near a boundary can result in a problem where it is not
+ // possible to have a stack element size and combination to exactly equal a target.
+ // To avoid this possibility, if the weight to add is less than 10 bytes greater than
+ // a boundary, the size will be split so that 2/3rds will be in one stack element, and
+ // the remaining 1/3rd in another. Using 3rds allows us to avoid additional boundaries.
+ // 10 bytes is used because that accounts for the maximum size. This does not need to be super precise.
+ if ((add_weight >= 253 && add_weight < 263)
+ || (add_weight > std::numeric_limits<uint16_t>::max() && add_weight <= std::numeric_limits<uint16_t>::max() + 10)
+ || (add_weight > std::numeric_limits<uint32_t>::max() && add_weight <= std::numeric_limits<uint32_t>::max() + 10)) {
+ int64_t first_weight = add_weight / 3;
+ add_weight -= first_weight;
+
+ first_weight -= GetSizeOfCompactSize(first_weight);
+ txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), first_weight, 0);
+ }
+
+ add_weight -= GetSizeOfCompactSize(add_weight);
+ txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), add_weight, 0);
+ assert(GetTransactionInputWeight(txin) == target_weight);
+
+ return true;
+}
+
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
{
@@ -1514,6 +1556,14 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
for (const auto& txout : txouts)
{
CTxIn& txin = txNew.vin[nIn];
+ // If weight was provided, fill the input to that weight
+ if (coin_control && coin_control->HasInputWeight(txin.prevout)) {
+ if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) {
+ return false;
+ }
+ nIn++;
+ continue;
+ }
// Use max sig if watch only inputs were used or if this particular input is an external input
// to ensure a sufficient fee is attained for the requested feerate.
const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout));
@@ -2601,9 +2651,9 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
// 4. For backwards compatibility, the name of a data file in -walletdir.
const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
fs::file_type path_type = fs::symlink_status(wallet_path).type();
- if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
- (path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
- (path_type == fs::regular_file && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
+ if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
+ (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
+ (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
@@ -3433,3 +3483,4 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
return spk_man;
}
+} // namespace wallet
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index cbcdcaf3b8..e2c5c69c91 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <consensus/amount.h>
+#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <outputtype.h>
@@ -40,12 +41,17 @@
#include <boost/signals2/signal.hpp>
-struct WalletContext;
using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
+class CScript;
+enum class FeeEstimateMode;
+struct FeeCalculation;
struct bilingual_str;
+namespace wallet {
+struct WalletContext;
+
//! Explicitly unload and delete the wallet.
//! Blocks the current thread after signaling the unload intent so that all
//! wallet pointer owners release the wallet.
@@ -60,7 +66,7 @@ std::vector<std::shared_ptr<CWallet>> GetWallets(WalletContext& context);
std::shared_ptr<CWallet> GetWallet(WalletContext& context, const std::string& name);
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);
std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
-std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(WalletContext& context, LoadWalletFn load_wallet);
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
@@ -107,10 +113,7 @@ static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
class CCoinControl;
class COutput;
-class CScript;
class CWalletTx;
-struct FeeCalculation;
-enum class FeeEstimateMode;
class ReserveDestination;
//! Default for -addresstype
@@ -937,4 +940,7 @@ bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_nam
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig);
+bool FillInputToWeight(CTxIn& txin, int64_t target_weight);
+} // namespace wallet
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4b51d60597..c11d4b562d 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -26,6 +26,7 @@
#include <optional>
#include <string>
+namespace wallet {
namespace DBKeys {
const std::string ACENTRY{"acentry"};
const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
@@ -1104,7 +1105,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
{
bool exists;
try {
- exists = fs::symlink_status(path).type() != fs::file_not_found;
+ exists = fs::symlink_status(path).type() != fs::file_type::not_found;
} catch (const fs::filesystem_error& e) {
error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH;
@@ -1194,3 +1195,4 @@ std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
#endif
}
+} // namespace wallet
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index be77fb03a2..7d38832aa5 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -15,6 +15,18 @@
#include <string>
#include <vector>
+class CScript;
+class uint160;
+class uint256;
+struct CBlockLocator;
+
+namespace wallet {
+class CKeyPool;
+class CMasterKey;
+class CWallet;
+class CWalletTx;
+struct WalletContext;
+
/**
* Overview of wallet database classes:
*
@@ -29,16 +41,6 @@
static const bool DEFAULT_FLUSHWALLET = true;
-struct CBlockLocator;
-struct WalletContext;
-class CKeyPool;
-class CMasterKey;
-class CScript;
-class CWallet;
-class CWalletTx;
-class uint160;
-class uint256;
-
/** Error statuses for the wallet database */
enum class DBErrors
{
@@ -297,5 +299,6 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();
/** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase();
+} // namespace wallet
#endif // BITCOIN_WALLET_WALLETDB_H
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 900651939c..9cd18dd0a5 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -16,6 +16,7 @@
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
+namespace wallet {
namespace WalletTool {
// The standard wallet deleter function blocks on the validation interface
@@ -219,3 +220,4 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
return true;
}
} // namespace WalletTool
+} // namespace wallet
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index cdd728db33..9e0fe2b0ec 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -9,10 +9,12 @@
class ArgsManager;
+namespace wallet {
namespace WalletTool {
bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command);
} // namespace WalletTool
+} // namespace wallet
#endif // BITCOIN_WALLET_WALLETTOOL_H
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index d32fdb1fa8..ce276451c3 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -7,6 +7,7 @@
#include <logging.h>
#include <util/system.h>
+namespace wallet {
fs::path GetWalletDir()
{
fs::path path;
@@ -42,3 +43,4 @@ WalletFeature GetClosestWalletFeature(int version)
}
return static_cast<WalletFeature>(0);
}
+} // namespace wallet
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 06569d2f5f..788d41ceb7 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -10,6 +10,7 @@
#include <vector>
+namespace wallet {
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
@@ -103,5 +104,6 @@ public:
WalletDescriptor() {}
WalletDescriptor(std::shared_ptr<Descriptor> descriptor, uint64_t creation_time, int32_t range_start, int32_t range_end, int32_t next_index) : descriptor(descriptor), creation_time(creation_time), range_start(range_start), range_end(range_end), next_index(next_index) {}
};
+} // namespace wallet
#endif // BITCOIN_WALLET_WALLETUTIL_H
diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h
index 660b0eed5d..7624c2b16d 100644
--- a/src/walletinitinterface.h
+++ b/src/walletinitinterface.h
@@ -7,7 +7,9 @@
class ArgsManager;
+namespace node {
struct NodeContext;
+} // namespace node
class WalletInitInterface {
public:
@@ -18,7 +20,7 @@ public:
/** Check wallet parameter interaction */
virtual bool ParameterInteraction() const = 0;
/** Add wallets that should be opened to list of chain clients. */
- virtual void Construct(NodeContext& node) const = 0;
+ virtual void Construct(node::NodeContext& node) const = 0;
virtual ~WalletInitInterface() {}
};
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 2748741e2c..2c6f24a239 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -23,6 +23,8 @@
#include <string>
#include <utility>
+using node::ReadBlockFromDisk;
+
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
static const char *MSG_HASHBLOCK = "hashblock";
@@ -207,9 +209,10 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s to %s\n", hash.GetHex(), this->address);
- char data[32];
- for (unsigned int i = 0; i < 32; i++)
+ uint8_t data[32];
+ for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
+ }
return SendZmqMessage(MSG_HASHBLOCK, data, 32);
}
@@ -217,9 +220,10 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t
{
uint256 hash = transaction.GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s to %s\n", hash.GetHex(), this->address);
- char data[32];
- for (unsigned int i = 0; i < 32; i++)
+ uint8_t data[32];
+ for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
+ }
return SendZmqMessage(MSG_HASHTX, data, 32);
}