aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy15
-rw-r--r--src/Makefile.am46
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.test.include5
-rw-r--r--src/addrdb.cpp5
-rw-r--r--src/addrman.cpp14
-rw-r--r--src/banman.cpp47
-rw-r--r--src/banman.h3
-rw-r--r--src/base58.h1
-rw-r--r--src/bench/bench.cpp7
-rw-r--r--src/bench/bench.h1
-rw-r--r--src/bench/bench_bitcoin.cpp16
-rw-r--r--src/bench/checkqueue.cpp8
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/mempool_stress.cpp13
-rw-r--r--src/bench/prevector.cpp4
-rw-r--r--src/bench/strencodings.cpp18
-rw-r--r--src/bitcoin-chainstate.cpp28
-rw-r--r--src/bitcoin-cli.cpp74
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/bitcoind.cpp8
-rw-r--r--src/blockencodings.cpp6
-rw-r--r--src/blockencodings.h2
-rw-r--r--src/checkqueue.h10
-rw-r--r--src/common/bloom.cpp2
-rw-r--r--src/compat/glibcxx_sanity.cpp62
-rw-r--r--src/compat/sanity.h10
-rw-r--r--src/consensus/params.h6
-rw-r--r--src/consensus/tx_verify.cpp3
-rw-r--r--src/core_io.h1
-rw-r--r--src/core_read.cpp8
-rw-r--r--src/crypto/chacha20.cpp48
-rw-r--r--src/dbwrapper.cpp6
-rw-r--r--src/deploymentstatus.cpp2
-rw-r--r--src/deploymentstatus.h15
-rw-r--r--src/dummywallet.cpp1
-rw-r--r--src/external_signer.cpp5
-rw-r--r--src/flatfile.cpp2
-rw-r--r--src/fs.cpp3
-rw-r--r--src/fs.h23
-rw-r--r--src/hash.h1
-rw-r--r--src/httprpc.cpp25
-rw-r--r--src/httpserver.cpp35
-rw-r--r--src/i2p.h6
-rw-r--r--src/index/base.cpp14
-rw-r--r--src/index/blockfilterindex.cpp2
-rw-r--r--src/index/blockfilterindex.h2
-rw-r--r--src/index/coinstatsindex.cpp44
-rw-r--r--src/index/coinstatsindex.h4
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp64
-rw-r--r--src/init.h7
-rw-r--r--src/init/common.cpp43
-rw-r--r--src/init/common.h7
-rw-r--r--src/interfaces/node.h29
-rw-r--r--src/kernel/chainstatemanager_opts.h23
-rw-r--r--src/kernel/checks.cpp30
-rw-r--r--src/kernel/checks.h27
-rw-r--r--src/kernel/coinstats.cpp (renamed from src/node/coinstats.cpp)70
-rw-r--r--src/kernel/coinstats.h (renamed from src/node/coinstats.h)26
-rw-r--r--src/kernel/context.cpp33
-rw-r--r--src/kernel/context.h31
-rw-r--r--src/key.cpp4
-rw-r--r--src/key.h4
-rw-r--r--src/logging.cpp112
-rw-r--r--src/logging.h44
-rw-r--r--src/net.cpp89
-rw-r--r--src/net.h63
-rw-r--r--src/net_processing.cpp888
-rw-r--r--src/net_processing.h5
-rw-r--r--src/net_types.cpp8
-rw-r--r--src/netaddress.cpp5
-rw-r--r--src/netaddress.h5
-rw-r--r--src/netgroup.cpp2
-rw-r--r--src/node/blockstorage.cpp10
-rw-r--r--src/node/blockstorage.h11
-rw-r--r--src/node/chainstate.cpp11
-rw-r--r--src/node/chainstate.h5
-rw-r--r--src/node/context.cpp5
-rw-r--r--src/node/context.h4
-rw-r--r--src/node/interfaces.cpp62
-rw-r--r--src/node/miner.cpp20
-rw-r--r--src/node/miner.h6
-rw-r--r--src/node/transaction.h1
-rw-r--r--src/outputtype.h1
-rw-r--r--src/policy/fees.cpp4
-rw-r--r--src/prevector.h18
-rw-r--r--src/psbt.cpp4
-rw-r--r--src/psbt.h1
-rw-r--r--src/qt/addressbookpage.cpp18
-rw-r--r--src/qt/addresstablemodel.cpp2
-rw-r--r--src/qt/bantablemodel.cpp5
-rw-r--r--src/qt/bitcoin.cpp18
-rw-r--r--src/qt/bitcoingui.cpp34
-rw-r--r--src/qt/bitcoingui.h4
-rw-r--r--src/qt/clientmodel.cpp124
-rw-r--r--src/qt/clientmodel.h10
-rw-r--r--src/qt/guiutil.cpp12
-rw-r--r--src/qt/notificator.cpp10
-rw-r--r--src/qt/notificator.h2
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/optionsmodel.cpp470
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/overviewpage.cpp5
-rw-r--r--src/qt/paymentserver.cpp4
-rw-r--r--src/qt/peertablemodel.cpp5
-rw-r--r--src/qt/recentrequeststablemodel.cpp5
-rw-r--r--src/qt/rpcconsole.cpp16
-rw-r--r--src/qt/sendcoinsdialog.cpp4
-rw-r--r--src/qt/test/addressbooktests.cpp71
-rw-r--r--src/qt/test/apptests.cpp4
-rw-r--r--src/qt/test/optiontests.cpp7
-rw-r--r--src/qt/test/optiontests.h2
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/transactiondesc.cpp32
-rw-r--r--src/qt/transactiontablemodel.cpp2
-rw-r--r--src/qt/walletframe.cpp4
-rw-r--r--src/qt/walletmodel.cpp16
-rw-r--r--src/qt/walletmodel.h19
-rw-r--r--src/qt/walletview.cpp4
-rw-r--r--src/random.cpp17
-rw-r--r--src/random.h24
-rw-r--r--src/randomenv.cpp3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp241
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/fees.cpp236
-rw-r--r--src/rpc/mempool.cpp107
-rw-r--r--src/rpc/mining.cpp257
-rw-r--r--src/rpc/net.cpp26
-rw-r--r--src/rpc/node.cpp (renamed from src/rpc/misc.cpp)387
-rw-r--r--src/rpc/output_script.cpp319
-rw-r--r--src/rpc/rawtransaction.cpp8
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/rpc/register.h18
-rw-r--r--src/rpc/request.cpp2
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/signmessage.cpp113
-rw-r--r--src/rpc/txoutproof.cpp4
-rw-r--r--src/rpc/util.cpp15
-rw-r--r--src/rpc/util.h9
-rw-r--r--src/scheduler.cpp24
-rw-r--r--src/scheduler.h48
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/script/sign.cpp22
-rw-r--r--src/script/sign.h10
-rw-r--r--src/script/standard.h1
-rw-r--r--src/shutdown.cpp6
-rw-r--r--src/support/lockedpool.cpp5
-rw-r--r--src/sync.h10
-rw-r--r--src/test/bip32_tests.cpp5
-rw-r--r--src/test/blockencodings_tests.cpp6
-rw-r--r--src/test/blockfilter_index_tests.cpp13
-rw-r--r--src/test/blockfilter_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp25
-rw-r--r--src/test/coins_tests.cpp1
-rw-r--r--src/test/coinstatsindex_tests.cpp14
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/denialofservice_tests.cpp21
-rw-r--r--src/test/descriptor_tests.cpp2
-rw-r--r--src/test/fuzz/checkqueue.cpp2
-rw-r--r--src/test/fuzz/coins_view.cpp15
-rw-r--r--src/test/fuzz/fuzz.cpp6
-rw-r--r--src/test/fuzz/hex.cpp2
-rw-r--r--src/test/fuzz/prevector.cpp2
-rw-r--r--src/test/fuzz/rpc.cpp1
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp2
-rw-r--r--src/test/fuzz/script_sign.cpp2
-rw-r--r--src/test/fuzz/signature_checker.cpp2
-rw-r--r--src/test/fuzz/string.cpp7
-rw-r--r--src/test/fuzz/tx_pool.cpp2
-rw-r--r--src/test/fuzz/util.h1
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp2
-rw-r--r--src/test/getarg_tests.cpp6
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/logging_tests.cpp120
-rw-r--r--src/test/mempool_tests.cpp10
-rw-r--r--src/test/miner_tests.cpp210
-rw-r--r--src/test/prevector_tests.cpp3
-rw-r--r--src/test/random_tests.cpp18
-rw-r--r--src/test/rpc_tests.cpp47
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/scheduler_tests.cpp34
-rw-r--r--src/test/script_standard_tests.cpp4
-rw-r--r--src/test/script_tests.cpp18
-rw-r--r--src/test/settings_tests.cpp8
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/transaction_tests.cpp10
-rw-r--r--src/test/txvalidationcache_tests.cpp8
-rw-r--r--src/test/util/chainstate.h2
-rw-r--r--src/test/util/mining.cpp4
-rw-r--r--src/test/util/setup_common.cpp83
-rw-r--r--src/test/util/setup_common.h19
-rw-r--r--src/test/util_tests.cpp70
-rw-r--r--src/test/validation_block_tests.cpp16
-rw-r--r--src/test/validation_chainstate_tests.cpp14
-rw-r--r--src/test/versionbits_tests.cpp59
-rw-r--r--src/threadinterrupt.h8
-rw-r--r--src/timedata.cpp2
-rw-r--r--src/torcontrol.cpp7
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/univalue/README.md2
-rw-r--r--src/univalue/TODO10
-rw-r--r--src/univalue/configure.ac4
-rw-r--r--src/univalue/gen/gen.cpp8
-rw-r--r--src/univalue/include/univalue.h83
-rw-r--r--src/univalue/lib/univalue.cpp11
-rw-r--r--src/univalue/lib/univalue_get.cpp70
-rw-r--r--src/univalue/lib/univalue_read.cpp13
-rw-r--r--src/univalue/lib/univalue_write.cpp8
-rw-r--r--src/univalue/test/no_nul.cpp2
-rw-r--r--src/univalue/test/object.cpp35
-rw-r--r--src/univalue/test/test_json.cpp4
-rw-r--r--src/univalue/test/unitester.cpp7
-rw-r--r--src/util/bip32.h1
-rw-r--r--src/util/bytevectorhash.cpp6
-rw-r--r--src/util/designator.h21
-rw-r--r--src/util/getuniquepath.cpp2
-rw-r--r--src/util/hasher.cpp6
-rw-r--r--src/util/moneystr.h1
-rw-r--r--src/util/settings.cpp4
-rw-r--r--src/util/settings.h8
-rw-r--r--src/util/sock.cpp16
-rw-r--r--src/util/spanparsing.h20
-rw-r--r--src/util/strencodings.cpp43
-rw-r--r--src/util/strencodings.h5
-rw-r--r--src/util/string.cpp7
-rw-r--r--src/util/string.h10
-rw-r--r--src/util/syserror.cpp34
-rw-r--r--src/util/syserror.h16
-rw-r--r--src/util/system.cpp69
-rw-r--r--src/util/system.h23
-rw-r--r--src/util/time.cpp17
-rw-r--r--src/util/time.h49
-rw-r--r--src/util/tokenpipe.cpp2
-rw-r--r--src/validation.cpp164
-rw-r--r--src/validation.h72
-rw-r--r--src/validationinterface.cpp31
-rw-r--r--src/validationinterface.h6
-rw-r--r--src/versionbits.cpp5
-rw-r--r--src/versionbits.h8
-rw-r--r--src/wallet/bdb.cpp46
-rw-r--r--src/wallet/bdb.h18
-rw-r--r--src/wallet/coinselection.cpp18
-rw-r--r--src/wallet/coinselection.h47
-rw-r--r--src/wallet/context.cpp4
-rw-r--r--src/wallet/feebumper.cpp11
-rw-r--r--src/wallet/init.cpp1
-rw-r--r--src/wallet/interfaces.cpp16
-rw-r--r--src/wallet/receive.cpp8
-rw-r--r--src/wallet/receive.h16
-rw-r--r--src/wallet/rpc/addresses.cpp6
-rw-r--r--src/wallet/rpc/backup.cpp4
-rw-r--r--src/wallet/rpc/coins.cpp51
-rw-r--r--src/wallet/rpc/encrypt.cpp2
-rw-r--r--src/wallet/rpc/spend.cpp19
-rw-r--r--src/wallet/rpc/transactions.cpp27
-rw-r--r--src/wallet/rpc/wallet.cpp2
-rw-r--r--src/wallet/spend.cpp110
-rw-r--r--src/wallet/spend.h24
-rw-r--r--src/wallet/test/coinselector_tests.cpp116
-rw-r--r--src/wallet/test/db_tests.cpp16
-rw-r--r--src/wallet/test/fuzz/coinselection.cpp6
-rw-r--r--src/wallet/test/fuzz/notifications.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.cpp7
-rw-r--r--src/wallet/test/spend_tests.cpp15
-rw-r--r--src/wallet/test/wallet_tests.cpp11
-rw-r--r--src/wallet/wallet.cpp71
-rw-r--r--src/wallet/wallet.h31
-rw-r--r--src/wallet/walletdb.cpp3
270 files changed, 4551 insertions, 3296 deletions
diff --git a/src/.clang-tidy b/src/.clang-tidy
index fda95967f5..e9807d4cb7 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -1,2 +1,13 @@
-Checks: '-*,bugprone-argument-comment,modernize-use-nullptr'
-WarningsAsErrors: 'bugprone-argument-comment,modernize-use-nullptr'
+Checks: '
+-*,
+bugprone-argument-comment,
+modernize-use-default-member-init,
+modernize-use-nullptr,
+readability-redundant-declaration,
+'
+WarningsAsErrors: '
+bugprone-argument-comment,
+modernize-use-default-member-init,
+modernize-use-nullptr,
+readability-redundant-declaration,
+'
diff --git a/src/Makefile.am b/src/Makefile.am
index f77c2f919c..89329f5f69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,7 @@ noinst_PROGRAMS =
TESTS =
BENCHMARKS =
-BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
LIBBITCOIN_NODE=libbitcoin_node.a
LIBBITCOIN_COMMON=libbitcoin_common.a
@@ -139,7 +139,6 @@ BITCOIN_CORE_H = \
compat/byteswap.h \
compat/cpuid.h \
compat/endian.h \
- compat/sanity.h \
compressor.h \
consensus/consensus.h \
consensus/tx_check.h \
@@ -171,6 +170,10 @@ BITCOIN_CORE_H = \
interfaces/ipc.h \
interfaces/node.h \
interfaces/wallet.h \
+ kernel/chainstatemanager_opts.h \
+ kernel/checks.h \
+ kernel/coinstats.h \
+ kernel/context.h \
key.h \
key_io.h \
logging.h \
@@ -190,7 +193,6 @@ BITCOIN_CORE_H = \
node/caches.h \
node/chainstate.h \
node/coin.h \
- node/coinstats.h \
node/context.h \
node/miner.h \
node/minisketchwrapper.h \
@@ -254,6 +256,7 @@ BITCOIN_CORE_H = \
util/bip32.h \
util/bytevectorhash.h \
util/check.h \
+ util/designator.h \
util/epochguard.h \
util/error.h \
util/fastrange.h \
@@ -275,6 +278,7 @@ BITCOIN_CORE_H = \
util/spanparsing.h \
util/string.h \
util/syscall_sandbox.h \
+ util/syserror.h \
util/system.h \
util/thread.h \
util/threadnames.h \
@@ -328,7 +332,6 @@ obj/build.h: FORCE
"$(abs_top_srcdir)"
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
-ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
# server: shared between bitcoind and bitcoin-qt
# Contains code accessing mempool and chain state that is meant to be separated
@@ -355,6 +358,9 @@ libbitcoin_node_a_SOURCES = \
index/coinstatsindex.cpp \
index/txindex.cpp \
init.cpp \
+ kernel/checks.cpp \
+ kernel/coinstats.cpp \
+ kernel/context.cpp \
mapport.cpp \
net.cpp \
netgroup.cpp \
@@ -363,7 +369,6 @@ libbitcoin_node_a_SOURCES = \
node/caches.cpp \
node/chainstate.cpp \
node/coin.cpp \
- node/coinstats.cpp \
node/context.cpp \
node/interfaces.cpp \
node/miner.cpp \
@@ -379,13 +384,16 @@ libbitcoin_node_a_SOURCES = \
pow.cpp \
rest.cpp \
rpc/blockchain.cpp \
+ rpc/fees.cpp \
rpc/mempool.cpp \
rpc/mining.cpp \
- rpc/misc.cpp \
+ rpc/node.cpp \
rpc/net.cpp \
+ rpc/output_script.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
rpc/server_util.cpp \
+ rpc/signmessage.cpp \
rpc/txoutproof.cpp \
script/sigcache.cpp \
shutdown.cpp \
@@ -403,6 +411,7 @@ libbitcoin_node_a_SOURCES = \
if ENABLE_WALLET
libbitcoin_node_a_SOURCES += wallet/init.cpp
+libbitcoin_node_a_CPPFLAGS += $(BDB_CPPFLAGS)
endif
if !ENABLE_WALLET
libbitcoin_node_a_SOURCES += dummywallet.cpp
@@ -422,7 +431,7 @@ endif
# wallet: shared between bitcoind and bitcoin-qt, but only linked
# when wallet enabled
-libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(SQLITE_CFLAGS)
+libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BDB_CPPFLAGS) $(SQLITE_CFLAGS)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
wallet/coincontrol.cpp \
@@ -625,15 +634,12 @@ libbitcoin_common_a_SOURCES = \
$(BITCOIN_CORE_H)
# util: shared between all executables.
-# This library *must* be included to make sure that the glibc
-# sanity checks are linked.
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
- compat/glibcxx_sanity.cpp \
fs.cpp \
interfaces/echo.cpp \
interfaces/handler.cpp \
@@ -654,6 +660,7 @@ libbitcoin_util_a_SOURCES = \
util/getuniquepath.cpp \
util/hasher.cpp \
util/sock.cpp \
+ util/syserror.cpp \
util/system.cpp \
util/message.cpp \
util/moneystr.cpp \
@@ -847,13 +854,11 @@ endif
libbitcoinkernel_la_SOURCES = \
kernel/bitcoinkernel.cpp \
arith_uint256.cpp \
- blockfilter.cpp \
chain.cpp \
chainparamsbase.cpp \
chainparams.cpp \
clientversion.cpp \
coins.cpp \
- compat/glibcxx_sanity.cpp \
compressor.cpp \
consensus/merkle.cpp \
consensus/tx_check.cpp \
@@ -865,16 +870,13 @@ libbitcoinkernel_la_SOURCES = \
flatfile.cpp \
fs.cpp \
hash.cpp \
- index/base.cpp \
- index/blockfilterindex.cpp \
- index/coinstatsindex.cpp \
- init/common.cpp \
+ kernel/checks.cpp \
+ kernel/coinstats.cpp \
+ kernel/context.cpp \
key.cpp \
logging.cpp \
- netaddress.cpp \
node/blockstorage.cpp \
node/chainstate.cpp \
- node/coinstats.cpp \
node/ui_interface.cpp \
policy/feerate.cpp \
policy/fees.cpp \
@@ -900,11 +902,9 @@ libbitcoinkernel_la_SOURCES = \
support/lockedpool.cpp \
sync.cpp \
threadinterrupt.cpp \
- timedata.cpp \
txdb.cpp \
txmempool.cpp \
uint256.cpp \
- util/asmap.cpp \
util/bytevectorhash.cpp \
util/check.cpp \
util/getuniquepath.cpp \
@@ -914,7 +914,9 @@ libbitcoinkernel_la_SOURCES = \
util/serfloat.cpp \
util/settings.cpp \
util/strencodings.cpp \
+ util/string.cpp \
util/syscall_sandbox.cpp \
+ util/syserror.cpp \
util/system.cpp \
util/thread.cpp \
util/threadnames.cpp \
@@ -1011,6 +1013,10 @@ libbitcoin_ipc_mpgen_input = \
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
%.capnp:
+# Explicitly list dependencies on generated headers as described in
+# https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html#Recording-Dependencies-manually
+ipc/capnp/libbitcoin_ipc_a-protocol.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
+
if BUILD_MULTIPROCESS
LIBBITCOIN_IPC=libbitcoin_ipc.a
libbitcoin_ipc_a_SOURCES = \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index e90c8530d8..532f668668 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -44,6 +44,7 @@ bench_bench_bitcoin_SOURCES = \
bench/rollingbloom.cpp \
bench/rpc_blockchain.cpp \
bench/rpc_mempool.cpp \
+ bench/strencodings.cpp \
bench/util_time.cpp \
bench/verify_script.cpp
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 781313e42d..ebd9e860cf 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -201,6 +201,7 @@ test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EV
test_test_bitcoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
+test_test_bitcoin_CPPFLAGS += $(BDB_CPPFLAGS)
endif
test_test_bitcoin_LDADD += $(LIBBITCOIN_NODE) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
@@ -364,8 +365,8 @@ endif
if TARGET_WINDOWS
else
if ENABLE_BENCH
- @echo "Running bench/bench_bitcoin ..."
- $(BENCH_BINARY) > /dev/null
+ @echo "Running bench/bench_bitcoin (one iteration sanity check)..."
+ $(BENCH_BINARY) --sanity-check > /dev/null
endif
endif
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 0a76f83150..31f8eadf98 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -49,12 +49,11 @@ template <typename Data>
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
{
// Generate random temporary filename
- uint16_t randv = 0;
- GetRandBytes({(unsigned char*)&randv, sizeof(randv)});
+ const uint16_t randv{GetRand<uint16_t>()};
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
// open temp output file, and associate with CAutoFile
- fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
+ fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, version);
if (fileout.IsNull()) {
diff --git a/src/addrman.cpp b/src/addrman.cpp
index f74729d47b..ed823eeb05 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -866,25 +866,27 @@ void AddrManImpl::ResolveCollisions_()
int id_old = vvTried[tried_bucket][tried_bucket_pos];
AddrInfo& info_old = mapInfo[id_old];
+ const auto current_time{GetAdjustedTime()};
+
// Has successfully connected in last X hours
- if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
+ if (current_time - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
erase_collision = true;
- } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
+ } else if (current_time - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
// Give address at least 60 seconds to successfully connect
- if (GetAdjustedTime() - info_old.nLastTry > 60) {
+ if (current_time - info_old.nLastTry > 60) {
LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
// Replaces an existing address already in the tried table with the new address
- Good_(info_new, false, GetAdjustedTime());
+ Good_(info_new, false, current_time);
erase_collision = true;
}
- } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
+ } else if (current_time - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
// If the collision hasn't resolved in some reasonable amount of time,
// just evict the old entry -- we must not be able to
// connect to it for some reason.
LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
- Good_(info_new, false, GetAdjustedTime());
+ Good_(info_new, false, current_time);
erase_collision = true;
}
} else { // Collision is not actually a collision anymore
diff --git a/src/banman.cpp b/src/banman.cpp
index b28e3f7f7c..2a6e0e010f 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -16,6 +16,19 @@
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
+ LoadBanlist();
+ DumpBanlist();
+}
+
+BanMan::~BanMan()
+{
+ DumpBanlist();
+}
+
+void BanMan::LoadBanlist()
+{
+ LOCK(m_cs_banned);
+
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
@@ -29,13 +42,6 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
m_banned = {};
m_is_dirty = true;
}
-
- DumpBanlist();
-}
-
-BanMan::~BanMan()
-{
- DumpBanlist();
}
void BanMan::DumpBanlist()
@@ -173,23 +179,24 @@ void BanMan::GetBanned(banmap_t& banmap)
void BanMan::SweepBanned()
{
+ AssertLockHeld(m_cs_banned);
+
int64_t now = GetTime();
bool notify_ui = false;
- {
- LOCK(m_cs_banned);
- banmap_t::iterator it = m_banned.begin();
- while (it != m_banned.end()) {
- CSubNet sub_net = (*it).first;
- CBanEntry ban_entry = (*it).second;
- if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
- m_banned.erase(it++);
- m_is_dirty = true;
- notify_ui = true;
- LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
- } else
- ++it;
+ banmap_t::iterator it = m_banned.begin();
+ while (it != m_banned.end()) {
+ CSubNet sub_net = (*it).first;
+ CBanEntry ban_entry = (*it).second;
+ if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
+ m_banned.erase(it++);
+ m_is_dirty = true;
+ notify_ui = true;
+ LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
+ } else {
+ ++it;
}
}
+
// update UI
if (notify_ui && m_client_interface) {
m_client_interface->BannedListChanged();
diff --git a/src/banman.h b/src/banman.h
index f268fffa5a..77b043f081 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -80,11 +80,12 @@ public:
void DumpBanlist();
private:
+ void LoadBanlist() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_banned);
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty = true);
//!clean unused entries (if bantime has expired)
- void SweepBanned();
+ void SweepBanned() EXCLUSIVE_LOCKS_REQUIRED(m_cs_banned);
RecursiveMutex m_cs_banned;
banmap_t m_banned GUARDED_BY(m_cs_banned);
diff --git a/src/base58.h b/src/base58.h
index 9ba5af73e0..d2a8d5e3bc 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,7 +14,6 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
-#include <attributes.h>
#include <span.h>
#include <string>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 033d319750..26975bb59d 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -57,6 +57,10 @@ void benchmark::BenchRunner::RunAll(const Args& args)
std::regex reFilter(args.regex_filter);
std::smatch baseMatch;
+ if (args.sanity_check) {
+ std::cout << "Running with --sanity-check option, benchmark results will be useless." << std::endl;
+ }
+
std::vector<ankerl::nanobench::Result> benchmarkResults;
for (const auto& p : benchmarks()) {
if (!std::regex_match(p.first, baseMatch, reFilter)) {
@@ -69,6 +73,9 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
Bench bench;
+ if (args.sanity_check) {
+ bench.epochs(1).epochIterations(1);
+ }
bench.name(p.first);
if (args.min_time > 0ms) {
// convert to nanos before dividing to reduce rounding errors
diff --git a/src/bench/bench.h b/src/bench/bench.h
index 6634138beb..17535e4e81 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -43,6 +43,7 @@ typedef std::function<void(Bench&)> BenchFunction;
struct Args {
bool is_list_only;
+ bool sanity_check;
std::chrono::milliseconds min_time;
std::vector<double> asymptote;
fs::path output_csv;
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index d6f9c0f8b5..1bb4d34db9 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -26,9 +26,10 @@ static void SetupBenchArgs(ArgsManager& argsman)
argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
- argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-min-time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
+ argsman.AddArg("-output-csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-output-json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-sanity-check", "Run benchmarks for only one iteration", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
// parses a comma separated list like "10,20,30,50"
@@ -73,7 +74,7 @@ int main(int argc, char** argv)
" sure each run has exactly the same preconditions.\n"
"\n"
" * If results are still not reliable, increase runtime with e.g.\n"
- " -min_time=5000 to let a benchmark run for at least 5 seconds.\n"
+ " -min-time=5000 to let a benchmark run for at least 5 seconds.\n"
"\n"
" * bench_bitcoin uses nanobench [3] for which there is extensive\n"
" documentation available online.\n"
@@ -108,10 +109,11 @@ int main(int argc, char** argv)
benchmark::Args args;
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
args.is_list_only = argsman.GetBoolArg("-list", false);
- args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min_time", DEFAULT_MIN_TIME_MS));
- args.output_csv = argsman.GetPathArg("-output_csv");
- args.output_json = argsman.GetPathArg("-output_json");
+ args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min-time", DEFAULT_MIN_TIME_MS));
+ args.output_csv = argsman.GetPathArg("-output-csv");
+ args.output_json = argsman.GetPathArg("-output-json");
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
+ args.sanity_check = argsman.GetBoolArg("-sanity-check", false);
benchmark::BenchRunner::RunAll(args);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index d7b8c1badc..602081fb9b 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -30,8 +30,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
struct PrevectorJob {
prevector<PREVECTOR_SIZE, uint8_t> p;
- PrevectorJob(){
- }
+ PrevectorJob() = default;
explicit PrevectorJob(FastRandomContext& insecure_rand){
p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
}
@@ -39,7 +38,10 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
{
return true;
}
- void swap(PrevectorJob& x){p.swap(x.p);};
+ void swap(PrevectorJob& x) noexcept
+ {
+ p.swap(x.p);
+ };
};
CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
// The main thread should be counted to prevent thread oversubscription, and
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index de8ab9807c..b2958bcc9f 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -58,7 +58,7 @@ static void CoinSelection(benchmark::Bench& bench)
// Create coins
std::vector<COutput> coins;
for (const auto& wtx : wtxs) {
- coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true);
+ coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -88,7 +88,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0);
set.emplace_back();
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
}
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index a58658c4f1..725a6f8f5b 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -88,7 +88,7 @@ static void ComplexMemPool(benchmark::Bench& bench)
}
std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /*min_ancestors=*/1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
- CTxMemPool pool;
+ CTxMemPool& pool = *testing_setup.get()->m_node.mempool;
LOCK2(cs_main, pool.cs);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
for (auto& tx : ordered_coins) {
@@ -102,16 +102,15 @@ static void ComplexMemPool(benchmark::Bench& bench)
static void MempoolCheck(benchmark::Bench& bench)
{
FastRandomContext det_rand{true};
- const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
- const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /*min_ancestors=*/5);
- const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
- CTxMemPool pool;
+ auto testing_setup = MakeNoLogFileContext<TestChain100Setup>(CBaseChainParams::REGTEST, {"-checkmempool=1"});
+ CTxMemPool& pool = *testing_setup.get()->m_node.mempool;
LOCK2(cs_main, pool.cs);
+ testing_setup->PopulateMempool(det_rand, 400, true);
const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
- for (auto& tx : ordered_coins) AddTx(tx, pool);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
- pool.check(coins_tip, /*spendheight=*/2);
+ // Bump up the spendheight so we don't hit premature coinbase spend errors.
+ pool.check(coins_tip, /*spendheight=*/300);
});
}
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 6343ed7848..b3688bab1b 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -10,8 +10,8 @@
#include <bench/bench.h>
struct nontrivial_t {
- int x;
- nontrivial_t() :x(-1) {}
+ int x{-1};
+ nontrivial_t() = default;
SERIALIZE_METHODS(nontrivial_t, obj) { READWRITE(obj.x); }
};
static_assert(!std::is_trivially_default_constructible<nontrivial_t>::value,
diff --git a/src/bench/strencodings.cpp b/src/bench/strencodings.cpp
new file mode 100644
index 0000000000..69b3a83cbf
--- /dev/null
+++ b/src/bench/strencodings.cpp
@@ -0,0 +1,18 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <bench/data.h>
+#include <util/strencodings.h>
+
+static void HexStrBench(benchmark::Bench& bench)
+{
+ auto const& data = benchmark::data::block413567;
+ bench.batch(data.size()).unit("byte").run([&] {
+ auto hex = HexStr(data);
+ ankerl::nanobench::doNotOptimizeAway(hex);
+ });
+}
+
+BENCHMARK(HexStrBench);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index e5b6875a36..1817aa1a53 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -11,10 +11,12 @@
//
// It is part of the libbitcoinkernel project.
+#include <kernel/checks.h>
+#include <kernel/context.h>
+
#include <chainparams.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <init/common.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <scheduler.h>
@@ -24,6 +26,7 @@
#include <validation.h>
#include <validationinterface.h>
+#include <cassert>
#include <filesystem>
#include <functional>
#include <iosfwd>
@@ -49,7 +52,11 @@ int main(int argc, char* argv[])
SelectParams(CBaseChainParams::MAIN);
const CChainParams& chainparams = Params();
- init::SetGlobals(); // ECC_Start, etc.
+ kernel::Context kernel_context{};
+ // We can't use a goto here, but we can use an assert since none of the
+ // things instantiated so far requires running the epilogue to be torn down
+ // properly
+ assert(!kernel::SanityChecks(kernel_context).has_value());
// Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
// which will try the script cache first and fall back to actually
@@ -70,13 +77,16 @@ int main(int argc, char* argv[])
// SETUP: Chainstate
- ChainstateManager chainman;
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ static_cast<int64_t(*)()>(GetTime),
+ };
+ ChainstateManager chainman{chainman_opts};
auto rv = node::LoadChainstate(false,
std::ref(chainman),
nullptr,
false,
- chainparams.GetConsensus(),
false,
2 << 20,
2 << 22,
@@ -91,10 +101,8 @@ int main(int argc, char* argv[])
auto maybe_verify_error = node::VerifyLoadedChainstate(std::ref(chainman),
false,
false,
- chainparams.GetConsensus(),
DEFAULT_CHECKBLOCKS,
- DEFAULT_CHECKLEVEL,
- /*get_unix_time_seconds=*/static_cast<int64_t (*)()>(GetTime));
+ DEFAULT_CHECKLEVEL);
if (maybe_verify_error.has_value()) {
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
goto epilogue;
@@ -163,7 +171,7 @@ int main(int argc, char* argv[])
LOCK(cs_main);
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
if (pindex) {
- UpdateUncommittedBlockStructures(block, pindex, chainparams.GetConsensus());
+ chainman.UpdateUncommittedBlockStructures(block, pindex);
}
}
@@ -190,7 +198,7 @@ int main(int argc, char* argv[])
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = chainman.ProcessNewBlock(chainparams, blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
+ bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
std::cerr << "duplicate" << std::endl;
@@ -253,6 +261,4 @@ epilogue:
}
}
GetMainSignals().UnregisterBackgroundSignalScheduler();
-
- init::UnsetGlobals();
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index dea46693bc..b9e5a81f8d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,23 +9,26 @@
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <compat/stdin.h>
#include <policy/feerate.h>
#include <rpc/client.h>
#include <rpc/mining.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <tinyformat.h>
+#include <univalue.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
#include <util/url.h>
#include <algorithm>
+#include <chrono>
#include <cmath>
+#include <cstdio>
#include <functional>
#include <memory>
#include <optional>
-#include <stdio.h>
#include <string>
#include <tuple>
@@ -37,8 +40,10 @@
#include <event2/keyvalq_struct.h>
#include <support/events.h>
-#include <univalue.h>
-#include <compat/stdin.h>
+// The server returns time values from a mockable system clock, but it is not
+// trivial to get the mocked time from the server, nor is it needed for now, so
+// just use a plain system_clock.
+using CliClock = std::chrono::system_clock;
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
UrlDecodeFn* const URL_DECODE = urlDecode;
@@ -174,10 +179,10 @@ static int AppInitRPC(int argc, char* argv[])
/** Reply structure for request_done to fill in */
struct HTTPReply
{
- HTTPReply(): status(0), error(-1) {}
+ HTTPReply() = default;
- int status;
- int error;
+ int status{0};
+ int error{-1};
std::string body;
};
@@ -238,7 +243,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx)
class BaseRequestHandler
{
public:
- virtual ~BaseRequestHandler() {}
+ virtual ~BaseRequestHandler() = default;
virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
};
@@ -407,8 +412,8 @@ private:
bool is_addr_relay_enabled;
bool is_bip152_hb_from;
bool is_bip152_hb_to;
- bool is_block_relay;
bool is_outbound;
+ bool is_tx_relay;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
};
std::vector<Peer> m_peers;
@@ -433,7 +438,6 @@ private:
if (conn_type == "addr-fetch") return "addr";
return "";
}
- const int64_t m_time_now{GetTimeSeconds()};
public:
static constexpr int ID_PEERINFO = 0;
@@ -462,9 +466,10 @@ public:
if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
- if (networkinfo["version"].get_int() < 209900) {
+ if (networkinfo["version"].getInt<int>() < 209900) {
throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
}
+ const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())};
// Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
@@ -472,7 +477,7 @@ public:
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
const bool is_outbound{!peer["inbound"].get_bool()};
- const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
@@ -482,25 +487,25 @@ public:
if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
// Push data for this peer to the peers vector.
- const int peer_id{peer["id"].get_int()};
- const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
- const int version{peer["version"].get_int()};
- const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].get_int64()};
- const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].get_int64()};
- const int64_t conn_time{peer["conntime"].get_int64()};
- const int64_t last_blck{peer["last_block"].get_int64()};
- const int64_t last_recv{peer["lastrecv"].get_int64()};
- const int64_t last_send{peer["lastsend"].get_int64()};
- const int64_t last_trxn{peer["last_transaction"].get_int64()};
+ const int peer_id{peer["id"].getInt<int>()};
+ const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
+ const int version{peer["version"].getInt<int>()};
+ const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
+ const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
+ const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
+ const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
+ const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
+ const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
+ const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
const std::string addr{peer["addr"].get_str()};
- const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)};
+ const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
const std::string sub_version{peer["subver"].get_str()};
const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
- m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
@@ -511,7 +516,7 @@ public:
}
// Generate report header.
- std::string result{strprintf("%s client %s%s - server %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
+ std::string result{strprintf("%s client %s%s - server %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt<int>(), networkinfo["subversion"].get_str())};
// Report detailed peer connections list sorted by direction and minimum ping time.
if (DetailsRequested() && !m_peers.empty()) {
@@ -531,10 +536,10 @@ public:
peer.network,
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
- peer.last_send ? ToString(m_time_now - peer.last_send) : "",
- peer.last_recv ? ToString(m_time_now - peer.last_recv) : "",
- peer.last_trxn ? ToString((m_time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "",
- peer.last_blck ? ToString((m_time_now - peer.last_blck) / 60) : "",
+ peer.last_send ? ToString(time_now - peer.last_send) : "",
+ peer.last_recv ? ToString(time_now - peer.last_recv) : "",
+ peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
+ peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_addr_processed_length, // variable spacing
peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
@@ -592,7 +597,7 @@ public:
max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
}
for (const UniValue& addr : local_addrs) {
- result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int());
+ result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
}
}
@@ -838,21 +843,20 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
- const auto deadline{GetTime<std::chrono::microseconds>() + 1s * timeout};
+ const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
do {
try {
response = CallRPC(rh, strMethod, args, rpcwallet);
if (fWait) {
const UniValue& error = find_value(response, "error");
- if (!error.isNull() && error["code"].get_int() == RPC_IN_WARMUP) {
+ if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
throw CConnectionFailed("server in warmup");
}
}
break; // Connection succeeded, no need to retry.
} catch (const CConnectionFailed& e) {
- const auto now{GetTime<std::chrono::microseconds>()};
- if (fWait && (timeout <= 0 || now < deadline)) {
+ if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
UninterruptibleSleep(1s);
} else {
throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
@@ -881,13 +885,13 @@ static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
if (err_msg.isStr()) {
strPrint += ("error message:\n" + err_msg.get_str());
}
- if (err_code.isNum() && err_code.get_int() == RPC_WALLET_NOT_SPECIFIED) {
+ if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
}
} else {
strPrint = "error: " + error.write();
}
- nRet = abs(error["code"].get_int());
+ nRet = abs(error["code"].getInt<int>());
}
/**
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index cdc5960c12..bcefb3f18e 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -612,7 +612,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
throw std::runtime_error("txid must be hexadecimal string (not '" + prevOut["txid"].get_str() + "')");
}
- const int nOut = prevOut["vout"].get_int();
+ const int nOut = prevOut["vout"].getInt<int>();
if (nOut < 0)
throw std::runtime_error("vout cannot be negative");
@@ -667,7 +667,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
- ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
+ ProduceSignature(keystore, MutableTransactionSignatureCreator(mergedTx, i, amount, nHashType), prevPubKey, sigdata);
if (amount == MAX_MONEY && !sigdata.scriptWitness.IsNull()) {
throw std::runtime_error(strprintf("Missing amount for CTxOut with scriptPubKey=%s", HexStr(prevPubKey)));
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 9843382682..92e73d7c2a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -20,6 +20,7 @@
#include <util/check.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
+#include <util/syserror.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/tokenpipe.h>
@@ -187,11 +188,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (!AppInitSanityChecks())
+
+ node.kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*node.kernel))
{
// InitError will have been called with detailed error, which ends up on console
return false;
}
+
if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
#if HAVE_DECL_FORK
tfm::format(std::cout, PACKAGE_NAME " starting\n");
@@ -206,7 +210,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
}
break;
case -1: // Error happened.
- return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", strerror(errno))));
+ return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", SysErrorString(errno))));
default: { // Parent: wait and exit.
int token = daemon_ep.TokenRead();
if (token) { // Success
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index aa111b5939..f96353510f 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -16,15 +16,15 @@
#include <unordered_map>
-CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
- nonce(GetRand(std::numeric_limits<uint64_t>::max())),
+CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) :
+ nonce(GetRand<uint64_t>()),
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
FillShortTxIDSelector();
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
prefilledtxn[0] = {0, block.vtx[0]};
for (size_t i = 1; i < block.vtx.size(); i++) {
const CTransaction& tx = *block.vtx[i];
- shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
+ shorttxids[i - 1] = GetShortID(tx.GetWitnessHash());
}
}
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 326db1b4a7..67c4e57156 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -104,7 +104,7 @@ public:
// Dummy for deserialization
CBlockHeaderAndShortTxIDs() {}
- CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID);
+ CBlockHeaderAndShortTxIDs(const CBlock& block);
uint64_t GetShortID(const uint256& txhash) const;
diff --git a/src/checkqueue.h b/src/checkqueue.h
index d0e88a3410..bead6f0c6f 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -66,7 +66,7 @@ private:
bool m_request_stop GUARDED_BY(m_mutex){false};
/** Internal function that does bulk of the verification work. */
- bool Loop(bool fMaster)
+ bool Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
std::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv;
std::vector<T> vChecks;
@@ -140,7 +140,7 @@ public:
}
//! Create a pool of new worker threads.
- void StartWorkerThreads(const int threads_num)
+ void StartWorkerThreads(const int threads_num) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
{
LOCK(m_mutex);
@@ -159,13 +159,13 @@ public:
}
//! Wait until execution finishes, and return whether all evaluations were successful.
- bool Wait()
+ bool Wait() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
return Loop(true /* master thread */);
}
//! Add a batch of checks to the queue
- void Add(std::vector<T>& vChecks)
+ void Add(std::vector<T>& vChecks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
if (vChecks.empty()) {
return;
@@ -188,7 +188,7 @@ public:
}
//! Stop all of the worker threads.
- void StopWorkerThreads()
+ void StopWorkerThreads() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
WITH_LOCK(m_mutex, m_request_stop = true);
m_worker_cv.notify_all();
diff --git a/src/common/bloom.cpp b/src/common/bloom.cpp
index 8b32a6c94a..aa3fcf1ce2 100644
--- a/src/common/bloom.cpp
+++ b/src/common/bloom.cpp
@@ -239,7 +239,7 @@ bool CRollingBloomFilter::contains(Span<const unsigned char> vKey) const
void CRollingBloomFilter::reset()
{
- nTweak = GetRand(std::numeric_limits<unsigned int>::max());
+ nTweak = GetRand<unsigned int>();
nEntriesThisGeneration = 0;
nGeneration = 1;
std::fill(data.begin(), data.end(), 0);
diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp
deleted file mode 100644
index f2ceeeeb9c..0000000000
--- a/src/compat/glibcxx_sanity.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <list>
-#include <locale>
-#include <stdexcept>
-#include <string>
-
-namespace
-{
-// trigger: use ctype<char>::widen to trigger ctype<char>::_M_widen_init().
-// test: convert a char from narrow to wide and back. Verify that the result
-// matches the original.
-bool sanity_test_widen(char testchar)
-{
- const std::ctype<char>& test(std::use_facet<std::ctype<char> >(std::locale()));
- return test.narrow(test.widen(testchar), 'b') == testchar;
-}
-
-// trigger: use list::push_back and list::pop_back to trigger _M_hook and
-// _M_unhook.
-// test: Push a sequence of integers into a list. Pop them off and verify that
-// they match the original sequence.
-bool sanity_test_list(unsigned int size)
-{
- std::list<unsigned int> test;
- for (unsigned int i = 0; i != size; ++i)
- test.push_back(i + 1);
-
- if (test.size() != size)
- return false;
-
- while (!test.empty()) {
- if (test.back() != test.size())
- return false;
- test.pop_back();
- }
- return true;
-}
-
-} // namespace
-
-// trigger: string::at(x) on an empty string to trigger __throw_out_of_range_fmt.
-// test: force std::string to throw an out_of_range exception. Verify that
-// it's caught correctly.
-bool sanity_test_range_fmt()
-{
- std::string test;
- try {
- test.at(1);
- } catch (const std::out_of_range&) {
- return true;
- } catch (...) {
- }
- return false;
-}
-
-bool glibcxx_sanity_test()
-{
- return sanity_test_widen('a') && sanity_test_list(100) && sanity_test_range_fmt();
-}
diff --git a/src/compat/sanity.h b/src/compat/sanity.h
deleted file mode 100644
index 8e5811f1fd..0000000000
--- a/src/compat/sanity.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2009-2021 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_COMPAT_SANITY_H
-#define BITCOIN_COMPAT_SANITY_H
-
-bool glibcxx_sanity_test();
-
-#endif // BITCOIN_COMPAT_SANITY_H
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 0881f2e388..794e0f5383 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -40,11 +40,11 @@ constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BIT
*/
struct BIP9Deployment {
/** Bit position to select the particular bit in nVersion. */
- int bit;
+ int bit{28};
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
- int64_t nStartTime;
+ int64_t nStartTime{NEVER_ACTIVE};
/** Timeout/expiry MedianTime for the deployment attempt. */
- int64_t nTimeout;
+ int64_t nTimeout{NEVER_ACTIVE};
/** If lock in occurs, delay activation until at least this block
* height. Note that activation will only occur on a retarget
* boundary.
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 5738c333ce..154146f08d 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -11,6 +11,7 @@
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
+#include <util/check.h>
#include <util/moneystr.h>
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
@@ -74,7 +75,7 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
int nCoinHeight = prevHeights[txinIndex];
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
- int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
+ const int64_t nCoinTime{Assert(block.GetAncestor(std::max(nCoinHeight - 1, 0)))->GetMedianTimePast()};
// NOTE: Subtract 1 to maintain nLockTime semantics
// BIP 68 relative lock times have the semantics of calculating
// the first block or time at which the transaction would be
diff --git a/src/core_io.h b/src/core_io.h
index aa1381c374..c91c8199d8 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -6,7 +6,6 @@
#define BITCOIN_CORE_IO_H
#include <consensus/amount.h>
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 3bab5b5d98..77c516427a 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -14,9 +14,6 @@
#include <util/strencodings.h>
#include <version.h>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/split.hpp>
-
#include <algorithm>
#include <string>
@@ -66,12 +63,11 @@ CScript ParseScript(const std::string& s)
{
CScript result;
- std::vector<std::string> words;
- boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on);
+ std::vector<std::string> words = SplitString(s, " \t\n");
for (const std::string& w : words) {
if (w.empty()) {
- // Empty string, ignore. (boost::split given '' will return one word)
+ // Empty string, ignore. (SplitString doesn't combine multiple separators)
} else if (std::all_of(w.begin(), w.end(), ::IsDigit) ||
(w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit)))
{
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
index f3ff4268ee..c7e12b0612 100644
--- a/src/crypto/chacha20.cpp
+++ b/src/crypto/chacha20.cpp
@@ -18,6 +18,8 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (
a += b; d = rotl32(d ^ a, 8); \
c += d; b = rotl32(b ^ c, 7);
+#define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0)
+
static const unsigned char sigma[] = "expand 32-byte k";
static const unsigned char tau[] = "expand 16-byte k";
@@ -119,16 +121,19 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes)
x13 = j13;
x14 = j14;
x15 = j15;
- for (i = 20;i > 0;i -= 2) {
- QUARTERROUND( x0, x4, x8,x12)
- QUARTERROUND( x1, x5, x9,x13)
- QUARTERROUND( x2, x6,x10,x14)
- QUARTERROUND( x3, x7,x11,x15)
- QUARTERROUND( x0, x5,x10,x15)
- QUARTERROUND( x1, x6,x11,x12)
- QUARTERROUND( x2, x7, x8,x13)
- QUARTERROUND( x3, x4, x9,x14)
- }
+
+ // The 20 inner ChaCha20 rounds are unrolled here for performance.
+ REPEAT10(
+ QUARTERROUND( x0, x4, x8,x12);
+ QUARTERROUND( x1, x5, x9,x13);
+ QUARTERROUND( x2, x6,x10,x14);
+ QUARTERROUND( x3, x7,x11,x15);
+ QUARTERROUND( x0, x5,x10,x15);
+ QUARTERROUND( x1, x6,x11,x12);
+ QUARTERROUND( x2, x7, x8,x13);
+ QUARTERROUND( x3, x4, x9,x14);
+ );
+
x0 += j0;
x1 += j1;
x2 += j2;
@@ -231,16 +236,19 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
x13 = j13;
x14 = j14;
x15 = j15;
- for (i = 20;i > 0;i -= 2) {
- QUARTERROUND( x0, x4, x8,x12)
- QUARTERROUND( x1, x5, x9,x13)
- QUARTERROUND( x2, x6,x10,x14)
- QUARTERROUND( x3, x7,x11,x15)
- QUARTERROUND( x0, x5,x10,x15)
- QUARTERROUND( x1, x6,x11,x12)
- QUARTERROUND( x2, x7, x8,x13)
- QUARTERROUND( x3, x4, x9,x14)
- }
+
+ // The 20 inner ChaCha20 rounds are unrolled here for performance.
+ REPEAT10(
+ QUARTERROUND( x0, x4, x8,x12);
+ QUARTERROUND( x1, x5, x9,x13);
+ QUARTERROUND( x2, x6,x10,x14);
+ QUARTERROUND( x3, x7,x11,x15);
+ QUARTERROUND( x0, x5,x10,x15);
+ QUARTERROUND( x1, x6,x11,x12);
+ QUARTERROUND( x2, x7, x8,x13);
+ QUARTERROUND( x3, x4, x9,x14);
+ );
+
x0 += j0;
x1 += j1;
x2 += j2;
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 50a601c684..a2f1f32780 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -19,7 +19,7 @@ public:
// This code is adapted from posix_logger.h, which is why it is using vsprintf.
// Please do not do this in normal code
void Logv(const char * format, va_list ap) override {
- if (!LogAcceptCategory(BCLog::LEVELDB)) {
+ if (!LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug)) {
return;
}
char buffer[500];
@@ -63,7 +63,7 @@ public:
assert(p <= limit);
base[std::min(bufsize - 1, (int)(p - base))] = '\0';
- LogPrintf("leveldb: %s", base); /* Continued */
+ LogPrintLevel(BCLog::LEVELDB, BCLog::Level::Debug, "%s", base); /* Continued */
if (base != buffer) {
delete[] base;
}
@@ -186,7 +186,7 @@ CDBWrapper::~CDBWrapper()
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
{
- const bool log_memory = LogAcceptCategory(BCLog::LEVELDB);
+ const bool log_memory = LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug);
double mem_before = 0;
if (log_memory) {
mem_before = DynamicMemoryUsage() / 1024.0 / 1024;
diff --git a/src/deploymentstatus.cpp b/src/deploymentstatus.cpp
index ae19a6e40d..3a524af29e 100644
--- a/src/deploymentstatus.cpp
+++ b/src/deploymentstatus.cpp
@@ -9,8 +9,6 @@
#include <type_traits>
-VersionBitsCache g_versionbitscache;
-
/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and
* ValidDeployment check */
diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h
index ba5103de74..9f5919f71b 100644
--- a/src/deploymentstatus.h
+++ b/src/deploymentstatus.h
@@ -10,33 +10,30 @@
#include <limits>
-/** Global cache for versionbits deployment status */
-extern VersionBitsCache g_versionbitscache;
-
/** Determine if a deployment is active for the next block */
-inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep)
+inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep, [[maybe_unused]] VersionBitsCache& versionbitscache)
{
assert(Consensus::ValidDeployment(dep));
return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep);
}
-inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep)
+inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep, VersionBitsCache& versionbitscache)
{
assert(Consensus::ValidDeployment(dep));
- return ThresholdState::ACTIVE == g_versionbitscache.State(pindexPrev, params, dep);
+ return ThresholdState::ACTIVE == versionbitscache.State(pindexPrev, params, dep);
}
/** Determine if a deployment is active for this block */
-inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep)
+inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep, [[maybe_unused]] VersionBitsCache& versionbitscache)
{
assert(Consensus::ValidDeployment(dep));
return index.nHeight >= params.DeploymentHeight(dep);
}
-inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep)
+inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep, VersionBitsCache& versionbitscache)
{
assert(Consensus::ValidDeployment(dep));
- return DeploymentActiveAfter(index.pprev, params, dep);
+ return DeploymentActiveAfter(index.pprev, params, dep, versionbitscache);
}
/** Determine if a deployment is enabled (can ever be active) */
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 2b94ed611b..028c6ebae1 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -50,6 +50,7 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-flushwallet",
"-privdb",
"-walletrejectlongchains",
+ "-walletcrosschain",
"-unsafesqlitesync",
});
}
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index 75070899c6..d125fe479b 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -74,11 +74,12 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
-
+ // parse ExternalSigner master fingerprint
+ std::vector<unsigned char> parsed_m_fingerprint = ParseHex(m_fingerprint);
// Check if signer fingerprint matches any input master key fingerprint
auto matches_signer_fingerprint = [&](const PSBTInput& input) {
for (const auto& entry : input.hd_keypaths) {
- if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) return true;
+ if (parsed_m_fingerprint == MakeUCharSpan(entry.second.fingerprint)) return true;
}
return false;
};
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
index d6cada0c46..0fecf4f504 100644
--- a/src/flatfile.cpp
+++ b/src/flatfile.cpp
@@ -27,7 +27,7 @@ std::string FlatFilePos::ToString() const
fs::path FlatFileSeq::FileName(const FlatFilePos& pos) const
{
- return m_dir / strprintf("%s%05u.dat", m_prefix, pos.nFile);
+ return m_dir / fs::u8path(strprintf("%s%05u.dat", m_prefix, pos.nFile));
}
FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only)
diff --git a/src/fs.cpp b/src/fs.cpp
index 219fdee959..b61115bf01 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#include <util/syserror.h>
#ifndef WIN32
#include <cstring>
@@ -44,7 +45,7 @@ fs::path AbsPathJoin(const fs::path& base, const fs::path& path)
static std::string GetErrorReason()
{
- return std::strerror(errno);
+ return SysErrorString(errno);
}
FileLock::FileLock(const fs::path& file)
diff --git a/src/fs.h b/src/fs.h
index 8e145cbb8e..cc55793b95 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -92,11 +92,30 @@ static inline auto quoted(const std::string& s)
}
// Allow safe path append operations.
-static inline path operator+(path p1, path p2)
+static inline path operator/(path p1, path p2)
{
- p1 += std::move(p2);
+ p1 /= std::move(p2);
return p1;
}
+static inline path operator/(path p1, const char* p2)
+{
+ p1 /= p2;
+ return p1;
+}
+static inline path operator+(path p1, const char* p2)
+{
+ p1 += p2;
+ return p1;
+}
+static inline path operator+(path p1, path::value_type p2)
+{
+ p1 += p2;
+ return p1;
+}
+
+// Disallow unsafe path append operations.
+template<typename T> static inline path operator/(path p1, T p2) = delete;
+template<typename T> static inline path operator+(path p1, T p2) = delete;
// Disallow implicit std::string conversion for copy_file
// to avoid locale-dependent encoding on Windows.
diff --git a/src/hash.h b/src/hash.h
index 9f582842c1..0ccef2105f 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
-#include <attributes.h>
#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 93d9acf5da..4e7e72b037 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -4,7 +4,6 @@
#include <httprpc.h>
-#include <chainparams.h>
#include <crypto/hmac_sha256.h>
#include <httpserver.h>
#include <rpc/protocol.h>
@@ -12,18 +11,15 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
-#include <util/translation.h>
#include <walletinitinterface.h>
#include <algorithm>
#include <iterator>
#include <map>
#include <memory>
-#include <stdio.h>
#include <set>
#include <string>
-
-#include <boost/algorithm/string.hpp>
+#include <vector>
/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
@@ -79,7 +75,7 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni
{
// Send error reply from json-rpc error object
int nStatus = HTTP_INTERNAL_SERVER_ERROR;
- int code = find_value(objError, "code").get_int();
+ int code = find_value(objError, "code").getInt<int>();
if (code == RPC_INVALID_REQUEST)
nStatus = HTTP_BAD_REQUEST;
@@ -254,13 +250,14 @@ static bool InitRPCAuthentication()
LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcauth for rpcauth auth generation.\n");
strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}
- if (gArgs.GetArg("-rpcauth","") != "")
- {
+ if (gArgs.GetArg("-rpcauth", "") != "") {
LogPrintf("Using rpcauth authentication.\n");
for (const std::string& rpcauth : gArgs.GetArgs("-rpcauth")) {
- std::vector<std::string> fields;
- boost::split(fields, rpcauth, boost::is_any_of(":$"));
- if (fields.size() == 3) {
+ std::vector<std::string> fields{SplitString(rpcauth, ':')};
+ const std::vector<std::string> salt_hmac{SplitString(fields.back(), '$')};
+ if (fields.size() == 2 && salt_hmac.size() == 2) {
+ fields.pop_back();
+ fields.insert(fields.end(), salt_hmac.begin(), salt_hmac.end());
g_rpcauth.push_back(fields);
} else {
LogPrintf("Invalid -rpcauth argument.\n");
@@ -277,8 +274,10 @@ static bool InitRPCAuthentication()
std::set<std::string>& whitelist = g_rpc_whitelist[strUser];
if (pos != std::string::npos) {
std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
- std::set<std::string> new_whitelist;
- boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
+ std::vector<std::string> whitelist_split = SplitString(strWhitelist, ", ");
+ std::set<std::string> new_whitelist{
+ std::make_move_iterator(whitelist_split.begin()),
+ std::make_move_iterator(whitelist_split.end())};
if (intersect) {
std::set<std::string> tmp_whitelist;
std::set_intersection(new_whitelist.begin(), new_whitelist.end(),
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 96bee8640d..44937dc523 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -73,21 +73,18 @@ private:
Mutex cs;
std::condition_variable cond GUARDED_BY(cs);
std::deque<std::unique_ptr<WorkItem>> queue GUARDED_BY(cs);
- bool running GUARDED_BY(cs);
+ bool running GUARDED_BY(cs){true};
const size_t maxDepth;
public:
- explicit WorkQueue(size_t _maxDepth) : running(true),
- maxDepth(_maxDepth)
+ explicit WorkQueue(size_t _maxDepth) : maxDepth(_maxDepth)
{
}
/** Precondition: worker threads have all stopped (they have been joined).
*/
- ~WorkQueue()
- {
- }
+ ~WorkQueue() = default;
/** Enqueue a work item */
- bool Enqueue(WorkItem* item)
+ bool Enqueue(WorkItem* item) EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
if (!running || queue.size() >= maxDepth) {
@@ -98,7 +95,7 @@ public:
return true;
}
/** Thread function */
- void Run()
+ void Run() EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
while (true) {
std::unique_ptr<WorkItem> i;
@@ -115,7 +112,7 @@ public:
}
}
/** Interrupt and exit loops */
- void Interrupt()
+ void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
running = false;
@@ -347,10 +344,22 @@ static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
- if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
- LogPrintf("libevent: %s\n", msg);
- else
- LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg);
+ BCLog::Level level;
+ switch (severity) {
+ case EVENT_LOG_DEBUG:
+ level = BCLog::Level::Debug;
+ break;
+ case EVENT_LOG_MSG:
+ level = BCLog::Level::Info;
+ break;
+ case EVENT_LOG_WARN:
+ level = BCLog::Level::Warning;
+ break;
+ default: // EVENT_LOG_ERR and others are mapped to error
+ level = BCLog::Level::Error;
+ break;
+ }
+ LogPrintLevel(BCLog::LIBEVENT, level, "%s\n", msg);
}
bool InitHTTPServer()
diff --git a/src/i2p.h b/src/i2p.h
index b211d4f5e4..433fcc3a08 100644
--- a/src/i2p.h
+++ b/src/i2p.h
@@ -84,7 +84,7 @@ public:
* to the listening socket and address.
* @return true on success
*/
- bool Listen(Connection& conn);
+ bool Listen(Connection& conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Wait for and accept a new incoming connection.
@@ -103,7 +103,7 @@ public:
* it is set to `false`. Only set if `false` is returned.
* @return true on success
*/
- bool Connect(const CService& to, Connection& conn, bool& proxy_error);
+ bool Connect(const CService& to, Connection& conn, bool& proxy_error) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
private:
/**
@@ -172,7 +172,7 @@ private:
/**
* Check the control socket for errors and possibly disconnect.
*/
- void CheckControlSock();
+ void CheckControlSock() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Generate a new destination with the SAM proxy and set `m_private_key` to it.
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 488a214ccf..9f0c1dea24 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -18,8 +18,8 @@ using node::ReadBlockFromDisk;
constexpr uint8_t DB_BEST_BLOCK{'B'};
-constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
-constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
+constexpr auto SYNC_LOG_INTERVAL{30s};
+constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
template <typename... Args>
static void FatalError(const char* fmt, const Args&... args)
@@ -75,7 +75,7 @@ bool BaseIndex::Init()
if (!m_best_block_index) {
// index is not built yet
// make sure we have all block data back to the genesis
- prune_violation = node::GetFirstStoredBlock(active_chain.Tip()) != active_chain.Genesis();
+ prune_violation = m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis();
}
// in case the index has a best block set and is not fully synced
// check if we have the required blocks to continue building the index
@@ -130,8 +130,8 @@ void BaseIndex::ThreadSync()
if (!m_synced) {
auto& consensus_params = Params().GetConsensus();
- int64_t last_log_time = 0;
- int64_t last_locator_write_time = 0;
+ std::chrono::steady_clock::time_point last_log_time{0s};
+ std::chrono::steady_clock::time_point last_locator_write_time{0s};
while (true) {
if (m_interrupt) {
SetBestBlockIndex(pindex);
@@ -160,7 +160,7 @@ void BaseIndex::ThreadSync()
pindex = pindex_next;
}
- int64_t current_time = GetTime();
+ auto current_time{std::chrono::steady_clock::now()};
if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
LogPrintf("Syncing %s with block chain from height %d\n",
GetName(), pindex->nHeight);
@@ -168,7 +168,7 @@ void BaseIndex::ThreadSync()
}
if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
- SetBestBlockIndex(pindex);
+ SetBestBlockIndex(pindex->pprev);
last_locator_write_time = current_time;
// No need to handle errors in Commit. See rationale above.
Commit();
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index 4f99eddfd7..c92b8c7e19 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -100,7 +100,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
const std::string& filter_name = BlockFilterTypeName(filter_type);
if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
- fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / filter_name;
+ fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / fs::u8path(filter_name);
fs::create_directories(path);
m_name = filter_name + " block filter index";
diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h
index b1836fe12f..6deff59000 100644
--- a/src/index/blockfilterindex.h
+++ b/src/index/blockfilterindex.h
@@ -64,7 +64,7 @@ public:
bool LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const;
/** Get a single filter header by block. */
- bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out);
+ bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_headers_cache);
/** Get a range of filters between two heights on a chain. */
bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 69078708f9..687e330fe0 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -12,10 +12,11 @@
#include <undo.h>
#include <validation.h>
-using node::CCoinsStats;
-using node::GetBogoSize;
+using kernel::CCoinsStats;
+using kernel::GetBogoSize;
+using kernel::TxOutSer;
+
using node::ReadBlockFromDisk;
-using node::TxOutSer;
using node::UndoReadFromDisk;
static constexpr uint8_t DB_BLOCK_HASH{'s'};
@@ -316,28 +317,31 @@ static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVa
return db.Read(DBHashKey(block_index->GetBlockHash()), result);
}
-bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const
+std::optional<CCoinsStats> CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const
{
+ CCoinsStats stats{Assert(block_index)->nHeight, block_index->GetBlockHash()};
+ stats.index_used = true;
+
DBVal entry;
if (!LookUpOne(*m_db, block_index, entry)) {
- return false;
+ return std::nullopt;
}
- coins_stats.hashSerialized = entry.muhash;
- coins_stats.nTransactionOutputs = entry.transaction_output_count;
- coins_stats.nBogoSize = entry.bogo_size;
- coins_stats.total_amount = entry.total_amount;
- coins_stats.total_subsidy = entry.total_subsidy;
- coins_stats.total_unspendable_amount = entry.total_unspendable_amount;
- coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
- coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
- coins_stats.total_coinbase_amount = entry.total_coinbase_amount;
- coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
- coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
- coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts;
- coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
-
- return true;
+ stats.hashSerialized = entry.muhash;
+ stats.nTransactionOutputs = entry.transaction_output_count;
+ stats.nBogoSize = entry.bogo_size;
+ stats.total_amount = entry.total_amount;
+ stats.total_subsidy = entry.total_subsidy;
+ stats.total_unspendable_amount = entry.total_unspendable_amount;
+ stats.total_prevout_spent_amount = entry.total_prevout_spent_amount;
+ stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
+ stats.total_coinbase_amount = entry.total_coinbase_amount;
+ stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
+ stats.total_unspendables_bip30 = entry.total_unspendables_bip30;
+ stats.total_unspendables_scripts = entry.total_unspendables_scripts;
+ stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
+
+ return stats;
}
bool CoinStatsIndex::Init()
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index 6f53bb74fb..cae052d913 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -9,7 +9,7 @@
#include <crypto/muhash.h>
#include <flatfile.h>
#include <index/base.h>
-#include <node/coinstats.h>
+#include <kernel/coinstats.h>
/**
* CoinStatsIndex maintains statistics on the UTXO set.
@@ -56,7 +56,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, node::CCoinsStats& coins_stats) const;
+ std::optional<kernel::CCoinsStats> LookUpStats(const CBlockIndex* block_index) const;
};
/// The global UTXO set hash object.
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index e1d807f39a..97c11c4383 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -52,7 +52,7 @@ TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
{}
-TxIndex::~TxIndex() {}
+TxIndex::~TxIndex() = default;
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
{
diff --git a/src/init.cpp b/src/init.cpp
index fdcb2b8ac7..d0fd6074b1 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -9,12 +9,13 @@
#include <init.h>
+#include <kernel/checks.h>
+
#include <addrman.h>
#include <banman.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <compat/sanity.h>
#include <consensus/amount.h>
#include <deploymentstatus.h>
#include <fs.h>
@@ -65,6 +66,7 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/syscall_sandbox.h>
+#include <util/syserror.h>
#include <util/system.h>
#include <util/thread.h>
#include <util/threadnames.h>
@@ -85,13 +87,11 @@
#include <vector>
#ifndef WIN32
-#include <attributes.h>
#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
#endif
-#include <boost/algorithm/string/replace.hpp>
#include <boost/signals2/signal.hpp>
#if ENABLE_ZMQ
@@ -150,7 +150,7 @@ static fs::path GetPidFile(const ArgsManager& args)
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), SysErrorString(errno)));
}
}
@@ -306,7 +306,7 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- init::UnsetGlobals();
+ node.kernel.reset();
node.mempool.reset();
node.fee_estimator.reset();
node.chainman.reset();
@@ -320,7 +320,6 @@ void Shutdown(NodeContext& node)
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
- node.args = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -408,7 +407,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless the peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -471,7 +470,7 @@ void SetupServerArgs(ArgsManager& argsman)
// TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from
// https://github.com/bitcoin/bitcoin/pull/23542 have become widespread.
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
- argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -570,6 +569,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcauth=<userpw>", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
+ argsman.AddArg("-rpcdoccheck", strprintf("Throw a non-fatal error at runtime if the documentation for an RPC is incorrect (default: %u)", DEFAULT_RPC_DOC_CHECK), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
@@ -1028,10 +1028,6 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
- if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
- return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
- }
-
if (args.GetBoolArg("-reindex-chainstate", false)) {
// indexes that must be deactivated to prevent index corruption, see #24630
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
@@ -1095,13 +1091,24 @@ static bool LockDataDirectory(bool probeOnly)
return true;
}
-bool AppInitSanityChecks()
+bool AppInitSanityChecks(const kernel::Context& kernel)
{
// ********************************************************* Step 4: sanity checks
+ auto maybe_error = kernel::SanityChecks(kernel);
+
+ if (maybe_error.has_value()) {
+ switch (maybe_error.value()) {
+ case kernel::SanityCheckError::ERROR_ECC:
+ InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_RANDOM:
+ InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_CHRONO:
+ InitError(Untranslated("Clock epoch mismatch. Aborting."));
+ break;
+ } // no default case, so the compiler can warn about missing cases
- init::SetGlobals();
-
- if (!init::SanityChecks()) {
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
}
@@ -1280,8 +1287,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
assert(!node.banman);
node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
- node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()),
- GetRand(std::numeric_limits<uint64_t>::max()),
+ node.connman = std::make_unique<CConnman>(GetRand<uint64_t>(),
+ GetRand<uint64_t>(),
*node.addrman, *node.netgroupman, args.GetBoolArg("-networkactive", true));
assert(!node.fee_estimator);
@@ -1424,7 +1431,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), mempool_check_ratio);
- node.chainman = std::make_unique<ChainstateManager>();
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ GetAdjustedTime,
+ };
+ node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
ChainstateManager& chainman = *node.chainman;
const bool fReset = fReindex;
@@ -1438,7 +1449,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
chainman,
Assert(node.mempool.get()),
fPruneMode,
- chainparams.GetConsensus(),
fReindexChainState,
cache_sizes.block_tree_db,
cache_sizes.coins_db,
@@ -1485,7 +1495,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
break;
case ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED:
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
- chainparams.GetConsensus().SegwitHeight);
+ chainman.GetConsensus().SegwitHeight);
break;
case ChainstateLoadingError::SHUTDOWN_PROBED:
break;
@@ -1502,10 +1512,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
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));
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL));
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
maybe_verify_error = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
@@ -1561,7 +1569,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ChainstateManager& chainman = *Assert(node.chainman);
assert(!node.peerman);
- node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
+ node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(),
chainman, *node.mempool, ignores_incoming_txs);
RegisterValidationInterface(node.peerman.get());
@@ -1640,7 +1648,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
uiInterface.NotifyBlockTip_connect([block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) {
if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) return;
std::string command = block_notify;
- boost::replace_all(command, "%s", pBlockIndex->GetBlockHash().GetHex());
+ ReplaceAll(command, "%s", pBlockIndex->GetBlockHash().GetHex());
std::thread t(runCommand, command);
t.detach(); // thread runs free
});
@@ -1683,8 +1691,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
chain_active_height = chainman.ActiveChain().Height();
if (tip_info) {
tip_info->block_height = chain_active_height;
- tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : Params().GenesisBlock().GetBlockTime();
- tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip());
+ tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : chainman.GetParams().GenesisBlock().GetBlockTime();
+ tip_info->verification_progress = GuessVerificationProgress(chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && chainman.m_best_header) {
tip_info->header_height = chainman.m_best_header->nHeight;
diff --git a/src/init.h b/src/init.h
index 2250ae20a0..e8e6a55eba 100644
--- a/src/init.h
+++ b/src/init.h
@@ -19,6 +19,9 @@ class ArgsManager;
namespace interfaces {
struct BlockAndHeaderTipInfo;
}
+namespace kernel {
+struct Context;
+}
namespace node {
struct NodeContext;
} // namespace node
@@ -43,11 +46,11 @@ bool AppInitBasicSetup(const ArgsManager& args);
*/
bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandbox = true);
/**
- * Initialization sanity checks: ecc init, sanity checks, dir lock.
+ * Initialization sanity checks.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
*/
-bool AppInitSanityChecks();
+bool AppInitSanityChecks(const kernel::Context& kernel);
/**
* Lock bitcoin core data directory.
* @note This should only be done after daemonization. Do not call Shutdown() if this function fails.
diff --git a/src/init/common.cpp b/src/init/common.cpp
index eac6732968..d4e45454d2 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -7,62 +7,19 @@
#endif
#include <clientversion.h>
-#include <compat/sanity.h>
-#include <crypto/sha256.h>
#include <fs.h>
-#include <key.h>
#include <logging.h>
#include <node/ui_interface.h>
-#include <pubkey.h>
-#include <random.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <algorithm>
-#include <memory>
#include <string>
#include <vector>
-static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
-
namespace init {
-void SetGlobals()
-{
- std::string sha256_algo = SHA256AutoDetect();
- LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
- RandomInit();
- ECC_Start();
- globalVerifyHandle.reset(new ECCVerifyHandle());
-}
-
-void UnsetGlobals()
-{
- globalVerifyHandle.reset();
- ECC_Stop();
-}
-
-bool SanityChecks()
-{
- if (!ECC_InitSanityCheck()) {
- return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
- }
-
- if (!glibcxx_sanity_test())
- return false;
-
- if (!Random_SanityCheck()) {
- return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
- }
-
- if (!ChronoSanityCheck()) {
- return InitError(Untranslated("Clock epoch mismatch. Aborting."));
- }
-
- return true;
-}
-
void AddLoggingArgs(ArgsManager& argsman)
{
argsman.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
diff --git a/src/init/common.h b/src/init/common.h
index fc4bc1b280..2c7f485908 100644
--- a/src/init/common.h
+++ b/src/init/common.h
@@ -11,13 +11,6 @@
class ArgsManager;
namespace init {
-void SetGlobals();
-void UnsetGlobals();
-/**
- * Ensure a usable environment with all
- * necessary library support.
- */
-bool SanityChecks();
void AddLoggingArgs(ArgsManager& args);
void SetLoggingOptions(const ArgsManager& args);
void SetLoggingCategories(const ArgsManager& args);
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index c4dc303dd5..2c31e12ada 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -5,12 +5,13 @@
#ifndef BITCOIN_INTERFACES_NODE_H
#define BITCOIN_INTERFACES_NODE_H
-#include <consensus/amount.h>
-#include <net.h> // For NodeId
-#include <net_types.h> // For banmap_t
-#include <netaddress.h> // For Network
-#include <netbase.h> // For ConnectionDirection
+#include <consensus/amount.h> // For CAmount
+#include <net.h> // For NodeId
+#include <net_types.h> // For banmap_t
+#include <netaddress.h> // For Network
+#include <netbase.h> // For ConnectionDirection
#include <support/allocators/secure.h> // For SecureString
+#include <util/settings.h> // For util::SettingsValue
#include <util/translation.h>
#include <functional>
@@ -97,6 +98,24 @@ public:
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
+ //! Return whether a particular setting in <datadir>/settings.json is or
+ //! would be ignored because it is also specified in the command line.
+ virtual bool isSettingIgnored(const std::string& name) = 0;
+
+ //! Return setting value from <datadir>/settings.json or bitcoin.conf.
+ virtual util::SettingsValue getPersistentSetting(const std::string& name) = 0;
+
+ //! Update a setting in <datadir>/settings.json.
+ virtual void updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0;
+
+ //! Force a setting value to be applied, overriding any other configuration
+ //! source, but not being persisted.
+ virtual void forceSetting(const std::string& name, const util::SettingsValue& value) = 0;
+
+ //! Clear all settings in <datadir>/settings.json and store a backup of
+ //! previous settings in <datadir>/settings.json.bak.
+ virtual void resetSettings() = 0;
+
//! Map port.
virtual void mapPort(bool use_upnp, bool use_natpmp) = 0;
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
new file mode 100644
index 0000000000..575d94e2e9
--- /dev/null
+++ b/src/kernel/chainstatemanager_opts.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
+#define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
+
+#include <cstdint>
+#include <functional>
+
+class CChainParams;
+
+/**
+ * An options struct for `ChainstateManager`, more ergonomically referred to as
+ * `ChainstateManager::Options` due to the using-declaration in
+ * `ChainstateManager`.
+ */
+struct ChainstateManagerOpts {
+ const CChainParams& chainparams;
+ const std::function<int64_t()> adjusted_time_callback{nullptr};
+};
+
+#endif // BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H
diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp
new file mode 100644
index 0000000000..2a1dd3bfa2
--- /dev/null
+++ b/src/kernel/checks.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <kernel/checks.h>
+
+#include <key.h>
+#include <random.h>
+#include <util/time.h>
+
+namespace kernel {
+
+std::optional<SanityCheckError> SanityChecks(const Context&)
+{
+ if (!ECC_InitSanityCheck()) {
+ return SanityCheckError::ERROR_ECC;
+ }
+
+ if (!Random_SanityCheck()) {
+ return SanityCheckError::ERROR_RANDOM;
+ }
+
+ if (!ChronoSanityCheck()) {
+ return SanityCheckError::ERROR_CHRONO;
+ }
+
+ return std::nullopt;
+}
+
+}
diff --git a/src/kernel/checks.h b/src/kernel/checks.h
new file mode 100644
index 0000000000..80b207f607
--- /dev/null
+++ b/src/kernel/checks.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_KERNEL_CHECKS_H
+#define BITCOIN_KERNEL_CHECKS_H
+
+#include <optional>
+
+namespace kernel {
+
+struct Context;
+
+enum class SanityCheckError {
+ ERROR_ECC,
+ ERROR_RANDOM,
+ ERROR_CHRONO,
+};
+
+/**
+ * Ensure a usable environment with all necessary library support.
+ */
+std::optional<SanityCheckError> SanityChecks(const Context&);
+
+}
+
+#endif // BITCOIN_KERNEL_CHECKS_H
diff --git a/src/node/coinstats.cpp b/src/kernel/coinstats.cpp
index eed43a1bc7..f380871627 100644
--- a/src/node/coinstats.cpp
+++ b/src/kernel/coinstats.cpp
@@ -1,14 +1,12 @@
-// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2021 The Bitcoin Core developers
+// Copyright (c) 2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <node/coinstats.h>
+#include <kernel/coinstats.h>
#include <coins.h>
#include <crypto/muhash.h>
#include <hash.h>
-#include <index/coinstatsindex.h>
#include <serialize.h>
#include <uint256.h>
#include <util/overflow.h>
@@ -17,7 +15,12 @@
#include <map>
-namespace node {
+namespace kernel {
+
+CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash)
+ : nHeight(block_height),
+ hashBlock(block_hash) {}
+
// Database-independent metric indicating the UTXO set size
uint64_t GetBogoSize(const CScript& script_pub_key)
{
@@ -93,24 +96,11 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<u
//! Calculate statistics about the unspent transaction output set
template <typename T>
-static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point, const CBlockIndex* pindex)
+static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
- if (!pindex) {
- LOCK(cs_main);
- pindex = blockman.LookupBlockIndex(view->GetBestBlock());
- }
- stats.nHeight = Assert(pindex)->nHeight;
- stats.hashBlock = pindex->GetBlockHash();
-
- // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
- if ((stats.m_hash_type == CoinStatsHashType::MUHASH || stats.m_hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && stats.index_requested) {
- stats.index_used = true;
- return g_coin_stats_index->LookUpStats(pindex, stats);
- }
-
PrepareHash(hash_obj, stats);
uint256 prevkey;
@@ -141,25 +131,36 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
FinalizeHash(hash_obj, stats);
stats.nDiskSize = view->EstimateSize();
+
return true;
}
-bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point, const CBlockIndex* pindex)
+std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point)
{
- switch (stats.m_hash_type) {
- case(CoinStatsHashType::HASH_SERIALIZED): {
- CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex);
- }
- case(CoinStatsHashType::MUHASH): {
- MuHash3072 muhash;
- return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex);
- }
- case(CoinStatsHashType::NONE): {
- return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex);
+ CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
+ CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()};
+
+ bool success = [&]() -> bool {
+ switch (hash_type) {
+ case(CoinStatsHashType::HASH_SERIALIZED): {
+ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
+ return ComputeUTXOStats(view, stats, ss, interruption_point);
+ }
+ case(CoinStatsHashType::MUHASH): {
+ MuHash3072 muhash;
+ return ComputeUTXOStats(view, stats, muhash, interruption_point);
+ }
+ case(CoinStatsHashType::NONE): {
+ return ComputeUTXOStats(view, stats, nullptr, interruption_point);
+ }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ }();
+
+ if (!success) {
+ return std::nullopt;
}
- } // no default case, so the compiler can warn about missing cases
- assert(false);
+ return stats;
}
// The legacy hash serializes the hashBlock
@@ -182,4 +183,5 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
stats.hashSerialized = out;
}
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
-} // namespace node
+
+} // namespace kernel
diff --git a/src/node/coinstats.h b/src/kernel/coinstats.h
index aa771b18b0..a15957233f 100644
--- a/src/node/coinstats.h
+++ b/src/kernel/coinstats.h
@@ -1,10 +1,9 @@
-// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2021 The Bitcoin Core developers
+// Copyright (c) 2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_NODE_COINSTATS_H
-#define BITCOIN_NODE_COINSTATS_H
+#ifndef BITCOIN_KERNEL_COINSTATS_H
+#define BITCOIN_KERNEL_COINSTATS_H
#include <chain.h>
#include <coins.h>
@@ -20,7 +19,7 @@ namespace node {
class BlockManager;
} // namespace node
-namespace node {
+namespace kernel {
enum class CoinStatsHashType {
HASH_SERIALIZED,
MUHASH,
@@ -28,9 +27,6 @@ enum class CoinStatsHashType {
};
struct CCoinsStats {
- //! Which hash type to use
- const CoinStatsHashType m_hash_type;
-
int nHeight{0};
uint256 hashBlock{};
uint64_t nTransactions{0};
@@ -44,8 +40,6 @@ struct CCoinsStats {
//! The number of coins contained.
uint64_t coins_count{0};
- //! Signals if the coinstatsindex should be used (when available).
- bool index_requested{true};
//! Signals if the coinstatsindex was used to retrieve the statistics.
bool index_used{false};
@@ -70,15 +64,15 @@ struct CCoinsStats {
//! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block
CAmount total_unspendables_unclaimed_rewards{0};
- CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {}
+ CCoinsStats() = default;
+ CCoinsStats(int block_height, const uint256& block_hash);
};
-//! Calculate statistics about the unspent transaction output set
-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
+std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point = {});
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_COINSTATS_H
diff --git a/src/kernel/context.cpp b/src/kernel/context.cpp
new file mode 100644
index 0000000000..15413c1840
--- /dev/null
+++ b/src/kernel/context.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <kernel/context.h>
+
+#include <crypto/sha256.h>
+#include <key.h>
+#include <logging.h>
+#include <pubkey.h>
+#include <random.h>
+
+#include <string>
+
+
+namespace kernel {
+
+Context::Context()
+{
+ std::string sha256_algo = SHA256AutoDetect();
+ LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
+ RandomInit();
+ ECC_Start();
+ ecc_verify_handle.reset(new ECCVerifyHandle());
+}
+
+Context::~Context()
+{
+ ecc_verify_handle.reset();
+ ECC_Stop();
+}
+
+} // namespace kernel
diff --git a/src/kernel/context.h b/src/kernel/context.h
new file mode 100644
index 0000000000..0a08511564
--- /dev/null
+++ b/src/kernel/context.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_KERNEL_CONTEXT_H
+#define BITCOIN_KERNEL_CONTEXT_H
+
+#include <memory>
+
+class ECCVerifyHandle;
+
+namespace kernel {
+//! Context struct holding the kernel library's logically global state, and
+//! passed to external libbitcoin_kernel functions which need access to this
+//! state. The kernel libary API is a work in progress, so state organization
+//! and member list will evolve over time.
+//!
+//! State stored directly in this struct should be simple. More complex state
+//! should be stored to std::unique_ptr members pointing to opaque types.
+struct Context {
+ std::unique_ptr<ECCVerifyHandle> ecc_verify_handle;
+
+ //! Declare default constructor and destructor that are not inline, so code
+ //! instantiating the kernel::Context struct doesn't need to #include class
+ //! definitions for all the unique_ptr members.
+ Context();
+ ~Context();
+};
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_CONTEXT_H
diff --git a/src/key.cpp b/src/key.cpp
index d1d521f97d..9b0971a2dd 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -340,11 +340,11 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
-void CExtKey::SetSeed(Span<const uint8_t> seed)
+void CExtKey::SetSeed(Span<const std::byte> seed)
{
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
- CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(seed.data(), seed.size()).Finalize(vout.data());
+ CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(UCharCast(seed.data()), seed.size()).Finalize(vout.data());
key.Set(vout.data(), vout.data() + 32, true);
memcpy(chaincode.begin(), vout.data() + 32, 32);
nDepth = 0;
diff --git a/src/key.h b/src/key.h
index b21e658107..12d03778a0 100644
--- a/src/key.h
+++ b/src/key.h
@@ -85,7 +85,7 @@ public:
//! Simple read-only vector-like interface.
unsigned int size() const { return (fValid ? keydata.size() : 0); }
- const unsigned char* data() const { return keydata.data(); }
+ const std::byte* data() const { return reinterpret_cast<const std::byte*>(keydata.data()); }
const unsigned char* begin() const { return keydata.data(); }
const unsigned char* end() const { return keydata.data() + size(); }
@@ -178,7 +178,7 @@ struct CExtKey {
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey& out, unsigned int nChild) const;
CExtPubKey Neuter() const;
- void SetSeed(Span<const uint8_t> seed);
+ void SetSeed(Span<const std::byte> seed);
};
/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
diff --git a/src/logging.cpp b/src/logging.cpp
index a5e5fdb4cd..1e2c1d5a77 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -171,7 +171,7 @@ const CLogCategoryDesc LogCategories[] =
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
{
- if (str == "") {
+ if (str.empty()) {
flag = BCLog::ALL;
return true;
}
@@ -184,6 +184,91 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
return false;
}
+std::string LogLevelToStr(BCLog::Level level)
+{
+ switch (level) {
+ case BCLog::Level::None:
+ return "none";
+ case BCLog::Level::Debug:
+ return "debug";
+ case BCLog::Level::Info:
+ return "info";
+ case BCLog::Level::Warning:
+ return "warning";
+ case BCLog::Level::Error:
+ return "error";
+ }
+ assert(false);
+}
+
+std::string LogCategoryToStr(BCLog::LogFlags category)
+{
+ // Each log category string representation should sync with LogCategories
+ switch (category) {
+ case BCLog::LogFlags::NONE:
+ return "none";
+ case BCLog::LogFlags::NET:
+ return "net";
+ case BCLog::LogFlags::TOR:
+ return "tor";
+ case BCLog::LogFlags::MEMPOOL:
+ return "mempool";
+ case BCLog::LogFlags::HTTP:
+ return "http";
+ case BCLog::LogFlags::BENCH:
+ return "bench";
+ case BCLog::LogFlags::ZMQ:
+ return "zmq";
+ case BCLog::LogFlags::WALLETDB:
+ return "walletdb";
+ case BCLog::LogFlags::RPC:
+ return "rpc";
+ case BCLog::LogFlags::ESTIMATEFEE:
+ return "estimatefee";
+ case BCLog::LogFlags::ADDRMAN:
+ return "addrman";
+ case BCLog::LogFlags::SELECTCOINS:
+ return "selectcoins";
+ case BCLog::LogFlags::REINDEX:
+ return "reindex";
+ case BCLog::LogFlags::CMPCTBLOCK:
+ return "cmpctblock";
+ case BCLog::LogFlags::RAND:
+ return "rand";
+ case BCLog::LogFlags::PRUNE:
+ return "prune";
+ case BCLog::LogFlags::PROXY:
+ return "proxy";
+ case BCLog::LogFlags::MEMPOOLREJ:
+ return "mempoolrej";
+ case BCLog::LogFlags::LIBEVENT:
+ return "libevent";
+ case BCLog::LogFlags::COINDB:
+ return "coindb";
+ case BCLog::LogFlags::QT:
+ return "qt";
+ case BCLog::LogFlags::LEVELDB:
+ return "leveldb";
+ case BCLog::LogFlags::VALIDATION:
+ return "validation";
+ case BCLog::LogFlags::I2P:
+ return "i2p";
+ case BCLog::LogFlags::IPC:
+ return "ipc";
+#ifdef DEBUG_LOCKCONTENTION
+ case BCLog::LogFlags::LOCK:
+ return "lock";
+#endif
+ case BCLog::LogFlags::UTIL:
+ return "util";
+ case BCLog::LogFlags::BLOCKSTORE:
+ return "blockstorage";
+ case BCLog::LogFlags::ALL:
+ return "all";
+ }
+ assert(false);
+}
+
std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
{
// Sort log categories by alphabetical order.
@@ -249,17 +334,38 @@ namespace BCLog {
}
} // namespace BCLog
-void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line)
+void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level)
{
StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str);
+ if ((category != LogFlags::NONE || level != Level::None) && m_started_new_line) {
+ std::string s{"["};
+
+ if (category != LogFlags::NONE) {
+ s += LogCategoryToStr(category);
+ }
+
+ if (category != LogFlags::NONE && level != Level::None) {
+ // Only add separator if both flag and level are not NONE
+ s += ":";
+ }
+
+ if (level != Level::None) {
+ s += LogLevelToStr(level);
+ }
+
+ s += "] ";
+ str_prefixed.insert(0, s);
+ }
+
if (m_log_sourcelocations && m_started_new_line) {
str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
}
if (m_log_threadnames && m_started_new_line) {
- str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
+ const auto threadname = util::ThreadGetInternalName();
+ str_prefixed.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
}
str_prefixed = LogTimestampStr(str_prefixed);
diff --git a/src/logging.h b/src/logging.h
index ae8cad906c..8a896b6b33 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -67,6 +67,13 @@ namespace BCLog {
BLOCKSTORE = (1 << 26),
ALL = ~(uint32_t)0,
};
+ enum class Level {
+ Debug = 0,
+ None = 1,
+ Info = 2,
+ Warning = 3,
+ Error = 4,
+ };
class Logger
{
@@ -105,7 +112,7 @@ namespace BCLog {
std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */
- void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line);
+ void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level);
/** Returns whether logs will be written to any output */
bool Enabled() const
@@ -159,9 +166,14 @@ namespace BCLog {
BCLog::Logger& LogInstance();
-/** Return true if log accepts specified category */
-static inline bool LogAcceptCategory(BCLog::LogFlags category)
+/** Return true if log accepts specified category, at the specified level. */
+static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
{
+ // Log messages at Warning and Error level unconditionally, so that
+ // important troubleshooting information doesn't get lost.
+ if (level >= BCLog::Level::Warning) {
+ return true;
+ }
return LogInstance().WillLogCategory(category);
}
@@ -173,7 +185,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
// peer can fill up a user's disk with debug.log entries.
template <typename... Args>
-static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args)
+static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags flag, const BCLog::Level level, const char* fmt, const Args&... args)
{
if (LogInstance().Enabled()) {
std::string log_msg;
@@ -183,19 +195,29 @@ static inline void LogPrintf_(const std::string& logging_function, const std::st
/* Original format string will have newline so don't add one here */
log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
}
- LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line);
+ LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line, flag, level);
}
}
-#define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__)
+
+#define LogPrintLevel_(category, level, ...) LogPrintf_(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
+
+#define LogPrintf(...) LogPrintLevel_(BCLog::LogFlags::NONE, BCLog::Level::None, __VA_ARGS__)
// Use a macro instead of a function for conditional logging to prevent
// evaluating arguments when logging for the category is not enabled.
-#define LogPrint(category, ...) \
- do { \
- if (LogAcceptCategory((category))) { \
- LogPrintf(__VA_ARGS__); \
- } \
+#define LogPrint(category, ...) \
+ do { \
+ if (LogAcceptCategory((category), BCLog::Level::Debug)) { \
+ LogPrintLevel_(category, BCLog::Level::None, __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define LogPrintLevel(category, level, ...) \
+ do { \
+ if (LogAcceptCategory((category), (level))) { \
+ LogPrintLevel_(category, level, __VA_ARGS__); \
+ } \
} while (0)
#endif // BITCOIN_LOGGING_H
diff --git a/src/net.cpp b/src/net.cpp
index aa70bed226..317366df90 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -25,6 +25,7 @@
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
+#include <util/designator.h>
#include <util/sock.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
@@ -103,7 +104,7 @@ enum BindFlags {
// The sleep time needs to be small to avoid new sockets stalling
static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50;
-const std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
+const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
@@ -430,7 +431,7 @@ static CAddress GetBindAddress(SOCKET sock)
if (!getsockname(sock, (struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) {
addr_bind.SetSockAddr((const struct sockaddr*)&sockaddr_bind);
} else {
- LogPrint(BCLog::NET, "Warning: getsockname failed\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n");
}
}
return addr_bind;
@@ -454,9 +455,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
}
/// debug print
- LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n",
- pszDest ? pszDest : addrConnect.ToString(),
- pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying connection %s lastseen=%.1fhrs\n",
+ pszDest ? pszDest : addrConnect.ToString(),
+ pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0);
// Resolve
const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
@@ -643,12 +644,12 @@ void CNode::CopyStats(CNodeStats& stats)
X(m_bip152_highbandwidth_from);
{
LOCK(cs_vSend);
- X(mapSendBytesPerMsgCmd);
+ X(mapSendBytesPerMsgType);
X(nSendBytes);
}
{
LOCK(cs_vRecv);
- X(mapRecvBytesPerMsgCmd);
+ X(mapRecvBytesPerMsgType);
X(nRecvBytes);
}
X(m_permissionFlags);
@@ -684,19 +685,19 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
bool reject_message{false};
CNetMessage msg = m_deserializer->GetMessage(time, reject_message);
if (reject_message) {
- // Message deserialization failed. Drop the message but don't disconnect the peer.
+ // Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
- mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += msg.m_raw_message_size;
+ mapRecvBytesPerMsgType.at(NET_MESSAGE_TYPE_OTHER) += msg.m_raw_message_size;
continue;
}
- // Store received bytes per message command
- // to prevent a memory DOS, only allow valid commands
- auto i = mapRecvBytesPerMsgCmd.find(msg.m_type);
- if (i == mapRecvBytesPerMsgCmd.end()) {
- i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
+ // Store received bytes per message type.
+ // To prevent a memory DOS, only allow known message types.
+ auto i = mapRecvBytesPerMsgType.find(msg.m_type);
+ if (i == mapRecvBytesPerMsgType.end()) {
+ i = mapRecvBytesPerMsgType.find(NET_MESSAGE_TYPE_OTHER);
}
- assert(i != mapRecvBytesPerMsgCmd.end());
+ assert(i != mapRecvBytesPerMsgType.end());
i->second += msg.m_raw_message_size;
// push the message to the process queue,
@@ -781,7 +782,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
// decompose a single CNetMessage from the TransportDeserializer
CNetMessage msg(std::move(vRecv));
- // store command string, time, and sizes
+ // store message type string, time, and sizes
msg.m_type = hdr.GetCommand();
msg.m_time = time;
msg.m_message_size = hdr.nMessageSize;
@@ -792,7 +793,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
// We just received a message off the wire, harvest entropy from the time (and the message checksum)
RandAddEvent(ReadLE32(hash.begin()));
- // Check checksum and header command string
+ // Check checksum and header message type 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_type), msg.m_message_size,
@@ -1101,12 +1102,20 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
- NodeEvictionCandidate candidate = {node->GetId(), node->m_connected, node->m_min_ping_time,
- node->m_last_block_time, node->m_last_tx_time,
- HasAllDesirableServiceFlags(node->nServices),
- node->m_relays_txs.load(), node->m_bloom_filter_loaded.load(),
- node->nKeyedNetGroup, node->m_prefer_evict, node->addr.IsLocal(),
- node->ConnectedThroughNetwork()};
+ NodeEvictionCandidate candidate{
+ Desig(id) node->GetId(),
+ Desig(m_connected) node->m_connected,
+ Desig(m_min_ping_time) node->m_min_ping_time,
+ Desig(m_last_block_time) node->m_last_block_time,
+ Desig(m_last_tx_time) node->m_last_tx_time,
+ Desig(fRelevantServices) HasAllDesirableServiceFlags(node->nServices),
+ Desig(m_relay_txs) node->m_relays_txs.load(),
+ Desig(fBloomFilter) node->m_bloom_filter_loaded.load(),
+ Desig(nKeyedNetGroup) node->nKeyedNetGroup,
+ Desig(prefer_evict) node->m_prefer_evict,
+ Desig(m_is_local) node->addr.IsLocal(),
+ Desig(m_network) node->ConnectedThroughNetwork(),
+ };
vEvictionCandidates.push_back(candidate);
}
}
@@ -1140,7 +1149,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "Unknown socket family\n");
} else {
addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}
@@ -1867,6 +1876,12 @@ void CConnman::SetTryNewOutboundPeer(bool flag)
LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
}
+void CConnman::StartExtraBlockRelayPeers()
+{
+ LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n");
+ m_start_extra_block_relay_peers = true;
+}
+
// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
// disconnected soon (eg ADDR_FETCH and FEELER)
@@ -2160,7 +2175,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (fFeeler) {
// Add small amount of random noise before connection to avoid synchronization.
- int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
+ int randsleep = GetRand<int>(FEELER_SLEEP_WINDOW * 1000);
if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep)))
return;
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
@@ -2391,15 +2406,15 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
- strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString());
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToString());
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
std::unique_ptr<Sock> sock = CreateSock(addrBind);
if (!sock) {
- strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2435,7 +2450,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
- LogPrintf("%s\n", strError.original);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
@@ -2443,8 +2458,8 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Listen for incoming connections
if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR)
{
- strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(_("Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2685,7 +2700,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
class CNetCleanup
{
public:
- CNetCleanup() {}
+ CNetCleanup() = default;
~CNetCleanup()
{
@@ -3053,8 +3068,8 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
for (const std::string &msg : getAllNetMessageTypes())
- mapRecvBytesPerMsgCmd[msg] = 0;
- mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
+ mapRecvBytesPerMsgType[msg] = 0;
+ mapRecvBytesPerMsgType[NET_MESSAGE_TYPE_OTHER] = 0;
if (fLogIPs) {
LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", m_addr_name, id);
@@ -3100,7 +3115,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
bool optimisticSend(pnode->vSendMsg.empty());
//log total amount of bytes per message type
- pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize;
+ pnode->mapSendBytesPerMsgType[msg.m_type] += nTotalSize;
pnode->nSendSize += nTotalSize;
if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true;
@@ -3153,7 +3168,7 @@ void CaptureMessageToFile(const CAddress& addr,
std::string clean_addr = addr.ToString();
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
- fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
+ fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr);
fs::create_directories(base_path);
fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");
diff --git a/src/net.h b/src/net.h
index 3271930a35..3d1a2658c7 100644
--- a/src/net.h
+++ b/src/net.h
@@ -13,7 +13,6 @@
#include <crypto/siphash.h>
#include <hash.h>
#include <i2p.h>
-#include <logging.h>
#include <net_permissions.h>
#include <netaddress.h>
#include <netbase.h>
@@ -252,8 +251,8 @@ struct LocalServiceInfo {
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
+extern const std::string NET_MESSAGE_TYPE_OTHER;
+using mapMsgTypeSize = std::map</* message type */ std::string, /* total bytes */ uint64_t>;
class CNodeStats
{
@@ -274,9 +273,9 @@ public:
bool m_bip152_highbandwidth_from;
int m_starting_height;
uint64_t nSendBytes;
- mapMsgCmdSize mapSendBytesPerMsgCmd;
+ mapMsgTypeSize mapSendBytesPerMsgType;
uint64_t nRecvBytes;
- mapMsgCmdSize mapRecvBytesPerMsgCmd;
+ mapMsgTypeSize mapRecvBytesPerMsgType;
NetPermissionFlags m_permissionFlags;
std::chrono::microseconds m_last_ping_time;
std::chrono::microseconds m_min_ping_time;
@@ -315,7 +314,7 @@ public:
/** The TransportDeserializer takes care of holding and deserializing the
* network receive buffer. It can deserialize the network buffer into a
- * transport protocol agnostic CNetMessage (command & payload)
+ * transport protocol agnostic CNetMessage (message type & payload)
*/
class TransportDeserializer {
public:
@@ -613,7 +612,7 @@ public:
* @return True if the peer should stay connected,
* False if the peer should be disconnected from.
*/
- bool ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete);
+ bool ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete) EXCLUSIVE_LOCKS_REQUIRED(!cs_vRecv);
void SetCommonVersion(int greatest_common_version)
{
@@ -625,9 +624,9 @@ public:
return m_greatest_common_version;
}
- CService GetAddrLocal() const LOCKS_EXCLUDED(m_addr_local_mutex);
+ CService GetAddrLocal() const EXCLUSIVE_LOCKS_REQUIRED(!m_addr_local_mutex);
//! May not be called more than once
- void SetAddrLocal(const CService& addrLocalIn) LOCKS_EXCLUDED(m_addr_local_mutex);
+ void SetAddrLocal(const CService& addrLocalIn) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_local_mutex);
CNode* AddRef()
{
@@ -640,9 +639,9 @@ public:
nRefCount--;
}
- void CloseSocketDisconnect();
+ void CloseSocketDisconnect() EXCLUSIVE_LOCKS_REQUIRED(!m_sock_mutex);
- void CopyStats(CNodeStats& stats);
+ void CopyStats(CNodeStats& stats) EXCLUSIVE_LOCKS_REQUIRED(!m_subver_mutex, !m_addr_local_mutex, !cs_vSend, !cs_vRecv);
ServiceFlags GetLocalServices() const
{
@@ -686,8 +685,8 @@ private:
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);
+ mapMsgTypeSize mapSendBytesPerMsgType GUARDED_BY(cs_vSend);
+ mapMsgTypeSize mapRecvBytesPerMsgType GUARDED_BY(cs_vRecv);
};
/**
@@ -761,7 +760,7 @@ public:
bool m_i2p_accept_incoming;
};
- void Init(const Options& connOptions) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex)
+ void Init(const Options& connOptions) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_total_bytes_sent_mutex)
{
AssertLockNotHeld(m_total_bytes_sent_mutex);
@@ -795,7 +794,8 @@ public:
bool network_active = true);
~CConnman();
- bool Start(CScheduler& scheduler, const Options& options) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
+
+ bool Start(CScheduler& scheduler, const Options& options) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !m_added_nodes_mutex, !m_addr_fetches_mutex, !mutexMsgProc);
void StopThreads();
void StopNodes();
@@ -805,7 +805,7 @@ public:
StopNodes();
};
- void Interrupt();
+ void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
@@ -857,10 +857,7 @@ public:
void SetTryNewOutboundPeer(bool flag);
bool GetTryNewOutboundPeer() const;
- void StartExtraBlockRelayPeers() {
- LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n");
- m_start_extra_block_relay_peers = true;
- }
+ void StartExtraBlockRelayPeers();
// Return the number of outbound peers we have in excess of our target (eg,
// if we previously called SetTryNewOutboundPeer(true), and have since set
@@ -872,9 +869,9 @@ public:
// Count the number of block-relay-only peers we have over our limit.
int GetExtraBlockRelayCount() const;
- bool AddNode(const std::string& node);
- bool RemoveAddedNode(const std::string& node);
- std::vector<AddedNodeInfo> GetAddedNodeInfo() const;
+ bool AddNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
+ bool RemoveAddedNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
+ std::vector<AddedNodeInfo> GetAddedNodeInfo() const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
/**
* Attempts to open a connection. Currently only used from tests.
@@ -927,7 +924,7 @@ public:
unsigned int GetReceiveFloodSize() const;
- void WakeMessageHandler();
+ void WakeMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
/** Return true if we should disconnect the peer for failing an inactivity check. */
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
@@ -954,11 +951,11 @@ private:
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
bool InitBinds(const Options& options);
- void ThreadOpenAddedConnections();
- void AddAddrFetch(const std::string& strDest);
- void ProcessAddrFetch();
- void ThreadOpenConnections(std::vector<std::string> connect);
- void ThreadMessageHandler();
+ void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
+ void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
+ void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
+ void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex);
+ void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
void ThreadI2PAcceptIncoming();
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -1009,7 +1006,7 @@ private:
/**
* Check connected and listening sockets for IO readiness and process them accordingly.
*/
- void SocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
+ void SocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
/**
* Do the read/write for connected sockets that are ready for IO.
@@ -1023,7 +1020,7 @@ private:
const std::set<SOCKET>& recv_set,
const std::set<SOCKET>& send_set,
const std::set<SOCKET>& error_set)
- EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
/**
* Accept incoming connections, one from each read-ready listening socket.
@@ -1031,8 +1028,8 @@ private:
*/
void SocketHandlerListening(const std::set<SOCKET>& recv_set);
- void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
- void ThreadDNSAddressSeed();
+ void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
+ void ThreadDNSAddressSeed() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_nodes_mutex);
uint64_t CalculateKeyedNetGroup(const CAddress& ad) const;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index e9e6544069..70b81b3eb2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -175,6 +175,8 @@ static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1};
* based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR
* is exempt from this limit). */
static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND};
+/** The compactblocks version we support. See BIP 152. */
+static constexpr uint64_t CMPCTBLOCKS_VERSION{2};
// Internal stuff
namespace {
@@ -237,36 +239,62 @@ struct Peer {
/** Whether this peer relays txs via wtxid */
std::atomic<bool> m_wtxid_relay{false};
+ /** The feerate in the most recent BIP133 `feefilter` message sent to the peer.
+ * It is *not* a p2p protocol violation for the peer to send us
+ * transactions with a lower fee rate than this. See BIP133. */
+ CAmount m_fee_filter_sent{0};
+ /** Timestamp after which we will send the next BIP133 `feefilter` message
+ * to the peer. */
+ std::chrono::microseconds m_next_send_feefilter{0};
struct TxRelay {
mutable RecursiveMutex m_bloom_filter_mutex;
- // We use m_relay_txs for two purposes -
- // a) it allows us to not relay tx invs before receiving the peer's version message
- // b) the peer may tell us in its version message that we should not relay tx invs
- // unless it loads a bloom filter.
+ /** Whether the peer wishes to receive transaction announcements.
+ *
+ * This is initially set based on the fRelay flag in the received
+ * `version` message. If initially set to false, it can only be flipped
+ * to true if we have offered the peer NODE_BLOOM services and it sends
+ * us a `filterload` or `filterclear` message. See BIP37. */
bool m_relay_txs GUARDED_BY(m_bloom_filter_mutex){false};
+ /** A bloom filter for which transactions to announce to the peer. See BIP37. */
std::unique_ptr<CBloomFilter> m_bloom_filter PT_GUARDED_BY(m_bloom_filter_mutex) GUARDED_BY(m_bloom_filter_mutex){nullptr};
mutable RecursiveMutex m_tx_inventory_mutex;
+ /** A filter of all the txids and wtxids that the peer has announced to
+ * us or we have announced to the peer. We use this to avoid announcing
+ * the same txid/wtxid to a peer that already has the transaction. */
CRollingBloomFilter m_tx_inventory_known_filter GUARDED_BY(m_tx_inventory_mutex){50000, 0.000001};
- // Set of transaction ids we still have to announce.
- // They are sorted by the mempool before relay, so the order is not important.
+ /** Set of transaction ids we still have to announce (txid for
+ * non-wtxid-relay peers, wtxid for wtxid-relay peers). We use the
+ * mempool to sort transactions in dependency order before relay, so
+ * this does not have to be sorted. */
std::set<uint256> m_tx_inventory_to_send;
- // Used for BIP35 mempool sending
+ /** Whether the peer has requested us to send our complete mempool. Only
+ * permitted if the peer has NetPermissionFlags::Mempool. See BIP35. */
bool m_send_mempool GUARDED_BY(m_tx_inventory_mutex){false};
- // Last time a "MEMPOOL" request was serviced.
+ /** The last time a BIP35 `mempool` request was serviced. */
std::atomic<std::chrono::seconds> m_last_mempool_req{0s};
+ /** The next time after which we will send an `inv` message containing
+ * transaction announcements to this peer. */
std::chrono::microseconds m_next_inv_send_time{0};
- /** Minimum fee rate with which to filter inv's to this node */
+ /** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
std::atomic<CAmount> m_fee_filter_received{0};
- CAmount m_fee_filter_sent{0};
- std::chrono::microseconds m_next_send_feefilter{0};
};
- /** Transaction relay data. Will be a nullptr if we're not relaying
- * transactions with this peer (e.g. if it's a block-relay-only peer) */
- std::unique_ptr<TxRelay> m_tx_relay;
+ /* Initializes a TxRelay struct for this peer. Can be called at most once for a peer. */
+ TxRelay* SetTxRelay() EXCLUSIVE_LOCKS_REQUIRED(!m_tx_relay_mutex)
+ {
+ LOCK(m_tx_relay_mutex);
+ Assume(!m_tx_relay);
+ m_tx_relay = std::make_unique<Peer::TxRelay>();
+ return m_tx_relay.get();
+ };
+
+ TxRelay* GetTxRelay()
+ {
+ return WITH_LOCK(m_tx_relay_mutex, return m_tx_relay.get());
+ };
/** A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
std::vector<CAddress> m_addrs_to_send;
@@ -326,46 +354,149 @@ struct Peer {
/** Work queue of items requested by this peer **/
std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
- explicit Peer(NodeId id, bool tx_relay)
- : m_id(id)
- , m_tx_relay(tx_relay ? std::make_unique<TxRelay>() : nullptr)
+ Peer(NodeId id)
+ : m_id{id}
{}
+
+private:
+ Mutex m_tx_relay_mutex;
+
+ /** Transaction relay data. Will be a nullptr if we're not relaying
+ * transactions with this peer (e.g. if it's a block-relay-only peer or
+ * the peer has sent us fRelay=false with bloom filters disabled). */
+ std::unique_ptr<TxRelay> m_tx_relay GUARDED_BY(m_tx_relay_mutex);
};
using PeerRef = std::shared_ptr<Peer>;
+/**
+ * Maintain validation-specific state about nodes, protected by cs_main, instead
+ * by CNode's own locks. This simplifies asynchronous operation, where
+ * processing of incoming data is done after the ProcessMessage call returns,
+ * and we're no longer holding the node's locks.
+ */
+struct CNodeState {
+ //! The best known block we know this peer has announced.
+ const CBlockIndex* pindexBestKnownBlock{nullptr};
+ //! The hash of the last unknown block this peer has announced.
+ uint256 hashLastUnknownBlock{};
+ //! The last full block we both have.
+ const CBlockIndex* pindexLastCommonBlock{nullptr};
+ //! The best header we have sent our peer.
+ const CBlockIndex* pindexBestHeaderSent{nullptr};
+ //! Length of current-streak of unconnecting headers announcements
+ int nUnconnectingHeaders{0};
+ //! Whether we've started headers synchronization with this peer.
+ bool fSyncStarted{false};
+ //! When to potentially disconnect peer for stalling headers download
+ std::chrono::microseconds m_headers_sync_timeout{0us};
+ //! Since when we're stalling block download progress (in microseconds), or 0.
+ std::chrono::microseconds m_stalling_since{0us};
+ std::list<QueuedBlock> vBlocksInFlight;
+ //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
+ std::chrono::microseconds m_downloading_since{0us};
+ int nBlocksInFlight{0};
+ //! Whether we consider this a preferred download peer.
+ bool fPreferredDownload{false};
+ //! Whether this peer wants invs or headers (when possible) for block announcements.
+ bool fPreferHeaders{false};
+ /** Whether this peer wants invs or cmpctblocks (when possible) for block announcements. */
+ bool m_requested_hb_cmpctblocks{false};
+ /** Whether this peer will send us cmpctblocks if we request them. */
+ bool m_provides_cmpctblocks{false};
+ //! Whether this peer can give us witnesses
+ bool fHaveWitness{false};
+
+ /** State used to enforce CHAIN_SYNC_TIMEOUT and EXTRA_PEER_CHECK_INTERVAL logic.
+ *
+ * Both are only in effect for outbound, non-manual, non-protected connections.
+ * Any peer protected (m_protect = true) is not chosen for eviction. A peer is
+ * marked as protected if all of these are true:
+ * - its connection type is IsBlockOnlyConn() == false
+ * - it gave us a valid connecting header
+ * - we haven't reached MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT yet
+ * - 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 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)
+ * - If at timeout their best known block still has less work than our
+ * tip did when the timeout was set, then send a getheaders message,
+ * and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future.
+ * If their best known block is still behind when that new timeout is
+ * reached, disconnect.
+ *
+ * EXTRA_PEER_CHECK_INTERVAL: after each interval, if we have too many outbound peers,
+ * drop the outbound one that least recently announced us a new block.
+ */
+ struct ChainSyncTimeoutState {
+ //! A timeout used for checking whether our peer has sufficiently synced
+ 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
+ bool m_sent_getheaders{false};
+ //! Whether this peer is protected from disconnection due to a bad/slow chain
+ bool m_protect{false};
+ };
+
+ ChainSyncTimeoutState m_chain_sync;
+
+ //! Time of last new block announcement
+ int64_t m_last_block_announcement{0};
+
+ //! Whether this peer is an inbound connection
+ const bool m_is_inbound;
+
+ //! A rolling bloom filter of all announced tx CInvs to this peer.
+ CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
+
+ CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {}
+};
+
class PeerManagerImpl final : public PeerManager
{
public:
- PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+ PeerManagerImpl(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
/** Overridden from CValidationInterface. */
- void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override;
- void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override;
- void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
- void BlockChecked(const CBlock& block, const BlockValidationState& state) override;
- void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
+ void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void BlockChecked(const CBlock& block, const BlockValidationState& state) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
/** Implement NetEventsInterface */
- void InitializeNode(CNode* pnode) override;
- void FinalizeNode(const CNode& node) override;
- bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
- bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
+ void InitializeNode(CNode* pnode) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
+ bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
/** Implement PeerManager */
void StartScheduledTasks(CScheduler& scheduler) override;
void CheckForStaleTipAndEvictPeers() override;
std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override;
- bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
+ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
- void SendPings() override;
- void RelayTransaction(const uint256& txid, const uint256& wtxid) override;
+ void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ void RelayTransaction(const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SetBestHeight(int height) override { m_best_height = height; };
- void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override;
+ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
- const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override;
+ const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
+ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
private:
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
@@ -375,15 +506,15 @@ private:
void EvictExtraOutboundPeers(std::chrono::seconds now) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */
- void ReattemptInitialBroadcast(CScheduler& scheduler);
+ void ReattemptInitialBroadcast(CScheduler& scheduler) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Get a shared pointer to the Peer object.
* May return an empty shared_ptr if the Peer object can't be found. */
- PeerRef GetPeerRef(NodeId id) const;
+ PeerRef GetPeerRef(NodeId id) const EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Get a shared pointer to the Peer object and remove it from m_peer_map.
* May return an empty shared_ptr if the Peer object can't be found. */
- PeerRef RemovePeer(NodeId id);
+ PeerRef RemovePeer(NodeId id) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/**
* Potentially mark a node discouraged based on the contents of a BlockValidationState object
@@ -396,14 +527,16 @@ private:
* @return Returns true if the peer was punished (probably disconnected)
*/
bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
- bool via_compact_block, const std::string& message = "");
+ bool via_compact_block, const std::string& message = "")
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/**
* Potentially disconnect and discourage a node based on the contents of a TxValidationState object
*
* @return Returns true if the peer was punished (probably disconnected)
*/
- bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "");
+ bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "")
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Maybe disconnect a peer and discourage future connections from its address.
*
@@ -413,13 +546,16 @@ private:
*/
bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
- void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
+ void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Process a single headers message from a peer. */
void ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
const std::vector<CBlockHeader>& headers,
- bool via_compact_block);
+ bool via_compact_block)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
- void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req);
+ void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Register with TxRequestTracker that an INV has been received from a
* peer. The announcement parameters are decided in PeerManager and then
@@ -446,7 +582,7 @@ private:
* @param[in] fReachable Whether the address' network is reachable. We relay unreachable
* addresses less.
*/
- void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable);
+ void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
/** Send `feefilter` message. */
void MaybeSendFeefilter(CNode& node, Peer& peer, std::chrono::microseconds current_time);
@@ -484,6 +620,16 @@ private:
*/
std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
+ /** Map maintaining per-node state. */
+ std::map<NodeId, CNodeState> m_node_states GUARDED_BY(cs_main);
+
+ /** Get a pointer to a const CNodeState, used when not mutating the CNodeState object. */
+ const CNodeState* State(NodeId pnode) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Get a pointer to a mutable CNodeState. */
+ CNodeState* State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ uint32_t GetFetchFlags(const CNode& pfrom) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
std::atomic<std::chrono::microseconds> m_next_inv_to_inbounds{0us};
/** Number of nodes with fSyncStarted. */
@@ -503,7 +649,11 @@ private:
/** Number of outbound peers with m_chain_sync.m_protect. */
int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
- bool AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Number of preferable block download peers. */
+ int m_num_preferred_download_peers GUARDED_BY(cs_main){0};
+
+ bool AlreadyHaveTx(const GenTxid& gtxid)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
/**
* Filter for transactions that were recently rejected by the mempool.
@@ -569,6 +719,16 @@ private:
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
std::chrono::seconds average_interval);
+
+ // All of the following cache a recent block, and are protected by m_most_recent_block_mutex
+ Mutex m_most_recent_block_mutex;
+ std::shared_ptr<const CBlock> m_most_recent_block GUARDED_BY(m_most_recent_block_mutex);
+ std::shared_ptr<const CBlockHeaderAndShortTxIDs> m_most_recent_compact_block GUARDED_BY(m_most_recent_block_mutex);
+ uint256 m_most_recent_block_hash GUARDED_BY(m_most_recent_block_mutex);
+
+ /** Height of the highest block announced using BIP 152 high-bandwidth mode. */
+ int m_highest_fast_announce{0};
+
/** Have we requested this block from a peer */
bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -599,7 +759,8 @@ private:
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
CTransactionRef FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main);
- void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex) LOCKS_EXCLUDED(::cs_main);
+ void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex, peer.m_getdata_requests_mutex) LOCKS_EXCLUDED(::cs_main);
/** Process a new block. Perform any post-processing housekeeping */
void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing);
@@ -650,7 +811,8 @@ private:
*/
bool BlockRequestAllowed(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv);
+ void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
/**
* Validation logic for compact filters request handling.
@@ -710,122 +872,20 @@ private:
*/
bool SetupAddressRelay(const CNode& node, Peer& peer);
};
-} // namespace
-
-namespace {
- /** Number of preferable block download peers. */
- int nPreferredDownload GUARDED_BY(cs_main) = 0;
-} // namespace
-namespace {
-/**
- * Maintain validation-specific state about nodes, protected by cs_main, instead
- * by CNode's own locks. This simplifies asynchronous operation, where
- * processing of incoming data is done after the ProcessMessage call returns,
- * and we're no longer holding the node's locks.
- */
-struct CNodeState {
- //! The best known block we know this peer has announced.
- const CBlockIndex* pindexBestKnownBlock{nullptr};
- //! The hash of the last unknown block this peer has announced.
- uint256 hashLastUnknownBlock{};
- //! The last full block we both have.
- const CBlockIndex* pindexLastCommonBlock{nullptr};
- //! The best header we have sent our peer.
- const CBlockIndex* pindexBestHeaderSent{nullptr};
- //! Length of current-streak of unconnecting headers announcements
- int nUnconnectingHeaders{0};
- //! Whether we've started headers synchronization with this peer.
- bool fSyncStarted{false};
- //! When to potentially disconnect peer for stalling headers download
- std::chrono::microseconds m_headers_sync_timeout{0us};
- //! Since when we're stalling block download progress (in microseconds), or 0.
- std::chrono::microseconds m_stalling_since{0us};
- std::list<QueuedBlock> vBlocksInFlight;
- //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
- std::chrono::microseconds m_downloading_since{0us};
- int nBlocksInFlight{0};
- //! Whether we consider this a preferred download peer.
- bool fPreferredDownload{false};
- //! Whether this peer wants invs or headers (when possible) for block announcements.
- bool fPreferHeaders{false};
- //! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
- bool fPreferHeaderAndIDs{false};
- /**
- * Whether this peer will send us cmpctblocks if we request them.
- * This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
- * but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
- */
- bool fProvidesHeaderAndIDs{false};
- //! Whether this peer can give us witnesses
- bool fHaveWitness{false};
- //! Whether this peer wants witnesses in cmpctblocks/blocktxns
- bool fWantsCmpctWitness{false};
- /**
- * If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
- * otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
- */
- bool fSupportsDesiredCmpctVersion{false};
-
- /** State used to enforce CHAIN_SYNC_TIMEOUT and EXTRA_PEER_CHECK_INTERVAL logic.
- *
- * Both are only in effect for outbound, non-manual, non-protected connections.
- * Any peer protected (m_protect = true) is not chosen for eviction. A peer is
- * marked as protected if all of these are true:
- * - its connection type is IsBlockOnlyConn() == false
- * - it gave us a valid connecting header
- * - we haven't reached MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT yet
- * - 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 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)
- * - If at timeout their best known block still has less work than our
- * tip did when the timeout was set, then send a getheaders message,
- * and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future.
- * If their best known block is still behind when that new timeout is
- * reached, disconnect.
- *
- * EXTRA_PEER_CHECK_INTERVAL: after each interval, if we have too many outbound peers,
- * drop the outbound one that least recently announced us a new block.
- */
- struct ChainSyncTimeoutState {
- //! A timeout used for checking whether our peer has sufficiently synced
- 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
- bool m_sent_getheaders{false};
- //! Whether this peer is protected from disconnection due to a bad/slow chain
- bool m_protect{false};
- };
-
- ChainSyncTimeoutState m_chain_sync;
-
- //! Time of last new block announcement
- int64_t m_last_block_announcement{0};
-
- //! Whether this peer is an inbound connection
- const bool m_is_inbound;
-
- //! A rolling bloom filter of all announced tx CInvs to this peer.
- CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
-
- CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {}
-};
-
-/** Map maintaining per-node state. */
-static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
-
-static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
- if (it == mapNodeState.end())
+const CNodeState* PeerManagerImpl::State(NodeId pnode) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ std::map<NodeId, CNodeState>::const_iterator it = m_node_states.find(pnode);
+ if (it == m_node_states.end())
return nullptr;
return &it->second;
}
+CNodeState* PeerManagerImpl::State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ return const_cast<CNodeState*>(std::as_const(*this).State(pnode));
+}
+
/**
* Whether the peer supports the address. For example, a peer that does not
* implement BIP155 cannot receive Tor v3 addresses because it requires
@@ -859,20 +919,11 @@ static void PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& ins
static void AddKnownTx(Peer& peer, const uint256& hash)
{
- if (peer.m_tx_relay != nullptr) {
- LOCK(peer.m_tx_relay->m_tx_inventory_mutex);
- peer.m_tx_relay->m_tx_inventory_known_filter.insert(hash);
- }
-}
-
-static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- nPreferredDownload -= state->fPreferredDownload;
+ auto tx_relay = peer.GetTxRelay();
+ if (!tx_relay) return;
- // Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node.IsInboundConn() || node.HasPermission(NetPermissionFlags::NoBan)) && !node.IsAddrFetchConn() && !node.fClient;
-
- nPreferredDownload += state->fPreferredDownload;
+ LOCK(tx_relay->m_tx_inventory_mutex);
+ tx_relay->m_tx_inventory_known_filter.insert(hash);
}
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
@@ -963,54 +1014,52 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
if (m_ignore_incoming_txs) return;
CNodeState* nodestate = State(nodeid);
- if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
- // Never ask from peers who can't provide witnesses.
+ if (!nodestate || !nodestate->m_provides_cmpctblocks) {
+ // Don't request compact blocks if the peer has not signalled support
return;
}
- if (nodestate->fProvidesHeaderAndIDs) {
- int num_outbound_hb_peers = 0;
- for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
- if (*it == nodeid) {
- lNodesAnnouncingHeaderAndIDs.erase(it);
- lNodesAnnouncingHeaderAndIDs.push_back(nodeid);
- return;
- }
- CNodeState *state = State(*it);
- if (state != nullptr && !state->m_is_inbound) ++num_outbound_hb_peers;
- }
- if (nodestate->m_is_inbound) {
- // If we're adding an inbound HB peer, make sure we're not removing
- // our last outbound HB peer in the process.
- if (lNodesAnnouncingHeaderAndIDs.size() >= 3 && num_outbound_hb_peers == 1) {
- CNodeState *remove_node = State(lNodesAnnouncingHeaderAndIDs.front());
- if (remove_node != nullptr && !remove_node->m_is_inbound) {
- // Put the HB outbound peer in the second slot, so that it
- // doesn't get removed.
- std::swap(lNodesAnnouncingHeaderAndIDs.front(), *std::next(lNodesAnnouncingHeaderAndIDs.begin()));
- }
- }
+
+ int num_outbound_hb_peers = 0;
+ for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) {
+ if (*it == nodeid) {
+ lNodesAnnouncingHeaderAndIDs.erase(it);
+ lNodesAnnouncingHeaderAndIDs.push_back(nodeid);
+ return;
}
- m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
- AssertLockHeld(::cs_main);
- uint64_t nCMPCTBLOCKVersion = 2;
- if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
- // As per BIP152, we only get 3 of our peers to announce
- // blocks using compact encodings.
- m_connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [this, nCMPCTBLOCKVersion](CNode* pnodeStop){
- m_connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
- // save BIP152 bandwidth state: we select peer to be low-bandwidth
- pnodeStop->m_bip152_highbandwidth_to = false;
- return true;
- });
- lNodesAnnouncingHeaderAndIDs.pop_front();
+ CNodeState *state = State(*it);
+ if (state != nullptr && !state->m_is_inbound) ++num_outbound_hb_peers;
+ }
+ if (nodestate->m_is_inbound) {
+ // If we're adding an inbound HB peer, make sure we're not removing
+ // our last outbound HB peer in the process.
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3 && num_outbound_hb_peers == 1) {
+ CNodeState *remove_node = State(lNodesAnnouncingHeaderAndIDs.front());
+ if (remove_node != nullptr && !remove_node->m_is_inbound) {
+ // Put the HB outbound peer in the second slot, so that it
+ // doesn't get removed.
+ std::swap(lNodesAnnouncingHeaderAndIDs.front(), *std::next(lNodesAnnouncingHeaderAndIDs.begin()));
}
- m_connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
- // save BIP152 bandwidth state: we select peer to be high-bandwidth
- pfrom->m_bip152_highbandwidth_to = true;
- lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
- return true;
- });
+ }
}
+ m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
+ if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
+ // As per BIP152, we only get 3 of our peers to announce
+ // blocks using compact encodings.
+ m_connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [this](CNode* pnodeStop){
+ m_connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION));
+ // save BIP152 bandwidth state: we select peer to be low-bandwidth
+ pnodeStop->m_bip152_highbandwidth_to = false;
+ return true;
+ });
+ lNodesAnnouncingHeaderAndIDs.pop_front();
+ }
+ m_connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/true, /*version=*/CMPCTBLOCKS_VERSION));
+ // save BIP152 bandwidth state: we select peer to be high-bandwidth
+ pfrom->m_bip152_highbandwidth_to = true;
+ lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
+ return true;
+ });
}
bool PeerManagerImpl::TipMayBeStale()
@@ -1099,7 +1148,6 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
return;
- const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
@@ -1129,7 +1177,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
// We consider the chain that this peer is on invalid.
return;
}
- if (!State(nodeid)->fHaveWitness && DeploymentActiveAt(*pindex, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
+ if (!State(nodeid)->fHaveWitness && DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) {
// We wouldn't download this block or its descendants from this peer.
return;
}
@@ -1175,7 +1223,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
CService addr_you = addr.IsRoutable() && !IsProxy(addr) && addr.IsAddrV1Compatible() ? addr : CService();
uint64_t your_services{addr.nServices};
- const bool tx_relay = !m_ignore_incoming_txs && peer.m_tx_relay != nullptr && !pnode.IsFeelerConn();
+ const bool tx_relay = !m_ignore_incoming_txs && !pnode.IsBlockOnlyConn() && !pnode.IsFeelerConn();
m_connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, my_services, nTime,
your_services, addr_you, // Together the pre-version-31402 serialization of CAddress "addrYou" (without nTime)
my_services, CService(), // Together the pre-version-31402 serialization of CAddress "addrMe" (without nTime)
@@ -1215,9 +1263,7 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
m_txrequest.ReceivedInv(nodeid, gtxid, preferred, current_time + delay);
}
-// This function is used for testing the stale tip eviction logic, see
-// denialofservice_tests.cpp
-void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
+void PeerManagerImpl::UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
{
LOCK(cs_main);
CNodeState *state = State(node);
@@ -1229,10 +1275,10 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(pnode->IsInboundConn()));
+ m_node_states.emplace_hint(m_node_states.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(pnode->IsInboundConn()));
assert(m_txrequest.Count(nodeid) == 0);
}
- PeerRef peer = std::make_shared<Peer>(nodeid, /*tx_relay=*/ !pnode->IsBlockOnlyConn());
+ PeerRef peer = std::make_shared<Peer>(nodeid);
{
LOCK(m_peer_mutex);
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, peer);
@@ -1291,18 +1337,18 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
- nPreferredDownload -= state->fPreferredDownload;
+ m_num_preferred_download_peers -= state->fPreferredDownload;
m_peers_downloading_from -= (state->nBlocksInFlight != 0);
assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0);
- mapNodeState.erase(nodeid);
+ m_node_states.erase(nodeid);
- if (mapNodeState.empty()) {
+ if (m_node_states.empty()) {
// Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty());
- assert(nPreferredDownload == 0);
+ assert(m_num_preferred_download_peers == 0);
assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0);
assert(m_wtxid_relay_peers == 0);
@@ -1343,7 +1389,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
{
{
LOCK(cs_main);
- CNodeState* state = State(nodeid);
+ const CNodeState* state = State(nodeid);
if (state == nullptr)
return false;
stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
@@ -1368,9 +1414,9 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
}
- if (peer->m_tx_relay != nullptr) {
- stats.m_relay_txs = WITH_LOCK(peer->m_tx_relay->m_bloom_filter_mutex, return peer->m_tx_relay->m_relay_txs);
- stats.m_fee_filter_received = peer->m_tx_relay->m_fee_filter_received.load();
+ if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
+ stats.m_relay_txs = WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs);
+ stats.m_fee_filter_received = tx_relay->m_fee_filter_received.load();
} else {
stats.m_relay_txs = false;
stats.m_fee_filter_received = 0;
@@ -1541,17 +1587,17 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl
return std::nullopt;
}
-std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+std::unique_ptr<PeerManager> PeerManager::make(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
{
- return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, ignore_incoming_txs);
+ return std::make_unique<PeerManagerImpl>(connman, addrman, banman, chainman, pool, ignore_incoming_txs);
}
-PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
- : m_chainparams(chainparams),
+ : m_chainparams(chainman.GetParams()),
m_connman(connman),
m_addrman(addrman),
m_banman(banman),
@@ -1617,43 +1663,35 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
m_recent_confirmed_transactions.reset();
}
-// All of the following cache a recent block, and are protected by cs_most_recent_block
-static RecursiveMutex cs_most_recent_block;
-static std::shared_ptr<const CBlock> most_recent_block GUARDED_BY(cs_most_recent_block);
-static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block GUARDED_BY(cs_most_recent_block);
-static uint256 most_recent_block_hash GUARDED_BY(cs_most_recent_block);
-static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_block);
-
/**
* Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock)
{
- std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true);
+ auto pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock);
const CNetMsgMaker msgMaker(PROTOCOL_VERSION);
LOCK(cs_main);
- static int nHighestFastAnnounce = 0;
- if (pindex->nHeight <= nHighestFastAnnounce)
+ if (pindex->nHeight <= m_highest_fast_announce)
return;
- nHighestFastAnnounce = pindex->nHeight;
+ m_highest_fast_announce = pindex->nHeight;
+
+ if (!DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) return;
- bool fWitnessEnabled = DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);
uint256 hashBlock(pblock->GetHash());
const std::shared_future<CSerializedNetMsg> lazy_ser{
std::async(std::launch::deferred, [&] { return msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock); })};
{
- LOCK(cs_most_recent_block);
- most_recent_block_hash = hashBlock;
- most_recent_block = pblock;
- most_recent_compact_block = pcmpctblock;
- fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
+ LOCK(m_most_recent_block_mutex);
+ m_most_recent_block_hash = hashBlock;
+ m_most_recent_block = pblock;
+ m_most_recent_compact_block = pcmpctblock;
}
- m_connman.ForEachNode([this, pindex, fWitnessEnabled, &lazy_ser, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
@@ -1662,8 +1700,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
CNodeState &state = *State(pnode->GetId());
// If the peer has, or we announced to them the previous block already,
// but we don't think they have this one, go ahead and announce it
- if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) &&
- !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
+ if (state.m_requested_hb_cmpctblocks && !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock",
hashBlock.ToString(), pnode->GetId());
@@ -1794,12 +1831,13 @@ void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid
LOCK(m_peer_mutex);
for(auto& it : m_peer_map) {
Peer& peer = *it.second;
- if (!peer.m_tx_relay) continue;
+ auto tx_relay = peer.GetTxRelay();
+ if (!tx_relay) continue;
const uint256& hash{peer.m_wtxid_relay ? wtxid : txid};
- LOCK(peer.m_tx_relay->m_tx_inventory_mutex);
- if (!peer.m_tx_relay->m_tx_inventory_known_filter.contains(hash)) {
- peer.m_tx_relay->m_tx_inventory_to_send.insert(hash);
+ LOCK(tx_relay->m_tx_inventory_mutex);
+ if (!tx_relay->m_tx_inventory_known_filter.contains(hash)) {
+ tx_relay->m_tx_inventory_to_send.insert(hash);
}
};
}
@@ -1858,12 +1896,10 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
{
std::shared_ptr<const CBlock> a_recent_block;
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
- bool fWitnessesPresentInARecentCompactBlock;
{
- LOCK(cs_most_recent_block);
- a_recent_block = most_recent_block;
- a_recent_compact_block = most_recent_compact_block;
- fWitnessesPresentInARecentCompactBlock = fWitnessesPresentInMostRecentCompactBlock;
+ LOCK(m_most_recent_block_mutex);
+ a_recent_block = m_most_recent_block;
+ a_recent_compact_block = m_most_recent_compact_block;
}
bool need_activate_chain = false;
@@ -1950,11 +1986,11 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
} else if (inv.IsMsgFilteredBlk()) {
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
- if (peer.m_tx_relay != nullptr) {
- LOCK(peer.m_tx_relay->m_bloom_filter_mutex);
- if (peer.m_tx_relay->m_bloom_filter) {
+ if (auto tx_relay = peer.GetTxRelay(); tx_relay != nullptr) {
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ if (tx_relay->m_bloom_filter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(*pblock, *peer.m_tx_relay->m_bloom_filter);
+ merkleBlock = CMerkleBlock(*pblock, *tx_relay->m_bloom_filter);
}
}
if (sendMerkleBlock) {
@@ -1976,17 +2012,15 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
- int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch() && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
- if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
- m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
- CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ CBlockHeaderAndShortTxIDs cmpctblock{*pblock};
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else {
- m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
}
}
}
@@ -2037,13 +2071,15 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
{
AssertLockNotHeld(cs_main);
+ auto tx_relay = peer.GetTxRelay();
+
std::deque<CInv>::iterator it = peer.m_getdata_requests.begin();
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
const auto now{GetTime<std::chrono::seconds>()};
// Get last mempool request time
- const auto mempool_req = peer.m_tx_relay != nullptr ? peer.m_tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min();
+ const auto mempool_req = tx_relay != nullptr ? 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
@@ -2056,8 +2092,9 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
const CInv &inv = *it++;
- if (peer.m_tx_relay == nullptr) {
- // Ignore GETDATA requests for transactions from blocks-only peers.
+ if (tx_relay == nullptr) {
+ // Ignore GETDATA requests for transactions from block-relay-only
+ // peers and peers that asked us not to announce transactions.
continue;
}
@@ -2084,7 +2121,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
}
for (const uint256& parent_txid : parent_ids_to_add) {
// Relaying a transaction with a recent but unconfirmed parent.
- if (WITH_LOCK(peer.m_tx_relay->m_tx_inventory_mutex, return !peer.m_tx_relay->m_tx_inventory_known_filter.contains(parent_txid))) {
+ if (WITH_LOCK(tx_relay->m_tx_inventory_mutex, return !tx_relay->m_tx_inventory_known_filter.contains(parent_txid))) {
LOCK(cs_main);
State(pfrom.GetId())->m_recently_announced_invs.insert(parent_txid);
}
@@ -2126,7 +2163,8 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
}
}
-static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+uint32_t PeerManagerImpl::GetFetchFlags(const CNode& pfrom) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
uint32_t nFetchFlags = 0;
if (State(pfrom.GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
@@ -2144,10 +2182,9 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, c
}
resp.txn[i] = block.vtx[req.indexes[i]];
}
- LOCK(cs_main);
+
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
- int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
}
void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
@@ -2212,7 +2249,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) {
+ if (!m_chainman.ProcessNewBlockHeaders(headers, state, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
return;
@@ -2256,7 +2293,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!IsBlockRequested(pindexWalk->GetBlockHash()) &&
- (!DeploymentActiveAt(*pindexWalk, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) {
+ (!DeploymentActiveAt(*pindexWalk, m_chainman, Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -2290,7 +2327,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
if (vGetData.size() > 0) {
if (!m_ignore_incoming_txs &&
- nodestate->fSupportsDesiredCmpctVersion &&
+ nodestate->m_provides_cmpctblocks &&
vGetData.size() == 1 &&
mapBlocksInFlight.size() == 1 &&
pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
@@ -2589,7 +2626,7 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv)
void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing)
{
bool new_block{false};
- m_chainman.ProcessNewBlock(m_chainparams, block, force_processing, &new_block);
+ m_chainman.ProcessNewBlock(block, force_processing, &new_block);
if (new_block) {
node.m_last_block_time = GetTime<std::chrono::seconds>();
} else {
@@ -2719,10 +2756,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// set nodes not capable of serving the complete blockchain history as "limited nodes"
pfrom.m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
- if (peer->m_tx_relay != nullptr) {
+ // We only initialize the m_tx_relay data structure if:
+ // - this isn't an outbound block-relay-only connection; and
+ // - fRelay=true or we're offering NODE_BLOOM to this peer
+ // (NODE_BLOOM means that the peer may turn on tx relay later)
+ if (!pfrom.IsBlockOnlyConn() &&
+ (fRelay || (pfrom.GetLocalServices() & NODE_BLOOM))) {
+ auto* const tx_relay = peer->SetTxRelay();
{
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
- peer->m_tx_relay->m_relay_txs = fRelay; // set to true after we get the first filter* message
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ tx_relay->m_relay_txs = fRelay; // set to true after we get the first filter* message
}
if (fRelay) pfrom.m_relays_txs = true;
}
@@ -2735,8 +2778,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Potentially mark this peer as a preferred download peer.
{
- LOCK(cs_main);
- UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
+ LOCK(cs_main);
+ CNodeState* state = State(pfrom.GetId());
+ state->fPreferredDownload = (!pfrom.IsInboundConn() || pfrom.HasPermission(NetPermissionFlags::NoBan)) && !pfrom.IsAddrFetchConn() && !pfrom.fClient;
+ m_num_preferred_download_peers += state->fPreferredDownload;
}
// Self advertisement & GETADDR logic
@@ -2858,16 +2903,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) {
- // Tell our peer we are willing to provide version 1 or 2 cmpctblocks
+ // Tell our peer we are willing to provide version 2 cmpctblocks.
// However, we do not request new block announcements using
// cmpctblock messages.
// We send this to non-NODE NETWORK peers as well, because
// they may wish to request compact blocks from us
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 2;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
- nCMPCTBLOCKVersion = 1;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION));
}
pfrom.fSuccessfullyConnected = true;
return;
@@ -2880,26 +2921,20 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (msg_type == NetMsgType::SENDCMPCT) {
- bool fAnnounceUsingCMPCTBLOCK = false;
- uint64_t nCMPCTBLOCKVersion = 0;
- vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
- if (nCMPCTBLOCKVersion == 1 || nCMPCTBLOCKVersion == 2) {
- LOCK(cs_main);
- // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness)
- if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) {
- State(pfrom.GetId())->fProvidesHeaderAndIDs = true;
- State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2;
- }
- if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) { // ignore later version announces
- State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK;
- // save whether peer selects us as BIP152 high-bandwidth peer
- // (receiving sendcmpct(1) signals high-bandwidth, sendcmpct(0) low-bandwidth)
- pfrom.m_bip152_highbandwidth_from = fAnnounceUsingCMPCTBLOCK;
- }
- if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) {
- State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2);
- }
- }
+ bool sendcmpct_hb{false};
+ uint64_t sendcmpct_version{0};
+ vRecv >> sendcmpct_hb >> sendcmpct_version;
+
+ // Only support compact block relay with witnesses
+ if (sendcmpct_version != CMPCTBLOCKS_VERSION) return;
+
+ LOCK(cs_main);
+ CNodeState* nodestate = State(pfrom.GetId());
+ nodestate->m_provides_cmpctblocks = true;
+ nodestate->m_requested_hb_cmpctblocks = sendcmpct_hb;
+ // save whether peer selects us as BIP152 high-bandwidth peer
+ // (receiving sendcmpct(1) signals high-bandwidth, sendcmpct(0) low-bandwidth)
+ pfrom.m_bip152_highbandwidth_from = sendcmpct_hb;
return;
}
@@ -3050,7 +3085,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Reject tx INVs when the -blocksonly setting is enabled, or this is a
// block-relay-only peer
- bool reject_tx_invs{m_ignore_incoming_txs || (peer->m_tx_relay == nullptr)};
+ bool reject_tx_invs{m_ignore_incoming_txs || pfrom.IsBlockOnlyConn()};
// Allow peers with relay permission to send data other than blocks in blocks only mode
if (pfrom.HasPermission(NetPermissionFlags::Relay)) {
@@ -3159,8 +3194,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
std::shared_ptr<const CBlock> a_recent_block;
{
- LOCK(cs_most_recent_block);
- a_recent_block = most_recent_block;
+ LOCK(m_most_recent_block_mutex);
+ a_recent_block = m_most_recent_block;
}
BlockValidationState state;
if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) {
@@ -3211,10 +3246,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
std::shared_ptr<const CBlock> recent_block;
{
- LOCK(cs_most_recent_block);
- if (most_recent_block_hash == req.blockhash)
- recent_block = most_recent_block;
- // Unlock cs_most_recent_block to avoid cs_main lock inversion
+ LOCK(m_most_recent_block_mutex);
+ if (m_most_recent_block_hash == req.blockhash)
+ recent_block = m_most_recent_block;
+ // Unlock m_most_recent_block_mutex to avoid cs_main lock inversion
}
if (recent_block) {
SendBlockTransactions(pfrom, *recent_block, req);
@@ -3248,9 +3283,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
- CInv inv;
- WITH_LOCK(cs_main, inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK);
- inv.hash = req.blockhash;
+ CInv inv{MSG_WITNESS_BLOCK, req.blockhash};
WITH_LOCK(peer->m_getdata_requests_mutex, peer->m_getdata_requests.push_back(inv));
// The message processing loop will go around again (without pausing) and we'll respond then
return;
@@ -3267,9 +3300,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d while importing/reindexing\n", pfrom.GetId());
+ return;
+ }
+
LOCK(cs_main);
- if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(NetPermissionFlags::Download)) {
- LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
+
+ // Note that if we were to be on a chain that forks from the checkpointed
+ // chain, then serving those headers to a peer that has seen the
+ // checkpointed chain would cause that peer to disconnect us. Requiring
+ // that our chainwork exceed nMinimumChainWork is a protection against
+ // being fed a bogus chain when we started up for the first time and
+ // getting partitioned off the honest network for serving that chain to
+ // others.
+ if (m_chainman.ActiveTip() == nullptr ||
+ (m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && !pfrom.HasPermission(NetPermissionFlags::Download))) {
+ LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work\n", pfrom.GetId());
return;
}
@@ -3325,9 +3372,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (msg_type == NetMsgType::TX) {
// Stop processing the transaction early if
- // 1) We are in blocks only mode and peer has no relay permission
+ // 1) We are in blocks only mode and peer has no relay permission; OR
// 2) This peer is a block-relay-only peer
- if ((m_ignore_incoming_txs && !pfrom.HasPermission(NetPermissionFlags::Relay)) || (peer->m_tx_relay == nullptr)) {
+ if ((m_ignore_incoming_txs && !pfrom.HasPermission(NetPermissionFlags::Relay)) || pfrom.IsBlockOnlyConn()) {
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -3565,7 +3612,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) {
+ if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block=*/true, "invalid header via cmpctblock");
return;
@@ -3625,12 +3672,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- if (DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) && !nodestate->fSupportsDesiredCmpctVersion) {
- // Don't bother trying to process compact blocks from v1 peers
- // after segwit activates.
- return;
- }
-
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
if (pindex->nHeight <= m_chainman.ActiveChain().Height() + 2) {
@@ -3939,9 +3980,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- if (peer->m_tx_relay != nullptr) {
- LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
- peer->m_tx_relay->m_send_mempool = true;
+ if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
+ LOCK(tx_relay->m_tx_inventory_mutex);
+ tx_relay->m_send_mempool = true;
}
return;
}
@@ -4034,16 +4075,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
// There is no excuse for sending a too-large filter
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
- }
- else if (peer->m_tx_relay != nullptr)
- {
+ } else if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
{
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
- peer->m_tx_relay->m_bloom_filter.reset(new CBloomFilter(filter));
- peer->m_tx_relay->m_relay_txs = true;
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ tx_relay->m_bloom_filter.reset(new CBloomFilter(filter));
+ tx_relay->m_relay_txs = true;
}
pfrom.m_bloom_filter_loaded = true;
- pfrom.m_relays_txs = true;
}
return;
}
@@ -4062,10 +4100,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
- } else if (peer->m_tx_relay != nullptr) {
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
- if (peer->m_tx_relay->m_bloom_filter) {
- peer->m_tx_relay->m_bloom_filter->insert(vData);
+ } else if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ if (tx_relay->m_bloom_filter) {
+ tx_relay->m_bloom_filter->insert(vData);
} else {
bad = true;
}
@@ -4082,14 +4120,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.fDisconnect = true;
return;
}
- if (peer->m_tx_relay == nullptr) {
- return;
- }
+ auto tx_relay = peer->GetTxRelay();
+ if (!tx_relay) return;
{
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
- peer->m_tx_relay->m_bloom_filter = nullptr;
- peer->m_tx_relay->m_relay_txs = true;
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ tx_relay->m_bloom_filter = nullptr;
+ tx_relay->m_relay_txs = true;
}
pfrom.m_bloom_filter_loaded = false;
pfrom.m_relays_txs = true;
@@ -4100,8 +4137,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
- if (peer->m_tx_relay != nullptr) {
- peer->m_tx_relay->m_fee_filter_received = newFeeFilter;
+ if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
+ tx_relay->m_fee_filter_received = newFeeFilter;
}
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId());
}
@@ -4475,10 +4512,10 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic
}
if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes({(unsigned char*)&nonce, sizeof(nonce)});
- }
+ uint64_t nonce;
+ do {
+ nonce = GetRand<uint64_t>();
+ } while (nonce == 0);
peer.m_ping_queued = false;
peer.m_ping_start = now;
if (node_to.GetCommonVersion() > BIP0031_VERSION) {
@@ -4562,10 +4599,12 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::microseconds current_time)
{
if (m_ignore_incoming_txs) return;
- if (!peer.m_tx_relay) return;
if (pto.GetCommonVersion() < FEEFILTER_VERSION) return;
// peers with the forcerelay permission should not filter txs to us
if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return;
+ // Don't send feefilter messages to outbound block-relay-only peers since they should never announce
+ // transactions to us, regardless of feefilter state.
+ if (pto.IsBlockOnlyConn()) return;
CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
@@ -4576,27 +4615,27 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
currentFilter = MAX_MONEY;
} else {
static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
- if (peer.m_tx_relay->m_fee_filter_sent == MAX_FILTER) {
+ if (peer.m_fee_filter_sent == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
- peer.m_tx_relay->m_next_send_feefilter = 0us;
+ peer.m_next_send_feefilter = 0us;
}
}
- if (current_time > peer.m_tx_relay->m_next_send_feefilter) {
+ if (current_time > peer.m_next_send_feefilter) {
CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
- if (filterToSend != peer.m_tx_relay->m_fee_filter_sent) {
+ if (filterToSend != peer.m_fee_filter_sent) {
m_connman.PushMessage(&pto, CNetMsgMaker(pto.GetCommonVersion()).Make(NetMsgType::FEEFILTER, filterToSend));
- peer.m_tx_relay->m_fee_filter_sent = filterToSend;
+ peer.m_fee_filter_sent = filterToSend;
}
- peer.m_tx_relay->m_next_send_feefilter = GetExponentialRand(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ peer.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.
- else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < peer.m_tx_relay->m_next_send_feefilter &&
- (currentFilter < 3 * peer.m_tx_relay->m_fee_filter_sent / 4 || currentFilter > 4 * peer.m_tx_relay->m_fee_filter_sent / 3)) {
- peer.m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
+ else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < peer.m_next_send_feefilter &&
+ (currentFilter < 3 * peer.m_fee_filter_sent / 4 || currentFilter > 4 * peer.m_fee_filter_sent / 3)) {
+ peer.m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
}
}
@@ -4678,10 +4717,31 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (m_chainman.m_best_header == nullptr) {
m_chainman.m_best_header = m_chainman.ActiveChain().Tip();
}
- bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
+
+ // Determine whether we might try initial headers sync or parallel
+ // block download from this peer -- this mostly affects behavior while
+ // in IBD (once out of IBD, we sync from all peers).
+ bool sync_blocks_and_headers_from_peer = false;
+ if (state.fPreferredDownload) {
+ sync_blocks_and_headers_from_peer = true;
+ } else if (!pto->fClient && !pto->IsAddrFetchConn()) {
+ // Typically this is an inbound peer. If we don't have any outbound
+ // peers, or if we aren't downloading any blocks from such peers,
+ // then allow block downloads from this peer, too.
+ // We prefer downloading blocks from outbound peers to avoid
+ // putting undue load on (say) some home user who is just making
+ // outbound connections to the network, but if our only source of
+ // the latest blocks is from an inbound peer, we have to be sure to
+ // eventually download it (and not just wait indefinitely for an
+ // outbound peer to have it).
+ if (m_num_preferred_download_peers == 0 || mapBlocksInFlight.empty()) {
+ sync_blocks_and_headers_from_peer = true;
+ }
+ }
+
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
(
@@ -4720,7 +4780,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
LOCK(peer->m_block_inv_mutex);
std::vector<CBlock> vHeaders;
bool fRevertToInv = ((!state.fPreferHeaders &&
- (!state.fPreferHeaderAndIDs || peer->m_blocks_for_headers_relay.size() > 1)) ||
+ (!state.m_requested_hb_cmpctblocks || peer->m_blocks_for_headers_relay.size() > 1)) ||
peer->m_blocks_for_headers_relay.size() > MAX_BLOCKS_TO_ANNOUNCE);
const CBlockIndex *pBestIndex = nullptr; // last header queued for delivery
ProcessBlockAvailability(pto->GetId()); // ensure pindexBestKnownBlock is up-to-date
@@ -4773,33 +4833,27 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
}
}
if (!fRevertToInv && !vHeaders.empty()) {
- if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) {
+ if (vHeaders.size() == 1 && state.m_requested_hb_cmpctblocks) {
// We only send up to 1 block as header-and-ids, as otherwise
// probably means we're doing an initial-ish-sync or they're slow
LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", __func__,
vHeaders.front().GetHash().ToString(), pto->GetId());
- int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
-
- bool fGotBlockFromCache = false;
+ std::optional<CSerializedNetMsg> cached_cmpctblock_msg;
{
- LOCK(cs_most_recent_block);
- if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
- if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
- m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
- else {
- CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
- m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- }
- fGotBlockFromCache = true;
+ LOCK(m_most_recent_block_mutex);
+ if (m_most_recent_block_hash == pBestIndex->GetBlockHash()) {
+ cached_cmpctblock_msg = msgMaker.Make(NetMsgType::CMPCTBLOCK, *m_most_recent_compact_block);
}
}
- if (!fGotBlockFromCache) {
+ if (cached_cmpctblock_msg.has_value()) {
+ m_connman.PushMessage(pto, std::move(cached_cmpctblock_msg.value()));
+ } else {
CBlock block;
bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams);
assert(ret);
- CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
- m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ CBlockHeaderAndShortTxIDs cmpctblock{block};
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::CMPCTBLOCK, cmpctblock));
}
state.pindexBestHeaderSent = pBestIndex;
} else if (state.fPreferHeaders) {
@@ -4864,45 +4918,45 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
peer->m_blocks_for_inv_relay.clear();
}
- if (peer->m_tx_relay != nullptr) {
- LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
+ if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
+ LOCK(tx_relay->m_tx_inventory_mutex);
// Check whether periodic sends should happen
bool fSendTrickle = pto->HasPermission(NetPermissionFlags::NoBan);
- if (peer->m_tx_relay->m_next_inv_send_time < current_time) {
+ if (tx_relay->m_next_inv_send_time < current_time) {
fSendTrickle = true;
if (pto->IsInboundConn()) {
- peer->m_tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
+ tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
} else {
- peer->m_tx_relay->m_next_inv_send_time = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
+ tx_relay->m_next_inv_send_time = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
}
}
// Time to send but the peer has requested we not relay transactions.
if (fSendTrickle) {
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
- if (!peer->m_tx_relay->m_relay_txs) peer->m_tx_relay->m_tx_inventory_to_send.clear();
+ LOCK(tx_relay->m_bloom_filter_mutex);
+ if (!tx_relay->m_relay_txs) tx_relay->m_tx_inventory_to_send.clear();
}
// Respond to BIP35 mempool requests
- if (fSendTrickle && peer->m_tx_relay->m_send_mempool) {
+ if (fSendTrickle && tx_relay->m_send_mempool) {
auto vtxinfo = m_mempool.infoAll();
- peer->m_tx_relay->m_send_mempool = false;
- const CFeeRate filterrate{peer->m_tx_relay->m_fee_filter_received.load()};
+ tx_relay->m_send_mempool = false;
+ const CFeeRate filterrate{tx_relay->m_fee_filter_received.load()};
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
+ LOCK(tx_relay->m_bloom_filter_mutex);
for (const auto& txinfo : vtxinfo) {
const uint256& hash = peer->m_wtxid_relay ? txinfo.tx->GetWitnessHash() : txinfo.tx->GetHash();
CInv inv(peer->m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
- peer->m_tx_relay->m_tx_inventory_to_send.erase(hash);
+ tx_relay->m_tx_inventory_to_send.erase(hash);
// Don't send transactions that peers will not put into their mempool
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
continue;
}
- if (peer->m_tx_relay->m_bloom_filter) {
- if (!peer->m_tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ if (tx_relay->m_bloom_filter) {
+ if (!tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
}
- peer->m_tx_relay->m_tx_inventory_known_filter.insert(hash);
+ tx_relay->m_tx_inventory_known_filter.insert(hash);
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
@@ -4910,18 +4964,18 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
vInv.clear();
}
}
- peer->m_tx_relay->m_last_mempool_req = std::chrono::duration_cast<std::chrono::seconds>(current_time);
+ tx_relay->m_last_mempool_req = std::chrono::duration_cast<std::chrono::seconds>(current_time);
}
// Determine transactions to relay
if (fSendTrickle) {
// Produce a vector with all candidates for sending
std::vector<std::set<uint256>::iterator> vInvTx;
- vInvTx.reserve(peer->m_tx_relay->m_tx_inventory_to_send.size());
- for (std::set<uint256>::iterator it = peer->m_tx_relay->m_tx_inventory_to_send.begin(); it != peer->m_tx_relay->m_tx_inventory_to_send.end(); it++) {
+ vInvTx.reserve(tx_relay->m_tx_inventory_to_send.size());
+ for (std::set<uint256>::iterator it = tx_relay->m_tx_inventory_to_send.begin(); it != tx_relay->m_tx_inventory_to_send.end(); it++) {
vInvTx.push_back(it);
}
- const CFeeRate filterrate{peer->m_tx_relay->m_fee_filter_received.load()};
+ const CFeeRate filterrate{tx_relay->m_fee_filter_received.load()};
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
// A heap is used so that not all items need sorting if only a few are being sent.
CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, peer->m_wtxid_relay);
@@ -4929,7 +4983,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// No reason to drain out at many times the network's capacity,
// especially since we have many peers and some will draw much shorter delays.
unsigned int nRelayedTransactions = 0;
- LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
+ LOCK(tx_relay->m_bloom_filter_mutex);
while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
// Fetch the top element from the heap
std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
@@ -4938,9 +4992,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
uint256 hash = *it;
CInv inv(peer->m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
// Remove it from the to-be-sent set
- peer->m_tx_relay->m_tx_inventory_to_send.erase(it);
+ tx_relay->m_tx_inventory_to_send.erase(it);
// Check if not in the filter already
- if (peer->m_tx_relay->m_tx_inventory_known_filter.contains(hash)) {
+ if (tx_relay->m_tx_inventory_known_filter.contains(hash)) {
continue;
}
// Not in the mempool anymore? don't bother sending it.
@@ -4954,7 +5008,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
continue;
}
- if (peer->m_tx_relay->m_bloom_filter && !peer->m_tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
// Send
State(pto->GetId())->m_recently_announced_invs.insert(hash);
vInv.push_back(inv);
@@ -4981,14 +5035,14 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
- peer->m_tx_relay->m_tx_inventory_known_filter.insert(hash);
+ tx_relay->m_tx_inventory_known_filter.insert(hash);
if (hash != txid) {
// Insert txid into m_tx_inventory_known_filter, even for
// wtxidrelay peers. This prevents re-adding of
// unconfirmed parents to the recently_announced
// filter, when a child tx is requested. See
// ProcessGetData().
- peer->m_tx_relay->m_tx_inventory_known_filter.insert(txid);
+ tx_relay->m_tx_inventory_known_filter.insert(txid);
}
}
}
@@ -5023,7 +5077,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) {
// Detect whether this is a stalling initial-headers-sync peer
if (m_chainman.m_best_header->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
- if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
+ if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (m_num_preferred_download_peers - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
@@ -5060,7 +5114,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((sync_blocks_and_headers_from_peer && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
diff --git a/src/net_processing.h b/src/net_processing.h
index 7dacaee5c1..d5c73e6c79 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -39,7 +39,7 @@ struct CNodeStateStats {
class PeerManager : public CValidationInterface, public NetEventsInterface
{
public:
- static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
+ static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
virtual ~PeerManager() { }
@@ -87,6 +87,9 @@ public:
/** Process a single message from a peer. Public for fuzz testing */
virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0;
+
+ /** This function is used for testing the stale tip eviction logic, see denialofservice_tests.cpp */
+ virtual void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) = 0;
};
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/net_types.cpp b/src/net_types.cpp
index e4101a9876..90346715f0 100644
--- a/src/net_types.cpp
+++ b/src/net_types.cpp
@@ -12,9 +12,9 @@
static const char* BANMAN_JSON_VERSION_KEY{"version"};
CBanEntry::CBanEntry(const UniValue& json)
- : nVersion(json[BANMAN_JSON_VERSION_KEY].get_int()),
- nCreateTime(json["ban_created"].get_int64()),
- nBanUntil(json["banned_until"].get_int64())
+ : nVersion(json[BANMAN_JSON_VERSION_KEY].getInt<int>()),
+ nCreateTime(json["ban_created"].getInt<int64_t>()),
+ nBanUntil(json["banned_until"].getInt<int64_t>())
{
}
@@ -58,7 +58,7 @@ UniValue BanMapToJson(const banmap_t& bans)
void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
{
for (const auto& ban_entry_json : bans_json.getValues()) {
- const int version{ban_entry_json[BANMAN_JSON_VERSION_KEY].get_int()};
+ const int version{ban_entry_json[BANMAN_JSON_VERSION_KEY].getInt<int>()};
if (version != CBanEntry::CURRENT_VERSION) {
LogPrintf("Dropping entry with unknown version (%s) from ban list\n", version);
continue;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 7bf11a3e48..ca148bfa51 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -20,9 +20,6 @@
#include <iterator>
#include <tuple>
-constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE;
-constexpr size_t CNetAddr::MAX_ADDRV2_SIZE;
-
CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
{
switch (m_net) {
@@ -101,7 +98,7 @@ bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t addre
*
* @note This address is considered invalid by CNetAddr::IsValid()
*/
-CNetAddr::CNetAddr() {}
+CNetAddr::CNetAddr() = default;
void CNetAddr::SetIP(const CNetAddr& ipIn)
{
diff --git a/src/netaddress.h b/src/netaddress.h
index b9a8dc589a..47ba045334 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,7 +9,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <prevector.h>
@@ -556,8 +555,8 @@ class CServiceHash
{
public:
CServiceHash()
- : m_salt_k0{GetRand(std::numeric_limits<uint64_t>::max())},
- m_salt_k1{GetRand(std::numeric_limits<uint64_t>::max())}
+ : m_salt_k0{GetRand<uint64_t>()},
+ m_salt_k1{GetRand<uint64_t>()}
{
}
diff --git a/src/netgroup.cpp b/src/netgroup.cpp
index 5f42d6c719..96b5e29684 100644
--- a/src/netgroup.cpp
+++ b/src/netgroup.cpp
@@ -71,7 +71,7 @@ std::vector<unsigned char> NetGroupManager::GetGroup(const CNetAddr& address) co
// ...for the last byte, push nBits and for the rest of the byte push 1's
if (nBits > 0) {
assert(num_bytes < addr_bytes.size());
- vchRet.push_back(addr_bytes[num_bytes] | ((1 << (8 - nBits)) - 1));
+ vchRet.push_back(addr_bytes[num_bytes + nStartByte] | ((1 << (8 - nBits)) - 1));
}
return vchRet;
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 25771aacee..9112ce3bf3 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -318,9 +318,9 @@ bool BlockManager::WriteBlockIndexDB()
return true;
}
-bool BlockManager::LoadBlockIndexDB()
+bool BlockManager::LoadBlockIndexDB(const Consensus::Params& consensus_params)
{
- if (!LoadBlockIndex(::Params().GetConsensus())) {
+ if (!LoadBlockIndex(consensus_params)) {
return false;
}
@@ -390,10 +390,10 @@ bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex)
return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
-const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) {
+const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& start_block)
+{
AssertLockHeld(::cs_main);
- assert(start_block);
- const CBlockIndex* last_block = start_block;
+ const CBlockIndex* last_block = &start_block;
while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) {
last_block = last_block->pprev;
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 622eac7fef..2e52716649 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -5,9 +5,10 @@
#ifndef BITCOIN_NODE_BLOCKSTORAGE_H
#define BITCOIN_NODE_BLOCKSTORAGE_H
+#include <attributes.h>
#include <chain.h>
#include <fs.h>
-#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <protocol.h>
#include <sync.h>
#include <txdb.h>
@@ -152,7 +153,7 @@ public:
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
- bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ bool LoadBlockIndexDB(const Consensus::Params& consensus_params) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, CBlockIndex*& best_header) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
@@ -178,6 +179,9 @@ public:
//! Returns last CBlockIndex* that is a checkpoint
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ //! Find the first block that is not pruned
+ const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
/** True if any block files have ever been pruned. */
bool m_have_pruned = false;
@@ -188,9 +192,6 @@ public:
void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
};
-//! Find the first block that is not pruned
-const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-
void CleanupBlockRevFiles();
/** Open a block file (blk?????.dat) */
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index 99615dea69..54ba5b7966 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -13,7 +13,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
ChainstateManager& chainman,
CTxMemPool* mempool,
bool fPruneMode,
- const Consensus::Params& consensus_params,
bool fReindexChainState,
int64_t nBlockTreeDBCache,
int64_t nCoinDBCache,
@@ -57,7 +56,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
}
if (!chainman.BlockIndex().empty() &&
- !chainman.m_blockman.LookupBlockIndex(consensus_params.hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) {
return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
}
@@ -126,10 +125,8 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman,
bool fReset,
bool fReindexChainState,
- const Consensus::Params& consensus_params,
int check_blocks,
- int check_level,
- std::function<int64_t()> get_unix_time_seconds)
+ int check_level)
{
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
@@ -140,12 +137,12 @@ 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() + MAX_FUTURE_BLOCK_TIME) {
+ if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE;
}
if (!CVerifyDB().VerifyDB(
- *chainstate, consensus_params, chainstate->CoinsDB(),
+ *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
check_level,
check_blocks)) {
return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB;
diff --git a/src/node/chainstate.h b/src/node/chainstate.h
index 8ba04f1436..ff7935e8e0 100644
--- a/src/node/chainstate.h
+++ b/src/node/chainstate.h
@@ -59,7 +59,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
ChainstateManager& chainman,
CTxMemPool* mempool,
bool fPruneMode,
- const Consensus::Params& consensus_params,
bool fReindexChainState,
int64_t nBlockTreeDBCache,
int64_t nCoinDBCache,
@@ -78,10 +77,8 @@ enum class ChainstateLoadVerifyError {
std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman,
bool fReset,
bool fReindexChainState,
- const Consensus::Params& consensus_params,
int check_blocks,
- int check_level,
- std::function<int64_t()> get_unix_time_seconds);
+ int check_level);
} // namespace node
#endif // BITCOIN_NODE_CHAINSTATE_H
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 0b31c10f44..d80b8ca7a7 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -7,6 +7,7 @@
#include <addrman.h>
#include <banman.h>
#include <interfaces/chain.h>
+#include <kernel/context.h>
#include <net.h>
#include <net_processing.h>
#include <netgroup.h>
@@ -16,6 +17,6 @@
#include <validation.h>
namespace node {
-NodeContext::NodeContext() {}
-NodeContext::~NodeContext() {}
+NodeContext::NodeContext() = default;
+NodeContext::~NodeContext() = default;
} // namespace node
diff --git a/src/node/context.h b/src/node/context.h
index 91ba456219..31be308787 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
+#include <kernel/context.h>
+
#include <cassert>
#include <functional>
#include <memory>
@@ -39,6 +41,8 @@ namespace node {
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
+ //! libbitcoin_kernel context
+ std::unique_ptr<kernel::Context> kernel;
//! Init interface for initializing current process and connecting to other processes.
interfaces::Init* init{nullptr};
std::unique_ptr<AddrMan> addrman;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 954bd1c31d..7752fb0f65 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -90,8 +90,16 @@ public:
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
- return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs, /*use_syscall_sandbox=*/false) && AppInitSanityChecks() &&
- AppInitLockDataDirectory() && AppInitInterfaces(*m_context);
+ if (!AppInitBasicSetup(gArgs)) return false;
+ if (!AppInitParameterInteraction(gArgs, /*use_syscall_sandbox=*/false)) return false;
+
+ m_context->kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*m_context->kernel)) return false;
+
+ if (!AppInitLockDataDirectory()) return false;
+ if (!AppInitInterfaces(*m_context)) return false;
+
+ return true;
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
@@ -112,6 +120,46 @@ public:
}
}
bool shutdownRequested() override { return ShutdownRequested(); }
+ bool isSettingIgnored(const std::string& name) override
+ {
+ bool ignored = false;
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (auto* options = util::FindKey(settings.command_line_options, name)) {
+ ignored = !options->empty();
+ }
+ });
+ return ignored;
+ }
+ util::SettingsValue getPersistentSetting(const std::string& name) override { return gArgs.GetPersistentSetting(name); }
+ void updateRwSetting(const std::string& name, const util::SettingsValue& value) override
+ {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (value.isNull()) {
+ settings.rw_settings.erase(name);
+ } else {
+ settings.rw_settings[name] = value;
+ }
+ });
+ gArgs.WriteSettingsFile();
+ }
+ void forceSetting(const std::string& name, const util::SettingsValue& value) override
+ {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (value.isNull()) {
+ settings.forced_settings.erase(name);
+ } else {
+ settings.forced_settings[name] = value;
+ }
+ });
+ }
+ void resetSettings() override
+ {
+ gArgs.WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true);
+ gArgs.LockSettings([&](util::Settings& settings) {
+ settings.rw_settings.clear();
+ });
+ gArgs.WriteSettingsFile();
+ }
void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(ConnectionDirection flags) override
@@ -228,7 +276,7 @@ public:
uint256 getBestBlockHash() override
{
const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
- return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
+ return tip ? tip->GetBlockHash() : chainman().GetParams().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
@@ -236,7 +284,7 @@ public:
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
- return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
+ return chainman().GetParams().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
double getVerificationProgress() override
{
@@ -245,7 +293,7 @@ public:
LOCK(::cs_main);
tip = chainman().ActiveChain().Tip();
}
- return GuessVerificationProgress(Params().TxData(), tip);
+ return GuessVerificationProgress(chainman().GetParams().TxData(), tip);
}
bool isInitialBlockDownload() override {
return chainman().ActiveChainstate().IsInitialBlockDownload();
@@ -426,7 +474,7 @@ public:
// try to handle the request. Otherwise, reraise the exception.
if (!last_handler) {
const UniValue& code = e["code"];
- if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) {
+ if (code.isNum() && code.getInt<int>() == RPC_WALLET_NOT_FOUND) {
return false;
}
}
@@ -546,7 +594,7 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
+ return GuessVerificationProgress(chainman().GetParams().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
{
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index be5d58527b..01db49d5cf 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -51,7 +51,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
block.vtx.at(0) = MakeTransactionRef(tx);
const CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
- GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
+ chainman.GenerateCoinbaseCommitment(block, prev_block);
block.hashMerkleRoot = BlockMerkleRoot(block);
}
@@ -62,8 +62,8 @@ BlockAssembler::Options::Options()
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options)
- : chainparams(params),
+BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const Options& options)
+ : chainparams{chainstate.m_chainman.GetParams()},
m_mempool(mempool),
m_chainstate(chainstate)
{
@@ -87,8 +87,8 @@ static BlockAssembler::Options DefaultOptions()
return options;
}
-BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params)
- : BlockAssembler(chainstate, mempool, params, DefaultOptions()) {}
+BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool)
+ : BlockAssembler(chainstate, mempool, DefaultOptions()) {}
void BlockAssembler::resetBlock()
{
@@ -126,7 +126,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
- pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
+ pblock->nVersion = m_chainstate.m_chainman.m_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand()) {
@@ -154,7 +154,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
- pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
+ pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
pblocktemplate->vTxFees[0] = -nFees;
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
@@ -167,7 +167,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
+ if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, GetAdjustedTime, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
int64_t nTime2 = GetTimeMicros();
@@ -310,10 +310,6 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
// Keep track of entries that failed inclusion, to avoid duplicate work
CTxMemPool::setEntries failedTx;
- // Start by adding all descendants of previously added txs to mapModifiedTx
- // and modifying them for their already included ancestors
- UpdatePackagesForAdded(inBlock, mapModifiedTx);
-
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = m_mempool.mapTx.get<ancestor_score>().begin();
CTxMemPool::txiter iter;
diff --git a/src/node/miner.h b/src/node/miner.h
index c8093ec883..7cf8e3fb9e 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -116,7 +116,7 @@ struct update_for_parent_inclusion
void operator() (CTxMemPoolModifiedEntry &e)
{
- e.nModFeesWithAncestors -= iter->GetFee();
+ e.nModFeesWithAncestors -= iter->GetModifiedFee();
e.nSizeWithAncestors -= iter->GetTxSize();
e.nSigOpCostWithAncestors -= iter->GetSigOpCost();
}
@@ -157,8 +157,8 @@ public:
CFeeRate blockMinFeeRate;
};
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params);
- explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
+ explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool);
+ explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
diff --git a/src/node/transaction.h b/src/node/transaction.h
index b7cf225636..0604754a46 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H
-#include <attributes.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <util/error.h>
diff --git a/src/outputtype.h b/src/outputtype.h
index 66fe489bb0..6b4e695760 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_OUTPUTTYPE_H
#define BITCOIN_OUTPUTTYPE_H
-#include <attributes.h>
#include <script/signingprovider.h>
#include <script/standard.h>
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 6499dbd97f..d2deaf69d0 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -537,9 +537,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
}
}
-CBlockPolicyEstimator::~CBlockPolicyEstimator()
-{
-}
+CBlockPolicyEstimator::~CBlockPolicyEstimator() = default;
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
{
diff --git a/src/prevector.h b/src/prevector.h
index aa20efaaa7..a52510930a 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -35,6 +35,8 @@
*/
template<unsigned int N, typename T, typename Size = uint32_t, typename Diff = int32_t>
class prevector {
+ static_assert(std::is_trivially_copyable_v<T>);
+
public:
typedef Size size_type;
typedef Diff difference_type;
@@ -411,15 +413,7 @@ public:
// representation (with capacity N and size <= N).
iterator p = first;
char* endp = (char*)&(*end());
- if (!std::is_trivially_destructible<T>::value) {
- while (p != last) {
- (*p).~T();
- _size--;
- ++p;
- }
- } else {
- _size -= last - p;
- }
+ _size -= last - p;
memmove(&(*first), &(*last), endp - ((char*)(&(*last))));
return first;
}
@@ -458,15 +452,13 @@ public:
return *item_ptr(size() - 1);
}
- void swap(prevector<N, T, Size, Diff>& other) {
+ void swap(prevector<N, T, Size, Diff>& other) noexcept
+ {
std::swap(_union, other._union);
std::swap(_size, other._size);
}
~prevector() {
- if (!std::is_trivially_destructible<T>::value) {
- clear();
- }
if (!is_direct()) {
free(_union.indirect_contents.indirect);
_union.indirect_contents.indirect = nullptr;
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 6465e353be..c1c8a385cc 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -234,7 +234,7 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
// Construct a would-be spend of this output, to update sigdata with.
// Note that ProduceSignature is used to fill in metadata (not actual signatures),
// so provider does not need to provide any private keys (it can be a HidingSigningProvider).
- MutableTransactionSignatureCreator creator(&tx, /*input_idx=*/0, out.nValue, SIGHASH_ALL);
+ MutableTransactionSignatureCreator creator(tx, /*input_idx=*/0, out.nValue, SIGHASH_ALL);
ProduceSignature(provider, creator, out.scriptPubKey, sigdata);
// Put redeem_script, witness_script, key paths, into PSBTOutput.
@@ -301,7 +301,7 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} else {
- MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
+ MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, sighash);
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
}
// Verify that a witness signature was produced in case one was required.
diff --git a/src/psbt.h b/src/psbt.h
index 8a9cbd33d2..8fda889bb4 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_PSBT_H
#define BITCOIN_PSBT_H
-#include <attributes.h>
#include <node/transaction.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index d59a4345f3..a82bd5f73e 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -19,6 +19,11 @@
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QRegularExpression>
+#else
+#include <QRegExp>
+#endif
class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
{
@@ -46,12 +51,13 @@ protected:
auto address = model->index(row, AddressTableModel::Address, parent);
- if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
- filterRegExp().indexIn(model->data(label).toString()) < 0) {
- return false;
- }
-
- return true;
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ const auto pattern = filterRegularExpression();
+#else
+ const auto pattern = filterRegExp();
+#endif
+ return (model->data(address).toString().contains(pattern) ||
+ model->data(label).toString().contains(pattern));
}
};
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index dcab631d98..27ee9509e6 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -30,7 +30,7 @@ struct AddressTableEntry
QString label;
QString address;
- AddressTableEntry() {}
+ AddressTableEntry() = default;
AddressTableEntry(Type _type, const QString &_label, const QString &_address):
type(_type), label(_label), address(_address) {}
};
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index e004fba308..3d0be69302 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -89,10 +89,7 @@ BanTableModel::BanTableModel(interfaces::Node& node, QObject* parent) :
refresh();
}
-BanTableModel::~BanTableModel()
-{
- // Intentionally left empty
-}
+BanTableModel::~BanTableModel() = default;
int BanTableModel::rowCount(const QModelIndex &parent) const
{
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8cac28400f..53fbac0106 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -261,7 +261,7 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(this, resetSettings);
+ optionsModel = new OptionsModel(node(), this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -292,7 +292,6 @@ void BitcoinApplication::createNode(interfaces::Init& init)
{
assert(!m_node);
m_node = init.makeNode();
- if (optionsModel) optionsModel->setNode(*m_node);
if (m_splash) m_splash->setNode(*m_node);
}
@@ -308,7 +307,9 @@ void BitcoinApplication::startThread()
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] {
+ QCoreApplication::exit(0);
+ });
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -633,6 +634,12 @@ int GuiMain(int argc, char* argv[])
// Allow parameter interaction before we create the options model
app.parameterSetup();
GUIUtil::LogQtInfo();
+
+ if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
+ app.createSplashScreen(networkStyle.data());
+
+ app.createNode(*init);
+
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
@@ -641,11 +648,6 @@ int GuiMain(int argc, char* argv[])
app.InitPruneSetting(prune_MiB);
}
- if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
- app.createSplashScreen(networkStyle.data());
-
- app.createNode(*init);
-
int rv = EXIT_SUCCESS;
try
{
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 81b0e711b2..bfcdf6f316 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -26,7 +26,7 @@
#include <qt/walletview.h>
#endif // ENABLE_WALLET
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
#include <qt/macdockiconhandler.h>
#endif
@@ -69,7 +69,7 @@
const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MACOS)
"macosx"
#elif defined(Q_OS_WIN)
"windows"
@@ -219,7 +219,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
m_app_nap_inhibitor = new CAppNapInhibitor;
#endif
@@ -235,7 +235,7 @@ BitcoinGUI::~BitcoinGUI()
settings.setValue("MainWindowGeometry", saveGeometry());
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide();
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
delete m_app_nap_inhibitor;
delete appMenuBar;
MacDockIconHandler::cleanup();
@@ -433,7 +433,7 @@ void BitcoinGUI::createActions()
void BitcoinGUI::createMenuBar()
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
// Create a decoupled menu bar on Mac which stays even if the window is closed
appMenuBar = new QMenuBar();
#else
@@ -482,7 +482,7 @@ void BitcoinGUI::createMenuBar()
minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized);
});
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
QAction* zoom_action = window_menu->addAction(tr("Zoom"));
connect(zoom_action, &QAction::triggered, [] {
QWindow* window = qApp->focusWindow();
@@ -499,7 +499,7 @@ void BitcoinGUI::createMenuBar()
#endif
if (walletFrame) {
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
window_menu->addSeparator();
QAction* main_window_action = window_menu->addAction(tr("Main Window"));
connect(main_window_action, &QAction::triggered, [this] {
@@ -755,7 +755,7 @@ void BitcoinGUI::createTrayIcon()
{
assert(QSystemTrayIcon::isSystemTrayAvailable());
-#ifndef Q_OS_MAC
+#ifndef Q_OS_MACOS
if (QSystemTrayIcon::isSystemTrayAvailable()) {
trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this);
QString toolTip = tr("%1 client").arg(PACKAGE_NAME) + " " + m_network_style->getTitleAddText();
@@ -766,17 +766,17 @@ void BitcoinGUI::createTrayIcon()
void BitcoinGUI::createTrayIconMenu()
{
-#ifndef Q_OS_MAC
+#ifndef Q_OS_MACOS
if (!trayIcon) return;
-#endif // Q_OS_MAC
+#endif // Q_OS_MACOS
// Configuration of the tray icon (or Dock icon) menu.
QAction* show_hide_action{nullptr};
-#ifndef Q_OS_MAC
+#ifndef Q_OS_MACOS
// Note: On macOS, the Dock icon's menu already has Show / Hide action.
show_hide_action = trayIconMenu->addAction(QString(), this, &BitcoinGUI::toggleHidden);
trayIconMenu->addSeparator();
-#endif // Q_OS_MAC
+#endif // Q_OS_MACOS
QAction* send_action{nullptr};
QAction* receive_action{nullptr};
@@ -794,7 +794,7 @@ void BitcoinGUI::createTrayIconMenu()
options_action->setMenuRole(QAction::PreferencesRole);
QAction* node_window_action = trayIconMenu->addAction(openRPCConsoleAction->text(), openRPCConsoleAction, &QAction::trigger);
QAction* quit_action{nullptr};
-#ifndef Q_OS_MAC
+#ifndef Q_OS_MACOS
// Note: On macOS, the Dock icon's menu already has Quit action.
trayIconMenu->addSeparator();
quit_action = trayIconMenu->addAction(quitAction->text(), quitAction, &QAction::trigger);
@@ -814,7 +814,7 @@ void BitcoinGUI::createTrayIconMenu()
activateWindow();
});
trayIconMenu->setAsDockMenu();
-#endif // Q_OS_MAC
+#endif // Q_OS_MACOS
connect(
// Using QSystemTrayIcon::Context is not reliable.
@@ -1006,7 +1006,7 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
{
// Disabling macOS App Nap on initial sync, disk and reindex operations.
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
if (sync_state == SynchronizationState::POST_INIT) {
m_app_nap_inhibitor->enableAppNap();
} else {
@@ -1192,7 +1192,7 @@ void BitcoinGUI::changeEvent(QEvent *e)
QMainWindow::changeEvent(e);
-#ifndef Q_OS_MAC // Ignored on Mac
+#ifndef Q_OS_MACOS // Ignored on Mac
if(e->type() == QEvent::WindowStateChange)
{
if(clientModel && clientModel->getOptionsModel() && clientModel->getOptionsModel()->getMinimizeToTray())
@@ -1215,7 +1215,7 @@ void BitcoinGUI::changeEvent(QEvent *e)
void BitcoinGUI::closeEvent(QCloseEvent *event)
{
-#ifndef Q_OS_MAC // Ignored on Mac
+#ifndef Q_OS_MACOS // Ignored on Mac
if(clientModel && clientModel->getOptionsModel())
{
if(!clientModel->getOptionsModel()->getMinimizeOnClose())
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 5d9a978695..6272d5d947 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -22,7 +22,7 @@
#include <QPoint>
#include <QSystemTrayIcon>
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
#include <qt/macos_appnap.h>
#endif
@@ -175,7 +175,7 @@ private:
QMenu* m_network_context_menu = new QMenu(this);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
CAppNapInhibitor* m_app_nap_inhibitor = nullptr;
#endif
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 4327d31787..f41da519df 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -21,9 +21,9 @@
#include <validation.h>
#include <stdint.h>
-#include <functional>
#include <QDebug>
+#include <QMetaObject>
#include <QThread>
#include <QTimer>
@@ -148,21 +148,6 @@ uint256 ClientModel::getBestBlockHash()
return m_cached_tip_blocks;
}
-void ClientModel::updateNumConnections(int numConnections)
-{
- Q_EMIT numConnectionsChanged(numConnections);
-}
-
-void ClientModel::updateNetworkActive(bool networkActive)
-{
- Q_EMIT networkActiveChanged(networkActive);
-}
-
-void ClientModel::updateAlert()
-{
- Q_EMIT alertsChanged(getStatusBarWarnings());
-}
-
enum BlockSource ClientModel::getBlockSource() const
{
if (m_node.getReindex())
@@ -230,94 +215,65 @@ QString ClientModel::blocksDir() const
return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
}
-void ClientModel::updateBanlist()
-{
- banTableModel->refresh();
-}
-
-// Handlers for core signals
-static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
-{
- // emits signal "showProgress"
- bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
- Q_ARG(QString, QString::fromStdString(title)),
- Q_ARG(int, nProgress));
- assert(invoked);
-}
-
-static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
-{
- // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
- bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
- Q_ARG(int, newNumConnections));
- assert(invoked);
-}
-
-static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
-{
- bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
- Q_ARG(bool, networkActive));
- assert(invoked);
-}
-
-static void NotifyAlertChanged(ClientModel *clientmodel)
-{
- qDebug() << "NotifyAlertChanged";
- bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
- assert(invoked);
-}
-
-static void BannedListChanged(ClientModel *clientmodel)
-{
- qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
- bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
- assert(invoked);
-}
-
-static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
+void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header)
{
- if (fHeader) {
+ if (header) {
// cache best headers time and height to reduce future cs_main locks
- clientmodel->cachedBestHeaderHeight = tip.block_height;
- clientmodel->cachedBestHeaderTime = tip.block_time;
+ cachedBestHeaderHeight = tip.block_height;
+ cachedBestHeaderTime = tip.block_time;
} else {
- clientmodel->m_cached_num_blocks = tip.block_height;
- WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
+ m_cached_num_blocks = tip.block_height;
+ WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;);
}
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
- const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
+ const bool throttle = (sync_state != SynchronizationState::POST_INIT && !header) || sync_state == SynchronizationState::INIT_REINDEX;
const int64_t now = throttle ? GetTimeMillis() : 0;
- int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
+ int64_t& nLastUpdateNotification = header ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
return;
}
- bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
- Q_ARG(int, tip.block_height),
- Q_ARG(QDateTime, QDateTime::fromSecsSinceEpoch(tip.block_time)),
- Q_ARG(double, verificationProgress),
- Q_ARG(bool, fHeader),
- Q_ARG(SynchronizationState, sync_state));
- assert(invoked);
+ Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, header, sync_state);
nLastUpdateNotification = now;
}
void ClientModel::subscribeToCoreSignals()
{
- // Connect signals to client
- m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
- m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
- m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
- m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
- m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
- m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
- m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
+ m_handler_show_progress = m_node.handleShowProgress(
+ [this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) {
+ Q_EMIT showProgress(QString::fromStdString(title), progress);
+ });
+ m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(
+ [this](int new_num_connections) {
+ Q_EMIT numConnectionsChanged(new_num_connections);
+ });
+ m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(
+ [this](bool network_active) {
+ Q_EMIT networkActiveChanged(network_active);
+ });
+ m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(
+ [this]() {
+ qDebug() << "ClientModel: NotifyAlertChanged";
+ Q_EMIT alertsChanged(getStatusBarWarnings());
+ });
+ m_handler_banned_list_changed = m_node.handleBannedListChanged(
+ [this]() {
+ qDebug() << "ClienModel: Requesting update for peer banlist";
+ QMetaObject::invokeMethod(banTableModel, [this] { banTableModel->refresh(); });
+ });
+ m_handler_notify_block_tip = m_node.handleNotifyBlockTip(
+ [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
+ TipChanged(sync_state, tip, verification_progress, /*header=*/false);
+ });
+ m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(
+ [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) {
+ TipChanged(sync_state, tip, verification_progress, /*header=*/true);
+ });
}
void ClientModel::unsubscribeFromCoreSignals()
{
- // Disconnect signals from client
m_handler_show_progress->disconnect();
m_handler_notify_num_connections_changed->disconnect();
m_handler_notify_network_active_changed->disconnect();
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 846691c0c0..b10ffb4523 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -23,6 +23,7 @@ enum class SynchronizationState;
namespace interfaces {
class Handler;
class Node;
+struct BlockTip;
}
QT_BEGIN_NAMESPACE
@@ -61,7 +62,7 @@ public:
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
- uint256 getBestBlockHash();
+ uint256 getBestBlockHash() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex);
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
@@ -104,6 +105,7 @@ private:
//! A thread to interact with m_node asynchronously
QThread* const m_thread;
+ void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header);
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
@@ -120,12 +122,6 @@ Q_SIGNALS:
// Show progress dialog e.g. for verifychain
void showProgress(const QString &title, int nProgress);
-
-public Q_SLOTS:
- void updateNumConnections(int numConnections);
- void updateNetworkActive(bool networkActive);
- void updateAlert();
- void updateBanlist();
};
#endif // BITCOIN_QT_CLIENTMODEL_H
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 6fb5fce5b3..e3c6d8a624 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -74,7 +74,7 @@
#include <string>
#include <vector>
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MACOS)
#include <QProcess>
@@ -399,7 +399,7 @@ bool isObscured(QWidget *w)
void bringToFront(QWidget* w)
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
ForceActivation();
#endif
@@ -443,7 +443,7 @@ bool openBitcoinConf()
/* Open bitcoin.conf with the associated application */
bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathConfig)));
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
// Workaround for macOS-specific behavior; see #15409.
if (!res) {
res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", PathToQString(pathConfig)});
@@ -508,7 +508,7 @@ fs::path static StartupShortcutPath()
return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4"
return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk";
- return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin (%s).lnk", chain);
+ return GetSpecialFolderPath(CSIDL_STARTUP) / fs::u8path(strprintf("Bitcoin (%s).lnk", chain));
}
bool GetStartOnSystemStartup()
@@ -589,7 +589,7 @@ fs::path static GetAutostartFilePath()
std::string chain = gArgs.GetChainName();
if (chain == CBaseChainParams::MAIN)
return GetAutostartDir() / "bitcoin.desktop";
- return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain);
+ return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", chain));
}
bool GetStartOnSystemStartup()
@@ -882,7 +882,7 @@ bool ItemDelegate::eventFilter(QObject *object, QEvent *event)
void PolishProgressDialog(QProgressDialog* dialog)
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
const int margin = TextWidth(dialog->fontMetrics(), ("X"));
dialog->resize(dialog->width() + 2 * margin, dialog->height());
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 51151b0be8..b311a9f32e 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -18,7 +18,7 @@
#include <QtDBus>
#include <stdint.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
#include <qt/macnotificationhandler.h>
#endif
@@ -50,7 +50,7 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon
mode = Freedesktop;
}
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
// check if users OS has support for NSUserNotification
if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) {
mode = UserNotificationCenter;
@@ -71,7 +71,7 @@ Notificator::~Notificator()
class FreedesktopImage
{
public:
- FreedesktopImage() {}
+ FreedesktopImage() = default;
explicit FreedesktopImage(const QImage &img);
// Image to variant that can be marshalled over DBus
@@ -210,7 +210,7 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString &
trayIcon->showMessage(title, text, sicon, millisTimeout);
}
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
void Notificator::notifyMacUserNotificationCenter(const QString &title, const QString &text)
{
// icon is not supported by the user notification center yet. OSX will use the app icon.
@@ -230,7 +230,7 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
case QSystemTray:
notifySystray(cls, title, text, millisTimeout);
break;
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
case UserNotificationCenter:
notifyMacUserNotificationCenter(title, text);
break;
diff --git a/src/qt/notificator.h b/src/qt/notificator.h
index 7d80b43e43..642d2b29ae 100644
--- a/src/qt/notificator.h
+++ b/src/qt/notificator.h
@@ -69,7 +69,7 @@ private:
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
#endif
void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
void notifyMacUserNotificationCenter(const QString &title, const QString &text);
#endif
};
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index f90765fe5b..e6ff43a379 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -78,7 +78,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
/* Window elements init */
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
/* remove Window tab on Mac */
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
/* hide launch at startup option on macOS */
@@ -261,7 +261,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor);
/* Window */
-#ifndef Q_OS_MAC
+#ifndef Q_OS_MACOS
if (QSystemTrayIcon::isSystemTrayAvailable()) {
mapper->addMapping(ui->showTrayIcon, OptionsModel::ShowTrayIcon);
mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 40b9ed5483..612d3009c1 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -30,8 +30,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
- QAbstractListModel(parent)
+OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent), m_node{node}
{
Init(resetSettings);
}
@@ -335,260 +335,272 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
if(role == Qt::EditRole)
{
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- return GUIUtil::GetStartOnSystemStartup();
- case ShowTrayIcon:
- return m_show_tray_icon;
- case MinimizeToTray:
- return fMinimizeToTray;
- case MapPortUPnP:
+ return getOption(OptionID(index.row()));
+ }
+ return QVariant();
+}
+
+// write QSettings values
+bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ bool successful = true; /* set to false on parse error */
+ if(role == Qt::EditRole)
+ {
+ successful = setOption(OptionID(index.row()), value);
+ }
+
+ Q_EMIT dataChanged(index, index);
+
+ return successful;
+}
+
+QVariant OptionsModel::getOption(OptionID option) const
+{
+ QSettings settings;
+ switch (option) {
+ case StartAtStartup:
+ return GUIUtil::GetStartOnSystemStartup();
+ case ShowTrayIcon:
+ return m_show_tray_icon;
+ case MinimizeToTray:
+ return fMinimizeToTray;
+ case MapPortUPnP:
#ifdef USE_UPNP
- return settings.value("fUseUPnP");
+ return settings.value("fUseUPnP");
#else
- return false;
+ return false;
#endif // USE_UPNP
- case MapPortNatpmp:
+ case MapPortNatpmp:
#ifdef USE_NATPMP
- return settings.value("fUseNatpmp");
+ return settings.value("fUseNatpmp");
#else
- return false;
+ return false;
#endif // USE_NATPMP
- case MinimizeOnClose:
- return fMinimizeOnClose;
-
- // default proxy
- case ProxyUse:
- return settings.value("fUseProxy", false);
- case ProxyIP:
- return GetProxySetting(settings, "addrProxy").ip;
- case ProxyPort:
- return GetProxySetting(settings, "addrProxy").port;
-
- // separate Tor proxy
- case ProxyUseTor:
- return settings.value("fUseSeparateProxyTor", false);
- case ProxyIPTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").ip;
- case ProxyPortTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").port;
+ case MinimizeOnClose:
+ return fMinimizeOnClose;
+
+ // default proxy
+ case ProxyUse:
+ return settings.value("fUseProxy", false);
+ case ProxyIP:
+ return GetProxySetting(settings, "addrProxy").ip;
+ case ProxyPort:
+ return GetProxySetting(settings, "addrProxy").port;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ return settings.value("fUseSeparateProxyTor", false);
+ case ProxyIPTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").ip;
+ case ProxyPortTor:
+ return GetProxySetting(settings, "addrSeparateProxyTor").port;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- return settings.value("bSpendZeroConfChange");
- case ExternalSignerPath:
- return settings.value("external_signer_path");
- case SubFeeFromAmount:
- return m_sub_fee_from_amount;
+ case SpendZeroConfChange:
+ return settings.value("bSpendZeroConfChange");
+ case ExternalSignerPath:
+ return settings.value("external_signer_path");
+ case SubFeeFromAmount:
+ return m_sub_fee_from_amount;
#endif
- case DisplayUnit:
- return QVariant::fromValue(m_display_bitcoin_unit);
- case ThirdPartyTxUrls:
- return strThirdPartyTxUrls;
- case Language:
- return settings.value("language");
- case UseEmbeddedMonospacedFont:
- 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:
- return settings.value("nPruneSize");
- case DatabaseCache:
- return settings.value("nDatabaseCache");
- case ThreadsScriptVerif:
- return settings.value("nThreadsScriptVerif");
- case Listen:
- return settings.value("fListen");
- case Server:
- return settings.value("server");
- default:
- return QVariant();
- }
+ case DisplayUnit:
+ return QVariant::fromValue(m_display_bitcoin_unit);
+ case ThirdPartyTxUrls:
+ return strThirdPartyTxUrls;
+ case Language:
+ return settings.value("language");
+ case UseEmbeddedMonospacedFont:
+ 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:
+ return settings.value("nPruneSize");
+ case DatabaseCache:
+ return settings.value("nDatabaseCache");
+ case ThreadsScriptVerif:
+ return settings.value("nThreadsScriptVerif");
+ case Listen:
+ return settings.value("fListen");
+ case Server:
+ return settings.value("server");
+ default:
+ return QVariant();
}
- return QVariant();
}
-// write QSettings values
-bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+bool OptionsModel::setOption(OptionID option, const QVariant& value)
{
bool successful = true; /* set to false on parse error */
- if(role == Qt::EditRole)
- {
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
- break;
- case ShowTrayIcon:
- m_show_tray_icon = value.toBool();
- settings.setValue("fHideTrayIcon", !m_show_tray_icon);
- Q_EMIT showTrayIconChanged(m_show_tray_icon);
- break;
- case MinimizeToTray:
- fMinimizeToTray = value.toBool();
- settings.setValue("fMinimizeToTray", fMinimizeToTray);
- break;
- case MapPortUPnP: // core option - can be changed on-the-fly
- settings.setValue("fUseUPnP", value.toBool());
- break;
- case MapPortNatpmp: // core option - can be changed on-the-fly
- settings.setValue("fUseNatpmp", value.toBool());
- break;
- case MinimizeOnClose:
- fMinimizeOnClose = value.toBool();
- settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
- break;
-
- // default proxy
- case ProxyUse:
- if (settings.value("fUseProxy") != value) {
- settings.setValue("fUseProxy", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIP: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ QSettings settings;
+
+ switch (option) {
+ case StartAtStartup:
+ successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
break;
- case ProxyPort: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
- setRestartRequired(true);
- }
- }
+ case ShowTrayIcon:
+ m_show_tray_icon = value.toBool();
+ settings.setValue("fHideTrayIcon", !m_show_tray_icon);
+ Q_EMIT showTrayIconChanged(m_show_tray_icon);
+ break;
+ case MinimizeToTray:
+ fMinimizeToTray = value.toBool();
+ settings.setValue("fMinimizeToTray", fMinimizeToTray);
+ break;
+ case MapPortUPnP: // core option - can be changed on-the-fly
+ settings.setValue("fUseUPnP", value.toBool());
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ settings.setValue("fUseNatpmp", value.toBool());
+ break;
+ case MinimizeOnClose:
+ fMinimizeOnClose = value.toBool();
+ settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
break;
- // separate Tor proxy
- case ProxyUseTor:
- if (settings.value("fUseSeparateProxyTor") != value) {
- settings.setValue("fUseSeparateProxyTor", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIPTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ // default proxy
+ case ProxyUse:
+ if (settings.value("fUseProxy") != value) {
+ settings.setValue("fUseProxy", value.toBool());
+ setRestartRequired(true);
}
break;
- case ProxyPortTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
- setRestartRequired(true);
- }
+ case ProxyIP: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPort: {
+ auto ip_port = GetProxySetting(settings, "addrProxy");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrProxy", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ if (settings.value("fUseSeparateProxyTor") != value) {
+ settings.setValue("fUseSeparateProxyTor", value.toBool());
+ setRestartRequired(true);
}
break;
+ case ProxyIPTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.ip != value.toString()) {
+ ip_port.ip = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
+ case ProxyPortTor: {
+ auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
+ if (!ip_port.is_set || ip_port.port != value.toString()) {
+ ip_port.port = value.toString();
+ SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ setRestartRequired(true);
+ }
+ }
+ break;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- if (settings.value("bSpendZeroConfChange") != value) {
- settings.setValue("bSpendZeroConfChange", value);
- setRestartRequired(true);
- }
- break;
- case ExternalSignerPath:
- if (settings.value("external_signer_path") != value.toString()) {
- settings.setValue("external_signer_path", value.toString());
- setRestartRequired(true);
- }
- break;
- case SubFeeFromAmount:
- m_sub_fee_from_amount = value.toBool();
- settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
- break;
+ case SpendZeroConfChange:
+ if (settings.value("bSpendZeroConfChange") != value) {
+ settings.setValue("bSpendZeroConfChange", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ExternalSignerPath:
+ if (settings.value("external_signer_path") != value.toString()) {
+ settings.setValue("external_signer_path", value.toString());
+ setRestartRequired(true);
+ }
+ break;
+ case SubFeeFromAmount:
+ m_sub_fee_from_amount = value.toBool();
+ settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
+ break;
#endif
- case DisplayUnit:
- setDisplayUnit(value);
- break;
- case ThirdPartyTxUrls:
- if (strThirdPartyTxUrls != value.toString()) {
- strThirdPartyTxUrls = value.toString();
- settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
- setRestartRequired(true);
- }
- break;
- case Language:
- if (settings.value("language") != value) {
- settings.setValue("language", value);
- setRestartRequired(true);
- }
- break;
- case UseEmbeddedMonospacedFont:
- m_use_embedded_monospaced_font = value.toBool();
- settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
- Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
- break;
- case CoinControlFeatures:
- fCoinControlFeatures = value.toBool();
- 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);
- setRestartRequired(true);
- }
- break;
- case PruneSize:
- if (settings.value("nPruneSize") != value) {
- settings.setValue("nPruneSize", value);
- setRestartRequired(true);
- }
- break;
- case DatabaseCache:
- if (settings.value("nDatabaseCache") != value) {
- settings.setValue("nDatabaseCache", value);
- setRestartRequired(true);
- }
- break;
- case ThreadsScriptVerif:
- if (settings.value("nThreadsScriptVerif") != value) {
- settings.setValue("nThreadsScriptVerif", value);
- setRestartRequired(true);
- }
- break;
- case Listen:
- if (settings.value("fListen") != value) {
- settings.setValue("fListen", value);
- setRestartRequired(true);
- }
- break;
- case Server:
- if (settings.value("server") != value) {
- settings.setValue("server", value);
- setRestartRequired(true);
- }
- break;
- default:
- break;
+ case DisplayUnit:
+ setDisplayUnit(value);
+ break;
+ case ThirdPartyTxUrls:
+ if (strThirdPartyTxUrls != value.toString()) {
+ strThirdPartyTxUrls = value.toString();
+ settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
+ setRestartRequired(true);
}
+ break;
+ case Language:
+ if (settings.value("language") != value) {
+ settings.setValue("language", value);
+ setRestartRequired(true);
+ }
+ break;
+ case UseEmbeddedMonospacedFont:
+ m_use_embedded_monospaced_font = value.toBool();
+ settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+ break;
+ case CoinControlFeatures:
+ fCoinControlFeatures = value.toBool();
+ 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);
+ setRestartRequired(true);
+ }
+ break;
+ case PruneSize:
+ if (settings.value("nPruneSize") != value) {
+ settings.setValue("nPruneSize", value);
+ setRestartRequired(true);
+ }
+ break;
+ case DatabaseCache:
+ if (settings.value("nDatabaseCache") != value) {
+ settings.setValue("nDatabaseCache", value);
+ setRestartRequired(true);
+ }
+ break;
+ case ThreadsScriptVerif:
+ if (settings.value("nThreadsScriptVerif") != value) {
+ settings.setValue("nThreadsScriptVerif", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Listen:
+ if (settings.value("fListen") != value) {
+ settings.setValue("fListen", value);
+ setRestartRequired(true);
+ }
+ break;
+ case Server:
+ if (settings.value("server") != value) {
+ settings.setValue("server", value);
+ setRestartRequired(true);
+ }
+ break;
+ default:
+ break;
}
- Q_EMIT dataChanged(index, index);
-
return successful;
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 510ebb5cfd..92f80ecf21 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -41,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -80,6 +80,8 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
+ QVariant getOption(OptionID option) const;
+ bool setOption(OptionID option, const QVariant& value);
/** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */
void setDisplayUnit(const QVariant& new_unit);
@@ -103,11 +105,10 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { assert(m_node); return *m_node; }
- void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
+ interfaces::Node& node() const { return m_node; }
private:
- interfaces::Node* m_node = nullptr;
+ interfaces::Node& m_node;
/* Qt-only settings */
bool m_show_tray_icon;
bool fMinimizeToTray;
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 820bcbf3cd..a8133f481e 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -35,8 +35,7 @@ class TxViewDelegate : public QAbstractItemDelegate
Q_OBJECT
public:
explicit TxViewDelegate(const PlatformStyle* _platformStyle, QObject* parent = nullptr)
- : QAbstractItemDelegate(parent), unit(BitcoinUnit::BTC),
- platformStyle(_platformStyle)
+ : QAbstractItemDelegate(parent), platformStyle(_platformStyle)
{
connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged);
}
@@ -125,7 +124,7 @@ public:
return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE};
}
- BitcoinUnit unit;
+ BitcoinUnit unit{BitcoinUnit::BTC};
Q_SIGNALS:
//! An intermediate signal for emitting from the `paint() const` member function.
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index c82f0683fc..be6f604932 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -158,9 +158,7 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
}
}
-PaymentServer::~PaymentServer()
-{
-}
+PaymentServer::~PaymentServer() = default;
//
// OSX-specific way of handling bitcoin: URIs
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 41c389d9cc..b7de88225e 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -28,10 +28,7 @@ PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) :
refresh();
}
-PeerTableModel::~PeerTableModel()
-{
- // Intentionally left empty
-}
+PeerTableModel::~PeerTableModel() = default;
void PeerTableModel::startAutoRefresh()
{
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 03ca9ad7dc..061513b58f 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -34,10 +34,7 @@ RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &RecentRequestsTableModel::updateDisplayUnit);
}
-RecentRequestsTableModel::~RecentRequestsTableModel()
-{
- /* Intentionally left empty */
-}
+RecentRequestsTableModel::~RecentRequestsTableModel() = default;
int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const
{
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 4a51990f88..b791fd30c4 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -27,14 +27,6 @@
#include <univalue.h>
-#ifdef ENABLE_WALLET
-#ifdef USE_BDB
-#include <wallet/bdb.h>
-#endif
-#include <wallet/db.h>
-#include <wallet/wallet.h>
-#endif
-
#include <QAbstractButton>
#include <QAbstractItemModel>
#include <QDateTime>
@@ -120,7 +112,7 @@ public:
connect(&timer, &QTimer::timeout, [this]{ func(); });
timer.start(millis);
}
- ~QtRPCTimerBase() {}
+ ~QtRPCTimerBase() = default;
private:
QTimer timer;
std::function<void()> func;
@@ -129,7 +121,7 @@ private:
class QtRPCTimerInterface: public RPCTimerInterface
{
public:
- ~QtRPCTimerInterface() {}
+ ~QtRPCTimerInterface() = default;
const char *Name() override { return "Qt"; }
RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
{
@@ -457,7 +449,7 @@ void RPCExecutor::request(const QString &command, const WalletModel* wallet_mode
{
try // Nice formatting for standard-format error
{
- int code = find_value(objError, "code").get_int();
+ int code = find_value(objError, "code").getInt<int>();
std::string message = find_value(objError, "message").get_str();
Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
}
@@ -871,7 +863,7 @@ void RPCConsole::clear(bool keep_prompt)
}
// Set default style sheet
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont(/*use_embedded_font=*/true));
#else
QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index fd8eccb86d..d8daccecbd 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -757,10 +757,6 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
case WalletModel::AbsurdFee:
msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
break;
- case WalletModel::PaymentRequestExpired:
- msgParams.first = tr("Payment request expired.");
- msgParams.second = CClientUIInterface::MSG_ERROR;
- break;
// included to prevent a compiler warning.
case WalletModel::OK:
default:
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 66637a5dcf..3b7a40438b 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -8,6 +8,7 @@
#include <interfaces/chain.h>
#include <interfaces/node.h>
+#include <qt/addressbookpage.h>
#include <qt/clientmodel.h>
#include <qt/editaddressdialog.h>
#include <qt/optionsmodel.h>
@@ -23,8 +24,10 @@
#include <chrono>
#include <QApplication>
-#include <QTimer>
+#include <QLineEdit>
#include <QMessageBox>
+#include <QTableView>
+#include <QTimer>
using wallet::AddWallet;
using wallet::CWallet;
@@ -100,11 +103,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
QString s_label("already here (s)");
// Define a new address (which should add to the address book successfully).
- QString new_address;
+ QString new_address_a;
+ QString new_address_b;
std::tie(r_key_dest, preexisting_r_address) = build_address();
std::tie(s_key_dest, preexisting_s_address) = build_address();
- std::tie(std::ignore, new_address) = build_address();
+ std::tie(std::ignore, new_address_a) = build_address();
+ std::tie(std::ignore, new_address_b) = build_address();
{
LOCK(wallet->cs_wallet);
@@ -122,7 +127,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
@@ -131,14 +136,19 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
+ AddressBookPage address_book{platformStyle.get(), AddressBookPage::ForEditing, AddressBookPage::SendingTab};
+ address_book.setModel(walletModel.getAddressTableModel());
+ auto table_view = address_book.findChild<QTableView*>("tableView");
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
EditAddressAndSubmit(
&editAddressDialog, QString("uhoh"), preexisting_r_address,
QString(
"Address \"%1\" already exists as a receiving address with label "
"\"%2\" and so cannot be added as a sending address."
).arg(preexisting_r_address).arg(r_label));
-
check_addbook_size(2);
+ QCOMPARE(table_view->model()->rowCount(), 1);
EditAddressAndSubmit(
&editAddressDialog, QString("uhoh, different"), preexisting_s_address,
@@ -146,22 +156,65 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
"The entered address \"%1\" is already in the address book with "
"label \"%2\"."
).arg(preexisting_s_address).arg(s_label));
-
check_addbook_size(2);
+ QCOMPARE(table_view->model()->rowCount(), 1);
// Submit a new address which should add successfully - we expect the
// warning message to be blank.
EditAddressAndSubmit(
- &editAddressDialog, QString("new"), new_address, QString(""));
-
+ &editAddressDialog, QString("io - new A"), new_address_a, QString(""));
check_addbook_size(3);
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("io - new B"), new_address_b, QString(""));
+ check_addbook_size(4);
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit");
+
+ search_line->setText(r_label);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(s_label);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("io");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wildcard "?".
+ search_line->setText("io?new");
+ QCOMPARE(table_view->model()->rowCount(), 0);
+ search_line->setText("io???new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+
+ // Check wildcard "*".
+ search_line->setText("io*new");
+ QCOMPARE(table_view->model()->rowCount(), 2);
+ search_line->setText("*");
+ QCOMPARE(table_view->model()->rowCount(), 3);
+
+ search_line->setText(preexisting_r_address);
+ QCOMPARE(table_view->model()->rowCount(), 0);
+
+ search_line->setText(preexisting_s_address);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_a);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText(new_address_b);
+ QCOMPARE(table_view->model()->rowCount(), 1);
+
+ search_line->setText("");
+ QCOMPARE(table_view->model()->rowCount(), 3);
}
} // namespace
void AddressBookTests::addressBookTests()
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
if (QApplication::platformName() == "minimal") {
// Disable for mac on "minimal" platform to avoid crashes inside the Qt
// framework when it tries to look up unimplemented cocoa functions,
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 099ac5fcae..9648ef6188 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -58,7 +58,7 @@ void TestRpcCommand(RPCConsole* console)
//! Entry point for BitcoinApplication tests.
void AppTests::appTests()
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
if (QApplication::platformName() == "minimal") {
// Disable for mac on "minimal" platform to avoid crashes inside the Qt
// framework when it tries to look up unimplemented cocoa functions,
@@ -119,6 +119,6 @@ AppTests::HandleCallback::~HandleCallback()
assert(it != callbacks.end());
callbacks.erase(it);
if (callbacks.empty()) {
- m_app_tests.m_app.quit();
+ m_app_tests.m_app.exit(0);
}
}
diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp
index 4a943a6343..2ef9230c59 100644
--- a/src/qt/test/optiontests.cpp
+++ b/src/qt/test/optiontests.cpp
@@ -13,8 +13,7 @@
#include <univalue.h>
-//! Entry point for BitcoinApplication tests.
-void OptionTests::optionTests()
+void OptionTests::integerGetArgBug()
{
// Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure
// that setting integer prune value doesn't cause an exception to be thrown
@@ -24,7 +23,7 @@ void OptionTests::optionTests()
settings.rw_settings["prune"] = 3814;
});
gArgs.WriteSettingsFile();
- OptionsModel{};
+ OptionsModel{m_node};
gArgs.LockSettings([&](util::Settings& settings) {
settings.rw_settings.erase("prune");
});
@@ -49,7 +48,7 @@ void OptionTests::parametersInteraction()
QSettings settings;
settings.setValue("fListen", false);
- OptionsModel{};
+ OptionsModel{m_node};
const bool expected{false};
diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h
index 39c1612c8f..257a0b65be 100644
--- a/src/qt/test/optiontests.h
+++ b/src/qt/test/optiontests.h
@@ -16,7 +16,7 @@ public:
explicit OptionTests(interfaces::Node& node) : m_node(node) {}
private Q_SLOTS:
- void optionTests();
+ void integerGetArgBug();
void parametersInteraction();
private:
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index c4cd0f4cd1..bc06f0f23b 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -184,7 +184,7 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel;
+ OptionsModel optionsModel(node);
ClientModel clientModel(node, &optionsModel);
WalletContext& context = *node.walletLoader().context();
AddWallet(context, wallet);
@@ -315,7 +315,7 @@ void TestGUI(interfaces::Node& node)
void WalletTests::walletTests()
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_MACOS
if (QApplication::platformName() == "minimal") {
// Disable for mac on "minimal" platform to avoid crashes inside the Qt
// framework when it tries to look up unimplemented cocoa functions,
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 9e92f89543..a61d5407b3 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -36,13 +36,41 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status
{
int depth = status.depth_in_main_chain;
if (depth < 0) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that conflicts with a confirmed
+ transaction. */
return tr("conflicted with a transaction with %1 confirmations").arg(-depth);
} else if (depth == 0) {
- const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()};
- return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned;
+ QString s;
+ if (inMempool) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that is in the memory pool. */
+ s = tr("0/unconfirmed, in memory pool");
+ } else {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents an unconfirmed transaction that is not in the memory pool. */
+ s = tr("0/unconfirmed, not in memory pool");
+ }
+ if (status.is_abandoned) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This
+ status represents an abandoned transaction. */
+ s += QLatin1String(", ") + tr("abandoned");
+ }
+ return s;
} else if (depth < 6) {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This
+ status represents a transaction confirmed in at least one block,
+ but less than 6 blocks. */
return tr("%1/unconfirmed").arg(depth);
} else {
+ /*: Text explaining the current status of a transaction, shown in the
+ status field of the details window for this transaction. This status
+ represents a transaction confirmed in 6 or more blocks. */
return tr("%1 confirmations").arg(depth);
}
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 7b932890cf..4312b3cd24 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -62,7 +62,7 @@ struct TxLessThan
struct TransactionNotification
{
public:
- TransactionNotification() {}
+ TransactionNotification() = default;
TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction):
hash(_hash), status(_status), showTransaction(_showTransaction) {}
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index dc4e25a02b..11bea85b21 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -55,9 +55,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
walletStack->addWidget(no_wallet_group);
}
-WalletFrame::~WalletFrame()
-{
-}
+WalletFrame::~WalletFrame() = default;
void WalletFrame::setClientModel(ClientModel *_clientModel)
{
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 5ee32e79d5..1f6c90af4a 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -145,7 +145,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
Q_EMIT notifyWatchonlyChanged(fHaveWatchonly);
}
-bool WalletModel::validateAddress(const QString &address)
+bool WalletModel::validateAddress(const QString& address) const
{
return IsValidDestinationString(address.toStdString());
}
@@ -284,22 +284,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
return SendCoinsReturn(OK);
}
-OptionsModel *WalletModel::getOptionsModel()
+OptionsModel* WalletModel::getOptionsModel() const
{
return optionsModel;
}
-AddressTableModel *WalletModel::getAddressTableModel()
+AddressTableModel* WalletModel::getAddressTableModel() const
{
return addressTableModel;
}
-TransactionTableModel *WalletModel::getTransactionTableModel()
+TransactionTableModel* WalletModel::getTransactionTableModel() const
{
return transactionTableModel;
}
-RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
+RecentRequestsTableModel* WalletModel::getRecentRequestsTableModel() const
{
return recentRequestsTableModel;
}
@@ -369,7 +369,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel,
QString strPurpose = QString::fromStdString(purpose);
qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
- bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
+ bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook",
Q_ARG(QString, strAddress),
Q_ARG(QString, strLabel),
Q_ARG(bool, isMine),
@@ -557,7 +557,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return true;
}
-bool WalletModel::displayAddress(std::string sAddress)
+bool WalletModel::displayAddress(std::string sAddress) const
{
CTxDestination dest = DecodeDestination(sAddress);
bool res = false;
@@ -585,7 +585,7 @@ QString WalletModel::getDisplayName() const
return name.isEmpty() ? "["+tr("default wallet")+"]" : name;
}
-bool WalletModel::isMultiwallet()
+bool WalletModel::isMultiwallet() const
{
return m_node.walletLoader().getWallets().size() > 1;
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index ad1239ccdc..d524d48111 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -66,8 +66,7 @@ public:
AmountWithFeeExceedsBalance,
DuplicateAddress,
TransactionCreationFailed, // Error returned when wallet is still locked
- AbsurdFee,
- PaymentRequestExpired
+ AbsurdFee
};
enum EncryptionStatus
@@ -77,15 +76,15 @@ public:
Unlocked // wallet->IsCrypted() && !wallet->IsLocked()
};
- OptionsModel *getOptionsModel();
- AddressTableModel *getAddressTableModel();
- TransactionTableModel *getTransactionTableModel();
- RecentRequestsTableModel *getRecentRequestsTableModel();
+ OptionsModel* getOptionsModel() const;
+ AddressTableModel* getAddressTableModel() const;
+ TransactionTableModel* getTransactionTableModel() const;
+ RecentRequestsTableModel* getRecentRequestsTableModel() const;
EncryptionStatus getEncryptionStatus() const;
// Check address for validity
- bool validateAddress(const QString &address);
+ bool validateAddress(const QString& address) const;
// Return status record for SendCoins, contains error id + information
struct SendCoinsReturn
@@ -137,7 +136,7 @@ public:
UnlockContext requestUnlock();
bool bumpFee(uint256 hash, uint256& new_hash);
- bool displayAddress(std::string sAddress);
+ bool displayAddress(std::string sAddress) const;
static bool isWalletEnabled();
@@ -149,9 +148,7 @@ public:
QString getWalletName() const;
QString getDisplayName() const;
- bool isMultiwallet();
-
- AddressTableModel* getAddressTableModel() const { return addressTableModel; }
+ bool isMultiwallet() const;
void refresh(bool pk_hash_only = false);
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 344bf628bb..2f92c57607 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -111,9 +111,7 @@ WalletView::WalletView(WalletModel* wallet_model, const PlatformStyle* _platform
connect(walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
}
-WalletView::~WalletView()
-{
-}
+WalletView::~WalletView() = default;
void WalletView::setClientModel(ClientModel *_clientModel)
{
diff --git a/src/random.cpp b/src/random.cpp
index 6ae08103b1..74ceb3d2a3 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -370,11 +370,9 @@ public:
InitHardwareRand();
}
- ~RNGState()
- {
- }
+ ~RNGState() = default;
- void AddEvent(uint32_t event_info) noexcept
+ void AddEvent(uint32_t event_info) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_events_mutex)
{
LOCK(m_events_mutex);
@@ -388,7 +386,7 @@ public:
/**
* Feed (the hash of) all events added through AddEvent() to hasher.
*/
- void SeedEvents(CSHA512& hasher) noexcept
+ void SeedEvents(CSHA512& hasher) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_events_mutex)
{
// We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256,
// since we want it to be fast as network peers may be able to trigger it repeatedly.
@@ -407,7 +405,7 @@ public:
*
* If this function has never been called with strong_seed = true, false is returned.
*/
- bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept
+ bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
assert(num <= 32);
unsigned char buf[64];
@@ -586,16 +584,11 @@ void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(e
bool g_mock_deterministic_tests{false};
-uint64_t GetRand(uint64_t nMax) noexcept
+uint64_t GetRandInternal(uint64_t nMax) noexcept
{
return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
}
-int GetRandInt(int nMax) noexcept
-{
- return GetRand(nMax);
-}
-
uint256 GetRandHash() noexcept
{
uint256 hash;
diff --git a/src/random.h b/src/random.h
index 285158b1c3..b92c29f0be 100644
--- a/src/random.h
+++ b/src/random.h
@@ -69,7 +69,17 @@
*/
void GetRandBytes(Span<unsigned char> bytes) noexcept;
/** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */
-uint64_t GetRand(uint64_t nMax) noexcept;
+uint64_t GetRandInternal(uint64_t nMax) noexcept;
+/** Generate a uniform random integer of type T in the range [0..nMax)
+ * nMax defaults to std::numeric_limits<T>::max()
+ * Precondition: nMax > 0, T is an integral type, no larger than uint64_t
+ */
+template<typename T>
+T GetRand(T nMax=std::numeric_limits<T>::max()) noexcept {
+ static_assert(std::is_integral<T>(), "T must be integral");
+ static_assert(std::numeric_limits<T>::max() <= std::numeric_limits<uint64_t>::max(), "GetRand only supports up to uint64_t");
+ return T(GetRandInternal(nMax));
+}
/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
template <typename D>
D GetRandomDuration(typename std::common_type<D>::type max) noexcept
@@ -95,7 +105,6 @@ constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
* */
std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval);
-int GetRandInt(int nMax) noexcept;
uint256 GetRandHash() noexcept;
/**
@@ -223,6 +232,17 @@ public:
/** Generate a random boolean. */
bool randbool() noexcept { return randbits(1); }
+ /** Return the time point advanced by a uniform random duration. */
+ template <typename Tp>
+ Tp rand_uniform_delay(const Tp& time, typename Tp::duration range)
+ {
+ using Dur = typename Tp::duration;
+ Dur dur{range.count() > 0 ? /* interval [0..range) */ Dur{randrange(range.count())} :
+ range.count() < 0 ? /* interval (range..0] */ -Dur{randrange(-range.count())} :
+ /* interval [0..0] */ Dur{0}};
+ return time + dur;
+ }
+
// Compatibility with the C++11 UniformRandomBitGenerator concept
typedef uint64_t result_type;
static constexpr uint64_t min() { return 0; }
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index 53ebeb31cd..c5dca346d6 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -57,8 +57,7 @@
#include <sys/auxv.h>
#endif
-//! Necessary on some platforms
-extern char** environ;
+extern char** environ; // NOLINT(readability-redundant-declaration): Necessary on some platforms
namespace {
diff --git a/src/rest.cpp b/src/rest.cpp
index 22b5d2e074..1b90baaf95 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -305,7 +305,7 @@ static bool rest_block(const std::any& context,
if (chainman.m_blockman.IsBlockPruned(pblockindex))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
- if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
+ if (!ReadBlockFromDisk(block, pblockindex, chainman.GetParams().GetConsensus()))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 398b948388..f2186c131f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -19,11 +19,11 @@
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
+#include <kernel/coinstats.h>
#include <logging/timer.h>
#include <net.h>
#include <net_processing.h>
#include <node/blockstorage.h>
-#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <primitives/transaction.h>
@@ -51,10 +51,10 @@
#include <memory>
#include <mutex>
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
+
using node::BlockManager;
-using node::CCoinsStats;
-using node::CoinStatsHashType;
-using node::GetUTXOStats;
using node::NodeContext;
using node::ReadBlockFromDisk;
using node::SnapshotMetadata;
@@ -110,7 +110,7 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan
CChain& active_chain = chainman.ActiveChain();
if (param.isNum()) {
- const int height{param.get_int()};
+ const int height{param.getInt<int>()};
if (height < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
}
@@ -271,7 +271,7 @@ static RPCHelpMan waitfornewblock()
{
int timeout = 0;
if (!request.params[0].isNull())
- timeout = request.params[0].get_int();
+ timeout = request.params[0].getInt<int>();
CUpdatedBlock block;
{
@@ -317,7 +317,7 @@ static RPCHelpMan waitforblock()
uint256 hash(ParseHashV(request.params[0], "blockhash"));
if (!request.params[1].isNull())
- timeout = request.params[1].get_int();
+ timeout = request.params[1].getInt<int>();
CUpdatedBlock block;
{
@@ -361,10 +361,10 @@ static RPCHelpMan waitforblockheight()
{
int timeout = 0;
- int height = request.params[0].get_int();
+ int height = request.params[0].getInt<int>();
if (!request.params[1].isNull())
- timeout = request.params[1].get_int();
+ timeout = request.params[1].getInt<int>();
CUpdatedBlock block;
{
@@ -445,7 +445,7 @@ static RPCHelpMan getblockfrompeer()
PeerManager& peerman = EnsurePeerman(node);
const uint256& block_hash{ParseHashV(request.params[0], "blockhash")};
- const NodeId peer_id{request.params[1].get_int64()};
+ const NodeId peer_id{request.params[1].getInt<int64_t>()};
const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
@@ -485,7 +485,7 @@ static RPCHelpMan getblockhash()
LOCK(cs_main);
const CChain& active_chain = chainman.ActiveChain();
- int nHeight = request.params[0].get_int();
+ int nHeight = request.params[0].getInt<int>();
if (nHeight < 0 || nHeight > active_chain.Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
@@ -694,7 +694,7 @@ static RPCHelpMan getblock()
if (request.params[1].isBool()) {
verbosity = request.params[1].get_bool() ? 1 : 0;
} else {
- verbosity = request.params[1].get_int();
+ verbosity = request.params[1].getInt<int>();
}
}
@@ -759,9 +759,10 @@ static RPCHelpMan pruneblockchain()
CChainState& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
- int heightParam = request.params[0].get_int();
- if (heightParam < 0)
+ int heightParam = request.params[0].getInt<int>();
+ if (heightParam < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
+ }
// Height value more than a billion is too high to be a block height, and
// too low to be a block time (corresponds to timestamp from Sep 2001).
@@ -776,18 +777,18 @@ static RPCHelpMan pruneblockchain()
unsigned int height = (unsigned int) heightParam;
unsigned int chainHeight = (unsigned int) active_chain.Height();
- if (chainHeight < Params().PruneAfterHeight())
+ if (chainHeight < chainman.GetParams().PruneAfterHeight()) {
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
- else if (height > chainHeight)
+ } else if (height > chainHeight) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
- else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
+ } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
PruneBlockFilesManual(active_chainstate, height);
- const CBlockIndex* block = CHECK_NONFATAL(active_chain.Tip());
- const CBlockIndex* last_block = node::GetFirstStoredBlock(block);
+ const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
+ const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)};
return static_cast<uint64_t>(last_block->nHeight);
},
@@ -807,6 +808,36 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input)
}
}
+/**
+ * Calculate statistics about the unspent transaction output set
+ *
+ * @param[in] index_requested Signals if the coinstatsindex should be used (when available).
+ */
+static std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
+ kernel::CoinStatsHashType hash_type,
+ const std::function<void()>& interruption_point = {},
+ const CBlockIndex* pindex = nullptr,
+ bool index_requested = true)
+{
+ // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
+ if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
+ if (pindex) {
+ return g_coin_stats_index->LookUpStats(pindex);
+ } else {
+ CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
+ return g_coin_stats_index->LookUpStats(block_index);
+ }
+ }
+
+ // If the coinstats index isn't requested or is otherwise not usable, the
+ // pindex should either be null or equal to the view's best block. This is
+ // because without the coinstats index we can only get coinstats about the
+ // best block.
+ CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
+
+ return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
+}
+
static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
@@ -861,8 +892,7 @@ static RPCHelpMan gettxoutsetinfo()
const CBlockIndex* pindex{nullptr};
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
- CCoinsStats stats{hash_type};
- stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
+ bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
@@ -883,14 +913,14 @@ static RPCHelpMan gettxoutsetinfo()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
}
- if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) {
+ if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
}
pindex = ParseHashOrHeight(request.params[1], chainman);
}
- if (stats.index_requested && g_coin_stats_index) {
+ if (index_requested && g_coin_stats_index) {
if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
const IndexSummary summary{g_coin_stats_index->GetSummary()};
@@ -902,7 +932,9 @@ static RPCHelpMan gettxoutsetinfo()
}
}
- if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
+ const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
+ if (maybe_stats.has_value()) {
+ const CCoinsStats& stats = maybe_stats.value();
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
@@ -921,10 +953,13 @@ static RPCHelpMan gettxoutsetinfo()
} else {
ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
- CCoinsStats prev_stats{hash_type};
-
+ CCoinsStats prev_stats{};
if (pindex->nHeight > 0) {
- GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev);
+ const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
+ if (!maybe_prev_stats) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
+ }
+ prev_stats = maybe_prev_stats.value();
}
UniValue block_info(UniValue::VOBJ);
@@ -992,8 +1027,7 @@ static RPCHelpMan gettxout()
UniValue ret(UniValue::VOBJ);
uint256 hash(ParseHashV(request.params[0], "txid"));
- int n = request.params[1].get_int();
- COutPoint out(hash, n);
+ COutPoint out{hash, request.params[1].getInt<uint32_t>()};
bool fMempool = true;
if (!request.params[2].isNull())
fMempool = request.params[2].get_bool();
@@ -1050,39 +1084,39 @@ static RPCHelpMan verifychain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int()};
- const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()};
+ const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()};
+ const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()};
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
CChainState& active_chainstate = chainman.ActiveChainstate();
return CVerifyDB().VerifyDB(
- active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
+ active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
},
};
}
-static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep)
+static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep)
{
// For buried deployments.
- if (!DeploymentEnabled(params, dep)) return;
+ if (!DeploymentEnabled(chainman, dep)) return;
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
// getdeploymentinfo reports the softfork as active from when the chain height is
// one below the activation height
- rv.pushKV("active", DeploymentActiveAfter(blockindex, params, dep));
- rv.pushKV("height", params.DeploymentHeight(dep));
+ rv.pushKV("active", DeploymentActiveAfter(blockindex, chainman, dep));
+ rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
softforks.pushKV(DeploymentName(dep), rv);
}
-static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
{
// For BIP9 deployments.
- if (!DeploymentEnabled(consensusParams, id)) return;
+ if (!DeploymentEnabled(chainman, id)) return;
if (blockindex == nullptr) return;
auto get_state_name = [](const ThresholdState state) -> std::string {
@@ -1098,29 +1132,29 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
UniValue bip9(UniValue::VOBJ);
- const ThresholdState next_state = g_versionbitscache.State(blockindex, consensusParams, id);
- const ThresholdState current_state = g_versionbitscache.State(blockindex->pprev, consensusParams, id);
+ const ThresholdState next_state = chainman.m_versionbitscache.State(blockindex, chainman.GetConsensus(), id);
+ const ThresholdState current_state = chainman.m_versionbitscache.State(blockindex->pprev, chainman.GetConsensus(), 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("bit", chainman.GetConsensus().vDeployments[id].bit);
}
- bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
- bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
+ bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime);
+ bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout);
+ bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
// BIP9 status
bip9.pushKV("status", get_state_name(current_state));
- bip9.pushKV("since", g_versionbitscache.StateSinceHeight(blockindex->pprev, consensusParams, id));
+ bip9.pushKV("since", chainman.m_versionbitscache.StateSinceHeight(blockindex->pprev, chainman.GetConsensus(), id));
bip9.pushKV("status_next", get_state_name(next_state));
// BIP9 signalling status, if applicable
if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
std::vector<bool> signals;
- BIP9Stats statsStruct = g_versionbitscache.Statistics(blockindex, consensusParams, id, &signals);
+ BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(blockindex, chainman.GetConsensus(), id, &signals);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
@@ -1141,7 +1175,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
if (ThresholdState::ACTIVE == next_state) {
- rv.pushKV("height", g_versionbitscache.StateSinceHeight(blockindex, consensusParams, id));
+ rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id));
}
rv.pushKV("active", ThresholdState::ACTIVE == next_state);
rv.pushKV("bip9", bip9);
@@ -1149,16 +1183,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
softforks.pushKV(DeploymentName(id), rv);
}
-namespace {
-/* TODO: when -deprecatedrpc=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",
{},
@@ -1177,15 +1204,9 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
{RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
{RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
- {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"},
+ {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (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", /*optional=*/true, "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks",
- {
- {RPCResult::Type::OBJ, "xxxx", "name of the softfork",
- RPCHelpForDeployment
- },
- }},
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
}},
RPCExamples{
@@ -1199,24 +1220,23 @@ RPCHelpMan getblockchaininfo()
LOCK(cs_main);
CChainState& active_chainstate = chainman.ActiveChainstate();
- const CBlockIndex* tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
- const int height = tip->nHeight;
+ const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
+ const int height{tip.nHeight};
UniValue obj(UniValue::VOBJ);
- obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("chain", chainman.GetParams().NetworkIDString());
obj.pushKV("blocks", height);
obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
- obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
- obj.pushKV("difficulty", (double)GetDifficulty(tip));
- obj.pushKV("time", (int64_t)tip->nTime);
- obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
- obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
+ obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
+ obj.pushKV("difficulty", GetDifficulty(&tip));
+ obj.pushKV("time", tip.GetBlockTime());
+ obj.pushKV("mediantime", tip.GetMedianTimePast());
+ obj.pushKV("verificationprogress", GuessVerificationProgress(chainman.GetParams().TxData(), &tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
- obj.pushKV("chainwork", tip->nChainWork.GetHex());
+ obj.pushKV("chainwork", tip.nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
obj.pushKV("pruned", node::fPruneMode);
if (node::fPruneMode) {
- const CBlockIndex* block = CHECK_NONFATAL(tip);
- obj.pushKV("pruneheight", node::GetFirstStoredBlock(block)->nHeight);
+ obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
// if 0, execution bypasses the whole if block.
bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
@@ -1226,11 +1246,6 @@ RPCHelpMan getblockchaininfo()
}
}
- if (IsDeprecatedRPCEnabled("softforks")) {
- const Consensus::Params& consensusParams = Params().GetConsensus();
- obj.pushKV("softforks", DeploymentInfo(tip, consensusParams));
- }
-
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
},
@@ -1263,16 +1278,16 @@ const std::vector<RPCResult> RPCHelpForDeployment{
}},
};
-UniValue DeploymentInfo(const CBlockIndex* blockindex, const Consensus::Params& consensusParams)
+UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& chainman)
{
UniValue softforks(UniValue::VOBJ);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CSV);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
- SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_HEIGHTINCB);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_DERSIG);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CLTV);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CSV);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY);
+ SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT);
return softforks;
}
} // anon namespace
@@ -1311,12 +1326,10 @@ static RPCHelpMan getdeploymentinfo()
}
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
-
UniValue deploymentinfo(UniValue::VOBJ);
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
deploymentinfo.pushKV("height", blockindex->nHeight);
- deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, consensusParams));
+ deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
return deploymentinfo;
},
};
@@ -1584,7 +1597,7 @@ static RPCHelpMan getchaintxstats()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
const CBlockIndex* pindex;
- int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
+ int blockcount = 30 * 24 * 60 * 60 / chainman.GetParams().GetConsensus().nPowTargetSpacing; // By default: 1 month
if (request.params[1].isNull()) {
LOCK(cs_main);
@@ -1606,16 +1619,16 @@ static RPCHelpMan getchaintxstats()
if (request.params[0].isNull()) {
blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
} else {
- blockcount = request.params[0].get_int();
+ blockcount = request.params[0].getInt<int>();
if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
}
}
- const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
- int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast();
- int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
+ const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
+ const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
+ const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
UniValue ret(UniValue::VOBJ);
ret.pushKV("time", (int64_t)pindex->nTime);
@@ -1756,8 +1769,7 @@ static RPCHelpMan getblockstats()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
- CHECK_NONFATAL(pindex != nullptr);
+ const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
std::set<std::string> stats;
if (!request.params[1].isNull()) {
@@ -1768,8 +1780,8 @@ static RPCHelpMan getblockstats()
}
}
- const CBlock block = GetBlockChecked(chainman.m_blockman, pindex);
- const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
+ const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
+ const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -1887,25 +1899,25 @@ static RPCHelpMan getblockstats()
ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
- ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex());
+ ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
ret_all.pushKV("feerate_percentiles", feerates_res);
- ret_all.pushKV("height", (int64_t)pindex->nHeight);
+ ret_all.pushKV("height", (int64_t)pindex.nHeight);
ret_all.pushKV("ins", inputs);
ret_all.pushKV("maxfee", maxfee);
ret_all.pushKV("maxfeerate", maxfeerate);
ret_all.pushKV("maxtxsize", maxtxsize);
ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
- ret_all.pushKV("mediantime", pindex->GetMedianTimePast());
+ ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
ret_all.pushKV("outs", outputs);
- ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus()));
+ ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus()));
ret_all.pushKV("swtotal_size", swtotal_size);
ret_all.pushKV("swtotal_weight", swtotal_weight);
ret_all.pushKV("swtxs", swtxs);
- ret_all.pushKV("time", pindex->GetBlockTime());
+ ret_all.pushKV("time", pindex.GetBlockTime());
ret_all.pushKV("total_out", total_out);
ret_all.pushKV("total_size", total_size);
ret_all.pushKV("total_weight", total_weight);
@@ -1970,9 +1982,9 @@ static std::atomic<bool> g_should_abort_scan;
class CoinsViewScanReserver
{
private:
- bool m_could_reserve;
+ bool m_could_reserve{false};
public:
- explicit CoinsViewScanReserver() : m_could_reserve(false) {}
+ explicit CoinsViewScanReserver() = default;
bool reserve() {
CHECK_NONFATAL(!m_could_reserve);
@@ -2073,7 +2085,7 @@ static RPCHelpMan scantxoutset()
// no scan in progress
return NullUniValue;
}
- result.pushKV("progress", g_scan_progress);
+ result.pushKV("progress", g_scan_progress.load());
return result;
} else if (request.params[0].get_str() == "abort") {
CoinsViewScanReserver reserver;
@@ -2282,6 +2294,12 @@ static RPCHelpMan dumptxoutset()
FILE* file{fsbridge::fopen(temppath, "wb")};
CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
+ if (afile.IsNull()) {
+ throw JSONRPCError(
+ RPC_INVALID_PARAMETER,
+ "Couldn't open file " + temppath.u8string() + " for writing.");
+ }
+
NodeContext& node = EnsureAnyNodeContext(request.context);
UniValue result = CreateUTXOSnapshot(
node, node.chainman->ActiveChainstate(), afile, path, temppath);
@@ -2301,7 +2319,7 @@ UniValue CreateUTXOSnapshot(
const fs::path& temppath)
{
std::unique_ptr<CCoinsViewCursor> pcursor;
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
+ std::optional<CCoinsStats> maybe_stats;
const CBlockIndex* tip;
{
@@ -2321,19 +2339,20 @@ UniValue CreateUTXOSnapshot(
chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) {
+ maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
+ if (!maybe_stats) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
pcursor = chainstate.CoinsDB().Cursor();
- tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock));
+ tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
}
LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
tip->nHeight, tip->GetBlockHash().ToString(),
fs::PathToString(path), fs::PathToString(temppath)));
- SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx};
+ SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
afile << metadata;
@@ -2355,11 +2374,11 @@ UniValue CreateUTXOSnapshot(
afile.fclose();
UniValue result(UniValue::VOBJ);
- result.pushKV("coins_written", stats.coins_count);
+ result.pushKV("coins_written", maybe_stats->coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.u8string());
- result.pushKV("txoutset_hash", stats.hashSerialized.ToString());
+ result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
// Cast required because univalue doesn't have serialization specified for
// `unsigned int`, nChainTx's type.
result.pushKV("nchaintx", uint64_t{tip->nChainTx});
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 23e9d4074c..ae0d0112ba 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -173,6 +173,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setwalletflag", 1, "value" },
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
+ { "gettxspendingprevout", 0, "outputs" },
{ "bumpfee", 1, "options" },
{ "psbtbumpfee", 1, "options" },
{ "logging", 0, "include" },
diff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp
new file mode 100644
index 0000000000..bfec0780aa
--- /dev/null
+++ b/src/rpc/fees.cpp
@@ -0,0 +1,236 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <core_io.h>
+#include <policy/feerate.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <rpc/protocol.h>
+#include <rpc/request.h>
+#include <rpc/server.h>
+#include <rpc/server_util.h>
+#include <rpc/util.h>
+#include <txmempool.h>
+#include <univalue.h>
+#include <util/fees.h>
+#include <util/system.h>
+#include <validation.h>
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <string>
+
+namespace node {
+struct NodeContext;
+}
+
+using node::NodeContext;
+
+static RPCHelpMan estimatesmartfee()
+{
+ return RPCHelpMan{"estimatesmartfee",
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within conf_target blocks if possible and return the number of blocks\n"
+ "for which the estimate is valid. Uses virtual transaction size as defined\n"
+ "in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"conservative"}, "The fee estimate mode.\n"
+ "Whether to return a more conservative estimate which also satisfies\n"
+ "a longer history. A conservative estimate potentially returns a\n"
+ "higher feerate and is more likely to be sufficient for the desired\n"
+ "target, but is not as responsive to short term drops in the\n"
+ "prevailing fee market. Must be one of (case insensitive):\n"
+ "\"" + FeeModes("\"\n\"") + "\""},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},
+ {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
+ {
+ {RPCResult::Type::STR, "", "error"},
+ }},
+ {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n"
+ "The request target will be clamped between 2 and the highest target\n"
+ "fee estimation is able to return based on how long it has been running.\n"
+ "An error is returned if not enough transactions and blocks\n"
+ "have been observed to make an estimate for any number of blocks."},
+ }},
+ RPCExamples{
+ HelpExampleCli("estimatesmartfee", "6") +
+ HelpExampleRpc("estimatesmartfee", "6")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
+ RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
+
+ CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+
+ unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
+ bool conservative = true;
+ if (!request.params[1].isNull()) {
+ FeeEstimateMode fee_mode;
+ if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
+ }
+ if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false;
+ }
+
+ UniValue result(UniValue::VOBJ);
+ UniValue errors(UniValue::VARR);
+ FeeCalculation feeCalc;
+ CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
+ if (feeRate != CFeeRate(0)) {
+ CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
+ CFeeRate min_relay_feerate{::minRelayTxFee};
+ feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
+ result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
+ } else {
+ errors.push_back("Insufficient data or no feerate found");
+ result.pushKV("errors", errors);
+ }
+ result.pushKV("blocks", feeCalc.returnedTarget);
+ return result;
+ },
+ };
+}
+
+static RPCHelpMan estimaterawfee()
+{
+ return RPCHelpMan{"estimaterawfee",
+ "\nWARNING: This interface is unstable and may disappear or change!\n"
+ "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
+ "implementation of fee estimation. The parameters it can be called with\n"
+ "and the results it returns will change if the internal implementation changes.\n"
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
+ "defined in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
+ {"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
+ "confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
+ "lower buckets."},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
+ {
+ {RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",
+ {
+ {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},
+ {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
+ {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
+ {RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold",
+ {
+ {RPCResult::Type::NUM, "startrange", "start of feerate range"},
+ {RPCResult::Type::NUM, "endrange", "end of feerate range"},
+ {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
+ {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
+ {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
+ {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
+ }},
+ {RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
+ {
+ {RPCResult::Type::STR, "error", ""},
+ }},
+ }},
+ {RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ {RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",
+ {
+ {RPCResult::Type::ELISION, "", ""},
+ }},
+ }},
+ RPCExamples{
+ HelpExampleCli("estimaterawfee", "6 0.9")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
+ RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
+
+ CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
+
+ unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
+ double threshold = 0.95;
+ if (!request.params[1].isNull()) {
+ threshold = request.params[1].get_real();
+ }
+ if (threshold < 0 || threshold > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
+ }
+
+ UniValue result(UniValue::VOBJ);
+
+ for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {
+ CFeeRate feeRate;
+ EstimationResult buckets;
+
+ // Only output results for horizons which track the target
+ if (conf_target > fee_estimator.HighestTargetTracked(horizon)) continue;
+
+ feeRate = fee_estimator.estimateRawFee(conf_target, threshold, horizon, &buckets);
+ UniValue horizon_result(UniValue::VOBJ);
+ UniValue errors(UniValue::VARR);
+ UniValue passbucket(UniValue::VOBJ);
+ passbucket.pushKV("startrange", round(buckets.pass.start));
+ passbucket.pushKV("endrange", round(buckets.pass.end));
+ passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
+ passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
+ passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
+ passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
+ UniValue failbucket(UniValue::VOBJ);
+ failbucket.pushKV("startrange", round(buckets.fail.start));
+ failbucket.pushKV("endrange", round(buckets.fail.end));
+ failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
+ failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
+ failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
+ failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
+
+ // CFeeRate(0) is used to indicate error as a return value from estimateRawFee
+ if (feeRate != CFeeRate(0)) {
+ horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
+ horizon_result.pushKV("decay", buckets.decay);
+ horizon_result.pushKV("scale", (int)buckets.scale);
+ horizon_result.pushKV("pass", passbucket);
+ // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
+ if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
+ } else {
+ // Output only information that is still meaningful in the event of error
+ horizon_result.pushKV("decay", buckets.decay);
+ horizon_result.pushKV("scale", (int)buckets.scale);
+ horizon_result.pushKV("fail", failbucket);
+ errors.push_back("Insufficient data or no feerate found which meets threshold");
+ horizon_result.pushKV("errors", errors);
+ }
+ result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
+ }
+ return result;
+ },
+ };
+}
+
+void RegisterFeeRPCCommands(CRPCTable& t)
+{
+ static const CRPCCommand commands[]{
+ {"util", &estimatesmartfee},
+ {"hidden", &estimaterawfee},
+ };
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
+}
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 27080d3881..90dc86cd01 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -229,23 +229,12 @@ static std::vector<RPCResult> MempoolEntryDescription()
return {
RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
- RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
- "transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
- "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT +
- " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true,
- "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " +
- CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
- RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true,
- "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " +
- CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
RPCResult{RPCResult::Type::OBJ, "fees", "",
{
@@ -269,24 +258,12 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("vsize", (int)e.GetTxSize());
info.pushKV("weight", (int)e.GetTxWeight());
- // TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24
- const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")};
- if (deprecated_fee_fields_enabled) {
- info.pushKV("fee", ValueFromAmount(e.GetFee()));
- info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
- }
info.pushKV("time", count_seconds(e.GetTime()));
info.pushKV("height", (int)e.GetHeight());
info.pushKV("descendantcount", e.GetCountWithDescendants());
info.pushKV("descendantsize", e.GetSizeWithDescendants());
- if (deprecated_fee_fields_enabled) {
- info.pushKV("descendantfees", e.GetModFeesWithDescendants());
- }
info.pushKV("ancestorcount", e.GetCountWithAncestors());
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
- if (deprecated_fee_fields_enabled) {
- info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
- }
info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
UniValue fees(UniValue::VOBJ);
@@ -587,6 +564,89 @@ static RPCHelpMan getmempoolentry()
};
}
+static RPCHelpMan gettxspendingprevout()
+{
+ return RPCHelpMan{"gettxspendingprevout",
+ "Scans the mempool to find transactions spending any of the given outputs",
+ {
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
+ {
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ },
+ },
+ },
+ },
+ },
+ RPCResult{
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
+ {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
+ {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
+ }},
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheckArgument(request.params[0], UniValue::VARR);
+ const UniValue& output_params = request.params[0];
+ if (output_params.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
+ }
+
+ std::vector<COutPoint> prevouts;
+ prevouts.reserve(output_params.size());
+
+ for (unsigned int idx = 0; idx < output_params.size(); idx++) {
+ const UniValue& o = output_params[idx].get_obj();
+
+ RPCTypeCheckObj(o,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ }, /*fAllowNull=*/false, /*fStrict=*/true);
+
+ const uint256 txid(ParseHashO(o, "txid"));
+ const int nOutput{find_value(o, "vout").getInt<int>()};
+ if (nOutput < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
+ }
+
+ prevouts.emplace_back(txid, nOutput);
+ }
+
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
+ LOCK(mempool.cs);
+
+ UniValue result{UniValue::VARR};
+
+ for (const COutPoint& prevout : prevouts) {
+ UniValue o(UniValue::VOBJ);
+ o.pushKV("txid", prevout.hash.ToString());
+ o.pushKV("vout", (uint64_t)prevout.n);
+
+ const CTransaction* spendingTx = mempool.GetConflictTx(prevout);
+ if (spendingTx != nullptr) {
+ o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
+ }
+
+ result.push_back(o);
+ }
+
+ return result;
+ },
+ };
+}
+
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
// Make sure this call is atomic in the pool.
@@ -677,6 +737,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t)
{"blockchain", &getmempoolancestors},
{"blockchain", &getmempooldescendants},
{"blockchain", &getmempoolentry},
+ {"blockchain", &gettxspendingprevout},
{"blockchain", &getmempoolinfo},
{"blockchain", &getrawmempool},
{"blockchain", &savemempool},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index e4b38efe07..8fb6daf0cb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -17,7 +17,6 @@
#include <net.h>
#include <node/context.h>
#include <node/miner.h>
-#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
@@ -28,9 +27,9 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <shutdown.h>
+#include <timedata.h>
#include <txmempool.h>
#include <univalue.h>
-#include <util/fees.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
@@ -111,7 +110,7 @@ static RPCHelpMan getnetworkhashps()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1, chainman.ActiveChain());
+ return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].getInt<int>() : 120, !request.params[1].isNull() ? request.params[1].getInt<int>() : -1, chainman.ActiveChain());
},
};
}
@@ -121,9 +120,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
block_hash.SetNull();
block.hashMerkleRoot = BlockMerkleRoot(block);
- CChainParams chainparams(Params());
-
- while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()) && !ShutdownRequested()) {
+ while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainman.GetConsensus()) && !ShutdownRequested()) {
++block.nNonce;
--max_tries;
}
@@ -135,7 +132,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- if (!chainman.ProcessNewBlock(chainparams, shared_pblock, true, nullptr)) {
+ if (!chainman.ProcessNewBlock(shared_pblock, true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
}
@@ -147,7 +144,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{
UniValue blockHashes(UniValue::VARR);
while (nGenerate > 0 && !ShutdownRequested()) {
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler{chainman.ActiveChainstate(), mempool}.CreateNewBlock(coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -219,8 +216,8 @@ static RPCHelpMan generatetodescriptor()
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const int num_blocks{request.params[0].get_int()};
- const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
+ const int num_blocks{request.params[0].getInt<int>()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].getInt<int>()};
CScript coinbase_script;
std::string error;
@@ -266,8 +263,8 @@ static RPCHelpMan generatetoaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const int num_blocks{request.params[0].get_int()};
- const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
+ const int num_blocks{request.params[0].getInt<int>()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].getInt<int>()};
CTxDestination destination = DecodeDestination(request.params[1].get_str());
if (!IsValidDestination(destination)) {
@@ -351,7 +348,6 @@ static RPCHelpMan generateblock()
}
}
- CChainParams chainparams(Params());
CBlock block;
ChainstateManager& chainman = EnsureChainman(node);
@@ -359,7 +355,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(chainman.ActiveChainstate(), empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler{chainman.ActiveChainstate(), empty_mempool}.CreateNewBlock(coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -376,7 +372,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
+ if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), GetAdjustedTime, false, false)) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
}
}
@@ -431,7 +427,7 @@ static RPCHelpMan getmininginfo()
obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip()));
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
- obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("chain", chainman.GetParams().NetworkIDString());
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
},
@@ -464,7 +460,7 @@ static RPCHelpMan prioritisetransaction()
LOCK(cs_main);
uint256 hash(ParseHashV(request.params[0], "txid"));
- CAmount nAmount = request.params[2].get_int64();
+ CAmount nAmount = request.params[2].getInt<int64_t>();
if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
@@ -645,7 +641,7 @@ static RPCHelpMan getblocktemplate()
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
BlockValidationState state;
- TestBlockValidity(state, Params(), active_chainstate, block, pindexPrev, false, true);
+ TestBlockValidity(state, chainman.GetParams(), active_chainstate, block, pindexPrev, GetAdjustedTime, false, true);
return BIP22ValidationResult(state);
}
@@ -659,7 +655,7 @@ static RPCHelpMan getblocktemplate()
// NOTE: It is important that this NOT be read if versionbits is supported
const UniValue& uvMaxVersion = find_value(oparam, "maxversion");
if (uvMaxVersion.isNum()) {
- nMaxVersionPreVB = uvMaxVersion.get_int64();
+ nMaxVersionPreVB = uvMaxVersion.getInt<int64_t>();
}
}
}
@@ -667,7 +663,7 @@ static RPCHelpMan getblocktemplate()
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
- if (!Params().IsTestChain()) {
+ if (!chainman.GetParams().IsTestChain()) {
const CConnman& connman = EnsureConnman(node);
if (connman.GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
@@ -728,7 +724,7 @@ static RPCHelpMan getblocktemplate()
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ const Consensus::Params& consensusParams = chainman.GetParams().GetConsensus();
// GBT must be called with 'signet' set in the rules for signet chains
if (consensusParams.signet_blocks && setClientRules.count("signet") != 1) {
@@ -757,7 +753,7 @@ static RPCHelpMan getblocktemplate()
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler{active_chainstate, mempool}.CreateNewBlock(scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
@@ -772,7 +768,7 @@ static RPCHelpMan getblocktemplate()
pblock->nNonce = 0;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
- const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
+ const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
@@ -838,7 +834,7 @@ static RPCHelpMan getblocktemplate()
UniValue vbavailable(UniValue::VOBJ);
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
- ThresholdState state = g_versionbitscache.State(pindexPrev, consensusParams, pos);
+ ThresholdState state = chainman.m_versionbitscache.State(pindexPrev, consensusParams, pos);
switch (state) {
case ThresholdState::DEFINED:
case ThresholdState::FAILED:
@@ -846,7 +842,7 @@ static RPCHelpMan getblocktemplate()
break;
case ThresholdState::LOCKED_IN:
// Ensure bit is set in block version
- pblock->nVersion |= g_versionbitscache.Mask(consensusParams, pos);
+ pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
[[fallthrough]];
case ThresholdState::STARTED:
{
@@ -855,7 +851,7 @@ static RPCHelpMan getblocktemplate()
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it in the [default] version
- pblock->nVersion &= ~g_versionbitscache.Mask(consensusParams, pos);
+ pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
}
}
break;
@@ -932,10 +928,10 @@ class submitblock_StateCatcher final : public CValidationInterface
{
public:
uint256 hash;
- bool found;
+ bool found{false};
BlockValidationState state;
- explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}
+ explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), state() {}
protected:
void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override {
@@ -995,14 +991,14 @@ static RPCHelpMan submitblock()
LOCK(cs_main);
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
if (pindex) {
- UpdateUncommittedBlockStructures(block, pindex, Params().GetConsensus());
+ chainman.UpdateUncommittedBlockStructures(block, pindex);
}
}
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
+ bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
@@ -1044,7 +1040,7 @@ static RPCHelpMan submitheader()
}
BlockValidationState state;
- chainman.ProcessNewBlockHeaders({h}, state, Params());
+ chainman.ProcessNewBlockHeaders({h}, state);
if (state.IsValid()) return NullUniValue;
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
@@ -1054,201 +1050,6 @@ static RPCHelpMan submitheader()
};
}
-static RPCHelpMan estimatesmartfee()
-{
- return RPCHelpMan{"estimatesmartfee",
- "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within conf_target blocks if possible and return the number of blocks\n"
- "for which the estimate is valid. Uses virtual transaction size as defined\n"
- "in BIP 141 (witness data is discounted).\n",
- {
- {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
- {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"conservative"}, "The fee estimate mode.\n"
- " Whether to return a more conservative estimate which also satisfies\n"
- " a longer history. A conservative estimate potentially returns a\n"
- " higher feerate and is more likely to be sufficient for the desired\n"
- " target, but is not as responsive to short term drops in the\n"
- " prevailing fee market. Must be one of (case insensitive):\n"
- "\"" + FeeModes("\"\n\"") + "\""},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},
- {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
- {
- {RPCResult::Type::STR, "", "error"},
- }},
- {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n"
- "The request target will be clamped between 2 and the highest target\n"
- "fee estimation is able to return based on how long it has been running.\n"
- "An error is returned if not enough transactions and blocks\n"
- "have been observed to make an estimate for any number of blocks."},
- }},
- RPCExamples{
- HelpExampleCli("estimatesmartfee", "6") +
- HelpExampleRpc("estimatesmartfee", "6")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
-
- CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
- const NodeContext& node = EnsureAnyNodeContext(request.context);
- const CTxMemPool& mempool = EnsureMemPool(node);
-
- unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
- unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
- bool conservative = true;
- if (!request.params[1].isNull()) {
- FeeEstimateMode fee_mode;
- if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
- }
- if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false;
- }
-
- UniValue result(UniValue::VOBJ);
- UniValue errors(UniValue::VARR);
- FeeCalculation feeCalc;
- CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
- if (feeRate != CFeeRate(0)) {
- CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
- CFeeRate min_relay_feerate{::minRelayTxFee};
- feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
- result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
- } else {
- errors.push_back("Insufficient data or no feerate found");
- result.pushKV("errors", errors);
- }
- result.pushKV("blocks", feeCalc.returnedTarget);
- return result;
-},
- };
-}
-
-static RPCHelpMan estimaterawfee()
-{
- return RPCHelpMan{"estimaterawfee",
- "\nWARNING: This interface is unstable and may disappear or change!\n"
- "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
- " implementation of fee estimation. The parameters it can be called with\n"
- " and the results it returns will change if the internal implementation changes.\n"
- "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
- "defined in BIP 141 (witness data is discounted).\n",
- {
- {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
- {"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
- " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
- " lower buckets."},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
- {
- {RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",
- {
- {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},
- {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
- {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
- {RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold",
- {
- {RPCResult::Type::NUM, "startrange", "start of feerate range"},
- {RPCResult::Type::NUM, "endrange", "end of feerate range"},
- {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
- {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
- {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
- {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
- }},
- {RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
- {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
- {
- {RPCResult::Type::STR, "error", ""},
- }},
- }},
- {RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
- {RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
- }},
- RPCExamples{
- HelpExampleCli("estimaterawfee", "6 0.9")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
-
- CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
-
- unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
- unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
- double threshold = 0.95;
- if (!request.params[1].isNull()) {
- threshold = request.params[1].get_real();
- }
- if (threshold < 0 || threshold > 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
- }
-
- UniValue result(UniValue::VOBJ);
-
- for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {
- CFeeRate feeRate;
- EstimationResult buckets;
-
- // Only output results for horizons which track the target
- if (conf_target > fee_estimator.HighestTargetTracked(horizon)) continue;
-
- feeRate = fee_estimator.estimateRawFee(conf_target, threshold, horizon, &buckets);
- UniValue horizon_result(UniValue::VOBJ);
- UniValue errors(UniValue::VARR);
- UniValue passbucket(UniValue::VOBJ);
- passbucket.pushKV("startrange", round(buckets.pass.start));
- passbucket.pushKV("endrange", round(buckets.pass.end));
- passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
- passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
- passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
- passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
- UniValue failbucket(UniValue::VOBJ);
- failbucket.pushKV("startrange", round(buckets.fail.start));
- failbucket.pushKV("endrange", round(buckets.fail.end));
- failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
- failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
- failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
- failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
-
- // CFeeRate(0) is used to indicate error as a return value from estimateRawFee
- if (feeRate != CFeeRate(0)) {
- horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
- horizon_result.pushKV("decay", buckets.decay);
- horizon_result.pushKV("scale", (int)buckets.scale);
- horizon_result.pushKV("pass", passbucket);
- // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
- if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
- } else {
- // Output only information that is still meaningful in the event of error
- horizon_result.pushKV("decay", buckets.decay);
- horizon_result.pushKV("scale", (int)buckets.scale);
- horizon_result.pushKV("fail", failbucket);
- errors.push_back("Insufficient data or no feerate found which meets threshold");
- horizon_result.pushKV("errors",errors);
- }
- result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
- }
- return result;
-},
- };
-}
-
void RegisterMiningRPCCommands(CRPCTable& t)
{
static const CRPCCommand commands[]{
@@ -1262,10 +1063,6 @@ void RegisterMiningRPCCommands(CRPCTable& t)
{"hidden", &generatetoaddress},
{"hidden", &generatetodescriptor},
{"hidden", &generateblock},
-
- {"util", &estimatesmartfee},
-
- {"hidden", &estimaterawfee},
{"hidden", &generate},
};
for (const auto& c : commands) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index a9f9cb0153..0a061f2451 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -156,7 +156,7 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n"
"When a message type is not listed in this json object, the bytes received are 0.\n"
"Only known message types can appear as keys in the object and all bytes received\n"
- "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."}
+ "of unknown message types are listed under '"+NET_MESSAGE_TYPE_OTHER+"'."}
}},
{RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n"
"Please note this output is unlikely to be stable in upcoming releases as we iterate to\n"
@@ -243,19 +243,19 @@ static RPCHelpMan getpeerinfo()
}
obj.pushKV("permissions", permissions);
- UniValue sendPerMsgCmd(UniValue::VOBJ);
- for (const auto& i : stats.mapSendBytesPerMsgCmd) {
+ UniValue sendPerMsgType(UniValue::VOBJ);
+ for (const auto& i : stats.mapSendBytesPerMsgType) {
if (i.second > 0)
- sendPerMsgCmd.pushKV(i.first, i.second);
+ sendPerMsgType.pushKV(i.first, i.second);
}
- obj.pushKV("bytessent_per_msg", sendPerMsgCmd);
+ obj.pushKV("bytessent_per_msg", sendPerMsgType);
- UniValue recvPerMsgCmd(UniValue::VOBJ);
- for (const auto& i : stats.mapRecvBytesPerMsgCmd) {
+ UniValue recvPerMsgType(UniValue::VOBJ);
+ for (const auto& i : stats.mapRecvBytesPerMsgType) {
if (i.second > 0)
- recvPerMsgCmd.pushKV(i.first, i.second);
+ recvPerMsgType.pushKV(i.first, i.second);
}
- obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
+ obj.pushKV("bytesrecv_per_msg", recvPerMsgType);
obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type));
ret.push_back(obj);
@@ -412,7 +412,7 @@ static RPCHelpMan disconnectnode()
success = connman.DisconnectNode(address_arg.get_str());
} else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
/* handle disconnect-by-id */
- NodeId nodeid = (NodeId) id_arg.get_int64();
+ NodeId nodeid = (NodeId) id_arg.getInt<int64_t>();
success = connman.DisconnectNode(nodeid);
} else {
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
@@ -720,7 +720,7 @@ static RPCHelpMan setban()
int64_t banTime = 0; //use standard bantime if not specified
if (!request.params[2].isNull())
- banTime = request.params[2].get_int64();
+ banTime = request.params[2].getInt<int64_t>();
bool absolute = false;
if (request.params[3].isTrue())
@@ -879,7 +879,7 @@ static RPCHelpMan getnodeaddresses()
NodeContext& node = EnsureAnyNodeContext(request.context);
const CConnman& connman = EnsureConnman(node);
- const int count{request.params[0].isNull() ? 1 : request.params[0].get_int()};
+ const int count{request.params[0].isNull() ? 1 : request.params[0].getInt<int>()};
if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
const std::optional<Network> network{request.params[1].isNull() ? std::nullopt : std::optional<Network>{ParseNetwork(request.params[1].get_str())}};
@@ -932,7 +932,7 @@ static RPCHelpMan addpeeraddress()
}
const std::string& addr_string{request.params[0].get_str()};
- const uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
+ const auto port{request.params[1].getInt<uint16_t>()};
const bool tried{request.params[2].isTrue()};
UniValue obj(UniValue::VOBJ);
diff --git a/src/rpc/misc.cpp b/src/rpc/node.cpp
index 98d751e69d..5475662b82 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/node.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 <chainparams.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
@@ -11,395 +12,23 @@
#include <interfaces/echo.h>
#include <interfaces/init.h>
#include <interfaces/ipc.h>
-#include <key_io.h>
#include <node/context.h>
-#include <outputtype.h>
-#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/server_util.h>
#include <rpc/util.h>
#include <scheduler.h>
-#include <script/descriptor.h>
+#include <univalue.h>
#include <util/check.h>
-#include <util/message.h> // For MessageSign(), MessageVerify()
-#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
#include <util/system.h>
-#include <optional>
#include <stdint.h>
-#include <tuple>
#ifdef HAVE_MALLOC_INFO
#include <malloc.h>
#endif
-#include <univalue.h>
-
using node::NodeContext;
-static RPCHelpMan validateaddress()
-{
- return RPCHelpMan{
- "validateaddress",
- "\nReturn information about the given bitcoin address.\n",
- {
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
- {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"},
- {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"},
- {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"},
- {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"},
- {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program"},
- {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"},
- {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)",
- {
- {RPCResult::Type::NUM, "index", "index of a potential error"},
- }},
- }
- },
- RPCExamples{
- HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
- HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- std::string error_msg;
- std::vector<int> error_locations;
- CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations);
- const bool isValid = IsValidDestination(dest);
- CHECK_NONFATAL(isValid == error_msg.empty());
-
- UniValue ret(UniValue::VOBJ);
- ret.pushKV("isvalid", isValid);
- if (isValid) {
- std::string currentAddress = EncodeDestination(dest);
- ret.pushKV("address", currentAddress);
-
- CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
-
- UniValue detail = DescribeAddress(dest);
- ret.pushKVs(detail);
- } else {
- UniValue error_indices(UniValue::VARR);
- for (int i : error_locations) error_indices.push_back(i);
- ret.pushKV("error_locations", error_indices);
- ret.pushKV("error", error_msg);
- }
-
- return ret;
-},
- };
-}
-
-static RPCHelpMan createmultisig()
-{
- return RPCHelpMan{"createmultisig",
- "\nCreates a multi-signature address with n signature of m keys required.\n"
- "It returns a json object with the address and redeemScript.\n",
- {
- {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
- {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.",
- {
- {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
- }},
- {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR, "address", "The value of the new multisig address."},
- {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
- {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
- {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig",
- {
- {RPCResult::Type::STR, "", ""},
- }},
- }
- },
- RPCExamples{
- "\nCreate a multisig address from 2 public keys\n"
- + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- int required = request.params[0].get_int();
-
- // Get the public keys
- const UniValue& keys = request.params[1].get_array();
- std::vector<CPubKey> pubkeys;
- for (unsigned int i = 0; i < keys.size(); ++i) {
- if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) {
- pubkeys.push_back(HexToPubKey(keys[i].get_str()));
- } else {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str()));
- }
- }
-
- // Get the output type
- OutputType output_type = OutputType::LEGACY;
- if (!request.params[2].isNull()) {
- std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str());
- if (!parsed) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
- } else if (parsed.value() == OutputType::BECH32M) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
- }
- output_type = parsed.value();
- }
-
- // Construct using pay-to-script-hash:
- FillableSigningProvider keystore;
- CScript inner;
- const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
-
- // Make the descriptor
- std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);
-
- UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(dest));
- result.pushKV("redeemScript", HexStr(inner));
- result.pushKV("descriptor", descriptor->ToString());
-
- UniValue warnings(UniValue::VARR);
- if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
- // Only warns if the user has explicitly chosen an address type we cannot generate
- warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
- }
- if (warnings.size()) result.pushKV("warnings", warnings);
-
- return result;
-},
- };
-}
-
-static RPCHelpMan getdescriptorinfo()
-{
- const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)";
-
- return RPCHelpMan{"getdescriptorinfo",
- {"\nAnalyses a descriptor.\n"},
- {
- {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
- {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
- {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
- {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
- {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"},
- }
- },
- RPCExamples{
- "Analyse a descriptor\n" +
- HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") +
- HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- RPCTypeCheck(request.params, {UniValue::VSTR});
-
- FlatSigningProvider provider;
- std::string error;
- auto desc = Parse(request.params[0].get_str(), provider, error);
- if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
- }
-
- UniValue result(UniValue::VOBJ);
- result.pushKV("descriptor", desc->ToString());
- result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
- result.pushKV("isrange", desc->IsRange());
- result.pushKV("issolvable", desc->IsSolvable());
- result.pushKV("hasprivatekeys", provider.keys.size() > 0);
- return result;
-},
- };
-}
-
-static RPCHelpMan deriveaddresses()
-{
- const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
-
- return RPCHelpMan{"deriveaddresses",
- {"\nDerives one or more addresses corresponding to an output descriptor.\n"
- "Examples of output descriptors are:\n"
- " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
- " wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
- " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
- "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
- "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
- "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
- {
- {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
- {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
- },
- RPCResult{
- RPCResult::Type::ARR, "", "",
- {
- {RPCResult::Type::STR, "address", "the derived addresses"},
- }
- },
- RPCExamples{
- "First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") +
- HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
- const std::string desc_str = request.params[0].get_str();
-
- int64_t range_begin = 0;
- int64_t range_end = 0;
-
- if (request.params.size() >= 2 && !request.params[1].isNull()) {
- std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
- }
-
- FlatSigningProvider key_provider;
- std::string error;
- auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
- if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
- }
-
- if (!desc->IsRange() && request.params.size() > 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
- }
-
- if (desc->IsRange() && request.params.size() == 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
- }
-
- UniValue addresses(UniValue::VARR);
-
- for (int i = range_begin; i <= range_end; ++i) {
- FlatSigningProvider provider;
- std::vector<CScript> scripts;
- if (!desc->Expand(i, key_provider, scripts, provider)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
- }
-
- for (const CScript &script : scripts) {
- CTxDestination dest;
- if (!ExtractDestination(script, dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
- }
-
- addresses.push_back(EncodeDestination(dest));
- }
- }
-
- // This should not be possible, but an assert seems overkill:
- if (addresses.empty()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
- }
-
- return addresses;
-},
- };
-}
-
-static RPCHelpMan verifymessage()
-{
- return RPCHelpMan{"verifymessage",
- "Verify a signed message.",
- {
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."},
- {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."},
- {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
- },
- RPCResult{
- RPCResult::Type::BOOL, "", "If the signature is verified or not."
- },
- RPCExamples{
- "\nUnlock the wallet for 30 seconds\n"
- + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
- "\nCreate the signature\n"
- + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
- "\nVerify the signature\n"
- + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- LOCK(cs_main);
-
- std::string strAddress = request.params[0].get_str();
- std::string strSign = request.params[1].get_str();
- std::string strMessage = request.params[2].get_str();
-
- switch (MessageVerify(strAddress, strSign, strMessage)) {
- case MessageVerificationResult::ERR_INVALID_ADDRESS:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
- case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
- throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
- case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
- throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding");
- case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
- case MessageVerificationResult::ERR_NOT_SIGNED:
- return false;
- case MessageVerificationResult::OK:
- return true;
- }
-
- return false;
-},
- };
-}
-
-static RPCHelpMan signmessagewithprivkey()
-{
- return RPCHelpMan{"signmessagewithprivkey",
- "\nSign a message with the private key of an address\n",
- {
- {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key to sign the message with."},
- {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
- },
- RPCResult{
- RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
- },
- RPCExamples{
- "\nCreate the signature\n"
- + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
- "\nVerify the signature\n"
- + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- std::string strPrivkey = request.params[0].get_str();
- std::string strMessage = request.params[1].get_str();
-
- CKey key = DecodeSecret(strPrivkey);
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
- }
-
- std::string signature;
-
- if (!MessageSign(key, strMessage, signature)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
- }
-
- return signature;
-},
- };
-}
-
static RPCHelpMan setmocktime()
{
return RPCHelpMan{"setmocktime",
@@ -424,7 +53,7 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
- const int64_t time{request.params[0].get_int64()};
+ const int64_t time{request.params[0].getInt<int64_t>()};
if (time < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
}
@@ -479,7 +108,7 @@ static RPCHelpMan mockscheduler()
// check params are valid values
RPCTypeCheck(request.params, {UniValue::VNUM});
- int64_t delta_seconds = request.params[0].get_int64();
+ int64_t delta_seconds = request.params[0].getInt<int64_t>();
if (delta_seconds <= 0 || delta_seconds > 3600) {
throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
}
@@ -790,17 +419,11 @@ static RPCHelpMan getindexinfo()
};
}
-void RegisterMiscRPCCommands(CRPCTable& t)
+void RegisterNodeRPCCommands(CRPCTable& t)
{
static const CRPCCommand commands[]{
{"control", &getmemoryinfo},
{"control", &logging},
- {"util", &validateaddress},
- {"util", &createmultisig},
- {"util", &deriveaddresses},
- {"util", &getdescriptorinfo},
- {"util", &verifymessage},
- {"util", &signmessagewithprivkey},
{"util", &getindexinfo},
{"hidden", &setmocktime},
{"hidden", &mockscheduler},
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
new file mode 100644
index 0000000000..115a656e12
--- /dev/null
+++ b/src/rpc/output_script.cpp
@@ -0,0 +1,319 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key_io.h>
+#include <outputtype.h>
+#include <pubkey.h>
+#include <rpc/protocol.h>
+#include <rpc/request.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <script/descriptor.h>
+#include <script/script.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/check.h>
+#include <util/strencodings.h>
+
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace node {
+struct NodeContext;
+}
+using node::NodeContext;
+
+static RPCHelpMan validateaddress()
+{
+ return RPCHelpMan{
+ "validateaddress",
+ "\nReturn information about the given bitcoin address.\n",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
+ {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"},
+ {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"},
+ {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"},
+ {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"},
+ {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program"},
+ {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"},
+ {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)",
+ {
+ {RPCResult::Type::NUM, "index", "index of a potential error"},
+ }},
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
+ HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ std::string error_msg;
+ std::vector<int> error_locations;
+ CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations);
+ const bool isValid = IsValidDestination(dest);
+ CHECK_NONFATAL(isValid == error_msg.empty());
+
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("isvalid", isValid);
+ if (isValid) {
+ std::string currentAddress = EncodeDestination(dest);
+ ret.pushKV("address", currentAddress);
+
+ CScript scriptPubKey = GetScriptForDestination(dest);
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
+
+ UniValue detail = DescribeAddress(dest);
+ ret.pushKVs(detail);
+ } else {
+ UniValue error_indices(UniValue::VARR);
+ for (int i : error_locations) error_indices.push_back(i);
+ ret.pushKV("error_locations", error_indices);
+ ret.pushKV("error", error_msg);
+ }
+
+ return ret;
+ },
+ };
+}
+
+static RPCHelpMan createmultisig()
+{
+ return RPCHelpMan{"createmultisig",
+ "\nCreates a multi-signature address with n signature of m keys required.\n"
+ "It returns a json object with the address and redeemScript.\n",
+ {
+ {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
+ {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.",
+ {
+ {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
+ }},
+ {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The value of the new multisig address."},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
+ {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ }
+ },
+ RPCExamples{
+ "\nCreate a multisig address from 2 public keys\n"
+ + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ int required = request.params[0].getInt<int>();
+
+ // Get the public keys
+ const UniValue& keys = request.params[1].get_array();
+ std::vector<CPubKey> pubkeys;
+ for (unsigned int i = 0; i < keys.size(); ++i) {
+ if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) {
+ pubkeys.push_back(HexToPubKey(keys[i].get_str()));
+ } else {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str()));
+ }
+ }
+
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+ if (!request.params[2].isNull()) {
+ std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str());
+ if (!parsed) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
+ } else if (parsed.value() == OutputType::BECH32M) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
+ }
+ output_type = parsed.value();
+ }
+
+ // Construct using pay-to-script-hash:
+ FillableSigningProvider keystore;
+ CScript inner;
+ const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
+
+ // Make the descriptor
+ std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("address", EncodeDestination(dest));
+ result.pushKV("redeemScript", HexStr(inner));
+ result.pushKV("descriptor", descriptor->ToString());
+
+ UniValue warnings(UniValue::VARR);
+ if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ // Only warns if the user has explicitly chosen an address type we cannot generate
+ warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
+ }
+ if (warnings.size()) result.pushKV("warnings", warnings);
+
+ return result;
+ },
+ };
+}
+
+static RPCHelpMan getdescriptorinfo()
+{
+ const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)";
+
+ return RPCHelpMan{"getdescriptorinfo",
+ {"\nAnalyses a descriptor.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
+ {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
+ {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
+ {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
+ {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"},
+ }
+ },
+ RPCExamples{
+ "Analyse a descriptor\n" +
+ HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") +
+ HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ FlatSigningProvider provider;
+ std::string error;
+ auto desc = Parse(request.params[0].get_str(), provider, error);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("descriptor", desc->ToString());
+ result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
+ result.pushKV("isrange", desc->IsRange());
+ result.pushKV("issolvable", desc->IsSolvable());
+ result.pushKV("hasprivatekeys", provider.keys.size() > 0);
+ return result;
+ },
+ };
+}
+
+static RPCHelpMan deriveaddresses()
+{
+ const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
+
+ return RPCHelpMan{"deriveaddresses",
+ {"\nDerives one or more addresses corresponding to an output descriptor.\n"
+ "Examples of output descriptors are:\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
+ },
+ RPCResult{
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "address", "the derived addresses"},
+ }
+ },
+ RPCExamples{
+ "First three native segwit receive addresses\n" +
+ HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") +
+ HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
+ const std::string desc_str = request.params[0].get_str();
+
+ int64_t range_begin = 0;
+ int64_t range_end = 0;
+
+ if (request.params.size() >= 2 && !request.params[1].isNull()) {
+ std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
+ }
+
+ FlatSigningProvider key_provider;
+ std::string error;
+ auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
+ }
+
+ if (!desc->IsRange() && request.params.size() > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
+ }
+
+ if (desc->IsRange() && request.params.size() == 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
+ }
+
+ UniValue addresses(UniValue::VARR);
+
+ for (int i = range_begin; i <= range_end; ++i) {
+ FlatSigningProvider provider;
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, key_provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
+ }
+
+ for (const CScript& script : scripts) {
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
+ }
+
+ addresses.push_back(EncodeDestination(dest));
+ }
+ }
+
+ // This should not be possible, but an assert seems overkill:
+ if (addresses.empty()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
+ }
+
+ return addresses;
+ },
+ };
+}
+
+void RegisterOutputScriptRPCCommands(CRPCTable& t)
+{
+ static const CRPCCommand commands[]{
+ {"util", &validateaddress},
+ {"util", &createmultisig},
+ {"util", &deriveaddresses},
+ {"util", &getdescriptorinfo},
+ };
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
+}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ce8f16540f..b9b8c36bb3 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -217,7 +217,7 @@ static RPCHelpMan getrawtransaction()
uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CBlockIndex* blockindex = nullptr;
- if (hash == Params().GenesisBlock().hashMerkleRoot) {
+ if (hash == chainman.GetParams().GenesisBlock().hashMerkleRoot) {
// Special exception for the genesis block coinbase transaction
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved");
}
@@ -225,7 +225,7 @@ static RPCHelpMan getrawtransaction()
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
if (!request.params[1].isNull()) {
- fVerbose = request.params[1].isNum() ? (request.params[1].get_int() != 0) : request.params[1].get_bool();
+ fVerbose = request.params[1].isNum() ? (request.params[1].getInt<int>() != 0) : request.params[1].get_bool();
}
if (!request.params[2].isNull()) {
@@ -245,7 +245,7 @@ static RPCHelpMan getrawtransaction()
}
uint256 hash_block;
- const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block);
+ const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, chainman.GetConsensus(), hash_block);
if (!tx) {
std::string errmsg;
if (blockindex) {
@@ -567,7 +567,7 @@ static RPCHelpMan combinerawtransaction()
sigdata.MergeSignatureData(DataFromTransaction(txv, i, coin.out));
}
}
- ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata);
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata);
UpdateInput(txin, sigdata);
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index e23fe34480..86b5b7e960 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -40,7 +40,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
CMutableTransaction rawTx;
if (!locktime.isNull()) {
- int64_t nLockTime = locktime.get_int64();
+ int64_t nLockTime = locktime.getInt<int64_t>();
if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
@@ -55,7 +55,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
const UniValue& vout_v = find_value(o, "vout");
if (!vout_v.isNum())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
- int nOutput = vout_v.get_int();
+ int nOutput = vout_v.getInt<int>();
if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
@@ -71,7 +71,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
// set the sequence number if passed in the parameters object
const UniValue& sequenceObj = find_value(o, "sequence");
if (sequenceObj.isNum()) {
- int64_t seqNr64 = sequenceObj.get_int64();
+ int64_t seqNr64 = sequenceObj.getInt<int64_t>();
if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
} else {
@@ -177,7 +177,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
uint256 txid = ParseHashO(prevOut, "txid");
- int nOut = find_value(prevOut, "vout").get_int();
+ int nOut = find_value(prevOut, "vout").getInt<int>();
if (nOut < 0) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative");
}
diff --git a/src/rpc/register.h b/src/rpc/register.h
index 5a604ad428..301f410da3 100644
--- a/src/rpc/register.h
+++ b/src/rpc/register.h
@@ -10,26 +10,32 @@
class CRPCTable;
void RegisterBlockchainRPCCommands(CRPCTable &tableRPC);
+void RegisterFeeRPCCommands(CRPCTable&);
void RegisterMempoolRPCCommands(CRPCTable&);
-void RegisterTxoutProofRPCCommands(CRPCTable&);
-void RegisterNetRPCCommands(CRPCTable &tableRPC);
-void RegisterMiscRPCCommands(CRPCTable &tableRPC);
void RegisterMiningRPCCommands(CRPCTable &tableRPC);
+void RegisterNodeRPCCommands(CRPCTable&);
+void RegisterNetRPCCommands(CRPCTable&);
+void RegisterOutputScriptRPCCommands(CRPCTable&);
void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC);
+void RegisterSignMessageRPCCommands(CRPCTable&);
void RegisterSignerRPCCommands(CRPCTable &tableRPC);
+void RegisterTxoutProofRPCCommands(CRPCTable&);
static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
{
RegisterBlockchainRPCCommands(t);
+ RegisterFeeRPCCommands(t);
RegisterMempoolRPCCommands(t);
- RegisterTxoutProofRPCCommands(t);
- RegisterNetRPCCommands(t);
- RegisterMiscRPCCommands(t);
RegisterMiningRPCCommands(t);
+ RegisterNodeRPCCommands(t);
+ RegisterNetRPCCommands(t);
+ RegisterOutputScriptRPCCommands(t);
RegisterRawTransactionRPCCommands(t);
+ RegisterSignMessageRPCCommands(t);
#ifdef ENABLE_EXTERNAL_SIGNER
RegisterSignerRPCCommands(t);
#endif // ENABLE_EXTERNAL_SIGNER
+ RegisterTxoutProofRPCCommands(t);
}
#endif // BITCOIN_RPC_REGISTER_H
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index d0e068de19..304c265b31 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -146,7 +146,7 @@ std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
if (!rec.isObject()) {
throw std::runtime_error("Batch member must be an object");
}
- size_t id = rec["id"].get_int();
+ size_t id = rec["id"].getInt<int>();
if (id >= num) {
throw std::runtime_error("Batch member id is larger than batch size");
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 3817a4a45c..af00acdc9f 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -176,7 +176,7 @@ static RPCHelpMan stop()
// this reply will get back to the client.
StartShutdown();
if (jsonRequest.params[0].isNum()) {
- UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
+ UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
}
return RESULT;
},
diff --git a/src/rpc/signmessage.cpp b/src/rpc/signmessage.cpp
new file mode 100644
index 0000000000..8c752ba1fd
--- /dev/null
+++ b/src/rpc/signmessage.cpp
@@ -0,0 +1,113 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <key_io.h>
+#include <rpc/protocol.h>
+#include <rpc/request.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <univalue.h>
+#include <util/message.h>
+
+#include <string>
+
+static RPCHelpMan verifymessage()
+{
+ return RPCHelpMan{"verifymessage",
+ "Verify a signed message.",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."},
+ {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
+ },
+ RPCResult{
+ RPCResult::Type::BOOL, "", "If the signature is verified or not."
+ },
+ RPCExamples{
+ "\nUnlock the wallet for 30 seconds\n"
+ + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
+ "\nCreate the signature\n"
+ + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
+ "\nVerify the signature\n"
+ + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ std::string strAddress = request.params[0].get_str();
+ std::string strSign = request.params[1].get_str();
+ std::string strMessage = request.params[2].get_str();
+
+ switch (MessageVerify(strAddress, strSign, strMessage)) {
+ case MessageVerificationResult::ERR_INVALID_ADDRESS:
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
+ throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
+ case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
+ throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding");
+ case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
+ case MessageVerificationResult::ERR_NOT_SIGNED:
+ return false;
+ case MessageVerificationResult::OK:
+ return true;
+ }
+
+ return false;
+ },
+ };
+}
+
+static RPCHelpMan signmessagewithprivkey()
+{
+ return RPCHelpMan{"signmessagewithprivkey",
+ "\nSign a message with the private key of an address\n",
+ {
+ {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key to sign the message with."},
+ {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
+ },
+ RPCResult{
+ RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
+ },
+ RPCExamples{
+ "\nCreate the signature\n"
+ + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
+ "\nVerify the signature\n"
+ + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ std::string strPrivkey = request.params[0].get_str();
+ std::string strMessage = request.params[1].get_str();
+
+ CKey key = DecodeSecret(strPrivkey);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
+ }
+
+ std::string signature;
+
+ if (!MessageSign(key, strMessage, signature)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
+ }
+
+ return signature;
+ },
+ };
+}
+
+void RegisterSignMessageRPCCommands(CRPCTable& t)
+{
+ static const CRPCCommand commands[]{
+ {"util", &verifymessage},
+ {"util", &signmessagewithprivkey},
+ };
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
+}
diff --git a/src/rpc/txoutproof.cpp b/src/rpc/txoutproof.cpp
index d16820baeb..dcf6c6bee1 100644
--- a/src/rpc/txoutproof.cpp
+++ b/src/rpc/txoutproof.cpp
@@ -87,7 +87,7 @@ static RPCHelpMan gettxoutproof()
LOCK(cs_main);
if (pblockindex == nullptr) {
- const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), Params().GetConsensus(), hashBlock);
+ const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), chainman.GetConsensus(), hashBlock);
if (!tx || hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
}
@@ -98,7 +98,7 @@ static RPCHelpMan gettxoutproof()
}
CBlock block;
- if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
+ if (!ReadBlockFromDisk(block, pblockindex, chainman.GetConsensus())) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ef3e58494e..7517f64ea1 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -12,6 +12,7 @@
#include <util/check.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/system.h>
#include <util/translation.h>
#include <tuple>
@@ -267,7 +268,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
class DescribeAddressVisitor
{
public:
- explicit DescribeAddressVisitor() {}
+ explicit DescribeAddressVisitor() = default;
UniValue operator()(const CNoDestination& dest) const
{
@@ -337,7 +338,7 @@ UniValue DescribeAddress(const CTxDestination& dest)
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
{
- const int target{value.get_int()};
+ const int target{value.getInt<int>()};
const unsigned int unsigned_target{static_cast<unsigned int>(target)};
if (target < 1 || unsigned_target > max_target) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
@@ -581,7 +582,9 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
throw std::runtime_error(ToString());
}
const UniValue ret = m_fun(*this, request);
- CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); }));
+ if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
+ CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [&ret](const RPCResult& res) { return res.MatchesType(ret); }));
+ }
return ret;
}
@@ -1023,11 +1026,11 @@ std::string RPCArg::ToString(const bool oneline) const
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
{
if (value.isNum()) {
- return {0, value.get_int64()};
+ return {0, value.getInt<int64_t>()};
}
if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
- int64_t low = value[0].get_int64();
- int64_t high = value[1].get_int64();
+ int64_t low = value[0].getInt<int64_t>();
+ int64_t high = value[1].getInt<int64_t>();
if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
return {low, high};
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e16fed75bc..e883dc008e 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
-#include <node/coinstats.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <protocol.h>
@@ -22,6 +21,14 @@
#include <variant>
#include <vector>
+static constexpr bool DEFAULT_RPC_DOC_CHECK{
+#ifdef RPC_DOC_CHECK
+ true
+#else
+ false
+#endif
+};
+
/**
* String used to describe UNIX epoch time in documentation, factored out to a
* constant for consistency.
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 197d009f7c..3df1d48b3c 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -4,17 +4,15 @@
#include <scheduler.h>
-#include <random.h>
+#include <sync.h>
#include <util/syscall_sandbox.h>
#include <util/time.h>
-#include <assert.h>
+#include <cassert>
#include <functional>
#include <utility>
-CScheduler::CScheduler()
-{
-}
+CScheduler::CScheduler() = default;
CScheduler::~CScheduler()
{
@@ -43,7 +41,7 @@ void CScheduler::serviceQueue()
// the time of the first item on the queue:
while (!shouldStop() && !taskQueue.empty()) {
- std::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first;
+ std::chrono::steady_clock::time_point timeToWaitFor = taskQueue.begin()->first;
if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) {
break; // Exit loop after timeout, it means we reached the time of the event
}
@@ -72,7 +70,7 @@ void CScheduler::serviceQueue()
newTaskScheduled.notify_one();
}
-void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t)
+void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::time_point t)
{
{
LOCK(newTaskMutex);
@@ -89,7 +87,7 @@ void CScheduler::MockForward(std::chrono::seconds delta_seconds)
LOCK(newTaskMutex);
// use temp_queue to maintain updated schedule
- std::multimap<std::chrono::system_clock::time_point, Function> temp_queue;
+ std::multimap<std::chrono::steady_clock::time_point, Function> temp_queue;
for (const auto& element : taskQueue) {
temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second);
@@ -114,8 +112,8 @@ void CScheduler::scheduleEvery(CScheduler::Function f, std::chrono::milliseconds
scheduleFromNow([this, f, delta] { Repeat(*this, f, delta); }, delta);
}
-size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point& first,
- std::chrono::system_clock::time_point& last) const
+size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point& first,
+ std::chrono::steady_clock::time_point& last) const
{
LOCK(newTaskMutex);
size_t result = taskQueue.size();
@@ -143,7 +141,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue()
if (m_are_callbacks_running) return;
if (m_callbacks_pending.empty()) return;
}
- m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this), std::chrono::system_clock::now());
+ m_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::steady_clock::now());
}
void SingleThreadedSchedulerClient::ProcessQueue()
@@ -179,8 +177,6 @@ void SingleThreadedSchedulerClient::ProcessQueue()
void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func)
{
- assert(m_pscheduler);
-
{
LOCK(m_callbacks_mutex);
m_callbacks_pending.emplace_back(std::move(func));
@@ -190,7 +186,7 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func
void SingleThreadedSchedulerClient::EmptyQueue()
{
- assert(!m_pscheduler->AreThreadsServicingQueue());
+ assert(!m_scheduler.AreThreadsServicingQueue());
bool should_continue = true;
while (should_continue) {
ProcessQueue();
diff --git a/src/scheduler.h b/src/scheduler.h
index bb0abfbf7a..749e5442b0 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -5,13 +5,18 @@
#ifndef BITCOIN_SCHEDULER_H
#define BITCOIN_SCHEDULER_H
+#include <attributes.h>
+#include <sync.h>
+#include <threadsafety.h>
+
+#include <chrono>
#include <condition_variable>
+#include <cstddef>
#include <functional>
#include <list>
#include <map>
#include <thread>
-
-#include <sync.h>
+#include <utility>
/**
* Simple class for background tasks that should be run
@@ -41,12 +46,12 @@ public:
typedef std::function<void()> Function;
/** Call func at/after time t */
- void schedule(Function f, std::chrono::system_clock::time_point t);
+ void schedule(Function f, std::chrono::steady_clock::time_point t) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
/** Call f once after the delta has passed */
- void scheduleFromNow(Function f, std::chrono::milliseconds delta)
+ void scheduleFromNow(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
{
- schedule(std::move(f), std::chrono::system_clock::now() + delta);
+ schedule(std::move(f), std::chrono::steady_clock::now() + delta);
}
/**
@@ -55,29 +60,29 @@ public:
* The timing is not exact: Every time f is finished, it is rescheduled to run again after delta. If you need more
* accurate scheduling, don't use this method.
*/
- void scheduleEvery(Function f, std::chrono::milliseconds delta);
+ void scheduleEvery(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
/**
* Mock the scheduler to fast forward in time.
* Iterates through items on taskQueue and reschedules them
* to be delta_seconds sooner.
*/
- void MockForward(std::chrono::seconds delta_seconds);
+ void MockForward(std::chrono::seconds delta_seconds) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
/**
* Services the queue 'forever'. Should be run in a thread.
*/
- void serviceQueue();
+ void serviceQueue() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
/** Tell any threads running serviceQueue to stop as soon as the current task is done */
- void stop()
+ void stop() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
{
WITH_LOCK(newTaskMutex, stopRequested = true);
newTaskScheduled.notify_all();
if (m_service_thread.joinable()) m_service_thread.join();
}
/** Tell any threads running serviceQueue to stop when there is no work left to be done */
- void StopWhenDrained()
+ void StopWhenDrained() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
{
WITH_LOCK(newTaskMutex, stopWhenEmpty = true);
newTaskScheduled.notify_all();
@@ -88,16 +93,17 @@ public:
* Returns number of tasks waiting to be serviced,
* and first and last task times
*/
- size_t getQueueInfo(std::chrono::system_clock::time_point& first,
- std::chrono::system_clock::time_point& last) const;
+ size_t getQueueInfo(std::chrono::steady_clock::time_point& first,
+ std::chrono::steady_clock::time_point& last) const
+ EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
/** Returns true if there are threads actively running in serviceQueue() */
- bool AreThreadsServicingQueue() const;
+ bool AreThreadsServicingQueue() const EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex);
private:
mutable Mutex newTaskMutex;
std::condition_variable newTaskScheduled;
- std::multimap<std::chrono::system_clock::time_point, Function> taskQueue GUARDED_BY(newTaskMutex);
+ std::multimap<std::chrono::steady_clock::time_point, Function> taskQueue GUARDED_BY(newTaskMutex);
int nThreadsServicingQueue GUARDED_BY(newTaskMutex){0};
bool stopRequested GUARDED_BY(newTaskMutex){false};
bool stopWhenEmpty GUARDED_BY(newTaskMutex){false};
@@ -117,17 +123,17 @@ private:
class SingleThreadedSchedulerClient
{
private:
- CScheduler* m_pscheduler;
+ CScheduler& m_scheduler;
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();
+ void MaybeScheduleProcessQueue() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
+ void ProcessQueue() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
public:
- explicit SingleThreadedSchedulerClient(CScheduler* pschedulerIn) : m_pscheduler(pschedulerIn) {}
+ explicit SingleThreadedSchedulerClient(CScheduler& scheduler LIFETIMEBOUND) : m_scheduler{scheduler} {}
/**
* Add a callback to be executed. Callbacks are executed serially
@@ -135,15 +141,15 @@ public:
* Practically, this means that callbacks can behave as if they are executed
* in order by a single thread.
*/
- void AddToProcessQueue(std::function<void()> func);
+ void AddToProcessQueue(std::function<void()> func) EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
/**
* Processes all remaining queue members on the calling thread, blocking until queue is empty
* Must be called after the CScheduler has no remaining processing threads!
*/
- void EmptyQueue();
+ void EmptyQueue() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
- size_t CallbacksPending();
+ size_t CallbacksPending() EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex);
};
#endif // BITCOIN_SCHEDULER_H
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index c4d13d7283..9f56301377 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1832,6 +1832,10 @@ uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script)
uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash)
{
+ assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
+ assert(control.size() <= TAPROOT_CONTROL_MAX_SIZE);
+ assert((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE == 0);
+
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
uint256 k = tapleaf_hash;
for (int i = 0; i < path_len; ++i) {
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index d77515f16c..2d569d674a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -18,16 +18,16 @@
typedef std::vector<unsigned char> valtype;
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* tx, unsigned int input_idx, const CAmount& amount, int hash_type)
- : txTo{tx}, nIn{input_idx}, nHashType{hash_type}, amount{amount}, checker{txTo, nIn, amount, MissingDataBehavior::FAIL},
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction& tx, unsigned int input_idx, const CAmount& amount, int hash_type)
+ : m_txto{tx}, nIn{input_idx}, nHashType{hash_type}, amount{amount}, checker{&m_txto, nIn, amount, MissingDataBehavior::FAIL},
m_txdata(nullptr)
{
}
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* tx, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type)
- : txTo{tx}, nIn{input_idx}, nHashType{hash_type}, amount{amount},
- checker{txdata ? MutableTransactionSignatureChecker{txTo, nIn, amount, *txdata, MissingDataBehavior::FAIL} :
- MutableTransactionSignatureChecker{txTo, nIn, amount, MissingDataBehavior::FAIL}},
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction& tx, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type)
+ : m_txto{tx}, nIn{input_idx}, nHashType{hash_type}, amount{amount},
+ checker{txdata ? MutableTransactionSignatureChecker{&m_txto, nIn, amount, *txdata, MissingDataBehavior::FAIL} :
+ MutableTransactionSignatureChecker{&m_txto, nIn, amount, MissingDataBehavior::FAIL}},
m_txdata(txdata)
{
}
@@ -50,7 +50,7 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
// BASE/WITNESS_V0 signatures don't support explicit SIGHASH_DEFAULT, use SIGHASH_ALL instead.
const int hashtype = nHashType == SIGHASH_DEFAULT ? SIGHASH_ALL : nHashType;
- uint256 hash = SignatureHash(scriptCode, *txTo, nIn, hashtype, amount, sigversion, m_txdata);
+ uint256 hash = SignatureHash(scriptCode, m_txto, nIn, hashtype, amount, sigversion, m_txdata);
if (!key.Sign(hash, vchSig))
return false;
vchSig.push_back((unsigned char)hashtype);
@@ -80,7 +80,7 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
execdata.m_tapleaf_hash = *leaf_hash;
}
uint256 hash;
- if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
+ if (!SignatureHashSchnorr(hash, execdata, m_txto, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
sig.resize(64);
// Use uint256{} as aux_rnd for now.
if (!key.SignSchnorr(hash, sig, merkle_root, {})) return false;
@@ -540,7 +540,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C
{
assert(nIn < txTo.vin.size());
- MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);
+ MutableTransactionSignatureCreator creator(txTo, nIn, amount, nHashType);
SignatureData sigdata;
bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
@@ -563,7 +563,7 @@ namespace {
class DummySignatureChecker final : public BaseSignatureChecker
{
public:
- DummySignatureChecker() {}
+ DummySignatureChecker() = default;
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, ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
@@ -679,7 +679,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
diff --git a/src/script/sign.h b/src/script/sign.h
index 7301826ba5..71203d08ec 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
+#include <attributes.h>
#include <coins.h>
#include <hash.h>
#include <pubkey.h>
@@ -34,8 +35,9 @@ public:
};
/** A signature creator for transactions. */
-class MutableTransactionSignatureCreator : public BaseSignatureCreator {
- const CMutableTransaction* txTo;
+class MutableTransactionSignatureCreator : public BaseSignatureCreator
+{
+ const CMutableTransaction& m_txto;
unsigned int nIn;
int nHashType;
CAmount amount;
@@ -43,8 +45,8 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
const PrecomputedTransactionData* m_txdata;
public:
- MutableTransactionSignatureCreator(const CMutableTransaction* tx, unsigned int input_idx, const CAmount& amount, int hash_type);
- MutableTransactionSignatureCreator(const CMutableTransaction* tx, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type);
+ MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, int hash_type);
+ MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
diff --git a/src/script/standard.h b/src/script/standard.h
index f0b143c52b..6a15ba4e3d 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
+#include <attributes.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <uint256.h>
diff --git a/src/shutdown.cpp b/src/shutdown.cpp
index 93f04e9021..fdf726b5f1 100644
--- a/src/shutdown.cpp
+++ b/src/shutdown.cpp
@@ -5,13 +5,15 @@
#include <shutdown.h>
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <logging.h>
#include <node/ui_interface.h>
#include <util/tokenpipe.h>
#include <warnings.h>
-#include <config/bitcoin-config.h>
-
#include <assert.h>
#include <atomic>
#ifdef WIN32
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index ea1a27c6f6..6907749c6d 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -282,9 +282,8 @@ LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, Lockin
{
}
-LockedPool::~LockedPool()
-{
-}
+LockedPool::~LockedPool() = default;
+
void* LockedPool::alloc(size_t size)
{
std::lock_guard<std::mutex> lock(mutex);
diff --git a/src/sync.h b/src/sync.h
index c69b58741b..a175926113 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -83,8 +83,6 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
-#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
-#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
/**
* Template mixin that adds -Wthread-safety locking annotations and lock order
@@ -129,7 +127,13 @@ public:
using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
/** Wrapped mutex: supports waiting but not recursive locking */
-typedef AnnotatedMixin<std::mutex> Mutex;
+using Mutex = AnnotatedMixin<std::mutex>;
+
+#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
+
+inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
+inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
+#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
/** Wrapper around std::unique_lock style lock for Mutex. */
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 0fa6b7784f..64cc924239 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -120,8 +120,9 @@ const std::vector<std::string> TEST5 = {
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL"
};
-void RunTest(const TestVector &test) {
- std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
+void RunTest(const TestVector& test)
+{
+ std::vector<std::byte> seed{ParseHex<std::byte>(test.strHexMaster)};
CExtKey key;
CExtPubKey pubkey;
key.SetSeed(seed);
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 9dfbd7ba7c..875241094d 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
// Do a simple ShortTxIDs RT
{
- CBlockHeaderAndShortTxIDs shortIDs(block, true);
+ CBlockHeaderAndShortTxIDs shortIDs{block};
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
@@ -122,7 +122,7 @@ public:
stream >> *this;
}
explicit TestHeaderAndShortIDs(const CBlock& block) :
- TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}
+ TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block}) {}
uint64_t GetShortID(const uint256& txhash) const {
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
@@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
// Test simple header round-trip with only coinbase
{
- CBlockHeaderAndShortTxIDs shortIDs(block, false);
+ CBlockHeaderAndShortTxIDs shortIDs{block};
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs;
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 82b9617384..ba1eacfc78 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -65,8 +65,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey)
{
- const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -83,7 +82,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
block.hashMerkleRoot = BlockMerkleRoot(block);
}
- while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
return block;
}
@@ -101,7 +100,7 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex,
CBlockHeader header = block->GetBlockHeader();
BlockValidationState state;
- if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, Params(), &pindex)) {
+ if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, &pindex)) {
return false;
}
}
@@ -178,7 +177,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
}
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
@@ -196,7 +195,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
uint256 chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
}
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
@@ -227,7 +226,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// Reorg back to chain A.
for (size_t i = 2; i < 4; i++) {
const auto& block = chainA[i];
- BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 8eb4dbc592..178757e7b4 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
}
unsigned int pos = 0;
- /*int block_height =*/ test[pos++].get_int();
+ /*int block_height =*/ test[pos++].getInt<int>();
uint256 block_hash;
BOOST_CHECK(ParseHashStr(test[pos++].get_str(), block_hash));
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 0d95bd7670..875522d744 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -42,7 +42,7 @@ struct FakeCheck {
{
return true;
}
- void swap(FakeCheck& x){};
+ void swap(FakeCheck& x) noexcept {};
};
struct FakeCheckCheckCompletion {
@@ -52,7 +52,7 @@ struct FakeCheckCheckCompletion {
n_calls.fetch_add(1, std::memory_order_relaxed);
return true;
}
- void swap(FakeCheckCheckCompletion& x){};
+ void swap(FakeCheckCheckCompletion& x) noexcept {};
};
struct FailingCheck {
@@ -63,7 +63,7 @@ struct FailingCheck {
{
return !fails;
}
- void swap(FailingCheck& x)
+ void swap(FailingCheck& x) noexcept
{
std::swap(fails, x.fails);
};
@@ -81,7 +81,10 @@ struct UniqueCheck {
results.insert(check_id);
return true;
}
- void swap(UniqueCheck& x) { std::swap(x.check_id, check_id); };
+ void swap(UniqueCheck& x) noexcept
+ {
+ std::swap(x.check_id, check_id);
+ };
};
@@ -92,7 +95,7 @@ struct MemoryCheck {
{
return true;
}
- MemoryCheck(){};
+ MemoryCheck() = default;
MemoryCheck(const MemoryCheck& x)
{
// We have to do this to make sure that destructor calls are paired
@@ -109,7 +112,10 @@ struct MemoryCheck {
{
fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed);
};
- void swap(MemoryCheck& x) { std::swap(b, x.b); };
+ void swap(MemoryCheck& x) noexcept
+ {
+ std::swap(b, x.b);
+ };
};
struct FrozenCleanupCheck {
@@ -123,7 +129,7 @@ struct FrozenCleanupCheck {
{
return true;
}
- FrozenCleanupCheck() {}
+ FrozenCleanupCheck() = default;
~FrozenCleanupCheck()
{
if (should_freeze) {
@@ -133,7 +139,10 @@ struct FrozenCleanupCheck {
cv.wait(l, []{ return nFrozen.load(std::memory_order_relaxed) == 0;});
}
}
- void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);};
+ void swap(FrozenCleanupCheck& x) noexcept
+ {
+ std::swap(should_freeze, x.should_freeze);
+ };
};
// Static Allocations
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 82e4e1c90f..b333a9f72d 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <attributes.h>
#include <clientversion.h>
#include <coins.h>
#include <script/standard.h>
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 5b73481bc1..50eb479035 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -13,8 +13,8 @@
#include <chrono>
-using node::CCoinsStats;
-using node::CoinStatsHashType;
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
@@ -33,7 +33,6 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
{
CoinStatsIndex coin_stats_index{1 << 20, true};
- CCoinsStats coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* block_index;
{
LOCK(cs_main);
@@ -41,7 +40,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
}
// CoinStatsIndex should not be found before it is started.
- BOOST_CHECK(!coin_stats_index.LookUpStats(block_index, coin_stats));
+ BOOST_CHECK(!coin_stats_index.LookUpStats(block_index));
// BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
// is started.
@@ -57,10 +56,10 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
LOCK(cs_main);
genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
- BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
+ BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index));
// Check that CoinStatsIndex updates with new blocks.
- coin_stats_index.LookUpStats(block_index, coin_stats);
+ BOOST_CHECK(coin_stats_index.LookUpStats(block_index));
const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
std::vector<CMutableTransaction> noTxns;
@@ -69,13 +68,12 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// Let the CoinStatsIndex to catch up again.
BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain());
- CCoinsStats new_coin_stats{CoinStatsHashType::MUHASH};
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
new_block_index = m_node.chainman->ActiveChain().Tip();
}
- coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
+ BOOST_CHECK(coin_stats_index.LookUpStats(new_block_index));
BOOST_CHECK(block_index != new_block_index);
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index fc89fe1450..ab4c587c46 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -321,7 +321,7 @@ struct StringContentsSerializer {
// Used to make two serialized objects the same while letting them have different lengths
// This is a terrible idea
std::string str;
- StringContentsSerializer() {}
+ StringContentsSerializer() = default;
explicit StringContentsSerializer(const std::string& inp) : str(inp) {}
StringContentsSerializer& operator+=(const std::string& s) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 7a917649a8..3b4a6f2637 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -32,8 +32,6 @@ static CService ip(uint32_t i)
return CService(CNetAddr(s), Params().GetDefaultPort());
}
-void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds);
-
BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// Test eviction of an outbound peer whose chain never advances
@@ -46,11 +44,10 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
// Disable inactivity checks for this test to avoid interference
static_cast<ConnmanTestMsg*>(connman.get())->SetPeerConnectTimeout(99999s);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
// Mock an outbound peer
@@ -136,9 +133,8 @@ static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerM
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
NodeId id{0};
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
@@ -149,7 +145,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
const auto time_init{GetTime<std::chrono::seconds>()};
SetMockTime(time_init);
- const auto time_later{time_init + 3 * std::chrono::seconds{chainparams.GetConsensus().nPowTargetSpacing} + 1s};
+ const auto time_later{time_init + 3 * std::chrono::seconds{m_node.chainman->GetConsensus().nPowTargetSpacing} + 1s};
connman->Init(options);
std::vector<CNode *> vNodes;
@@ -195,7 +191,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// Update the last announced block time for the last
// peer, and check that the next newest node gets evicted.
- UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
+ peerLogic->UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay - 1; ++i) {
@@ -214,9 +210,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
{
NodeId id{0};
- const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS};
@@ -276,10 +271,9 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
- const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, false);
CNetAddr tor_netaddr;
@@ -392,10 +386,9 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
- const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
- auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
+ auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, false);
banman->ClearBanned();
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 30add9c16d..63c86a896d 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -312,7 +312,7 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string&
std::vector<CTxOut> utxos(1);
PrecomputedTransactionData txdata;
txdata.Init(spend, std::move(utxos), /*force=*/true);
- MutableTransactionSignatureCreator creator(&spend, 0, CAmount{0}, &txdata, SIGHASH_DEFAULT);
+ MutableTransactionSignatureCreator creator{spend, 0, CAmount{0}, &txdata, SIGHASH_DEFAULT};
SignatureData sigdata;
BOOST_CHECK_MESSAGE(ProduceSignature(Merge(keys_priv, script_provider), creator, spks[n], sigdata), prv);
}
diff --git a/src/test/fuzz/checkqueue.cpp b/src/test/fuzz/checkqueue.cpp
index 0b16f0f0d5..7d107995aa 100644
--- a/src/test/fuzz/checkqueue.cpp
+++ b/src/test/fuzz/checkqueue.cpp
@@ -26,7 +26,7 @@ struct DumbCheck {
return result;
}
- void swap(DumbCheck& x)
+ void swap(DumbCheck& x) noexcept
{
}
};
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 360dc00307..6c96702f1e 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -10,7 +10,6 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <key.h>
-#include <node/coinstats.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <pubkey.h>
@@ -26,10 +25,6 @@
#include <string>
#include <vector>
-using node::CCoinsStats;
-using node::CoinStatsHashType;
-using node::GetUTXOStats;
-
namespace {
const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
@@ -270,16 +265,6 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
(void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
},
[&] {
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
- bool expected_code_path = false;
- try {
- (void)GetUTXOStats(&coins_view_cache, g_setup->m_node.chainman->m_blockman, stats);
- } catch (const std::logic_error&) {
- expected_code_path = true;
- }
- assert(expected_code_path);
- },
- [&] {
(void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
});
}
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 59adec075e..24ae34bd9e 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -194,7 +194,7 @@ int main(int argc, char** argv)
return 0;
}
std::signal(SIGABRT, signal_handler);
- int64_t start_time = GetTimeSeconds();
+ const auto start_time{Now<SteadySeconds>()};
int tested = 0;
for (int i = 1; i < argc; ++i) {
fs::path input_path(*(argv + i));
@@ -215,8 +215,8 @@ int main(int argc, char** argv)
buffer.clear();
}
}
- int64_t end_time = GetTimeSeconds();
- std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << (end_time - start_time) << "s." << std::endl;
+ const auto end_time{Now<SteadySeconds>()};
+ std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << count_seconds(end_time - start_time) << "s." << std::endl;
#endif
return 0;
}
diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp
index cc1bc1c8cf..e637975b48 100644
--- a/src/test/fuzz/hex.cpp
+++ b/src/test/fuzz/hex.cpp
@@ -25,6 +25,8 @@ FUZZ_TARGET_INIT(hex, initialize_hex)
{
const std::string random_hex_string(buffer.begin(), buffer.end());
const std::vector<unsigned char> data = ParseHex(random_hex_string);
+ const std::vector<std::byte> bytes{ParseHex<std::byte>(random_hex_string)};
+ assert(AsBytes(Span{data}) == Span{bytes});
const std::string hex_data = HexStr(data);
if (IsHex(random_hex_string)) {
assert(ToLower(random_hex_string) == hex_data);
diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp
index a48bab1ee2..e2d65a4796 100644
--- a/src/test/fuzz/prevector.cpp
+++ b/src/test/fuzz/prevector.cpp
@@ -161,7 +161,7 @@ public:
pre_vector.shrink_to_fit();
}
- void swap()
+ void swap() noexcept
{
real_vector.swap(real_vector_alt);
pre_vector.swap(pre_vector_alt);
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 03a84b697d..e4e83c3f32 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -128,6 +128,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getmempoolancestors",
"getmempooldescendants",
"getmempoolentry",
+ "gettxspendingprevout",
"getmempoolinfo",
"getmininginfo",
"getnettotals",
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index a18a0de47e..35d7246ed8 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -149,7 +149,7 @@ void Test(const std::string& str)
CMutableTransaction tx = TxFromHex(test["tx"].get_str());
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
- size_t idx = test["index"].get_int64();
+ size_t idx = test["index"].getInt<int64_t>();
if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
bool final = test.exists("final") && test["final"].get_bool();
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index 1446eafe92..3ddb30d870 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -113,7 +113,7 @@ FUZZ_TARGET_INIT(script_sign, initialize_script_sign)
}
if (n_in < script_tx_to.vin.size()) {
(void)SignSignature(provider, ConsumeScript(fuzzed_data_provider), script_tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>());
- MutableTransactionSignatureCreator signature_creator{&tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>()};
+ MutableTransactionSignatureCreator signature_creator{tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>()};
std::vector<unsigned char> vch_sig;
CKeyID address;
if (fuzzed_data_provider.ConsumeBool()) {
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index f6c591aca4..a585680de1 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -49,7 +49,7 @@ public:
return m_fuzzed_data_provider.ConsumeBool();
}
- virtual ~FuzzedSignatureChecker() {}
+ virtual ~FuzzedSignatureChecker() = default;
};
} // namespace
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index e6064d19b6..94399faf04 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -224,7 +224,12 @@ FUZZ_TARGET(string)
int64_t amount_out;
(void)ParseFixedPoint(random_string_1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1024), &amount_out);
}
- (void)SplitString(random_string_1, fuzzed_data_provider.ConsumeIntegral<char>());
+ {
+ const auto single_split{SplitString(random_string_1, fuzzed_data_provider.ConsumeIntegral<char>())};
+ assert(single_split.size() >= 1);
+ const auto any_split{SplitString(random_string_1, random_string_2)};
+ assert(any_split.size() >= 1);
+ }
{
(void)Untranslated(random_string_1);
const bilingual_str bs1{random_string_1, random_string_2};
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index f686f4fd86..771d7a11cb 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
- auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), chainstate.m_params, options};
+ auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), options};
auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
Assert(block_template->block.vtx.size() >= 1);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 580105e442..3fc6fa1cd5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -6,7 +6,6 @@
#define BITCOIN_TEST_FUZZ_UTIL_H
#include <arith_uint256.h>
-#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
#include <compat.h>
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index e513f1883c..33496a457e 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -58,7 +58,7 @@ FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
if (fuzzed_data_provider.ConsumeBool()) {
for (const auto& block : *g_chain) {
BlockValidationState dummy;
- bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())};
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy)};
Assert(processed);
const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
Assert(index);
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index c877105fe7..70dd137e22 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -13,7 +13,6 @@
#include <utility>
#include <vector>
-#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)
@@ -21,8 +20,9 @@ BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)
void ResetArgs(ArgsManager& local_args, const std::string& strArg)
{
std::vector<std::string> vecArg;
- if (strArg.size())
- boost::split(vecArg, strArg, IsSpace, boost::token_compress_on);
+ if (strArg.size()) {
+ vecArg = SplitString(strArg, ' ');
+ }
// Insert dummy executable name:
vecArg.insert(vecArg.begin(), "testbitcoin");
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index b06157e99f..e70b8b3dfd 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse)
continue;
}
std::string exp_base58string = test[0].get_str();
- std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
+ const std::vector<std::byte> exp_payload{ParseHex<std::byte>(test[1].get_str())};
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
SelectParams(find_value(metadata, "chain").get_str());
diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp
index cbfb6c67c3..3f6a605945 100644
--- a/src/test/logging_tests.cpp
+++ b/src/test/logging_tests.cpp
@@ -5,13 +5,55 @@
#include <logging.h>
#include <logging/timer.h>
#include <test/util/setup_common.h>
+#include <util/string.h>
#include <chrono>
+#include <fstream>
+#include <iostream>
+#include <utility>
+#include <vector>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
+struct LogSetup : public BasicTestingSetup {
+ fs::path prev_log_path;
+ fs::path tmp_log_path;
+ bool prev_reopen_file;
+ bool prev_print_to_file;
+ bool prev_log_timestamps;
+ bool prev_log_threadnames;
+ bool prev_log_sourcelocations;
+
+ LogSetup() : prev_log_path{LogInstance().m_file_path},
+ tmp_log_path{m_args.GetDataDirBase() / "tmp_debug.log"},
+ prev_reopen_file{LogInstance().m_reopen_file},
+ prev_print_to_file{LogInstance().m_print_to_file},
+ prev_log_timestamps{LogInstance().m_log_timestamps},
+ prev_log_threadnames{LogInstance().m_log_threadnames},
+ prev_log_sourcelocations{LogInstance().m_log_sourcelocations}
+ {
+ LogInstance().m_file_path = tmp_log_path;
+ LogInstance().m_reopen_file = true;
+ LogInstance().m_print_to_file = true;
+ LogInstance().m_log_timestamps = false;
+ LogInstance().m_log_threadnames = false;
+ LogInstance().m_log_sourcelocations = true;
+ }
+
+ ~LogSetup()
+ {
+ LogInstance().m_file_path = prev_log_path;
+ LogPrintf("Sentinel log to reopen log file\n");
+ LogInstance().m_print_to_file = prev_print_to_file;
+ LogInstance().m_reopen_file = prev_reopen_file;
+ LogInstance().m_log_timestamps = prev_log_timestamps;
+ LogInstance().m_log_threadnames = prev_log_threadnames;
+ LogInstance().m_log_sourcelocations = prev_log_sourcelocations;
+ }
+};
+
BOOST_AUTO_TEST_CASE(logging_timer)
{
SetMockTime(1);
@@ -30,4 +72,82 @@ BOOST_AUTO_TEST_CASE(logging_timer)
BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
}
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintf_, LogSetup)
+{
+ LogPrintf_("fn1", "src1", 1, BCLog::LogFlags::NET, BCLog::Level::Debug, "foo1: %s", "bar1\n");
+ LogPrintf_("fn2", "src2", 2, BCLog::LogFlags::NET, BCLog::Level::None, "foo2: %s", "bar2\n");
+ LogPrintf_("fn3", "src3", 3, BCLog::LogFlags::NONE, BCLog::Level::Debug, "foo3: %s", "bar3\n");
+ LogPrintf_("fn4", "src4", 4, BCLog::LogFlags::NONE, BCLog::Level::None, "foo4: %s", "bar4\n");
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ std::vector<std::string> expected = {
+ "[src1:1] [fn1] [net:debug] foo1: bar1",
+ "[src2:2] [fn2] [net] foo2: bar2",
+ "[src3:3] [fn3] [debug] foo3: bar3",
+ "[src4:4] [fn4] foo4: bar4",
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup)
+{
+ // Prevent tests from failing when the line number of the following log calls changes.
+ LogInstance().m_log_sourcelocations = false;
+
+ LogPrintf("foo5: %s\n", "bar5");
+ LogPrint(BCLog::NET, "foo6: %s\n", "bar6");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "foo7: %s\n", "bar7");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Info, "foo8: %s\n", "bar8");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "foo9: %s\n", "bar9");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "foo10: %s\n", "bar10");
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ std::vector<std::string> expected = {
+ "foo5: bar5",
+ "[net] foo6: bar6",
+ "[net:debug] foo7: bar7",
+ "[net:info] foo8: bar8",
+ "[net:warning] foo9: bar9",
+ "[net:error] foo10: bar10"};
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
+BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)
+{
+ // Prevent tests from failing when the line number of the following log calls changes.
+ LogInstance().m_log_sourcelocations = false;
+ LogInstance().EnableCategory(BCLog::LogFlags::ALL);
+ const auto concated_categery_names = LogInstance().LogCategoriesString();
+ std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names;
+ const auto category_names = SplitString(concated_categery_names, ',');
+ for (const auto& category_name : category_names) {
+ BCLog::LogFlags category = BCLog::NONE;
+ const auto trimmed_category_name = TrimString(category_name);
+ BOOST_TEST(GetLogCategory(category, trimmed_category_name));
+ expected_category_names.emplace_back(category, trimmed_category_name);
+ }
+
+ std::vector<std::string> expected;
+ for (const auto& [category, name] : expected_category_names) {
+ LogPrint(category, "foo: %s\n", "bar");
+ std::string expected_log = "[";
+ expected_log += name;
+ expected_log += "] foo: bar";
+ expected.push_back(expected_log);
+ }
+
+ std::ifstream file{tmp_log_path};
+ std::vector<std::string> log_lines;
+ for (std::string log; std::getline(file, log);) {
+ log_lines.push_back(log);
+ }
+ BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index d6f24210eb..89424a0cd2 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -747,6 +747,15 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
pool.GetTransactionAncestry(ty6->GetHash(), ancestors, descendants);
BOOST_CHECK_EQUAL(ancestors, 9ULL);
BOOST_CHECK_EQUAL(descendants, 6ULL);
+}
+
+BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)
+{
+ size_t ancestors, descendants;
+
+ CTxMemPool pool;
+ LOCK2(::cs_main, pool.cs);
+ TestMemPoolEntryHelper entry;
/* Ancestors represented more than once ("diamond") */
//
@@ -759,7 +768,6 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
tc = make_tx(/*output_values=*/{2 * COIN}, /*inputs=*/{tb}, /*input_indices=*/{1});
td = make_tx(/*output_values=*/{6 * COIN}, /*inputs=*/{tb, tc}, /*input_indices=*/{0, 0});
- pool.clear();
pool.addUnchecked(entry.Fee(10000LL).FromTx(ta));
pool.addUnchecked(entry.Fee(10000LL).FromTx(tb));
pool.addUnchecked(entry.Fee(10000LL).FromTx(tc));
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index f6c1d1efad..439ad174b3 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -10,6 +10,7 @@
#include <node/miner.h>
#include <policy/policy.h>
#include <script/standard.h>
+#include <timedata.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
@@ -30,6 +31,8 @@ using node::CBlockTemplate;
namespace miner_tests {
struct MinerTestingSetup : public TestingSetup {
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
+ void TestBasicMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
+ void TestPrioritisedMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
bool TestSequenceLocks(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
{
CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
@@ -49,7 +52,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
+ return BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool, options};
}
constexpr static struct {
@@ -191,60 +194,17 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
-// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
-BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
+void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst, int baseheight)
{
- // Note that by default, these tests run with size accounting enabled.
- const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
- const CChainParams& chainparams = *chainParams;
- CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
- std::unique_ptr<CBlockTemplate> pblocktemplate;
- CMutableTransaction tx;
- CScript script;
uint256 hash;
+ CMutableTransaction tx;
TestMemPoolEntryHelper entry;
entry.nFee = 11;
entry.nHeight = 11;
- fCheckpointsEnabled = false;
-
- // Simple block creation, nothing special yet:
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
-
- // We can't make transactions until we have inputs
- // Therefore, load 110 blocks :)
- static_assert(std::size(BLOCKINFO) == 110, "Should have 110 blocks to import");
- int baseheight = 0;
- std::vector<CTransactionRef> txFirst;
- for (const auto& bi : BLOCKINFO) {
- CBlock *pblock = &pblocktemplate->block; // pointer for convenience
- {
- LOCK(cs_main);
- pblock->nVersion = VERSIONBITS_TOP_BITS;
- pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
- CMutableTransaction txCoinbase(*pblock->vtx[0]);
- txCoinbase.nVersion = 1;
- txCoinbase.vin[0].scriptSig = CScript{} << (m_node.chainman->ActiveChain().Height() + 1) << bi.extranonce;
- txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
- txCoinbase.vout[0].scriptPubKey = CScript();
- pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
- if (txFirst.size() == 0)
- baseheight = m_node.chainman->ActiveChain().Height();
- if (txFirst.size() < 4)
- txFirst.push_back(pblock->vtx[0]);
- pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
- pblock->nNonce = bi.nonce;
- }
- std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr));
- pblock->hashPrevBlock = pblock->GetHash();
- }
-
- LOCK(cs_main);
- LOCK(m_node.mempool->cs);
-
// Just to make sure we can still make simple blocks
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ BOOST_CHECK(pblocktemplate);
const CAmount BLOCKSUBSIDY = 50*COIN;
const CAmount LOWFEE = CENT;
@@ -386,7 +346,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
- script = CScript() << OP_0;
+ CScript script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
@@ -446,16 +406,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
- for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
-
+ const int SEQUENCE_LOCK_TIME = 512; // Sequence locks pass 512 seconds later
+ for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i)
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
{
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
}
- for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
+ CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
+ ancestor->nTime -= SEQUENCE_LOCK_TIME; // undo tricked MTP
+ }
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
@@ -500,14 +462,140 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// but relative locked txs will if inconsistently added to mempool.
// For now these will still generate a valid template until BIP68 soft fork
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
- // However if we advance height by 1 and time by 512, all of them should be mined
- for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ // However if we advance height by 1 and time by SEQUENCE_LOCK_TIME, all of them should be mined
+ for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
+ CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
+ ancestor->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
+ }
m_node.chainman->ActiveChain().Tip()->nHeight++;
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
+}
+
+void MinerTestingSetup::TestPrioritisedMining(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
+{
+ TestMemPoolEntryHelper entry;
+
+ // Test that a tx below min fee but prioritised is included
+ CMutableTransaction tx;
+ tx.vin.resize(1);
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vout.resize(1);
+ tx.vout[0].nValue = 5000000000LL; // 0 fee
+ uint256 hashFreePrioritisedTx = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(0).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->PrioritiseTransaction(hashFreePrioritisedTx, 5 * COIN);
+
+ tx.vin[0].prevout.hash = txFirst[1]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vout[0].nValue = 5000000000LL - 1000;
+ // This tx has a low fee: 1000 satoshis
+ uint256 hashParentTx = tx.GetHash(); // save this txid for later use
+ m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+
+ // This tx has a medium fee: 10000 satoshis
+ tx.vin[0].prevout.hash = txFirst[2]->GetHash();
+ tx.vout[0].nValue = 5000000000LL - 10000;
+ uint256 hashMediumFeeTx = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->PrioritiseTransaction(hashMediumFeeTx, -5 * COIN);
+
+ // This tx also has a low fee, but is prioritised
+ tx.vin[0].prevout.hash = hashParentTx;
+ tx.vout[0].nValue = 5000000000LL - 1000 - 1000; // 1000 satoshi fee
+ uint256 hashPrioritsedChild = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->PrioritiseTransaction(hashPrioritsedChild, 2 * COIN);
+
+ // Test that transaction selection properly updates ancestor fee calculations as prioritised
+ // parents get included in a block. Create a transaction with two prioritised ancestors, each
+ // included by itself: FreeParent <- FreeChild <- FreeGrandchild.
+ // When FreeParent is added, a modified entry will be created for FreeChild + FreeGrandchild
+ // FreeParent's prioritisation should not be included in that entry.
+ // When FreeChild is included, FreeChild's prioritisation should also not be included.
+ tx.vin[0].prevout.hash = txFirst[3]->GetHash();
+ tx.vout[0].nValue = 5000000000LL; // 0 fee
+ uint256 hashFreeParent = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->PrioritiseTransaction(hashFreeParent, 10 * COIN);
+
+ tx.vin[0].prevout.hash = hashFreeParent;
+ tx.vout[0].nValue = 5000000000LL; // 0 fee
+ uint256 hashFreeChild = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->PrioritiseTransaction(hashFreeChild, 1 * COIN);
+
+ tx.vin[0].prevout.hash = hashFreeChild;
+ tx.vout[0].nValue = 5000000000LL; // 0 fee
+ uint256 hashFreeGrandchild = tx.GetHash();
+ m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(false).FromTx(tx));
+
+ auto pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
+ BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashFreeParent);
+ BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashFreePrioritisedTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashParentTx);
+ BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashPrioritsedChild);
+ BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashFreeChild);
+ for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
+ // The FreeParent and FreeChild's prioritisations should not impact the child.
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeGrandchild);
+ // De-prioritised transaction should not be included.
+ BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashMediumFeeTx);
+ }
+}
+
+// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
+BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
+{
+ // Note that by default, these tests run with size accounting enabled.
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
+ const CChainParams& chainparams = *chainParams;
+ CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
+ std::unique_ptr<CBlockTemplate> pblocktemplate;
+
+ fCheckpointsEnabled = false;
+
+ // Simple block creation, nothing special yet:
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+
+ // We can't make transactions until we have inputs
+ // Therefore, load 110 blocks :)
+ static_assert(std::size(BLOCKINFO) == 110, "Should have 110 blocks to import");
+ int baseheight = 0;
+ std::vector<CTransactionRef> txFirst;
+ for (const auto& bi : BLOCKINFO) {
+ CBlock *pblock = &pblocktemplate->block; // pointer for convenience
+ {
+ LOCK(cs_main);
+ pblock->nVersion = VERSIONBITS_TOP_BITS;
+ pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
+ CMutableTransaction txCoinbase(*pblock->vtx[0]);
+ txCoinbase.nVersion = 1;
+ txCoinbase.vin[0].scriptSig = CScript{} << (m_node.chainman->ActiveChain().Height() + 1) << bi.extranonce;
+ txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
+ txCoinbase.vout[0].scriptPubKey = CScript();
+ pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
+ if (txFirst.size() == 0)
+ baseheight = m_node.chainman->ActiveChain().Height();
+ if (txFirst.size() < 4)
+ txFirst.push_back(pblock->vtx[0]);
+ pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
+ pblock->nNonce = bi.nonce;
+ }
+ std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr));
+ pblock->hashPrevBlock = pblock->GetHash();
+ }
+
+ LOCK(cs_main);
+ LOCK(m_node.mempool->cs);
+
+ TestBasicMining(chainparams, scriptPubKey, txFirst, baseheight);
m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
@@ -515,6 +603,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
TestPackageSelection(chainparams, scriptPubKey, txFirst);
+ m_node.chainman->ActiveChain().Tip()->nHeight--;
+ SetMockTime(0);
+ m_node.mempool->clear();
+
+ TestPrioritisedMining(chainparams, scriptPubKey, txFirst);
+
fCheckpointsEnabled = true;
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 89814748fe..3977a3d548 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -165,7 +165,8 @@ public:
test();
}
- void swap() {
+ void swap() noexcept
+ {
real_vector.swap(real_vector_alt);
pre_vector.swap(pre_vector_alt);
test();
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 978a7bee4d..9b2760fd1c 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -26,11 +26,21 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
FastRandomContext ctx2(true);
for (int i = 10; i > 0; --i) {
- BOOST_CHECK_EQUAL(GetRand(std::numeric_limits<uint64_t>::max()), uint64_t{10393729187455219830U});
- BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits<int>::max()), int{769702006});
+ BOOST_CHECK_EQUAL(GetRand<uint64_t>(), uint64_t{10393729187455219830U});
+ BOOST_CHECK_EQUAL(GetRand<int>(), int{769702006});
BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654);
BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374);
}
+ {
+ constexpr SteadySeconds time_point{1s};
+ FastRandomContext ctx{true};
+ BOOST_CHECK_EQUAL(7, ctx.rand_uniform_delay(time_point, 9s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(-6, ctx.rand_uniform_delay(time_point, -9s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(1, ctx.rand_uniform_delay(time_point, 0s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(1467825113502396065, ctx.rand_uniform_delay(time_point, 9223372036854775807s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(-970181367944767837, ctx.rand_uniform_delay(time_point, -9223372036854775807s).time_since_epoch().count());
+ BOOST_CHECK_EQUAL(24761, ctx.rand_uniform_delay(time_point, 9h).time_since_epoch().count());
+ }
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64());
@@ -47,8 +57,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
// Check that a nondeterministic ones are not
g_mock_deterministic_tests = false;
for (int i = 10; i > 0; --i) {
- BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U});
- BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006});
+ BOOST_CHECK(GetRand<uint64_t>() != uint64_t{10393729187455219830U});
+ BOOST_CHECK(GetRand<int>() != int{769702006});
BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654});
BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374});
}
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 50b5078110..3e9e04da25 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -2,25 +2,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <rpc/client.h>
-#include <rpc/server.h>
-#include <rpc/util.h>
-
#include <core_io.h>
#include <interfaces/chain.h>
#include <node/context.h>
+#include <rpc/blockchain.h>
+#include <rpc/client.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
#include <test/util/setup_common.h>
+#include <univalue.h>
#include <util/time.h>
#include <any>
-#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
-#include <univalue.h>
-
-#include <rpc/blockchain.h>
-
class RPCTestingSetup : public TestingSetup
{
public:
@@ -29,8 +25,7 @@ public:
UniValue RPCTestingSetup::CallRPC(std::string args)
{
- std::vector<std::string> vArgs;
- boost::split(vArgs, args, boost::is_any_of(" \t"));
+ std::vector<std::string> vArgs{SplitString(args, ' ')};
std::string strMethod = vArgs[0];
vArgs.erase(vArgs.begin());
JSONRPCRequest request;
@@ -71,9 +66,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), std::runtime_error);
std::string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx));
- BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 193);
- BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1);
- BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0);
+ BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").getInt<int>(), 193);
+ BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").getInt<int>(), 1);
+ BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").getInt<int>(), 0);
BOOST_CHECK_THROW(CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error);
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false"));
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error);
@@ -95,7 +90,7 @@ BOOST_AUTO_TEST_CASE(rpc_togglenetwork)
BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive false"));
r = CallRPC("getnetworkinfo");
- int numConnection = find_value(r.get_obj(), "connections").get_int();
+ int numConnection = find_value(r.get_obj(), "connections").getInt<int>();
BOOST_CHECK_EQUAL(numConnection, 0);
netState = find_value(r.get_obj(), "networkactive").get_bool();
@@ -269,7 +264,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- int64_t banned_until{find_value(o1, "banned_until").get_int64()};
+ int64_t banned_until{find_value(o1, "banned_until").getInt<int64_t>()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
BOOST_CHECK_EQUAL(banned_until, 9907731200); // absolute time check
@@ -284,10 +279,10 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- banned_until = find_value(o1, "banned_until").get_int64();
- const int64_t ban_created{find_value(o1, "ban_created").get_int64()};
- const int64_t ban_duration{find_value(o1, "ban_duration").get_int64()};
- const int64_t time_remaining{find_value(o1, "time_remaining").get_int64()};
+ banned_until = find_value(o1, "banned_until").getInt<int64_t>();
+ const int64_t ban_created{find_value(o1, "ban_created").getInt<int64_t>()};
+ const int64_t ban_duration{find_value(o1, "ban_duration").getInt<int64_t>()};
+ const int64_t time_remaining{find_value(o1, "time_remaining").getInt<int64_t>()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
BOOST_CHECK_EQUAL(banned_until, time_remaining_expected + now.count());
BOOST_CHECK_EQUAL(ban_duration, banned_until - ban_created);
@@ -342,22 +337,22 @@ BOOST_AUTO_TEST_CASE(rpc_convert_values_generatetoaddress)
UniValue result;
BOOST_CHECK_NO_THROW(result = RPCConvertValues("generatetoaddress", {"101", "mkESjLZW66TmHhiFX8MCaBjrhZ543PPh9a"}));
- BOOST_CHECK_EQUAL(result[0].get_int(), 101);
+ BOOST_CHECK_EQUAL(result[0].getInt<int>(), 101);
BOOST_CHECK_EQUAL(result[1].get_str(), "mkESjLZW66TmHhiFX8MCaBjrhZ543PPh9a");
BOOST_CHECK_NO_THROW(result = RPCConvertValues("generatetoaddress", {"101", "mhMbmE2tE9xzJYCV9aNC8jKWN31vtGrguU"}));
- BOOST_CHECK_EQUAL(result[0].get_int(), 101);
+ BOOST_CHECK_EQUAL(result[0].getInt<int>(), 101);
BOOST_CHECK_EQUAL(result[1].get_str(), "mhMbmE2tE9xzJYCV9aNC8jKWN31vtGrguU");
BOOST_CHECK_NO_THROW(result = RPCConvertValues("generatetoaddress", {"1", "mkESjLZW66TmHhiFX8MCaBjrhZ543PPh9a", "9"}));
- BOOST_CHECK_EQUAL(result[0].get_int(), 1);
+ BOOST_CHECK_EQUAL(result[0].getInt<int>(), 1);
BOOST_CHECK_EQUAL(result[1].get_str(), "mkESjLZW66TmHhiFX8MCaBjrhZ543PPh9a");
- BOOST_CHECK_EQUAL(result[2].get_int(), 9);
+ BOOST_CHECK_EQUAL(result[2].getInt<int>(), 9);
BOOST_CHECK_NO_THROW(result = RPCConvertValues("generatetoaddress", {"1", "mhMbmE2tE9xzJYCV9aNC8jKWN31vtGrguU", "9"}));
- BOOST_CHECK_EQUAL(result[0].get_int(), 1);
+ BOOST_CHECK_EQUAL(result[0].getInt<int>(), 1);
BOOST_CHECK_EQUAL(result[1].get_str(), "mhMbmE2tE9xzJYCV9aNC8jKWN31vtGrguU");
- BOOST_CHECK_EQUAL(result[2].get_int(), 9);
+ BOOST_CHECK_EQUAL(result[2].getInt<int>(), 9);
}
BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight)
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index a7057f8361..907a3fd15b 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <compat/sanity.h>
#include <key.h>
#include <test/util/setup_common.h>
#include <util/time.h>
@@ -13,7 +12,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(basic_sanity)
{
- BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 4195d413fc..7b5dda8114 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -15,13 +15,13 @@
BOOST_AUTO_TEST_SUITE(scheduler_tests)
-static void microTask(CScheduler& s, std::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
+static void microTask(CScheduler& s, std::mutex& mutex, int& counter, int delta, std::chrono::steady_clock::time_point rescheduleTime)
{
{
std::lock_guard<std::mutex> lock(mutex);
counter += delta;
}
- std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
+ auto noTime = std::chrono::steady_clock::time_point::min();
if (rescheduleTime != noTime) {
CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
s.schedule(f, rescheduleTime);
@@ -49,15 +49,15 @@ BOOST_AUTO_TEST_CASE(manythreads)
auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]
- std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
- std::chrono::system_clock::time_point now = start;
- std::chrono::system_clock::time_point first, last;
+ auto start = std::chrono::steady_clock::now();
+ auto now = start;
+ std::chrono::steady_clock::time_point first, last;
size_t nTasks = microTasks.getQueueInfo(first, last);
BOOST_CHECK(nTasks == 0);
for (int i = 0; i < 100; ++i) {
- std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
- std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
+ auto t = now + std::chrono::microseconds(randomMsec(rng));
+ auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
@@ -75,14 +75,14 @@ BOOST_AUTO_TEST_CASE(manythreads)
microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, &microTasks));
UninterruptibleSleep(std::chrono::microseconds{600});
- now = std::chrono::system_clock::now();
+ now = std::chrono::steady_clock::now();
// More threads and more tasks:
for (int i = 0; i < 5; i++)
microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, &microTasks));
for (int i = 0; i < 100; i++) {
- std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
- std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
+ auto t = now + std::chrono::microseconds(randomMsec(rng));
+ auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
int whichCounter = zeroToNine(rng);
CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
@@ -111,8 +111,8 @@ BOOST_AUTO_TEST_CASE(wait_until_past)
Mutex mtx;
WAIT_LOCK(mtx, lock);
- const auto no_wait= [&](const std::chrono::seconds& d) {
- return condvar.wait_until(lock, std::chrono::system_clock::now() - d);
+ const auto no_wait = [&](const std::chrono::seconds& d) {
+ return condvar.wait_until(lock, std::chrono::steady_clock::now() - d);
};
BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1}));
@@ -128,8 +128,8 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
CScheduler scheduler;
// each queue should be well ordered with respect to itself but not other queues
- SingleThreadedSchedulerClient queue1(&scheduler);
- SingleThreadedSchedulerClient queue2(&scheduler);
+ SingleThreadedSchedulerClient queue1(scheduler);
+ SingleThreadedSchedulerClient queue2(scheduler);
// create more threads than queues
// if the queues only permit execution of one task at once then
@@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
// if they don't we'll get out of order behaviour
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
- threads.emplace_back(std::bind(&CScheduler::serviceQueue, &scheduler));
+ threads.emplace_back([&] { scheduler.serviceQueue(); });
}
// these are not atomic, if SinglethreadedSchedulerClient prevents
@@ -183,7 +183,7 @@ BOOST_AUTO_TEST_CASE(mockforward)
scheduler.scheduleFromNow(dummy, std::chrono::minutes{8});
// check taskQueue
- std::chrono::system_clock::time_point first, last;
+ std::chrono::steady_clock::time_point first, last;
size_t num_tasks = scheduler.getQueueInfo(first, last);
BOOST_CHECK_EQUAL(num_tasks, 3ul);
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(mockforward)
BOOST_CHECK_EQUAL(counter, 2);
// check that the time of the remaining job has been updated
- std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ auto now = std::chrono::steady_clock::now();
int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
// should be between 2 & 3 minutes from now
BOOST_CHECK(delta > 2*60 && delta < 3*60);
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 75bc616cf9..25e47c0fb0 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -406,8 +406,8 @@ BOOST_AUTO_TEST_CASE(bip341_spk_test_vectors)
if (node.isObject()) {
auto script_bytes = ParseHex(node["script"].get_str());
CScript script(script_bytes.begin(), script_bytes.end());
- int idx = node["id"].get_int();
- int leaf_version = node["leafVersion"].get_int();
+ int idx = node["id"].getInt<int>();
+ int leaf_version = node["leafVersion"].getInt<int>();
scriptposes[{script, leaf_version}] = idx;
spktest.Add(depth, script, leaf_version);
} else {
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index c453f22594..05bb89ab55 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -257,11 +257,11 @@ private:
CScriptWitness scriptWitness;
CTransactionRef creditTx;
CMutableTransaction spendTx;
- bool havePush;
+ bool havePush{false};
std::vector<unsigned char> push;
std::string comment;
uint32_t flags;
- int scriptError;
+ int scriptError{SCRIPT_ERR_OK};
CAmount nValue;
void DoPush()
@@ -280,7 +280,7 @@ private:
}
public:
- TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK), nValue(nValue_)
+ TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
{
CScript scriptPubKey = script;
if (wm == WitnessMode::PKH) {
@@ -1162,7 +1162,7 @@ SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction&
SignatureData data;
data.MergeSignatureData(scriptSig1);
data.MergeSignatureData(scriptSig2);
- ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&tx, 0, txout.nValue, SIGHASH_DEFAULT), txout.scriptPubKey, data);
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(tx, 0, txout.nValue, SIGHASH_DEFAULT), txout.scriptPubKey, data);
return data;
}
@@ -1678,7 +1678,7 @@ static void AssetTest(const UniValue& test)
CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
BOOST_CHECK(prevouts.size() == mtx.vin.size());
- size_t idx = test["index"].get_int64();
+ size_t idx = test["index"].getInt<int64_t>();
uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())};
bool fin = test.exists("final") && test["final"].get_bool();
@@ -1760,7 +1760,7 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
for (const auto& utxo_spent : vec["given"]["utxosSpent"].getValues()) {
auto script_bytes = ParseHex(utxo_spent["scriptPubKey"].get_str());
CScript script{script_bytes.begin(), script_bytes.end()};
- CAmount amount{utxo_spent["amountSats"].get_int()};
+ CAmount amount{utxo_spent["amountSats"].getInt<int>()};
utxos.emplace_back(amount, script);
}
@@ -1775,8 +1775,8 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
BOOST_CHECK_EQUAL(HexStr(txdata.m_sequences_single_hash), vec["intermediary"]["hashSequences"].get_str());
for (const auto& input : vec["inputSpending"].getValues()) {
- int txinpos = input["given"]["txinIndex"].get_int();
- int hashtype = input["given"]["hashType"].get_int();
+ int txinpos = input["given"]["txinIndex"].getInt<int>();
+ int hashtype = input["given"]["hashType"].getInt<int>();
// Load key.
auto privkey = ParseHex(input["given"]["internalPrivkey"].get_str());
@@ -1796,7 +1796,7 @@ BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
// Sign and verify signature.
FlatSigningProvider provider;
provider.keys[key.GetPubKey().GetID()] = key;
- MutableTransactionSignatureCreator creator(&tx, txinpos, utxos[txinpos].nValue, &txdata, hashtype);
+ MutableTransactionSignatureCreator creator(tx, txinpos, utxos[txinpos].nValue, &txdata, hashtype);
std::vector<unsigned char> signature;
BOOST_CHECK(creator.CreateSchnorrSig(provider, signature, pubkey, nullptr, &merkle_root, SigVersion::TAPROOT));
BOOST_CHECK_EQUAL(HexStr(signature), input["expected"]["witness"][0].get_str());
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 15ffd068c7..0feb68b9b1 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(ReadWrite)
//! Check settings struct contents against expected json strings.
static void CheckValues(const util::Settings& settings, const std::string& single_val, const std::string& list_val)
{
- util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false);
+ util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false, false);
util::SettingsValue list_value(util::SettingsValue::VARR);
for (const auto& item : GetSettingsList(settings, "section", "name", false)) {
list_value.push_back(item);
@@ -141,9 +141,9 @@ BOOST_AUTO_TEST_CASE(NullOverride)
{
util::Settings settings;
settings.command_line_options["name"].push_back("value");
- BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false).write().c_str());
+ BOOST_CHECK_EQUAL(R"("value")", GetSetting(settings, "section", "name", false, false, false).write().c_str());
settings.forced_settings["name"] = {};
- BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false).write().c_str());
+ BOOST_CHECK_EQUAL(R"(null)", GetSetting(settings, "section", "name", false, false, false).write().c_str());
}
// Test different ways settings can be merged, and verify results. This test can
@@ -224,7 +224,7 @@ BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup)
}
desc += " || ";
- desc += GetSetting(settings, network, name, ignore_default_section_config, /* get_chain_name= */ false).write();
+ desc += GetSetting(settings, network, name, ignore_default_section_config, /*ignore_nonpersistent=*/false, /*get_chain_name=*/false).write();
desc += " |";
for (const auto& s : GetSettingsList(settings, network, name, ignore_default_section_config)) {
desc += " ";
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 1601b02356..7376f2ff5c 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -184,8 +184,8 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
// deserialize test data
raw_tx = test[0].get_str();
raw_script = test[1].get_str();
- nIn = test[2].get_int();
- nHashType = test[3].get_int();
+ nIn = test[2].getInt<int>();
+ nHashType = test[3].getInt<int>();
sigHashHex = test[4].get_str();
CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index df67841b66..4e6c223ccc 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -217,11 +217,11 @@ BOOST_AUTO_TEST_CASE(tx_valid)
fValid = false;
break;
}
- COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())};
+ COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].getInt<int>())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
- mapprevOutValues[outpoint] = vinput[3].get_int64();
+ mapprevOutValues[outpoint] = vinput[3].getInt<int64_t>();
}
}
if (!fValid)
@@ -305,11 +305,11 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
fValid = false;
break;
}
- COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())};
+ COutPoint outpoint{uint256S(vinput[0].get_str()), uint32_t(vinput[1].getInt<int>())};
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
if (vinput.size() >= 4)
{
- mapprevOutValues[outpoint] = vinput[3].get_int64();
+ mapprevOutValues[outpoint] = vinput[3].getInt<int64_t>();
}
}
if (!fValid)
@@ -559,7 +559,7 @@ SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutabl
SignatureData sigdata;
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
- ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
+ ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(input1, 0, tx->vout[0].nValue, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
return sigdata;
}
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index b1e2119c64..dd4bc5af75 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -15,7 +15,7 @@
struct Dersig100Setup : public TestChain100Setup {
Dersig100Setup()
- : TestChain100Setup{{"-testactivationheight=dersig@102"}} {}
+ : TestChain100Setup{CBaseChainParams::REGTEST, {"-testactivationheight=dersig@102"}} {}
};
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
@@ -329,7 +329,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
// Sign
SignatureData sigdata;
- BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata));
+ BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(valid_with_witness_tx, 0, 11 * CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata));
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
@@ -355,9 +355,9 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
// Sign
- for (int i=0; i<2; ++i) {
+ for (int i = 0; i < 2; ++i) {
SignatureData sigdata;
- BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata));
+ BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(tx, i, 11 * CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata));
UpdateInput(tx.vin[i], sigdata);
}
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index 09f96a033c..5ac504c24f 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -30,7 +30,7 @@ CreateAndActivateUTXOSnapshot(node::NodeContext& node, const fs::path root, F ma
//
int height;
WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
- fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
+ fs::path snapshot_path = root / fs::u8path(tfm::format("test_snapshot.%d.dat", height));
FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 5ed8598e8e..a6d624fe84 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -68,7 +68,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
assert(block->nNonce);
}
- bool processed{Assert(node.chainman)->ProcessNewBlock(Params(), block, true, nullptr)};
+ bool processed{Assert(node.chainman)->ProcessNewBlock(block, true, nullptr)};
assert(processed);
return CTxIn{block->vtx[0]->GetHash(), 0};
@@ -77,7 +77,7 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool)}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 2fc71c2a6e..8f03481f72 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -12,6 +12,7 @@
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <init.h>
+#include <init/common.h>
#include <interfaces/chain.h>
#include <net.h>
#include <net_processing.h>
@@ -29,6 +30,7 @@
#include <shutdown.h>
#include <streams.h>
#include <test/util/net.h>
+#include <timedata.h>
#include <txdb.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -42,6 +44,7 @@
#include <validationinterface.h>
#include <walletinitinterface.h>
+#include <algorithm>
#include <functional>
#include <stdexcept>
@@ -124,8 +127,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
InitLogging(*m_node.args);
AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
- SHA256AutoDetect();
- ECC_Start();
+ m_node.kernel = std::make_unique<kernel::Context>();
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
@@ -145,12 +147,13 @@ BasicTestingSetup::~BasicTestingSetup()
LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
gArgs.ClearArgs();
- ECC_Stop();
}
ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
: BasicTestingSetup(chainName, extra_args)
{
+ const CChainParams& chainparams = Params();
+
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
m_node.scheduler = std::make_unique<CScheduler>();
@@ -158,11 +161,15 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
- m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
+ m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), m_node.args->GetIntArg("-checkmempool", 1));
m_cache_sizes = CalculateCacheSizes(m_args);
- m_node.chainman = std::make_unique<ChainstateManager>();
+ const ChainstateManager::Options chainman_opts{
+ chainparams,
+ GetAdjustedTime,
+ };
+ m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(m_cache_sizes.block_tree_db, true);
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
@@ -190,7 +197,6 @@ ChainTestingSetup::~ChainTestingSetup()
TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
: ChainTestingSetup(chainName, extra_args)
{
- const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
@@ -199,7 +205,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
*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,
@@ -212,10 +217,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
*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),
- /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
+ m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL));
assert(!maybe_verify_error.has_value());
BlockValidationState state;
@@ -229,7 +232,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
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<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); // Deterministic randomness for tests.
- m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
+ m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
m_node.banman.get(), *m_node.chainman,
*m_node.mempool, false);
{
@@ -239,8 +242,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup(const std::vector<const char*>& extra_args)
- : TestingSetup{CBaseChainParams::REGTEST, extra_args}
+TestChain100Setup::TestChain100Setup(const std::string& chain_name, const std::vector<const char*>& extra_args)
+ : TestingSetup{chain_name, extra_args}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -274,9 +277,8 @@ CBlock TestChain100Setup::CreateBlock(
const CScript& scriptPubKey,
CChainState& chainstate)
{
- const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(chainstate, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler{chainstate, empty_pool}.CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -284,7 +286,7 @@ CBlock TestChain100Setup::CreateBlock(
}
RegenerateCommitments(block, *Assert(m_node.chainman));
- while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
return block;
}
@@ -298,10 +300,9 @@ CBlock TestChain100Setup::CreateAndProcessBlock(
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
}
- const CChainParams& chainparams = Params();
const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
- Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
+ Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr);
return block;
}
@@ -356,6 +357,52 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
return mempool_txn;
}
+std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit)
+{
+ std::vector<CTransactionRef> mempool_transactions;
+ std::deque<std::pair<COutPoint, CAmount>> unspent_prevouts;
+ std::transform(m_coinbase_txns.begin(), m_coinbase_txns.end(), std::back_inserter(unspent_prevouts),
+ [](const auto& tx){ return std::make_pair(COutPoint(tx->GetHash(), 0), tx->vout[0].nValue); });
+ while (num_transactions > 0 && !unspent_prevouts.empty()) {
+ // The number of inputs and outputs are random, between 1 and 24.
+ CMutableTransaction mtx = CMutableTransaction();
+ const size_t num_inputs = det_rand.randrange(24) + 1;
+ CAmount total_in{0};
+ for (size_t n{0}; n < num_inputs; ++n) {
+ if (unspent_prevouts.empty()) break;
+ const auto& [prevout, amount] = unspent_prevouts.front();
+ mtx.vin.push_back(CTxIn(prevout, CScript()));
+ total_in += amount;
+ unspent_prevouts.pop_front();
+ }
+ const size_t num_outputs = det_rand.randrange(24) + 1;
+ // Approximately 1000sat "fee," equal output amounts.
+ const CAmount amount_per_output = (total_in - 1000) / num_outputs;
+ for (size_t n{0}; n < num_outputs; ++n) {
+ CScript spk = CScript() << CScriptNum(num_transactions + n);
+ mtx.vout.push_back(CTxOut(amount_per_output, spk));
+ }
+ CTransactionRef ptx = MakeTransactionRef(mtx);
+ mempool_transactions.push_back(ptx);
+ if (amount_per_output > 2000) {
+ // If the value is high enough to fund another transaction + fees, keep track of it so
+ // it can be used to build a more complex transaction graph. Insert randomly into
+ // unspent_prevouts for extra randomness in the resulting structures.
+ for (size_t n{0}; n < num_outputs; ++n) {
+ unspent_prevouts.push_back(std::make_pair(COutPoint(ptx->GetHash(), n), amount_per_output));
+ std::swap(unspent_prevouts.back(), unspent_prevouts[det_rand.randrange(unspent_prevouts.size())]);
+ }
+ }
+ if (submit) {
+ LOCK2(m_node.mempool->cs, cs_main);
+ LockPoints lp;
+ m_node.mempool->addUnchecked(CTxMemPoolEntry(ptx, 1000, 0, 1, false, 4, lp));
+ }
+ --num_transactions;
+ }
+ return mempool_transactions;
+}
+
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
{
return FromTx(MakeTransactionRef(tx));
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index a1b7525cf4..37407bcb92 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -81,8 +81,7 @@ static constexpr CAmount CENT{1000000};
* This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
- ECCVerifyHandle globalVerifyHandle;
- node::NodeContext m_node;
+ node::NodeContext m_node; // keep as first member to be destructed last
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
~BasicTestingSetup();
@@ -122,7 +121,8 @@ class CScript;
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public TestingSetup {
- TestChain100Setup(const std::vector<const char*>& extra_args = {});
+ TestChain100Setup(const std::string& chain_name = CBaseChainParams::REGTEST,
+ const std::vector<const char*>& extra_args = {});
/**
* Create a new block with just given transactions, coinbase paying to
@@ -164,6 +164,19 @@ struct TestChain100Setup : public TestingSetup {
CAmount output_amount = CAmount(1 * COIN),
bool submit = true);
+ /** Create transactions spending from m_coinbase_txns. These transactions will only spend coins
+ * that exist in the current chain, but may be premature coinbase spends, have missing
+ * signatures, or violate some other consensus rules. They should only be used for testing
+ * mempool consistency. All transactions will have some random number of inputs and outputs
+ * (between 1 and 24). Transactions may or may not be dependent upon each other; if dependencies
+ * exit, every parent will always be somewhere in the list before the child so each transaction
+ * can be submitted in the same order they appear in the list.
+ * @param[in] submit When true, submit transactions to the mempool.
+ * When false, return them but don't submit them.
+ * @returns A vector of transactions that can be submitted to the mempool.
+ */
+ std::vector<CTransactionRef> PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit);
+
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 1ca20fd848..fda56ccff7 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -153,7 +153,7 @@ static const unsigned char ParseHex_expected[65] = {
0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d,
0x5f
};
-BOOST_AUTO_TEST_CASE(util_ParseHex)
+BOOST_AUTO_TEST_CASE(parse_hex)
{
std::vector<unsigned char> result;
std::vector<unsigned char> expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected));
@@ -169,6 +169,14 @@ BOOST_AUTO_TEST_CASE(util_ParseHex)
result = ParseHex(" 89 34 56 78");
BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78);
+ // Embedded null is treated as end
+ const std::string with_embedded_null{" 11 "s
+ " \0 "
+ " 22 "s};
+ BOOST_CHECK_EQUAL(with_embedded_null.size(), 11);
+ result = ParseHex(with_embedded_null);
+ BOOST_CHECK(result.size() == 1 && result[0] == 0x11);
+
// Stop parsing at invalid value
result = ParseHex("1234 invalid 1234");
BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34);
@@ -198,6 +206,24 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
BOOST_CHECK_EQUAL(HexStr(in_s), out_exp);
BOOST_CHECK_EQUAL(HexStr(in_b), out_exp);
}
+
+ {
+ auto input = std::string();
+ for (size_t i=0; i<256; ++i) {
+ input.push_back(static_cast<char>(i));
+ }
+
+ auto hex = HexStr(input);
+ BOOST_TEST_REQUIRE(hex.size() == 512);
+ static constexpr auto hexmap = std::string_view("0123456789abcdef");
+ for (size_t i = 0; i < 256; ++i) {
+ auto upper = hexmap.find(hex[i * 2]);
+ auto lower = hexmap.find(hex[i * 2 + 1]);
+ BOOST_TEST_REQUIRE(upper != std::string_view::npos);
+ BOOST_TEST_REQUIRE(lower != std::string_view::npos);
+ BOOST_TEST_REQUIRE(i == upper*16 + lower);
+ }
+ }
}
BOOST_AUTO_TEST_CASE(span_write_bytes)
@@ -247,9 +273,6 @@ BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime)
BOOST_CHECK_EQUAL(ParseISO8601DateTime("1970-01-01T00:00:00Z"), 0);
BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0);
BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777);
-
- auto time = GetTimeSeconds();
- BOOST_CHECK_EQUAL(ParseISO8601DateTime(FormatISO8601DateTime(time)), time);
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
@@ -1470,19 +1493,27 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime)
{
SetMockTime(111);
// Check that mock time does not change after a sleep
- for (const auto& num_sleep : {0, 1}) {
- UninterruptibleSleep(std::chrono::milliseconds{num_sleep});
+ for (const auto& num_sleep : {0ms, 1ms}) {
+ UninterruptibleSleep(num_sleep);
BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
+ BOOST_CHECK_EQUAL(111, Now<NodeSeconds>().time_since_epoch().count());
+ BOOST_CHECK_EQUAL(111, TicksSinceEpoch<std::chrono::seconds>(NodeClock::now()));
+ BOOST_CHECK_EQUAL(111, TicksSinceEpoch<SecondsDouble>(Now<NodeSeconds>()));
BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
+ BOOST_CHECK_EQUAL(111000, TicksSinceEpoch<std::chrono::milliseconds>(NodeClock::now()));
BOOST_CHECK_EQUAL(111000000, GetTime<std::chrono::microseconds>().count());
}
SetMockTime(0);
- // Check that system time changes after a sleep
+ // Check that steady time and system time changes after a sleep
+ const auto steady_ms_0 = Now<SteadyMilliseconds>();
+ const auto steady_0 = std::chrono::steady_clock::now();
const auto ms_0 = GetTime<std::chrono::milliseconds>();
const auto us_0 = GetTime<std::chrono::microseconds>();
- UninterruptibleSleep(std::chrono::milliseconds{1});
+ UninterruptibleSleep(1ms);
+ BOOST_CHECK(steady_ms_0 < Now<SteadyMilliseconds>());
+ BOOST_CHECK(steady_0 + 1ms <= std::chrono::steady_clock::now());
BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
}
@@ -2049,7 +2080,7 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
BOOST_CHECK(!ParseFixedPoint("31.999999999999999999999", 3, &amount));
}
-static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
+static void TestOtherThread(fs::path dirname, fs::path lockname, bool *result)
{
*result = LockDirectory(dirname, lockname);
}
@@ -2059,7 +2090,7 @@ static constexpr char LockCommand = 'L';
static constexpr char UnlockCommand = 'U';
static constexpr char ExitCommand = 'X';
-[[noreturn]] static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
+[[noreturn]] static void TestOtherProcess(fs::path dirname, fs::path lockname, int fd)
{
char ch;
while (true) {
@@ -2090,7 +2121,7 @@ static constexpr char ExitCommand = 'X';
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
fs::path dirname = m_args.GetDataDirBase() / "lock_dir";
- const std::string lockname = ".lock";
+ const fs::path lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
// it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined
@@ -2396,6 +2427,19 @@ BOOST_AUTO_TEST_CASE(test_SplitString)
BOOST_CHECK_EQUAL(result.size(), 1);
BOOST_CHECK_EQUAL(result[0], "AAA");
}
+
+ // multiple split characters
+ {
+ using V = std::vector<std::string>;
+ BOOST_TEST(SplitString("a,b.c:d;e", ",;") == V({"a", "b.c:d", "e"}));
+ BOOST_TEST(SplitString("a,b.c:d;e", ",;:.") == V({"a", "b", "c", "d", "e"}));
+ BOOST_TEST(SplitString("a,b.c:d;e", "") == V({"a,b.c:d;e"}));
+ BOOST_TEST(SplitString("aaa", "bcdefg") == V({"aaa"}));
+ BOOST_TEST(SplitString("x\0a,b"s, "\0"s) == V({"x", "a,b"}));
+ BOOST_TEST(SplitString("x\0a,b"s, '\0') == V({"x", "a,b"}));
+ BOOST_TEST(SplitString("x\0a,b"s, "\0,"s) == V({"x", "a", "b"}));
+ BOOST_TEST(SplitString("abcdefg", "bcd") == V({"a", "", "", "efg"}));
+ }
}
BOOST_AUTO_TEST_CASE(test_LogEscapeMessage)
@@ -2418,9 +2462,9 @@ struct Tracker
//! Points to the original object (possibly itself) we moved/copied from
const Tracker* origin;
//! How many copies where involved between the original object and this one (moves are not counted)
- int copies;
+ int copies{0};
- Tracker() noexcept : origin(this), copies(0) {}
+ Tracker() noexcept : origin(this) {}
Tracker(const Tracker& t) noexcept : origin(t.origin), copies(t.copies + 1) {}
Tracker(Tracker&& t) noexcept : origin(t.origin), copies(t.copies) {}
Tracker& operator=(const Tracker& t) noexcept
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index c5b1dabcb7..331da691b5 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -65,7 +65,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
- auto ptemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(CScript{} << i++ << OP_TRUE);
+ auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(CScript{} << i++ << OP_TRUE);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
@@ -89,7 +89,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
const CBlockIndex* prev_block{WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock))};
- GenerateCoinbaseCommitment(*pblock, prev_block, Params().GetConsensus());
+ m_node.chainman->GenerateCoinbaseCommitment(*pblock, prev_block);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -100,7 +100,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock>
// submit block header, so that miner can get the block height from the
// global state and the node has the topology of the chain
BlockValidationState ignored;
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored, Params()));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored));
return pblock;
}
@@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
bool ignored;
// Connect the genesis block and drain any outstanding events
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
@@ -179,13 +179,13 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
FastRandomContext insecure;
for (int i = 0; i < 1000; i++) {
auto block = blocks[insecure.randrange(blocks.size() - 1)];
- Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
+ Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
}
// to make sure that eventually we process the full chain - do it here
for (auto block : blocks) {
if (block->vtx.size() == 1) {
- bool processed = Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
+ bool processed = Assert(m_node.chainman)->ProcessNewBlock(block, true, &ignored);
assert(processed);
}
}
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
bool ignored;
auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
- return Assert(m_node.chainman)->ProcessNewBlock(Params(), block, /*force_processing=*/true, /*new_block=*/&ignored);
+ return Assert(m_node.chainman)->ProcessNewBlock(block, /*force_processing=*/true, /*new_block=*/&ignored);
};
// Process all mined blocks
@@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
{
CScript pubKey;
pubKey << 1 << OP_TRUE;
- auto ptemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(pubKey);
+ auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), *m_node.mempool}.CreateNewBlock(pubKey);
CBlock pblock = ptemplate->block;
CTxOut witness;
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 2a3990bb7c..98cb713a81 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <chainparams.h>
-#include <random.h>
-#include <uint256.h>
#include <consensus/validation.h>
-#include <sync.h>
+#include <random.h>
#include <rpc/blockchain.h>
+#include <sync.h>
#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
+#include <timedata.h>
+#include <uint256.h>
#include <validation.h>
#include <vector>
@@ -22,7 +23,12 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, TestingSetup)
//!
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
- ChainstateManager manager;
+ const ChainstateManager::Options chainman_opts{
+ Params(),
+ GetAdjustedTime,
+ };
+ ChainstateManager manager{chainman_opts};
+
WITH_LOCK(::cs_main, manager.m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(1 << 20, true));
CTxMemPool mempool;
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index bf87812a8a..e6203af4b4 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -5,9 +5,7 @@
#include <chain.h>
#include <chainparams.h>
#include <consensus/params.h>
-#include <deploymentstatus.h>
#include <test/util/setup_common.h>
-#include <validation.h>
#include <versionbits.h>
#include <boost/test/unit_test.hpp>
@@ -184,7 +182,7 @@ public:
CBlockIndex* Tip() { return vpblock.empty() ? nullptr : vpblock.back(); }
};
-BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
+BOOST_FIXTURE_TEST_SUITE(versionbits_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(versionbits_test)
{
@@ -257,10 +255,10 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
/** Check that ComputeBlockVersion will set the appropriate bit correctly */
-static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
+static void check_computeblockversion(VersionBitsCache& versionbitscache, const Consensus::Params& params, Consensus::DeploymentPos dep)
{
- // This implicitly uses g_versionbitscache, so clear it every time
- g_versionbitscache.Clear();
+ // Clear the cache every time
+ versionbitscache.Clear();
int64_t bit = params.vDeployments[dep].bit;
int64_t nStartTime = params.vDeployments[dep].nStartTime;
@@ -268,13 +266,14 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
int min_activation_height = params.vDeployments[dep].min_activation_height;
// should not be any signalling for first block
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
// always/never active deployments shouldn't need to be tested further
if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE ||
nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE)
{
BOOST_CHECK_EQUAL(min_activation_height, 0);
+ BOOST_CHECK_EQUAL(nTimeout, Consensus::BIP9Deployment::NO_TIMEOUT);
return;
}
@@ -288,7 +287,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Check min_activation_height is on a retarget boundary
BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0U);
- const uint32_t bitmask{g_versionbitscache.Mask(params, dep)};
+ const uint32_t bitmask{versionbitscache.Mask(params, dep)};
BOOST_CHECK_EQUAL(bitmask, uint32_t{1} << bit);
// In the first chain, test that the bit is set by CBV until it has failed.
@@ -307,9 +306,9 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// earlier time, so will transition from DEFINED to STARTED at the
// end of the first period by mining blocks at nTime == 0
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// then we'll keep mining at nStartTime...
} else {
// use a time 1s earlier than start time to check we stay DEFINED
@@ -317,28 +316,28 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Start generating blocks before nStartTime
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
// Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// Next we will advance to the next period and transition to STARTED,
}
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
@@ -347,8 +346,8 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
blocksToMine--;
nTime += 600;
nHeight += 1;
@@ -362,7 +361,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// finish the last period before we start timing out
while (nHeight % params.nMinerConfirmationWindow != 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
nHeight += 1;
}
@@ -370,12 +369,12 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// the bit until the period transition.
for (uint32_t i = 0; i < params.nMinerConfirmationWindow - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
nHeight += 1;
}
// The next block should trigger no longer setting the bit.
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
// On a new chain:
@@ -386,34 +385,36 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
if (lastBlock->nHeight + 1 < min_activation_height) {
// check signalling continues while min_activation_height is not reached
lastBlock = secondChain.Mine(min_activation_height - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ BOOST_CHECK((versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// then reach min_activation_height, which was already REQUIRE'd to start a new period
lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
}
// Check that we don't signal after activation
- BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
+ BOOST_CHECK_EQUAL(versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit), 0);
}
BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
+ VersionBitsCache vbcache;
+
// check that any deployment on any chain can conceivably reach both
// ACTIVE and FAILED states in roughly the way we expect
for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST}) {
@@ -426,10 +427,10 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// not take precedence over STARTED/LOCKED_IN. So all softforks on
// the same bit might overlap, even when non-overlapping start-end
// times are picked.
- const uint32_t dep_mask{g_versionbitscache.Mask(chainParams->GetConsensus(), dep)};
+ const uint32_t dep_mask{vbcache.Mask(chainParams->GetConsensus(), dep)};
BOOST_CHECK(!(chain_all_vbits & dep_mask));
chain_all_vbits |= dep_mask;
- check_computeblockversion(chainParams->GetConsensus(), dep);
+ check_computeblockversion(vbcache, chainParams->GetConsensus(), dep);
}
}
@@ -439,7 +440,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
ArgsManager args;
args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999"); // January 1, 2008 - December 31, 2008
const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
- check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
+ check_computeblockversion(vbcache, chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
}
{
@@ -449,7 +450,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
ArgsManager args;
args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200
const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
- check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
+ check_computeblockversion(vbcache, chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
}
}
diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h
index cb9a5fbf8b..992016b4f6 100644
--- a/src/threadinterrupt.h
+++ b/src/threadinterrupt.h
@@ -21,11 +21,11 @@ class CThreadInterrupt
public:
CThreadInterrupt();
explicit operator bool() const;
- void operator()();
+ void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
void reset();
- bool sleep_for(std::chrono::milliseconds rel_time);
- bool sleep_for(std::chrono::seconds rel_time);
- bool sleep_for(std::chrono::minutes rel_time);
+ bool sleep_for(std::chrono::milliseconds rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
+ bool sleep_for(std::chrono::seconds rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
+ bool sleep_for(std::chrono::minutes rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
private:
std::condition_variable cond;
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 06659871e5..7faf22aba0 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -99,7 +99,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
}
}
- if (LogAcceptCategory(BCLog::NET)) {
+ if (LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
std::string log_message{"time data samples: "};
for (const int64_t n : vSorted) {
log_message += strprintf("%+d ", n);
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index e2a7cb4066..2064f0fb8a 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -24,8 +24,6 @@
#include <set>
#include <vector>
-#include <boost/algorithm/string/replace.hpp>
-
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/event.h>
@@ -306,8 +304,7 @@ std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
base(_base),
- m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(nullptr),
- reconnect_timeout(RECONNECT_TIMEOUT_START),
+ m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_timeout(RECONNECT_TIMEOUT_START),
m_target(target)
{
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
@@ -566,7 +563,7 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
if (!torpassword.empty()) {
if (methods.count("HASHEDPASSWORD")) {
LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n");
- boost::replace_all(torpassword, "\"", "\\\"");
+ ReplaceAll(torpassword, "\"", "\\\"");
_conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n");
diff --git a/src/txdb.cpp b/src/txdb.cpp
index afcd1985f5..a0939873ad 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -207,7 +207,7 @@ public:
// cache warmup on instantiation.
CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
- ~CCoinsViewDBCursor() {}
+ ~CCoinsViewDBCursor() = default;
bool GetKey(COutPoint &key) const override;
bool GetValue(Coin &coin) const override;
diff --git a/src/univalue/README.md b/src/univalue/README.md
index 7c62c33970..d622f5b1e0 100644
--- a/src/univalue/README.md
+++ b/src/univalue/README.md
@@ -15,7 +15,7 @@ This class is aligned with the JSON standard, [RFC
## Library usage
This is a fork of univalue used by Bitcoin Core. It is not maintained for usage
-by other projects. Notably, the API may break in non-backward-compatible ways.
+by other projects. Notably, the API is broken in non-backward-compatible ways.
Other projects looking for a maintained library should use the upstream
univalue at https://github.com/jgarzik/univalue.
diff --git a/src/univalue/TODO b/src/univalue/TODO
deleted file mode 100644
index 5530048e92..0000000000
--- a/src/univalue/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-
-Rearrange tree for easier 'git subtree' style use
-
-Move towards C++11 etc.
-
-Namespace support - must come up with useful shorthand, avoiding
-long Univalue::Univalue::Univalue usages forced upon library users.
-
-Improve test suite
-
diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac
index 495b25a53d..ed9c5f0c5c 100644
--- a/src/univalue/configure.ac
+++ b/src/univalue/configure.ac
@@ -45,8 +45,8 @@ AC_SUBST(LIBUNIVALUE_AGE)
LT_INIT
LT_LANG([C++])
-dnl Require C++11 compiler (no GNU extensions)
-AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault])
+dnl Require C++17 compiler (no GNU extensions)
+AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory], [nodefault])
case $host in
*mingw*)
diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp
index b8a6c73f4e..ca5b470ddc 100644
--- a/src/univalue/gen/gen.cpp
+++ b/src/univalue/gen/gen.cpp
@@ -8,9 +8,11 @@
// $ ./gen > univalue_escapes.h
//
-#include <stdio.h>
-#include <string.h>
-#include "univalue.h"
+#include <univalue.h>
+
+#include <cstdio>
+#include <cstring>
+#include <string>
static bool initEscapes;
static std::string escapes[256];
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index fc5cf402be..f0d4de2035 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -6,13 +6,14 @@
#ifndef __UNIVALUE_H__
#define __UNIVALUE_H__
-#include <stdint.h>
-#include <string.h>
-
+#include <charconv>
+#include <cstdint>
+#include <cstring>
+#include <map>
+#include <stdexcept>
#include <string>
+#include <type_traits>
#include <vector>
-#include <map>
-#include <cassert>
class UniValue {
public:
@@ -82,66 +83,10 @@ public:
bool isObject() const { return (typ == VOBJ); }
bool push_back(const UniValue& val);
- bool push_back(const std::string& val_) {
- UniValue tmpVal(VSTR, val_);
- return push_back(tmpVal);
- }
- bool push_back(const char *val_) {
- std::string s(val_);
- return push_back(s);
- }
- bool push_back(uint64_t val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(int64_t val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(bool val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(int val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
- bool push_back(double val_) {
- UniValue tmpVal(val_);
- return push_back(tmpVal);
- }
bool push_backV(const std::vector<UniValue>& vec);
void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val);
- bool pushKV(const std::string& key, const std::string& val_) {
- UniValue tmpVal(VSTR, val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, const char *val_) {
- std::string _val(val_);
- return pushKV(key, _val);
- }
- bool pushKV(const std::string& key, int64_t val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, uint64_t val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, bool val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, int val_) {
- UniValue tmpVal((int64_t)val_);
- return pushKV(key, tmpVal);
- }
- bool pushKV(const std::string& key, double val_) {
- UniValue tmpVal(val_);
- return pushKV(key, tmpVal);
- }
bool pushKVs(const UniValue& obj);
std::string write(unsigned int prettyIndent = 0,
@@ -168,10 +113,22 @@ public:
// value is of unexpected type
const std::vector<std::string>& getKeys() const;
const std::vector<UniValue>& getValues() const;
+ template <typename Int>
+ auto getInt() const
+ {
+ static_assert(std::is_integral<Int>::value);
+ if (typ != VNUM) {
+ throw std::runtime_error("JSON value is not an integer as expected");
+ }
+ Int result;
+ const auto [first_nonmatching, error_condition] = std::from_chars(val.data(), val.data() + val.size(), result);
+ if (first_nonmatching != val.data() + val.size() || error_condition != std::errc{}) {
+ throw std::runtime_error("JSON integer out of range");
+ }
+ return result;
+ }
bool get_bool() const;
const std::string& get_str() const;
- int get_int() const;
- int64_t get_int64() const;
double get_real() const;
const UniValue& get_obj() const;
const UniValue& get_array() const;
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index c4e59fae74..3553995c28 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -3,12 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <stdint.h>
+#include <univalue.h>
+
#include <iomanip>
+#include <map>
+#include <memory>
#include <sstream>
-#include <stdlib.h>
-
-#include "univalue.h"
+#include <string>
+#include <utility>
+#include <vector>
const UniValue NullUniValue;
diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp
index 5af89a3561..9bbdb1fe69 100644
--- a/src/univalue/lib/univalue_get.cpp
+++ b/src/univalue/lib/univalue_get.cpp
@@ -3,17 +3,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <stdint.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdexcept>
-#include <vector>
+#include <univalue.h>
+
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
#include <limits>
-#include <string>
+#include <locale>
#include <sstream>
-
-#include "univalue.h"
+#include <stdexcept>
+#include <string>
+#include <vector>
namespace
{
@@ -28,37 +29,6 @@ static bool ParsePrechecks(const std::string& str)
return true;
}
-bool ParseInt32(const std::string& str, int32_t *out)
-{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtol will not set errno if valid
- long int n = strtol(str.c_str(), &endp, 10);
- if(out) *out = (int32_t)n;
- // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int32_t>::min() &&
- n <= std::numeric_limits<int32_t>::max();
-}
-
-bool ParseInt64(const std::string& str, int64_t *out)
-{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtoll will not set errno if valid
- long long int n = strtoll(str.c_str(), &endp, 10);
- if(out) *out = (int64_t)n;
- // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
- // we still have to check that the returned value is within the range of an *int64_t*.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int64_t>::min() &&
- n <= std::numeric_limits<int64_t>::max();
-}
-
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
@@ -102,26 +72,6 @@ const std::string& UniValue::get_str() const
return getValStr();
}
-int UniValue::get_int() const
-{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not an integer as expected");
- int32_t retval;
- if (!ParseInt32(getValStr(), &retval))
- throw std::runtime_error("JSON integer out of range");
- return retval;
-}
-
-int64_t UniValue::get_int64() const
-{
- if (typ != VNUM)
- throw std::runtime_error("JSON value is not an integer as expected");
- int64_t retval;
- if (!ParseInt64(getValStr(), &retval))
- throw std::runtime_error("JSON integer out of range");
- return retval;
-}
-
double UniValue::get_real() const
{
if (typ != VNUM)
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
index be39bfe57a..a6ed75e57a 100644
--- a/src/univalue/lib/univalue_read.cpp
+++ b/src/univalue/lib/univalue_read.cpp
@@ -2,19 +2,22 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <string.h>
-#include <vector>
-#include <stdio.h>
-#include "univalue.h"
+#include <univalue.h>
#include "univalue_utffilter.h"
+#include <cstdio>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
/*
* According to stackexchange, the original json test suite wanted
* to limit depth to 22. Widely-deployed PHP bails at depth 512,
* so we will follow PHP's lead, which should be more than sufficient
* (further stackexchange comments indicate depth > 32 rarely occurs).
*/
-static const size_t MAX_JSON_DEPTH = 512;
+static constexpr size_t MAX_JSON_DEPTH = 512;
static bool json_isdigit(int ch)
{
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
index 3a2c580c7f..18833077b7 100644
--- a/src/univalue/lib/univalue_write.cpp
+++ b/src/univalue/lib/univalue_write.cpp
@@ -2,11 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <iomanip>
-#include <stdio.h>
-#include "univalue.h"
+#include <univalue.h>
#include "univalue_escapes.h"
+#include <memory>
+#include <string>
+#include <vector>
+
static std::string json_escape(const std::string& inS)
{
std::string outS;
diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp
index 83d292200b..3a7a727abb 100644
--- a/src/univalue/test/no_nul.cpp
+++ b/src/univalue/test/no_nul.cpp
@@ -1,4 +1,4 @@
-#include "univalue.h"
+#include <univalue.h>
int main (int argc, char *argv[])
{
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
index c2f52f83ac..8a35bf914d 100644
--- a/src/univalue/test/object.cpp
+++ b/src/univalue/test/object.cpp
@@ -3,13 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <stdint.h>
-#include <vector>
-#include <string>
-#include <map>
+#include <univalue.h>
+
#include <cassert>
+#include <cstdint>
+#include <map>
+#include <memory>
#include <stdexcept>
-#include <univalue.h>
+#include <string>
+#include <vector>
#define BOOST_FIXTURE_TEST_SUITE(a, b)
#define BOOST_AUTO_TEST_CASE(funcName) void funcName()
@@ -90,23 +92,30 @@ BOOST_AUTO_TEST_CASE(univalue_typecheck)
BOOST_CHECK(v1.isNum());
BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error);
+ {
+ UniValue v_negative;
+ BOOST_CHECK(v_negative.setNumStr("-1"));
+ BOOST_CHECK_THROW(v_negative.getInt<uint8_t>(), std::runtime_error);
+ BOOST_CHECK_EQUAL(v_negative.getInt<int8_t>(), -1);
+ }
+
UniValue v2;
BOOST_CHECK(v2.setBool(true));
BOOST_CHECK_EQUAL(v2.get_bool(), true);
- BOOST_CHECK_THROW(v2.get_int(), std::runtime_error);
+ BOOST_CHECK_THROW(v2.getInt<int>(), std::runtime_error);
UniValue v3;
BOOST_CHECK(v3.setNumStr("32482348723847471234"));
- BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error);
+ BOOST_CHECK_THROW(v3.getInt<int64_t>(), std::runtime_error);
BOOST_CHECK(v3.setNumStr("1000"));
- BOOST_CHECK_EQUAL(v3.get_int64(), 1000);
+ BOOST_CHECK_EQUAL(v3.getInt<int64_t>(), 1000);
UniValue v4;
BOOST_CHECK(v4.setNumStr("2147483648"));
- BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648);
- BOOST_CHECK_THROW(v4.get_int(), std::runtime_error);
+ BOOST_CHECK_EQUAL(v4.getInt<int64_t>(), 2147483648);
+ BOOST_CHECK_THROW(v4.getInt<int>(), std::runtime_error);
BOOST_CHECK(v4.setNumStr("1000"));
- BOOST_CHECK_EQUAL(v4.get_int(), 1000);
+ BOOST_CHECK_EQUAL(v4.getInt<int>(), 1000);
BOOST_CHECK_THROW(v4.get_str(), std::runtime_error);
BOOST_CHECK_EQUAL(v4.get_real(), 1000);
BOOST_CHECK_THROW(v4.get_array(), std::runtime_error);
@@ -118,10 +127,10 @@ BOOST_AUTO_TEST_CASE(univalue_typecheck)
BOOST_CHECK(v5.read("[true, 10]"));
BOOST_CHECK_NO_THROW(v5.get_array());
std::vector<UniValue> vals = v5.getValues();
- BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error);
+ BOOST_CHECK_THROW(vals[0].getInt<int>(), std::runtime_error);
BOOST_CHECK_EQUAL(vals[0].get_bool(), true);
- BOOST_CHECK_EQUAL(vals[1].get_int(), 10);
+ BOOST_CHECK_EQUAL(vals[1].getInt<int>(), 10);
BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error);
}
diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp
index 2943bae2b1..f8c10238d4 100644
--- a/src/univalue/test/test_json.cpp
+++ b/src/univalue/test/test_json.cpp
@@ -4,9 +4,11 @@
// It reads JSON input from stdin and exits with code 0 if it can be parsed
// successfully. It also pretty prints the parsed JSON value to stdout.
+#include <univalue.h>
+
#include <iostream>
+#include <iterator>
#include <string>
-#include "univalue.h"
using namespace std;
diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp
index 02e1a83c6d..81b1c5d3b1 100644
--- a/src/univalue/test/unitester.cpp
+++ b/src/univalue/test/unitester.cpp
@@ -2,12 +2,11 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include <univalue.h>
+
#include <cassert>
+#include <cstdio>
#include <string>
-#include "univalue.h"
#ifndef JSON_TEST_SRC
#error JSON_TEST_SRC must point to test source directory
diff --git a/src/util/bip32.h b/src/util/bip32.h
index 8f86f2aaa6..aa4eac3791 100644
--- a/src/util/bip32.h
+++ b/src/util/bip32.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_UTIL_BIP32_H
#define BITCOIN_UTIL_BIP32_H
-#include <attributes.h>
#include <string>
#include <vector>
diff --git a/src/util/bytevectorhash.cpp b/src/util/bytevectorhash.cpp
index bc060a44c9..9054db4759 100644
--- a/src/util/bytevectorhash.cpp
+++ b/src/util/bytevectorhash.cpp
@@ -6,10 +6,10 @@
#include <random.h>
#include <util/bytevectorhash.h>
-ByteVectorHash::ByteVectorHash()
+ByteVectorHash::ByteVectorHash() :
+ m_k0(GetRand<uint64_t>()),
+ m_k1(GetRand<uint64_t>())
{
- GetRandBytes({reinterpret_cast<unsigned char*>(&m_k0), sizeof(m_k0)});
- GetRandBytes({reinterpret_cast<unsigned char*>(&m_k1), sizeof(m_k1)});
}
size_t ByteVectorHash::operator()(const std::vector<unsigned char>& input) const
diff --git a/src/util/designator.h b/src/util/designator.h
new file mode 100644
index 0000000000..3670b11e00
--- /dev/null
+++ b/src/util/designator.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_DESIGNATOR_H
+#define BITCOIN_UTIL_DESIGNATOR_H
+
+/**
+ * Designated initializers can be used to avoid ordering mishaps in aggregate
+ * initialization. However, they do not prevent uninitialized members. The
+ * checks can be disabled by defining DISABLE_DESIGNATED_INITIALIZER_ERRORS.
+ * This should only be needed on MSVC 2019. MSVC 2022 supports them with the
+ * option "/std:c++20"
+ */
+#ifndef DISABLE_DESIGNATED_INITIALIZER_ERRORS
+#define Desig(field_name) .field_name =
+#else
+#define Desig(field_name)
+#endif
+
+#endif // BITCOIN_UTIL_DESIGNATOR_H
diff --git a/src/util/getuniquepath.cpp b/src/util/getuniquepath.cpp
index 6776e7785b..1d8e511c83 100644
--- a/src/util/getuniquepath.cpp
+++ b/src/util/getuniquepath.cpp
@@ -9,6 +9,6 @@
fs::path GetUniquePath(const fs::path& base)
{
FastRandomContext rnd;
- fs::path tmpFile = base / HexStr(rnd.randbytes(8));
+ fs::path tmpFile = base / fs::u8path(HexStr(rnd.randbytes(8)));
return tmpFile;
} \ No newline at end of file
diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp
index 5900daf050..c21941eb88 100644
--- a/src/util/hasher.cpp
+++ b/src/util/hasher.cpp
@@ -7,11 +7,11 @@
#include <limits>
-SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
+SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand<uint64_t>()), k1(GetRand<uint64_t>()) {}
-SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
+SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand<uint64_t>()), k1(GetRand<uint64_t>()) {}
-SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand(std::numeric_limits<uint64_t>::max())), m_k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
+SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand<uint64_t>()), m_k1(GetRand<uint64_t>()) {}
size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const
{
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index 8180604342..3d33bd7f99 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_MONEYSTR_H
#define BITCOIN_UTIL_MONEYSTR_H
-#include <attributes.h>
#include <consensus/amount.h>
#include <optional>
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
index 26439b010b..924a9cfab2 100644
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -127,6 +127,7 @@ SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
+ bool ignore_nonpersistent,
bool get_chain_name)
{
SettingsValue result;
@@ -162,6 +163,9 @@ SettingsValue GetSetting(const Settings& settings,
return;
}
+ // Ignore nonpersistent settings if requested.
+ if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
+
// Skip negated command line settings.
if (skip_negated_command_line && span.last_negated()) return;
diff --git a/src/util/settings.h b/src/util/settings.h
index ed36349232..e97158dc09 100644
--- a/src/util/settings.h
+++ b/src/util/settings.h
@@ -20,7 +20,7 @@ namespace util {
//! @note UniValue is used here for convenience and because it can be easily
//! serialized in a readable format. But any other variant type that can
//! be assigned strings, int64_t, and bool values and has get_str(),
-//! get_int64(), get_bool(), isNum(), isBool(), isFalse(), isTrue() and
+//! getInt<int64_t>(), get_bool(), isNum(), isBool(), isFalse(), isTrue() and
//! isNull() methods can be substituted if there's a need to move away
//! from UniValue. (An implementation with boost::variant was posted at
//! https://github.com/bitcoin/bitcoin/pull/15934/files#r337691812)
@@ -55,12 +55,18 @@ bool WriteSettings(const fs::path& path,
//! @param ignore_default_section_config - ignore values in the default section
//! of the config file (part before any
//! [section] keywords)
+//! @param ignore_nonpersistent - ignore non-persistent settings values (forced
+//! settings values and values specified on the
+//! command line). Only return settings in the
+//! read-only config and read-write settings
+//! files.
//! @param get_chain_name - enable special backwards compatible behavior
//! for GetChainName
SettingsValue GetSetting(const Settings& settings,
const std::string& section,
const std::string& name,
bool ignore_default_section_config,
+ bool ignore_nonpersistent,
bool get_chain_name);
//! Get combined setting value similar to GetSetting(), except if setting was
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index b5c1e28294..3579af4458 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -7,6 +7,7 @@
#include <threadinterrupt.h>
#include <tinyformat.h>
#include <util/sock.h>
+#include <util/syserror.h>
#include <util/system.h>
#include <util/time.h>
@@ -344,19 +345,8 @@ std::string NetworkErrorString(int err)
#else
std::string NetworkErrorString(int err)
{
- char buf[256];
- buf[0] = 0;
- /* Too bad there are two incompatible implementations of the
- * thread-safe strerror. */
- const char *s;
-#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
- s = strerror_r(err, buf, sizeof(buf));
-#else /* POSIX variant always returns message in buffer */
- s = buf;
- if (strerror_r(err, buf, sizeof(buf)))
- buf[0] = 0;
-#endif
- return strprintf("%s (%d)", s, err);
+ // On BSD sockets implementations, NetworkErrorString is the same as SysErrorString.
+ return SysErrorString(err);
}
#endif
diff --git a/src/util/spanparsing.h b/src/util/spanparsing.h
index ebec8714a7..51795271de 100644
--- a/src/util/spanparsing.h
+++ b/src/util/spanparsing.h
@@ -8,6 +8,7 @@
#include <span.h>
#include <string>
+#include <string_view>
#include <vector>
namespace spanparsing {
@@ -36,7 +37,7 @@ bool Func(const std::string& str, Span<const char>& sp);
*/
Span<const char> Expr(Span<const char>& sp);
-/** Split a string on every instance of sep, returning a vector.
+/** Split a string on any char found in separators, returning a vector.
*
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
*
@@ -44,13 +45,13 @@ Span<const char> Expr(Span<const char>& sp);
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
*/
template <typename T = Span<const char>>
-std::vector<T> Split(const Span<const char>& sp, char sep)
+std::vector<T> Split(const Span<const char>& sp, std::string_view separators)
{
std::vector<T> ret;
auto it = sp.begin();
auto start = it;
while (it != sp.end()) {
- if (*it == sep) {
+ if (separators.find(*it) != std::string::npos) {
ret.emplace_back(start, it);
start = it + 1;
}
@@ -60,6 +61,19 @@ std::vector<T> Split(const Span<const char>& sp, char sep)
return ret;
}
+/** Split a string on every instance of sep, returning a vector.
+ *
+ * If sep does not occur in sp, a singleton with the entirety of sp is returned.
+ *
+ * Note that this function does not care about braces, so splitting
+ * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
+ */
+template <typename T = Span<const char>>
+std::vector<T> Split(const Span<const char>& sp, char sep)
+{
+ return Split<T>(sp, std::string_view{&sep, 1});
+}
+
} // namespace spanparsing
#endif // BITCOIN_UTIL_SPANPARSING_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 35f62f0422..675fe7d2d7 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -9,6 +9,7 @@
#include <tinyformat.h>
#include <algorithm>
+#include <array>
#include <cstdlib>
#include <cstring>
#include <limits>
@@ -76,10 +77,10 @@ bool IsHexNumber(std::string_view str)
return str.size() > 0;
}
-std::vector<unsigned char> ParseHex(std::string_view str)
+template <typename Byte>
+std::vector<Byte> ParseHex(std::string_view str)
{
- // convert hex dump to vector
- std::vector<unsigned char> vch;
+ std::vector<Byte> vch;
auto it = str.begin();
while (it != str.end() && it + 1 != str.end()) {
if (IsSpace(*it)) {
@@ -89,10 +90,12 @@ std::vector<unsigned char> ParseHex(std::string_view str)
auto c1 = HexDigit(*(it++));
auto c2 = HexDigit(*(it++));
if (c1 < 0 || c2 < 0) break;
- vch.push_back(uint8_t(c1 << 4) | c2);
+ vch.push_back(Byte(c1 << 4) | Byte(c2));
}
return vch;
}
+template std::vector<std::byte> ParseHex(std::string_view);
+template std::vector<uint8_t> ParseHex(std::string_view);
void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
{
@@ -452,17 +455,37 @@ std::string Capitalize(std::string str)
return str;
}
+namespace {
+
+using ByteAsHex = std::array<char, 2>;
+
+constexpr std::array<ByteAsHex, 256> CreateByteToHexMap()
+{
+ constexpr char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ std::array<ByteAsHex, 256> byte_to_hex{};
+ for (size_t i = 0; i < byte_to_hex.size(); ++i) {
+ byte_to_hex[i][0] = hexmap[i >> 4];
+ byte_to_hex[i][1] = hexmap[i & 15];
+ }
+ return byte_to_hex;
+}
+
+} // namespace
+
std::string HexStr(const Span<const uint8_t> s)
{
std::string rv(s.size() * 2, '\0');
- static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- auto it = rv.begin();
+ static constexpr auto byte_to_hex = CreateByteToHexMap();
+ static_assert(sizeof(byte_to_hex) == 512);
+
+ char* it = rv.data();
for (uint8_t v : s) {
- *it++ = hexmap[v >> 4];
- *it++ = hexmap[v & 15];
+ std::memcpy(it, byte_to_hex[v].data(), 2);
+ it += 2;
}
- assert(it == rv.end());
+
+ assert(it == rv.data() + rv.size());
return rv;
}
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index ebb6d88952..9a96bbe67b 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -9,7 +9,6 @@
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
-#include <attributes.h>
#include <span.h>
#include <util/string.h>
@@ -55,7 +54,9 @@ enum class ByteUnit : uint64_t {
* @return A new string without unsafe chars
*/
std::string SanitizeString(std::string_view str, int rule = SAFE_CHARS_DEFAULT);
-std::vector<unsigned char> ParseHex(std::string_view str);
+/** Parse the hex string into bytes (uint8_t or std::byte). Ignores whitespace. */
+template <typename Byte = uint8_t>
+std::vector<Byte> ParseHex(std::string_view str);
signed char HexDigit(char c);
/* Returns true if each character in str is a hex character, and has an even
* number of hex digits.*/
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 8ea3a1afc6..d05222e8b8 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -3,3 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/string.h>
+
+#include <boost/algorithm/string/replace.hpp>
+
+void ReplaceAll(std::string& in_out, std::string_view search, std::string_view substitute)
+{
+ boost::replace_all(in_out, search, substitute);
+}
diff --git a/src/util/string.h b/src/util/string.h
index 36b9787db4..df20e34ae9 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -5,22 +5,30 @@
#ifndef BITCOIN_UTIL_STRING_H
#define BITCOIN_UTIL_STRING_H
-#include <attributes.h>
#include <util/spanparsing.h>
#include <algorithm>
#include <array>
+#include <cstdint>
#include <cstring>
#include <locale>
#include <sstream>
#include <string>
+#include <string_view>
#include <vector>
+void ReplaceAll(std::string& in_out, std::string_view search, std::string_view substitute);
+
[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, char sep)
{
return spanparsing::Split<std::string>(str, sep);
}
+[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, std::string_view separators)
+{
+ return spanparsing::Split<std::string>(str, separators);
+}
+
[[nodiscard]] inline std::string_view TrimStringView(std::string_view str, std::string_view pattern = " \f\n\r\t\v")
{
std::string::size_type front = str.find_first_not_of(pattern);
diff --git a/src/util/syserror.cpp b/src/util/syserror.cpp
new file mode 100644
index 0000000000..391ddd3560
--- /dev/null
+++ b/src/util/syserror.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2020-2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <tinyformat.h>
+#include <util/syserror.h>
+
+#include <cstring>
+
+std::string SysErrorString(int err)
+{
+ char buf[1024];
+ /* Too bad there are three incompatible implementations of the
+ * thread-safe strerror. */
+ const char *s = nullptr;
+#ifdef WIN32
+ if (strerror_s(buf, sizeof(buf), err) == 0) s = buf;
+#else
+#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
+ s = strerror_r(err, buf, sizeof(buf));
+#else /* POSIX variant always returns message in buffer */
+ if (strerror_r(err, buf, sizeof(buf)) == 0) s = buf;
+#endif
+#endif
+ if (s != nullptr) {
+ return strprintf("%s (%d)", s, err);
+ } else {
+ return strprintf("Unknown error (%d)", err);
+ }
+}
diff --git a/src/util/syserror.h b/src/util/syserror.h
new file mode 100644
index 0000000000..a54ba553ee
--- /dev/null
+++ b/src/util/syserror.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2010-2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_SYSERROR_H
+#define BITCOIN_UTIL_SYSERROR_H
+
+#include <string>
+
+/** Return system error string from errno value. Use this instead of
+ * std::strerror, which is not thread-safe. For network errors use
+ * NetworkErrorString from sock.h instead.
+ */
+std::string SysErrorString(int err);
+
+#endif // BITCOIN_UTIL_SYSERROR_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index f9a9ad3e20..f88b0fac77 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -25,6 +25,7 @@
#include <util/getuniquepath.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/syserror.h>
#include <util/translation.h>
@@ -75,7 +76,6 @@
#include <malloc.h>
#endif
-#include <boost/algorithm/string/replace.hpp>
#include <univalue.h>
#include <fstream>
@@ -104,7 +104,7 @@ static Mutex cs_dir_locks;
*/
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
-bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
+bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
{
LOCK(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
@@ -128,7 +128,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
return true;
}
-void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
+void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
{
LOCK(cs_dir_locks);
dir_locks.erase(fs::PathToString(directory / lockfile_name));
@@ -236,7 +236,7 @@ KeyInfo InterpretKey(std::string key)
* @return parsed settings value if it is valid, otherwise nullopt accompanied
* by a descriptive error string
*/
-static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string& value,
+static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
unsigned int flags, std::string& error)
{
// Return negated settings as false values.
@@ -246,20 +246,24 @@ static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, con
return std::nullopt;
}
// Double negatives like -nofoo=0 are supported (but discouraged)
- if (!InterpretBool(value)) {
- LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, value);
+ if (value && !InterpretBool(*value)) {
+ LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
return true;
}
return false;
}
- return value;
+ if (!value && (flags & ArgsManager::DISALLOW_ELISION)) {
+ error = strprintf("Can not set -%s with no value. Please specify value with -%s=value.", key.name, key.name);
+ return std::nullopt;
+ }
+ return value ? *value : "";
}
// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
// #include class definitions for all members.
// For example, m_settings has an internal dependency on univalue.
-ArgsManager::ArgsManager() {}
-ArgsManager::~ArgsManager() {}
+ArgsManager::ArgsManager() = default;
+ArgsManager::~ArgsManager() = default;
const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
{
@@ -320,7 +324,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
#endif
if (key == "-") break; //bitcoin-tx using stdin
- std::string val;
+ std::optional<std::string> val;
size_t is_index = key.find('=');
if (is_index != std::string::npos) {
val = key.substr(is_index + 1);
@@ -366,7 +370,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
return false;
}
- std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val, *flags, error);
+ std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
if (!value) return false;
m_settings.command_line_options[keyinfo.name].push_back(*value);
@@ -526,12 +530,15 @@ bool ArgsManager::InitSettings(std::string& error)
return true;
}
-bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
+bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
{
fs::path settings = GetPathArg("-settings", fs::path{BITCOIN_SETTINGS_FILENAME});
if (settings.empty()) {
return false;
}
+ if (backup) {
+ settings += ".bak";
+ }
if (filepath) {
*filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
}
@@ -572,10 +579,10 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
return true;
}
-bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
+bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
{
fs::path path, path_tmp;
- if (!GetSettingsPath(&path, /* temp= */ false) || !GetSettingsPath(&path_tmp, /* temp= */ true)) {
+ if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
}
@@ -592,6 +599,13 @@ bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
return true;
}
+util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
+{
+ LOCK(cs_args);
+ return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
+ /*ignore_nonpersistent=*/true, /*get_chain_name=*/false);
+}
+
bool ArgsManager::IsArgNegated(const std::string& strArg) const
{
return GetSetting(strArg).isFalse();
@@ -600,18 +614,33 @@ bool ArgsManager::IsArgNegated(const std::string& strArg) const
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
+ return SettingToString(value, strDefault);
+}
+
+std::string SettingToString(const util::SettingsValue& value, const std::string& strDefault)
+{
return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.isNum() ? value.getValStr() : value.get_str();
}
int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
- return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : LocaleIndependentAtoi<int64_t>(value.get_str());
+ return SettingToInt(value, nDefault);
+}
+
+int64_t SettingToInt(const util::SettingsValue& value, int64_t nDefault)
+{
+ return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.getInt<int64_t>() : LocaleIndependentAtoi<int64_t>(value.get_str());
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
+ return SettingToBool(value, fDefault);
+}
+
+bool SettingToBool(const util::SettingsValue& value, bool fDefault)
+{
return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
}
@@ -887,7 +916,7 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
KeyInfo key = InterpretKey(option.first);
std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
if (flags) {
- std::optional<util::SettingsValue> value = InterpretValue(key, option.second, *flags, error);
+ std::optional<util::SettingsValue> value = InterpretValue(key, &option.second, *flags, error);
if (!value) {
return false;
}
@@ -1002,6 +1031,7 @@ std::string ArgsManager::GetChainName() const
LOCK(cs_args);
util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
/* ignore_default_section_config= */ false,
+ /*ignore_nonpersistent=*/false,
/* get_chain_name= */ true);
return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
};
@@ -1034,7 +1064,8 @@ util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
{
LOCK(cs_args);
return util::GetSetting(
- m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), /* get_chain_name= */ false);
+ m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
+ /*ignore_nonpersistent=*/false, /*get_chain_name=*/false);
}
std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
@@ -1252,7 +1283,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
std::string ShellEscape(const std::string& arg)
{
std::string escaped = arg;
- boost::replace_all(escaped, "'", "'\"'\"'");
+ ReplaceAll(escaped, "'", "'\"'\"'");
return "'" + escaped + "'";
}
#endif
@@ -1374,7 +1405,7 @@ void ScheduleBatchPriority()
const static sched_param param{};
const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param);
if (rc != 0) {
- LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(rc));
+ LogPrintf("Failed to pthread_setschedparam: %s\n", SysErrorString(rc));
}
#endif
}
diff --git a/src/util/system.h b/src/util/system.h
index a66b597d41..07d7a533aa 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -14,7 +14,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <attributes.h>
#include <compat.h>
#include <compat/assumptions.h>
#include <fs.h>
@@ -76,8 +75,8 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
*/
[[nodiscard]] bool RenameOver(fs::path src, fs::path dest);
-bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
-void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name);
+bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only=false);
+void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name);
bool DirIsWritable(const fs::path& directory);
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
@@ -161,6 +160,10 @@ struct SectionInfo
int m_line;
};
+std::string SettingToString(const util::SettingsValue&, const std::string&);
+int64_t SettingToInt(const util::SettingsValue&, int64_t);
+bool SettingToBool(const util::SettingsValue&, bool);
+
class ArgsManager
{
public:
@@ -175,6 +178,7 @@ public:
// ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
// ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
+ DISALLOW_ELISION = 0x40, //!< disallow -foo syntax that doesn't assign any value
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
@@ -436,7 +440,7 @@ protected:
* Get settings file path, or return false if read-write settings were
* disabled with -nosettings.
*/
- bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false) const;
+ bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const;
/**
* Read settings file. Push errors to vector, or log them if null.
@@ -444,9 +448,16 @@ protected:
bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
/**
- * Write settings file. Push errors to vector, or log them if null.
+ * Write settings file or backup settings file. Push errors to vector, or
+ * log them if null.
+ */
+ bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const;
+
+ /**
+ * Get current setting from config file or read/write settings file,
+ * ignoring nonpersistent command line or forced settings values.
*/
- bool WriteSettingsFile(std::vector<std::string>* errors = nullptr) const;
+ util::SettingsValue GetPersistentSetting(const std::string& name) const;
/**
* Access settings with lock held.
diff --git a/src/util/time.cpp b/src/util/time.cpp
index e428430bac..7d9d6bcff1 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -66,20 +66,16 @@ bool ChronoSanityCheck()
return true;
}
-template <typename T>
-T GetTime()
+NodeClock::time_point NodeClock::now() noexcept
{
const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
const auto ret{
mocktime.count() ?
mocktime :
- std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch())};
+ std::chrono::system_clock::now().time_since_epoch()};
assert(ret > 0s);
- return ret;
-}
-template std::chrono::seconds GetTime();
-template std::chrono::milliseconds GetTime();
-template std::chrono::microseconds GetTime();
+ return time_point{ret};
+};
template <typename T>
static T GetSystemTime()
@@ -115,11 +111,6 @@ int64_t GetTimeMicros()
return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
}
-int64_t GetTimeSeconds()
-{
- return int64_t{GetSystemTime<std::chrono::seconds>().count()};
-}
-
int64_t GetTime() { return GetTime<std::chrono::seconds>().count(); }
std::string FormatISO8601DateTime(int64_t nTime) {
diff --git a/src/util/time.h b/src/util/time.h
index 9d92b23725..ad91a72860 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -14,18 +14,37 @@
using namespace std::chrono_literals;
+/** Mockable clock in the context of tests, otherwise the system clock */
+struct NodeClock : public std::chrono::system_clock {
+ using time_point = std::chrono::time_point<NodeClock>;
+ /** Return current system time or mocked time, if set */
+ static time_point now() noexcept;
+ static std::time_t to_time_t(const time_point&) = delete; // unused
+ static time_point from_time_t(std::time_t) = delete; // unused
+};
+using NodeSeconds = std::chrono::time_point<NodeClock, std::chrono::seconds>;
+
+using SteadySeconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::seconds>;
+using SteadyMilliseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::milliseconds>;
+using SteadyMicroseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds>;
+
void UninterruptibleSleep(const std::chrono::microseconds& n);
/**
- * Helper to count the seconds of a duration.
+ * Helper to count the seconds of a duration/time_point.
*
- * All durations should be using std::chrono and calling this should generally
+ * All durations/time_points should be using std::chrono and calling this should generally
* be avoided in code. Though, it is still preferred to an inline t.count() to
* protect against a reliance on the exact type of t.
*
- * This helper is used to convert durations before passing them over an
+ * This helper is used to convert durations/time_points before passing them over an
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
+template <typename Duration, typename Timepoint>
+constexpr auto TicksSinceEpoch(Timepoint t)
+{
+ return std::chrono::time_point_cast<Duration>(t).time_since_epoch().count();
+}
constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
@@ -39,7 +58,11 @@ inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
/**
* DEPRECATED
- * Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
+ * Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
+ * ClockType is
+ * - std::chrono::steady_clock for steady time
+ * - std::chrono::system_clock for system time
+ * - NodeClock for mockable system time
*/
int64_t GetTime();
@@ -47,8 +70,6 @@ int64_t GetTime();
int64_t GetTimeMillis();
/** Returns the system time (not mockable) */
int64_t GetTimeMicros();
-/** Returns the system time (not mockable) */
-int64_t GetTimeSeconds(); // Like GetTime(), but not mockable
/**
* DEPRECATED
@@ -64,9 +85,21 @@ void SetMockTime(std::chrono::seconds mock_time_in);
/** For testing */
std::chrono::seconds GetMockTime();
-/** Return system time (or mocked time, if set) */
+/**
+ * Return the current time point cast to the given precision. Only use this
+ * when an exact precision is needed, otherwise use T::clock::now() directly.
+ */
+template <typename T>
+T Now()
+{
+ return std::chrono::time_point_cast<typename T::duration>(T::clock::now());
+}
+/** DEPRECATED, see GetTime */
template <typename T>
-T GetTime();
+T GetTime()
+{
+ return Now<std::chrono::time_point<NodeClock, T>>().time_since_epoch();
+}
/**
* ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date}
diff --git a/src/util/tokenpipe.cpp b/src/util/tokenpipe.cpp
index 4c091cd2e6..49456814e2 100644
--- a/src/util/tokenpipe.cpp
+++ b/src/util/tokenpipe.cpp
@@ -3,7 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/tokenpipe.h>
+#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
+#endif
#ifndef WIN32
diff --git a/src/validation.cpp b/src/validation.cpp
index 208bcee008..448720ff83 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -16,13 +16,12 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <cuckoocache.h>
-#include <deploymentstatus.h>
#include <flatfile.h>
#include <hash.h>
+#include <kernel/coinstats.h>
#include <logging.h>
#include <logging/timer.h>
#include <node/blockstorage.h>
-#include <node/coinstats.h>
#include <node/ui_interface.h>
#include <node/utxo_snapshot.h>
#include <policy/policy.h>
@@ -37,7 +36,6 @@
#include <script/sigcache.h>
#include <shutdown.h>
#include <signet.h>
-#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
@@ -55,23 +53,23 @@
#include <warnings.h>
#include <algorithm>
+#include <deque>
#include <numeric>
#include <optional>
#include <string>
-#include <boost/algorithm/string/replace.hpp>
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
+using kernel::ComputeUTXOStats;
using node::BLOCKFILE_CHUNK_SIZE;
using node::BlockManager;
using node::BlockMap;
using node::CBlockIndexHeightOnlyComparator;
using node::CBlockIndexWorkComparator;
-using node::CCoinsStats;
-using node::CoinStatsHashType;
using node::fImporting;
using node::fPruneMode;
using node::fReindex;
-using node::GetUTXOStats;
using node::nPruneTarget;
using node::OpenBlockFile;
using node::ReadBlockFromDisk;
@@ -251,14 +249,19 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
maxInputHeight = std::max(maxInputHeight, height);
}
}
- lp->maxInputBlock = tip->GetAncestor(maxInputHeight);
+ // tip->GetAncestor(maxInputHeight) should never return a nullptr
+ // because maxInputHeight is always less than the tip height.
+ // It would, however, be a bad bug to continue execution, since a
+ // LockPoints object with the maxInputBlock member set to nullptr
+ // signifies no relative lock time.
+ lp->maxInputBlock = Assert(tip->GetAncestor(maxInputHeight));
}
}
return EvaluateSequenceLocks(index, lockPair);
}
// Returns the script flags which should be checked for a given block
-static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Consensus::Params& chainparams);
+static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
@@ -811,7 +814,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return false; // state filled in by CheckTxInputs
}
- // Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}
@@ -1023,7 +1025,6 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
TxValidationState& state = ws.m_state;
- const CChainParams& chainparams = args.m_chainparams;
// Check again against the current block tip's script verification
// flags to cache our script execution flags. This is, of course,
@@ -1040,7 +1041,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus())};
+ unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
@@ -1116,6 +1117,7 @@ bool MemPoolAccept::SubmitPackage(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.
+ Assume(false);
all_submitted = false;
package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
strprintf("BUG! PolicyScriptChecks succeeded but ConsensusScriptChecks failed: %s",
@@ -1130,6 +1132,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
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.
+ Assume(false);
all_submitted = false;
package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
strprintf("BUG! Mempool ancestors or descendants were underestimated: %s",
@@ -1143,6 +1146,7 @@ bool MemPoolAccept::SubmitPackage(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.
+ Assume(false);
all_submitted = false;
package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
strprintf("BUG! Adding to mempool failed: %s", ws.m_ptx->GetHash().ToString()));
@@ -1450,7 +1454,7 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
std::vector<COutPoint> coins_to_uncache;
- const CChainParams& chainparams = Params();
+ const CChainParams& chainparams = active_chainstate.m_params;
const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
if (test_accept) {
@@ -1488,7 +1492,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
}
CoinsViews::CoinsViews(
- std::string ldb_name,
+ fs::path ldb_name,
size_t cache_size_bytes,
bool in_memory,
bool should_wipe) : m_dbview(
@@ -1508,7 +1512,7 @@ CChainState::CChainState(
std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_blockman(blockman),
- m_params(::Params()),
+ m_params(chainman.GetParams()),
m_chainman(chainman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
@@ -1516,7 +1520,7 @@ void CChainState::InitCoinsDB(
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
- std::string leveldb_name)
+ fs::path leveldb_name)
{
if (m_from_snapshot_blockhash) {
leveldb_name += "_" + m_from_snapshot_blockhash->ToString();
@@ -1574,7 +1578,7 @@ static void AlertNotify(const std::string& strMessage)
std::string singleQuote("'");
std::string safeStatus = SanitizeString(strMessage);
safeStatus = singleQuote+safeStatus+singleQuote;
- boost::replace_all(strCmd, "%s", safeStatus);
+ ReplaceAll(strCmd, "%s", safeStatus);
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
@@ -1902,10 +1906,11 @@ void StopScriptCheckWorkerThreads()
class WarningBitsConditionChecker : public AbstractThresholdConditionChecker
{
private:
- int bit;
+ const ChainstateManager& m_chainman;
+ int m_bit;
public:
- explicit WarningBitsConditionChecker(int bitIn) : bit(bitIn) {}
+ explicit WarningBitsConditionChecker(const ChainstateManager& chainman, int bit) : m_chainman{chainman}, m_bit(bit) {}
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
@@ -1916,15 +1921,17 @@ public:
{
return pindex->nHeight >= params.MinBIP9WarningHeight &&
((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
- ((pindex->nVersion >> bit) & 1) != 0 &&
- ((g_versionbitscache.ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
+ ((pindex->nVersion >> m_bit) & 1) != 0 &&
+ ((m_chainman.m_versionbitscache.ComputeBlockVersion(pindex->pprev, params) >> m_bit) & 1) == 0;
}
};
static std::array<ThresholdConditionCache, VERSIONBITS_NUM_BITS> warningcache GUARDED_BY(cs_main);
-static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Consensus::Params& consensusparams)
+static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
{
+ const Consensus::Params& consensusparams = chainman.GetConsensus();
+
// BIP16 didn't become active until Apr 1 2012 (on mainnet, and
// retroactively applied to testnet)
// However, only one historical block violated the P2SH rules (on both
@@ -1940,22 +1947,22 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Co
}
// Enforce the DERSIG (BIP66) rule
- if (DeploymentActiveAt(block_index, consensusparams, Consensus::DEPLOYMENT_DERSIG)) {
+ if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_DERSIG)) {
flags |= SCRIPT_VERIFY_DERSIG;
}
// Enforce CHECKLOCKTIMEVERIFY (BIP65)
- if (DeploymentActiveAt(block_index, consensusparams, Consensus::DEPLOYMENT_CLTV)) {
+ if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_CLTV)) {
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
// Enforce CHECKSEQUENCEVERIFY (BIP112)
- if (DeploymentActiveAt(block_index, consensusparams, Consensus::DEPLOYMENT_CSV)) {
+ if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_CSV)) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
- if (DeploymentActiveAt(block_index, consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
+ if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_SEGWIT)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -1998,7 +2005,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
- // GetAdjustedTime() to go backward).
+ // m_adjusted_time_callback() to go backward).
if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
@@ -2146,12 +2153,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Enforce BIP68 (sequence locks)
int nLockTimeFlags = 0;
- if (DeploymentActiveAt(*pindex, m_params.GetConsensus(), Consensus::DEPLOYMENT_CSV)) {
+ if (DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_CSV)) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
// Get the script flags for this block
- unsigned int flags{GetBlockScriptFlags(*pindex, m_params.GetConsensus())};
+ unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal);
@@ -2447,9 +2454,9 @@ bool CChainState::FlushStateToDisk(
full_flush_completed = true;
TRACE5(utxocache, flush,
(int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
- (u_int32_t)mode,
- (u_int64_t)coins_count,
- (u_int64_t)coins_mem_usage,
+ (uint32_t)mode,
+ (uint64_t)coins_count,
+ (uint64_t)coins_mem_usage,
(bool)fFlushForPrune);
}
}
@@ -2549,7 +2556,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
if (!this->IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
- WarningBitsConditionChecker checker(bit);
+ WarningBitsConditionChecker checker(m_chainman, bit);
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache.at(bit));
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
@@ -2646,7 +2653,7 @@ static int64_t nTimePostConnect = 0;
struct PerBlockConnectTrace {
CBlockIndex* pindex = nullptr;
std::shared_ptr<const CBlock> pblock;
- PerBlockConnectTrace() {}
+ PerBlockConnectTrace() = default;
};
/**
* Used to track blocks whose transactions were applied to the UTXO state as a
@@ -3273,7 +3280,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
- if (DeploymentActiveAt(*pindexNew, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
+ if (DeploymentActiveAt(*pindexNew, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) {
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
@@ -3391,11 +3398,11 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
return true;
}
-void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
+void ChainstateManager::UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev) const
{
int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00);
- if (commitpos != NO_WITNESS_COMMITMENT && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT) && !block.vtx[0]->HasWitness()) {
+ if (commitpos != NO_WITNESS_COMMITMENT && DeploymentActiveAfter(pindexPrev, *this, Consensus::DEPLOYMENT_SEGWIT) && !block.vtx[0]->HasWitness()) {
CMutableTransaction tx(*block.vtx[0]);
tx.vin[0].scriptWitness.stack.resize(1);
tx.vin[0].scriptWitness.stack[0] = nonce;
@@ -3403,7 +3410,7 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
}
}
-std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
+std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const
{
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
@@ -3426,7 +3433,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
tx.vout.push_back(out);
block.vtx[0] = MakeTransactionRef(std::move(tx));
}
- UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
+ UpdateUncommittedBlockStructures(block, pindexPrev);
return commitment;
}
@@ -3439,14 +3446,14 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
// Check proof of work
- const Consensus::Params& consensusParams = params.GetConsensus();
+ const Consensus::Params& consensusParams = chainman.GetConsensus();
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work");
@@ -3455,7 +3462,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
- const CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
+ const CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(chainman.GetParams().Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint");
@@ -3471,9 +3478,9 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
// Reject blocks with outdated version
- if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB)) ||
- (block.nVersion < 3 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DERSIG)) ||
- (block.nVersion < 4 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CLTV))) {
+ if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_HEIGHTINCB)) ||
+ (block.nVersion < 3 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_DERSIG)) ||
+ (block.nVersion < 4 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_CLTV))) {
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
}
@@ -3487,13 +3494,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
+static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const ChainstateManager& chainman, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Enforce BIP113 (Median Time Past).
int nLockTimeFlags = 0;
- if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV)) {
+ if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_CSV)) {
assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3510,7 +3517,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
}
// Enforce rule that the coinbase starts with serialized block height
- if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB))
+ if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_HEIGHTINCB))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
@@ -3528,7 +3535,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) {
+ if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT)) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != NO_WITNESS_COMMITMENT) {
bool malleated = false;
@@ -3569,13 +3576,13 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
return true;
}
-bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
+bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf{m_blockman.m_block_index.find(hash)};
- if (hash != chainparams.GetConsensus().hashGenesisBlock) {
+ if (hash != GetConsensus().hashGenesisBlock) {
if (miSelf != m_blockman.m_block_index.end()) {
// Block header is already known.
CBlockIndex* pindex = &(miSelf->second);
@@ -3588,7 +3595,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
return true;
}
- if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) {
+ if (!CheckBlockHeader(block, state, GetConsensus())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3605,7 +3612,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, m_blockman, chainparams, pindexPrev, GetAdjustedTime())) {
+ if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_adjusted_time_callback())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3658,14 +3665,14 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
// Exposed wrapper for AcceptBlockHeader
-bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
+bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CBlockIndex** ppindex)
{
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- bool accepted{AcceptBlockHeader(header, state, chainparams, &pindex)};
+ bool accepted{AcceptBlockHeader(header, state, &pindex)};
ActiveChainstate().CheckBlockIndex();
if (!accepted) {
@@ -3679,7 +3686,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
if (NotifyHeaderTip(ActiveChainstate())) {
if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) {
const CBlockIndex& last_accepted{**ppindex};
- const int64_t blocks_left{(GetTime() - last_accepted.GetBlockTime()) / chainparams.GetConsensus().nPowTargetSpacing};
+ const int64_t blocks_left{(GetTime() - last_accepted.GetBlockTime()) / 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);
}
@@ -3698,7 +3705,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
- bool accepted_header{m_chainman.AcceptBlockHeader(block, state, m_params, &pindex)};
+ bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex)};
CheckBlockIndex();
if (!accepted_header)
@@ -3738,7 +3745,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
}
if (!CheckBlock(block, state, m_params.GetConsensus()) ||
- !ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) {
+ !ContextualCheckBlock(block, state, m_chainman, pindex->pprev)) {
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(pindex);
@@ -3771,7 +3778,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
return true;
}
-bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
+bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
@@ -3789,7 +3796,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
// malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
// https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
// not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
- bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
+ bool ret = CheckBlock(*block, state, GetConsensus());
if (ret) {
// Store to disk
ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
@@ -3829,6 +3836,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -3842,11 +3850,11 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
+ if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, adjusted_time_callback()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
- if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
+ if (!ContextualCheckBlock(block, state, chainstate.m_chainman, pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, true)) {
return false;
@@ -4106,10 +4114,11 @@ bool CChainState::ReplayBlocks()
// Roll forward from the forking point to the new tip.
int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
- const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
- LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
+ const CBlockIndex& pindex{*Assert(pindexNew->GetAncestor(nHeight))};
+
+ LogPrintf("Rolling forward %s (%i)\n", pindex.GetBlockHash().ToString(), nHeight);
uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
- if (!RollforwardBlock(pindex, cache)) return false;
+ if (!RollforwardBlock(&pindex, cache)) return false;
}
cache.SetBestBlock(pindexNew->GetBlockHash());
@@ -4125,7 +4134,7 @@ bool CChainState::NeedsRedownload() const
// At and above m_params.SegwitHeight, segwit consensus rules must be validated
CBlockIndex* block{m_chain.Tip()};
- while (block != nullptr && DeploymentActiveAt(*block, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
+ while (block != nullptr && DeploymentActiveAt(*block, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) {
if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
// block is insufficiently validated for a segwit client
return true;
@@ -4149,7 +4158,7 @@ bool ChainstateManager::LoadBlockIndex()
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = m_blockman.LoadBlockIndexDB();
+ bool ret = m_blockman.LoadBlockIndexDB(GetConsensus());
if (!ret) return false;
std::vector<CBlockIndex*> vSortedByHeight{m_blockman.GetAllBlockIndices()};
@@ -4978,14 +4987,15 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));
if (!snapshot_start_block) {
- // Needed for GetUTXOStats and ExpectedAssumeutxo to determine the height and to avoid a crash when base_blockhash.IsNull()
+ // Needed for ComputeUTXOStats and ExpectedAssumeutxo to determine the
+ // height and to avoid a crash when base_blockhash.IsNull()
LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
base_blockhash.ToString());
return false;
}
int base_height = snapshot_start_block->nHeight;
- auto maybe_au_data = ExpectedAssumeutxo(base_height, ::Params());
+ auto maybe_au_data = ExpectedAssumeutxo(base_height, GetParams());
if (!maybe_au_data) {
LogPrintf("[snapshot] assumeutxo height in snapshot metadata not recognized " /* Continued */
@@ -5086,22 +5096,22 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(coins_cache.GetBestBlock() == base_blockhash);
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
// As above, okay to immediately release cs_main here since no other context knows
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
+ const std::optional<CCoinsStats> maybe_stats = ComputeUTXOStats(CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, breakpoint_fnc);
+ if (!maybe_stats.has_value()) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
- if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) {
+ if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
- au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
+ au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
return false;
}
@@ -5139,7 +5149,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
// won't ask to rewind the entire assumed-valid chain on startup.
- if (DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
+ if (DeploymentActiveAt(*index, *this, Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
@@ -5208,9 +5218,9 @@ ChainstateManager::~ChainstateManager()
{
LOCK(::cs_main);
- // TODO: The version bits cache and warning cache should probably become
- // non-globals
- g_versionbitscache.Clear();
+ m_versionbitscache.Clear();
+
+ // TODO: The warning cache should probably become non-global
for (auto& i : warningcache) {
i.clear();
}
diff --git a/src/validation.h b/src/validation.h
index e3ea8617e7..31dd089005 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -13,7 +13,10 @@
#include <arith_uint256.h>
#include <attributes.h>
#include <chain.h>
+#include <chainparams.h>
+#include <kernel/chainstatemanager_opts.h>
#include <consensus/amount.h>
+#include <deploymentstatus.h>
#include <fs.h>
#include <node/blockstorage.h>
#include <policy/feerate.h>
@@ -26,6 +29,7 @@
#include <util/check.h>
#include <util/hasher.h>
#include <util/translation.h>
+#include <versionbits.h>
#include <atomic>
#include <map>
@@ -40,7 +44,6 @@
class CChainState;
class CBlockTreeDB;
-class CChainParams;
class CTxMemPool;
class ChainstateManager;
struct ChainTxData;
@@ -51,6 +54,9 @@ struct AssumeutxoData;
namespace node {
class SnapshotMetadata;
} // namespace node
+namespace Consensus {
+struct Params;
+} // namespace Consensus
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
@@ -328,7 +334,8 @@ public:
bool operator()();
- void swap(CScriptCheck &check) {
+ void swap(CScriptCheck& check) noexcept
+ {
std::swap(ptxTo, check.ptxTo);
std::swap(m_tx_out, check.m_tx_out);
std::swap(nIn, check.nIn);
@@ -355,15 +362,10 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW = true,
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
-void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
-
-/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */
-std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
-
/** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
class CVerifyDB {
public:
@@ -423,7 +425,7 @@ public:
//! state to disk, which should not be done until the health of the database is verified.
//!
//! All arguments forwarded onto CCoinsViewDB.
- CoinsViews(std::string ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe);
+ CoinsViews(fs::path ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe);
//! Initialize the CCoinsViewCache member.
void InitCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
@@ -494,6 +496,7 @@ public:
node::BlockManager& m_blockman;
/** Chain parameters for this chainstate */
+ /* TODO: replace with m_chainman.GetParams() */
const CChainParams& m_params;
//! The chainstate manager that owns this chainstate. The reference is
@@ -517,7 +520,7 @@ public:
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
- std::string leveldb_name = "chainstate");
+ fs::path leveldb_name = "chainstate");
//! Initialize the in-memory coins cache (to be done after the health of the on-disk database
//! is verified).
@@ -833,6 +836,10 @@ private:
CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr};
+ const CChainParams m_chainparams;
+
+ const std::function<int64_t()> m_adjusted_time_callback;
+
//! Internal helper for ActivateSnapshot().
[[nodiscard]] bool PopulateAndValidateSnapshot(
CChainState& snapshot_chainstate,
@@ -846,11 +853,19 @@ private:
bool AcceptBlockHeader(
const CBlockHeader& block,
BlockValidationState& state,
- const CChainParams& chainparams,
CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
friend CChainState;
public:
+ using Options = ChainstateManagerOpts;
+
+ explicit ChainstateManager(const Options& opts)
+ : m_chainparams{opts.chainparams},
+ m_adjusted_time_callback{Assert(opts.adjusted_time_callback)} {};
+
+ const CChainParams& GetParams() const { return m_chainparams; }
+ const Consensus::Params& GetConsensus() const { return m_chainparams.GetConsensus(); }
+
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
@@ -931,6 +946,11 @@ public:
return m_blockman.m_block_index;
}
+ /**
+ * Track versionbit status
+ */
+ mutable VersionBitsCache m_versionbitscache;
+
//! @returns true if a snapshot-based chainstate is in use. Also implies
//! that a background validation chainstate is also in use.
bool IsSnapshotActive() const;
@@ -959,7 +979,7 @@ public:
* @param[out] new_block A boolean which is set to indicate if the block was first received via this call
* @returns If the block was processed, independently of block validity
*/
- bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
+ bool ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
@@ -969,10 +989,9 @@ public:
*
* @param[in] block The block headers themselves
* @param[out] state This may be set to an Error state if any error occurred processing them
- * @param[in] chainparams The params for the chain we want to connect to
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
*/
- bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
+ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
/**
* Try to add a transaction to the memory pool.
@@ -990,9 +1009,34 @@ public:
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ /** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
+ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev) const;
+
+ /** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */
+ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const;
+
~ChainstateManager();
};
+/** Deployment* info via ChainstateManager */
+template<typename DEP>
+bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const ChainstateManager& chainman, DEP dep)
+{
+ return DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), dep, chainman.m_versionbitscache);
+}
+
+template<typename DEP>
+bool DeploymentActiveAt(const CBlockIndex& index, const ChainstateManager& chainman, DEP dep)
+{
+ return DeploymentActiveAt(index, chainman.GetConsensus(), dep, chainman.m_versionbitscache);
+}
+
+template<typename DEP>
+bool DeploymentEnabled(const ChainstateManager& chainman, DEP dep)
+{
+ return DeploymentEnabled(chainman.GetConsensus(), dep);
+}
+
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
/** Dump the mempool to disk. */
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 1e07ff23ae..613c5b65ef 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -5,6 +5,7 @@
#include <validationinterface.h>
+#include <attributes.h>
#include <chain.h>
#include <consensus/validation.h>
#include <logging.h>
@@ -16,14 +17,16 @@
#include <unordered_map>
#include <utility>
-//! The MainSignalsInstance manages a list of shared_ptr<CValidationInterface>
-//! callbacks.
-//!
-//! A std::unordered_map is used to track what callbacks are currently
-//! registered, and a std::list is to used to store the callbacks that are
-//! currently registered as well as any callbacks that are just unregistered
-//! and about to be deleted when they are done executing.
-struct MainSignalsInstance {
+/**
+ * MainSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
+ *
+ * A std::unordered_map is used to track what callbacks are currently
+ * registered, and a std::list is used to store the callbacks that are
+ * currently registered as well as any callbacks that are just unregistered
+ * and about to be deleted when they are done executing.
+ */
+class MainSignalsImpl
+{
private:
Mutex m_mutex;
//! List entries consist of a callback pointer and reference count. The
@@ -40,9 +43,9 @@ public:
// our own queue here :(
SingleThreadedSchedulerClient m_schedulerClient;
- explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
+ explicit MainSignalsImpl(CScheduler& scheduler LIFETIMEBOUND) : m_schedulerClient(scheduler) {}
- void Register(std::shared_ptr<CValidationInterface> callbacks)
+ void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
auto inserted = m_map.emplace(callbacks.get(), m_list.end());
@@ -50,7 +53,7 @@ public:
inserted.first->second->callbacks = std::move(callbacks);
}
- void Unregister(CValidationInterface* callbacks)
+ void Unregister(CValidationInterface* callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
auto it = m_map.find(callbacks);
@@ -64,7 +67,7 @@ public:
//! map entry. After this call, the list may still contain callbacks that
//! are currently executing, but it will be cleared when they are done
//! executing.
- void Clear()
+ void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
for (const auto& entry : m_map) {
@@ -73,7 +76,7 @@ public:
m_map.clear();
}
- template<typename F> void Iterate(F&& f)
+ template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
WAIT_LOCK(m_mutex, lock);
for (auto it = m_list.begin(); it != m_list.end();) {
@@ -92,7 +95,7 @@ static CMainSignals g_signals;
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler)
{
assert(!m_internals);
- m_internals.reset(new MainSignalsInstance(&scheduler));
+ m_internals = std::make_unique<MainSignalsImpl>(scheduler);
}
void CMainSignals::UnregisterBackgroundSignalScheduler()
diff --git a/src/validationinterface.h b/src/validationinterface.h
index ac62f8b467..a929a3d56b 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -17,9 +17,7 @@ class BlockValidationState;
class CBlock;
class CBlockIndex;
struct CBlockLocator;
-class CConnman;
class CValidationInterface;
-class uint256;
class CScheduler;
enum class MemPoolRemovalReason;
@@ -177,10 +175,10 @@ protected:
friend class ValidationInterfaceTest;
};
-struct MainSignalsInstance;
+class MainSignalsImpl;
class CMainSignals {
private:
- std::unique_ptr<MainSignalsInstance> m_internals;
+ std::unique_ptr<MainSignalsImpl> m_internals;
friend void ::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface>);
friend void ::UnregisterValidationInterface(CValidationInterface*);
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 7a297c2bbb..dc85655028 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -2,8 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <versionbits.h>
#include <consensus/params.h>
+#include <util/check.h>
+#include <versionbits.h>
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
@@ -158,7 +159,7 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex*
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
// The parent of the genesis block is represented by nullptr.
- pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
+ pindexPrev = Assert(pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)));
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
diff --git a/src/versionbits.h b/src/versionbits.h
index 1b3fa11e61..9f7ee1b48e 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -92,16 +92,16 @@ public:
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
/** Get the BIP9 state for a given deployment for the block after pindexPrev. */
- ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+ ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/** Get the block height at which the BIP9 deployment switched into the state for the block after pindexPrev. */
- int StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+ int StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/** Determine what nVersion a new block should use
*/
- int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
+ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
- void Clear();
+ void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
};
#endif // BITCOIN_VERSIONBITS_H
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 7f709ffa3e..f8230f7a1d 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -99,7 +99,7 @@ void BerkeleyEnvironment::Close()
if (ret != 0)
LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
if (!fMockDb)
- DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
+ DbEnv((uint32_t)0).remove(strPath.c_str(), 0);
if (error_file) fclose(error_file);
@@ -248,7 +248,7 @@ const void* BerkeleyBatch::SafeDbt::get_data() const
return m_dbt.get_data();
}
-u_int32_t BerkeleyBatch::SafeDbt::get_size() const
+uint32_t BerkeleyBatch::SafeDbt::get_size() const
{
return m_dbt.get_size();
}
@@ -261,7 +261,7 @@ BerkeleyBatch::SafeDbt::operator Dbt*()
bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
{
fs::path walletDir = env->Directory();
- fs::path file_path = walletDir / strFile;
+ fs::path file_path = walletDir / m_filename;
LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
@@ -275,6 +275,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
assert(m_refcount == 0);
Db db(env->dbenv.get(), 0);
+ const std::string strFile = fs::PathToString(m_filename);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result != 0) {
errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
@@ -297,11 +298,11 @@ BerkeleyDatabase::~BerkeleyDatabase()
{
if (env) {
LOCK(cs_db);
- env->CloseDb(strFile);
+ env->CloseDb(m_filename);
assert(!m_db);
- size_t erased = env->m_databases.erase(strFile);
+ size_t erased = env->m_databases.erase(m_filename);
assert(erased == 1);
- env->m_fileids.erase(strFile);
+ env->m_fileids.erase(fs::PathToString(m_filename));
}
}
@@ -313,7 +314,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, b
fFlushOnClose = fFlushOnCloseIn;
env = database.env.get();
pdb = database.m_db.get();
- strFile = database.strFile;
+ strFile = fs::PathToString(database.m_filename);
if (!Exists(std::string("version"))) {
bool fTmp = fReadOnly;
fReadOnly = false;
@@ -335,6 +336,7 @@ void BerkeleyDatabase::Open()
if (m_db == nullptr) {
int ret;
std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
+ const std::string strFile = fs::PathToString(m_filename);
bool fMockDb = env->IsMock();
if (fMockDb) {
@@ -407,11 +409,11 @@ void BerkeleyBatch::Close()
Flush();
}
-void BerkeleyEnvironment::CloseDb(const std::string& strFile)
+void BerkeleyEnvironment::CloseDb(const fs::path& filename)
{
{
LOCK(cs_db);
- auto it = m_databases.find(strFile);
+ auto it = m_databases.find(filename);
assert(it != m_databases.end());
BerkeleyDatabase& database = it->second.get();
if (database.m_db) {
@@ -434,12 +436,12 @@ void BerkeleyEnvironment::ReloadDbEnv()
return true;
});
- std::vector<std::string> filenames;
+ std::vector<fs::path> filenames;
for (auto it : m_databases) {
filenames.push_back(it.first);
}
// Close the individual Db's
- for (const std::string& filename : filenames) {
+ for (const fs::path& filename : filenames) {
CloseDb(filename);
}
// Reset the environment
@@ -454,9 +456,10 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
while (true) {
{
LOCK(cs_db);
+ const std::string strFile = fs::PathToString(m_filename);
if (m_refcount <= 0) {
// Flush log data to the dat file
- env->CloseDb(strFile);
+ env->CloseDb(m_filename);
env->CheckpointLSN(strFile);
m_refcount = -1;
@@ -508,7 +511,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
}
if (fSuccess) {
db.Close();
- env->CloseDb(strFile);
+ env->CloseDb(m_filename);
if (pdbCopy->close(0))
fSuccess = false;
} else {
@@ -544,13 +547,14 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
LOCK(cs_db);
bool no_dbs_accessed = true;
for (auto& db_it : m_databases) {
- std::string strFile = db_it.first;
+ const fs::path& filename = db_it.first;
int nRefCount = db_it.second.get().m_refcount;
if (nRefCount < 0) continue;
+ const std::string strFile = fs::PathToString(filename);
LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
if (nRefCount == 0) {
// Move log data to the dat file
- CloseDb(strFile);
+ CloseDb(filename);
LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
dbenv->txn_checkpoint(0, 0, 0);
LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
@@ -590,11 +594,12 @@ bool BerkeleyDatabase::PeriodicFlush()
// Don't flush if there haven't been any batch writes for this database.
if (m_refcount < 0) return false;
+ const std::string strFile = fs::PathToString(m_filename);
LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained
- env->CloseDb(strFile);
+ env->CloseDb(m_filename);
env->CheckpointLSN(strFile);
m_refcount = -1;
@@ -605,6 +610,7 @@ bool BerkeleyDatabase::PeriodicFlush()
bool BerkeleyDatabase::Backup(const std::string& strDest) const
{
+ const std::string strFile = fs::PathToString(m_filename);
while (true)
{
{
@@ -612,14 +618,14 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const
if (m_refcount <= 0)
{
// Flush log data to the dat file
- env->CloseDb(strFile);
+ env->CloseDb(m_filename);
env->CheckpointLSN(strFile);
// Copy wallet file
- fs::path pathSrc = env->Directory() / strFile;
+ fs::path pathSrc = env->Directory() / m_filename;
fs::path pathDest(fs::PathFromString(strDest));
if (fs::is_directory(pathDest))
- pathDest /= fs::PathFromString(strFile);
+ pathDest /= m_filename;
try {
if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
@@ -831,7 +837,7 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
std::unique_ptr<BerkeleyDatabase> db;
{
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
- std::string data_filename = fs::PathToString(data_file.filename());
+ fs::path data_filename = data_file.filename();
std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
if (env->m_databases.count(data_filename)) {
error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index fd6c76183e..ddab85521b 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -34,7 +34,7 @@ struct bilingual_str;
namespace wallet {
struct WalletDatabaseFileId {
- u_int8_t value[DB_FILE_ID_LEN];
+ uint8_t value[DB_FILE_ID_LEN];
bool operator==(const WalletDatabaseFileId& rhs) const;
};
@@ -51,7 +51,7 @@ private:
public:
std::unique_ptr<DbEnv> dbenv;
- std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
+ std::map<fs::path, std::reference_wrapper<BerkeleyDatabase>> m_databases;
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
std::condition_variable_any m_db_in_use;
bool m_use_shared_memory;
@@ -70,7 +70,7 @@ public:
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
- void CloseDb(const std::string& strFile);
+ void CloseDb(const fs::path& filename);
void ReloadDbEnv();
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
@@ -97,10 +97,10 @@ public:
BerkeleyDatabase() = delete;
/** Create DB handle to real database */
- BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename, const DatabaseOptions& options) :
- WalletDatabase(), env(std::move(env)), strFile(std::move(filename)), m_max_log_mb(options.max_log_mb)
+ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
+ WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb)
{
- auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
+ auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
assert(inserted.second);
}
@@ -141,7 +141,7 @@ public:
bool Verify(bilingual_str& error);
/** Return path to main database filename */
- std::string Filename() override { return fs::PathToString(env->Directory() / strFile); }
+ std::string Filename() override { return fs::PathToString(env->Directory() / m_filename); }
std::string Format() override { return "bdb"; }
/**
@@ -158,7 +158,7 @@ public:
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
std::unique_ptr<Db> m_db;
- std::string strFile;
+ fs::path m_filename;
int64_t m_max_log_mb;
/** Make a BerkeleyBatch connected to this database */
@@ -182,7 +182,7 @@ class BerkeleyBatch : public DatabaseBatch
// delegate to Dbt
const void* get_data() const;
- u_int32_t get_size() const;
+ uint32_t get_size() const;
// conversion operator to access the underlying Dbt
operator Dbt*();
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 0b236e2e48..07df8d9fc8 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -307,7 +307,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
}
}
- if (LogAcceptCategory(BCLog::SELECTCOINS)) {
+ if (LogAcceptCategory(BCLog::SELECTCOINS, BCLog::Level::Debug)) {
std::string log_message{"Coin selection best subset: "};
for (unsigned int i = 0; i < applicable_groups.size(); i++) {
if (vfBest[i]) {
@@ -328,24 +328,18 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
******************************************************************************/
void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descendants, bool positive_only) {
- // Compute the effective value first
- const CAmount coin_fee = output.input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.input_bytes);
- const CAmount ev = output.txout.nValue - coin_fee;
-
// Filter for positive only here before adding the coin
- if (positive_only && ev <= 0) return;
+ if (positive_only && output.GetEffectiveValue() <= 0) return;
m_outputs.push_back(output);
COutput& coin = m_outputs.back();
- coin.fee = coin_fee;
- fee += coin.fee;
+ fee += coin.GetFee();
coin.long_term_fee = coin.input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.input_bytes);
long_term_fee += coin.long_term_fee;
- coin.effective_value = ev;
- effective_value += coin.effective_value;
+ effective_value += coin.GetEffectiveValue();
m_from_me &= coin.from_me;
m_value += coin.txout.nValue;
@@ -380,8 +374,8 @@ CAmount GetSelectionWaste(const std::set<COutput>& inputs, CAmount change_cost,
CAmount waste = 0;
CAmount selected_effective_value = 0;
for (const COutput& coin : inputs) {
- waste += coin.fee - coin.long_term_fee;
- selected_effective_value += use_effective_value ? coin.effective_value : coin.txout.nValue;
+ waste += coin.GetFee() - coin.long_term_fee;
+ selected_effective_value += use_effective_value ? coin.GetEffectiveValue() : coin.txout.nValue;
}
if (change_cost) {
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 25c672eda0..9135e48104 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -20,6 +20,14 @@ static constexpr CAmount CHANGE_UPPER{1000000};
/** A UTXO under consideration for use in funding a new transaction. */
struct COutput {
+private:
+ /** The output's value minus fees required to spend it.*/
+ std::optional<CAmount> effective_value;
+
+ /** The fee required to spend this output at the transaction's target feerate. */
+ std::optional<CAmount> fee;
+
+public:
/** The outpoint identifying this UTXO */
COutPoint outpoint;
@@ -55,16 +63,10 @@ struct COutput {
/** Whether the transaction containing this output is sent from the owning wallet */
bool from_me;
- /** The output's value minus fees required to spend it. Initialized as the output's absolute value. */
- CAmount effective_value;
-
- /** The fee required to spend this output at the transaction's target feerate. */
- CAmount fee{0};
-
/** The fee required to spend this output at the consolidation feerate. */
CAmount long_term_fee{0};
- COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me)
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
: outpoint{outpoint},
txout{txout},
depth{depth},
@@ -73,9 +75,22 @@ struct COutput {
solvable{solvable},
safe{safe},
time{time},
- from_me{from_me},
- effective_value{txout.nValue}
- {}
+ from_me{from_me}
+ {
+ if (feerate) {
+ fee = input_bytes < 0 ? 0 : feerate.value().GetFee(input_bytes);
+ effective_value = txout.nValue - fee.value();
+ }
+ }
+
+ COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
+ : COutput(outpoint, txout, depth, input_bytes, spendable, solvable, safe, time, from_me)
+ {
+ // if input_bytes is unknown, then fees should be 0, if input_bytes is known, then the fees should be a positive integer or 0 (input_bytes known and fees = 0 only happens in the tests)
+ assert((input_bytes < 0 && fees == 0) || (input_bytes > 0 && fees >= 0));
+ fee = fees;
+ effective_value = txout.nValue - fee.value();
+ }
std::string ToString() const;
@@ -83,6 +98,18 @@ struct COutput {
{
return outpoint < rhs.outpoint;
}
+
+ CAmount GetFee() const
+ {
+ assert(fee.has_value());
+ return fee.value();
+ }
+
+ CAmount GetEffectiveValue() const
+ {
+ assert(effective_value.has_value());
+ return effective_value.value();
+ }
};
/** Parameters for one iteration of Coin Selection. */
diff --git a/src/wallet/context.cpp b/src/wallet/context.cpp
index 800aa5bf9c..3d4bf9d703 100644
--- a/src/wallet/context.cpp
+++ b/src/wallet/context.cpp
@@ -5,6 +5,6 @@
#include <wallet/context.h>
namespace wallet {
-WalletContext::WalletContext() {}
-WalletContext::~WalletContext() {}
+WalletContext::WalletContext() = default;
+WalletContext::~WalletContext() = default;
} // namespace wallet
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 73042424ad..afd2b83971 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -218,21 +218,20 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
// We cannot source new unconfirmed inputs(bip125 rule 2)
new_coin_control.m_min_depth = 1;
- CTransactionRef tx_new;
- CAmount fee_ret;
- int change_pos_in_out = -1; // No requested location for change
+ constexpr int RANDOM_CHANGE_POSITION = -1;
bilingual_str fail_reason;
FeeCalculation fee_calc_out;
- if (!CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, fail_reason, new_coin_control, fee_calc_out, false);
+ if (!txr) {
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR;
}
// Write back new fee if successful
- new_fee = fee_ret;
+ new_fee = txr->fee;
// Write back transaction
- mtx = CMutableTransaction(*tx_new);
+ mtx = CMutableTransaction(*txr->tx);
return Result::OK;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 7e21126298..7f038eda84 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -94,6 +94,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
#endif
argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ argsman.AddArg("-walletcrosschain", strprintf("Allow reusing wallet files across chains (default: %u)", DEFAULT_WALLETCROSSCHAIN), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddHiddenArgs({"-zapwallettxes"});
}
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 98e843385c..e1203817e0 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -80,7 +80,10 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
+ AssertLockHeld(wallet.cs_wallet);
+
WalletTxStatus result;
result.block_height =
wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height :
@@ -257,13 +260,14 @@ public:
bilingual_str& fail_reason) override
{
LOCK(m_wallet->cs_wallet);
- CTransactionRef tx;
FeeCalculation fee_calc_out;
- if (!CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
- fail_reason, coin_control, fee_calc_out, sign)) {
- return {};
- }
- return tx;
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(*m_wallet, recipients, change_pos,
+ fail_reason, coin_control, fee_calc_out, sign);
+ if (!txr) return {};
+ fee = txr->fee;
+ change_pos = txr->change_pos;
+
+ return txr->tx;
}
void commitTransaction(CTransactionRef tx,
WalletValueMap value_map,
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index cddf94aab2..8cce07b921 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -123,6 +123,8 @@ static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CW
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{
+ AssertLockHeld(wallet.cs_wallet);
+
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (wallet.IsTxImmatureCoinBase(wtx))
return 0;
@@ -164,6 +166,8 @@ CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache)
{
+ AssertLockHeld(wallet.cs_wallet);
+
if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
@@ -173,6 +177,8 @@ CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, b
CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache)
{
+ AssertLockHeld(wallet.cs_wallet);
+
if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
@@ -182,6 +188,8 @@ CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletT
CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache, const isminefilter& filter)
{
+ AssertLockHeld(wallet.cs_wallet);
+
// Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
diff --git a/src/wallet/receive.h b/src/wallet/receive.h
index d7705b5262..1caef293f2 100644
--- a/src/wallet/receive.h
+++ b/src/wallet/receive.h
@@ -24,17 +24,17 @@ bool OutputIsChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_
CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx);
-CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
+CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
//! filter decides which addresses will count towards the debit
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx);
-CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true);
-CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache = true);
-// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
-// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
-// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
-// having to resolve the issue of member access into incomplete type CWallet.
-CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) NO_THREAD_SAFETY_ANALYSIS;
+CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache = true)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
struct COutputEntry
{
CTxDestination destination;
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index d4f6c9d805..d5444f5051 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -264,7 +264,7 @@ RPCHelpMan addmultisigaddress()
if (!request.params[2].isNull())
label = LabelFromValue(request.params[2]);
- int required = request.params[0].get_int();
+ int required = request.params[0].getInt<int>();
// Get the public keys
const UniValue& keys_or_addrs = request.params[1].get_array();
@@ -340,9 +340,9 @@ RPCHelpMan keypoolrefill()
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
unsigned int kpSize = 0;
if (!request.params[0].isNull()) {
- if (request.params[0].get_int() < 0)
+ if (request.params[0].getInt<int>() < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
- kpSize = (unsigned int)request.params[0].get_int();
+ kpSize = (unsigned int)request.params[0].getInt<int>();
}
EnsureWalletIsUnlocked(*pwallet);
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index b4f01b00de..c289c05f2c 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -1232,7 +1232,7 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
if (data.exists("timestamp")) {
const UniValue& timestamp = data["timestamp"];
if (timestamp.isNum()) {
- return timestamp.get_int64();
+ return timestamp.getInt<int64_t>();
} else if (timestamp.isStr() && timestamp.get_str() == "now") {
return now;
}
@@ -1473,7 +1473,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
next_index = range_start;
if (data.exists("next_index")) {
- next_index = data["next_index"].get_int64();
+ next_index = data["next_index"].getInt<int64_t>();
// bound checks
if (next_index < range_start || next_index >= range_end) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index 4eccff3969..2649fa586c 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -18,40 +18,40 @@
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;
-
+ std::set<CTxDestination> addresses;
if (by_label) {
// Get the set of addresses assigned to label
- std::string label = LabelFromValue(params[0]);
- address_set = wallet.GetLabelAddresses(label);
+ addresses = wallet.GetLabelAddresses(LabelFromValue(params[0]));
+ if (addresses.empty()) throw JSONRPCError(RPC_WALLET_ERROR, "Label not found in wallet");
} else {
// Get the address
CTxDestination dest = DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
- CScript script_pub_key = GetScriptForDestination(dest);
- if (!wallet.IsMine(script_pub_key)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
+ addresses.insert(dest);
+ }
+
+ // Filter by own scripts only
+ std::set<CScript> output_scripts;
+ for (const auto& address : addresses) {
+ auto output_script{GetScriptForDestination(address)};
+ if (wallet.IsMine(output_script)) {
+ output_scripts.insert(output_script);
}
- address_set.insert(dest);
+ }
+
+ if (output_scripts.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
int min_depth = 1;
if (!params[1].isNull())
- min_depth = params[1].get_int();
+ min_depth = params[1].getInt<int>();
const bool include_immature_coinbase{params[2].isNull() ? false : params[2].get_bool()};
- // Excluding coinbase outputs is deprecated
- // It can be enabled by setting deprecatedrpc=exclude_coinbase
- const bool include_coinbase{!wallet.chain().rpcEnableDeprecated("exclude_coinbase")};
-
- if (include_immature_coinbase && !include_coinbase) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "include_immature_coinbase is incompatible with deprecated exclude_coinbase");
- }
-
// Tally
CAmount amount = 0;
for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
@@ -59,15 +59,14 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
int depth{wallet.GetTxDepthInMainChain(wtx)};
if (depth < min_depth
// Coinbase with less than 1 confirmation is no longer in the main chain
- || (wtx.IsCoinBase() && (depth < 1 || !include_coinbase))
+ || (wtx.IsCoinBase() && (depth < 1))
|| (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
{
continue;
}
for (const CTxOut& txout : wtx.tx->vout) {
- CTxDestination address;
- if (ExtractDestination(txout.scriptPubKey, address) && wallet.IsMine(address) && address_set.count(address)) {
+ if (output_scripts.count(txout.scriptPubKey) > 0) {
amount += txout.nValue;
}
}
@@ -200,7 +199,7 @@ RPCHelpMan getbalance()
int min_depth = 0;
if (!request.params[1].isNull()) {
- min_depth = request.params[1].get_int();
+ min_depth = request.params[1].getInt<int>();
}
bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
@@ -324,7 +323,7 @@ RPCHelpMan lockunspent()
});
const uint256 txid(ParseHashO(o, "txid"));
- const int nOutput = find_value(o, "vout").get_int();
+ const int nOutput = find_value(o, "vout").getInt<int>();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
}
@@ -565,13 +564,13 @@ RPCHelpMan listunspent()
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- nMinDepth = request.params[0].get_int();
+ nMinDepth = request.params[0].getInt<int>();
}
int nMaxDepth = 9999999;
if (!request.params[1].isNull()) {
RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
- nMaxDepth = request.params[1].get_int();
+ nMaxDepth = request.params[1].getInt<int>();
}
std::set<CTxDestination> destinations;
@@ -623,7 +622,7 @@ RPCHelpMan listunspent()
nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
if (options.exists("maximumCount"))
- nMaximumCount = options["maximumCount"].get_int64();
+ nMaximumCount = options["maximumCount"].getInt<int64_t>();
}
// Make sure the results are valid at least up to the most recent block
@@ -639,7 +638,7 @@ RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet);
- AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ AvailableCoinsListUnspent(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp
index 802cc63d6d..931c64ca06 100644
--- a/src/wallet/rpc/encrypt.cpp
+++ b/src/wallet/rpc/encrypt.cpp
@@ -54,7 +54,7 @@ RPCHelpMan walletpassphrase()
strWalletPass = request.params[0].get_str().c_str();
// Get the timeout
- nSleepTime = request.params[1].get_int64();
+ nSleepTime = request.params[1].getInt<int64_t>();
// Timeout cannot be negative, otherwise it will relock immediately
if (nSleepTime < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index 07119133b7..d1a0ba50f6 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -155,15 +155,14 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
// Send
- CAmount nFeeRequired = 0;
- int nChangePosRet = -1;
+ constexpr int RANDOM_CHANGE_POSITION = -1;
bilingual_str error;
- CTransactionRef tx;
FeeCalculation fee_calc_out;
- const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
- if (!fCreated) {
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc_out, true);
+ if (!txr) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
+ CTransactionRef tx = txr->tx;
wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
if (verbose) {
UniValue entry(UniValue::VOBJ);
@@ -552,7 +551,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
}
if (options.exists("changePosition") || options.exists("change_position")) {
- change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).get_int();
+ change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
}
if (options.exists("change_type")) {
@@ -660,7 +659,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
if (!vout_v.isNum()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
}
- int vout = vout_v.get_int();
+ int vout = vout_v.getInt<int>();
if (vout < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
}
@@ -669,7 +668,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
if (!weight_v.isNum()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key");
}
- int64_t weight = weight_v.get_int64();
+ int64_t weight = weight_v.getInt<int64_t>();
const int64_t min_input_weight = GetTransactionInputWeight(CTxIn());
CHECK_NONFATAL(min_input_weight == 165);
if (weight < min_input_weight) {
@@ -690,7 +689,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
- int pos = subtractFeeFromOutputs[idx].get_int();
+ int pos = subtractFeeFromOutputs[idx].getInt<int>();
if (setSubtractFeeFromOutputs.count(pos))
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
if (pos < 0)
@@ -1399,7 +1398,7 @@ RPCHelpMan sendall()
total_input_value += tx->tx->vout[input.prevout.n].nValue;
}
} else {
- AvailableCoins(*pwallet, all_the_utxos, &coin_control, /*nMinimumAmount=*/0);
+ AvailableCoins(*pwallet, all_the_utxos, &coin_control, fee_rate, /*nMinimumAmount=*/0);
for (const COutput& output : all_the_utxos) {
CHECK_NONFATAL(output.input_bytes > 0);
if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index c87af2ea30..fae9bf3ea5 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -15,6 +15,7 @@ using interfaces::FoundBlock;
namespace wallet {
static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
interfaces::Chain& chain = wallet.chain();
int confirms = wallet.GetTxDepthInMainChain(wtx);
@@ -63,9 +64,7 @@ struct tallyitem
int nConf{std::numeric_limits<int>::max()};
std::vector<uint256> txids;
bool fIsWatchonly{false};
- tallyitem()
- {
- }
+ tallyitem() = default;
};
static UniValue ListReceived(const CWallet& wallet, const UniValue& params, const bool by_label, const bool include_immature_coinbase) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -73,7 +72,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
- nMinDepth = params[0].get_int();
+ nMinDepth = params[0].getInt<int>();
// Whether to include empty labels
bool fIncludeEmpty = false;
@@ -96,14 +95,6 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
has_filtered_address = true;
}
- // Excluding coinbase outputs is deprecated
- // It can be enabled by setting deprecatedrpc=exclude_coinbase
- const bool include_coinbase{!wallet.chain().rpcEnableDeprecated("exclude_coinbase")};
-
- if (include_immature_coinbase && !include_coinbase) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "include_immature_coinbase is incompatible with deprecated exclude_coinbase");
- }
-
// Tally
std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
@@ -114,7 +105,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
continue;
// Coinbase with less than 1 confirmation is no longer in the main chain
- if ((wtx.IsCoinBase() && (nDepth < 1 || !include_coinbase))
+ if ((wtx.IsCoinBase() && (nDepth < 1))
|| (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
{
continue;
@@ -513,10 +504,10 @@ RPCHelpMan listtransactions()
}
int nCount = 10;
if (!request.params[1].isNull())
- nCount = request.params[1].get_int();
+ nCount = request.params[1].getInt<int>();
int nFrom = 0;
if (!request.params[2].isNull())
- nFrom = request.params[2].get_int();
+ nFrom = request.params[2].getInt<int>();
isminefilter filter = ISMINE_SPENDABLE;
if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
@@ -639,7 +630,7 @@ RPCHelpMan listsinceblock()
}
if (!request.params[1].isNull()) {
- target_confirms = request.params[1].get_int();
+ target_confirms = request.params[1].getInt<int>();
if (target_confirms < 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
@@ -893,14 +884,14 @@ RPCHelpMan rescanblockchain()
int tip_height = pwallet->GetLastBlockHeight();
if (!request.params[0].isNull()) {
- start_height = request.params[0].get_int();
+ start_height = request.params[0].getInt<int>();
if (start_height < 0 || start_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
}
}
if (!request.params[1].isNull()) {
- stop_height = request.params[1].get_int();
+ stop_height = request.params[1].getInt<int>();
if (*stop_height < 0 || *stop_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
} else if (*stop_height < start_height) {
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index efb4d8fc7e..4c44c333a6 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -559,7 +559,7 @@ static RPCHelpMan upgradewallet()
int version = 0;
if (!request.params[0].isNull()) {
- version = request.params[0].get_int();
+ version = request.params[0].getInt<int>();
}
bilingual_str error;
const int previous_version{pwallet->GetVersion()};
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 55c0a2cb7f..6f79ae4e9b 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -84,7 +84,7 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
}
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, std::optional<CFeeRate> feerate, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{
AssertLockHeld(wallet.cs_wallet);
@@ -192,7 +192,7 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly));
- vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me);
+ vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@@ -211,13 +211,18 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
}
}
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
+{
+ AvailableCoins(wallet, vCoins, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+}
+
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
{
LOCK(wallet.cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(wallet, vCoins, coinControl);
+ AvailableCoinsListUnspent(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
if (out.spendable) {
balance += out.txout.nValue;
@@ -257,7 +262,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(wallet, availableCoins);
+ AvailableCoinsListUnspent(wallet, availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
@@ -477,12 +482,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
/* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
- COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- output.effective_value = output.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(output.input_bytes);
+ COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, coin_selection_params.m_effective_feerate);
if (coin_selection_params.m_subtract_fee_outputs) {
value_to_select -= output.txout.nValue;
} else {
- value_to_select -= output.effective_value;
+ value_to_select -= output.GetEffectiveValue();
}
preset_coins.insert(outpoint);
/* Set ancestors and descendants to 0 as they don't matter for preset inputs since no actual selection is being done.
@@ -656,12 +660,10 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
}
}
-static bool CreateTransactionInternal(
+static std::optional<CreatedTransactionResult> CreateTransactionInternal(
CWallet& wallet,
const std::vector<CRecipient>& vecSend,
- CTransactionRef& tx,
- CAmount& nFeeRet,
- int& nChangePosInOut,
+ int change_pos,
bilingual_str& error,
const CCoinControl& coin_control,
FeeCalculation& fee_calc_out,
@@ -669,6 +671,11 @@ static bool CreateTransactionInternal(
{
AssertLockHeld(wallet.cs_wallet);
+ // out variables, to be packed into returned result structure
+ CTransactionRef tx;
+ CAmount nFeeRet;
+ int nChangePosInOut = change_pos;
+
FastRandomContext rng_fast;
CMutableTransaction txNew; // The resulting transaction that we make
@@ -742,12 +749,12 @@ static bool CreateTransactionInternal(
// provided one
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
- return false;
+ return std::nullopt;
}
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
// eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
+ return std::nullopt;
}
// Calculate the cost of change
@@ -774,7 +781,7 @@ static bool CreateTransactionInternal(
if (IsDust(txout, wallet.chain().relayDustFee()))
{
error = _("Transaction amount too small");
- return false;
+ return std::nullopt;
}
txNew.vout.push_back(txout);
}
@@ -785,13 +792,13 @@ static bool CreateTransactionInternal(
// Get available coins
std::vector<COutput> vAvailableCoins;
- AvailableCoins(wallet, vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ AvailableCoins(wallet, vAvailableCoins, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
if (!result) {
error = _("Insufficient funds");
- return false;
+ return std::nullopt;
}
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
@@ -808,7 +815,7 @@ static bool CreateTransactionInternal(
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
{
error = _("Transaction change output index out of range");
- return false;
+ return std::nullopt;
}
assert(nChangePosInOut != -1);
@@ -836,7 +843,7 @@ static bool CreateTransactionInternal(
int nBytes = tx_sizes.vsize;
if (nBytes == -1) {
error = _("Missing solving data for estimating transaction size");
- return false;
+ return std::nullopt;
}
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
@@ -900,7 +907,7 @@ static bool CreateTransactionInternal(
} else {
error = _("The transaction amount is too small to send after the fee has been deducted");
}
- return false;
+ return std::nullopt;
}
}
++i;
@@ -910,12 +917,12 @@ static bool CreateTransactionInternal(
// Give up if change keypool ran out and change is required
if (scriptChange.empty() && nChangePosInOut != -1) {
- return false;
+ return std::nullopt;
}
if (sign && !wallet.SignTransaction(txNew)) {
error = _("Signing transaction failed");
- return false;
+ return std::nullopt;
}
// Return the constructed transaction data.
@@ -926,19 +933,19 @@ static bool CreateTransactionInternal(
(!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
{
error = _("Transaction too large");
- return false;
+ return std::nullopt;
}
if (nFeeRet > wallet.m_default_max_tx_fee) {
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
- return false;
+ return std::nullopt;
}
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
if (!wallet.chain().checkChainLimits(tx)) {
error = _("Transaction has too long of a mempool chain");
- return false;
+ return std::nullopt;
}
}
@@ -955,15 +962,13 @@ static bool CreateTransactionInternal(
feeCalc.est.fail.start, feeCalc.est.fail.end,
(feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
- return true;
+ return CreatedTransactionResult(tx, nFeeRet, nChangePosInOut);
}
-bool CreateTransaction(
+std::optional<CreatedTransactionResult> CreateTransaction(
CWallet& wallet,
const std::vector<CRecipient>& vecSend,
- CTransactionRef& tx,
- CAmount& nFeeRet,
- int& nChangePosInOut,
+ int change_pos,
bilingual_str& error,
const CCoinControl& coin_control,
FeeCalculation& fee_calc_out,
@@ -971,42 +976,38 @@ bool CreateTransaction(
{
if (vecSend.empty()) {
error = _("Transaction must have at least one recipient");
- return false;
+ return std::nullopt;
}
if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
error = _("Transaction amounts must not be negative");
- return false;
+ return std::nullopt;
}
LOCK(wallet.cs_wallet);
- int nChangePosIn = nChangePosInOut;
- Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
- bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
- TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res, nFeeRet, nChangePosInOut);
+ std::optional<CreatedTransactionResult> txr_ungrouped = CreateTransactionInternal(wallet, vecSend, change_pos, error, coin_control, fee_calc_out, sign);
+ TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), txr_ungrouped.has_value(),
+ txr_ungrouped.has_value() ? txr_ungrouped->fee : 0, txr_ungrouped.has_value() ? txr_ungrouped->change_pos : 0);
+ if (!txr_ungrouped) return std::nullopt;
// try with avoidpartialspends unless it's enabled already
- if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
+ if (txr_ungrouped->fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
CCoinControl tmp_cc = coin_control;
tmp_cc.m_avoid_partial_spends = true;
- CAmount nFeeRet2;
- CTransactionRef tx2;
- int nChangePosInOut2 = nChangePosIn;
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
- if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
- // if fee of this alternative one is within the range of the max fee, we use this one
- const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
- wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
- TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, res, nFeeRet2, nChangePosInOut2);
- if (use_aps) {
- tx = tx2;
- nFeeRet = nFeeRet2;
- nChangePosInOut = nChangePosInOut2;
- }
+ std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, error2, tmp_cc, fee_calc_out, sign);
+ // if fee of this alternative one is within the range of the max fee, we use this one
+ const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped->fee + wallet.m_max_aps_fee) : false};
+ TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(),
+ txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() ? txr_grouped->change_pos : 0);
+ if (txr_grouped) {
+ wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n",
+ txr_ungrouped->fee, txr_grouped->fee, use_aps ? "grouped" : "non-grouped");
+ if (use_aps) return txr_grouped;
}
}
- return res;
+ return txr_ungrouped;
}
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -1030,11 +1031,12 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
LOCK(wallet.cs_wallet);
- CTransactionRef tx_new;
FeeCalculation fee_calc_out;
- if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
- return false;
- }
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false);
+ if (!txr) return false;
+ CTransactionRef tx_new = txr->tx;
+ nFeeRet = txr->fee;
+ nChangePosInOut = txr->change_pos;
if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index e43aac5273..988058a25a 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -10,6 +10,8 @@
#include <wallet/transaction.h>
#include <wallet/wallet.h>
+#include <optional>
+
namespace wallet {
/** Get the marginal bytes if spending the specified output from this transaction.
* use_max_sig indicates whether to use the maximum sized, 72 byte signature when calculating the
@@ -35,7 +37,13 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* walle
/**
* populate vCoins with vector of available COutputs.
*/
-void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+
+/**
+ * Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function
+ * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
+ */
+void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
@@ -82,12 +90,22 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, const CCoinControl& coin_control,
const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
+struct CreatedTransactionResult
+{
+ CTransactionRef tx;
+ CAmount fee;
+ int change_pos;
+
+ CreatedTransactionResult(CTransactionRef tx, CAmount fee, int change_pos)
+ : tx(tx), fee(fee), change_pos(change_pos) {}
+};
+
/**
* Create a new transaction paying the recipients with a set of coins
* selected by SelectCoins(); Also create the change output, when needed
- * @note passing nChangePosInOut as -1 will result in setting a random position
+ * @note passing change_pos as -1 will result in setting a random position
*/
-bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
+std::optional<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, int change_pos, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
/**
* Insert additional inputs into the transaction by
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 72e749477b..76f28917a4 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -41,7 +41,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& se
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
}
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
@@ -50,7 +50,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
OutputGroup group;
group.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ true);
result.AddInput(group);
@@ -62,14 +62,12 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
- coin.effective_value = nValue - fee;
- coin.fee = fee;
+ COutput coin(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ 148, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fee);
coin.long_term_fee = long_term_fee;
set.insert(coin);
}
-static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, CFeeRate feerate = CFeeRate(0), int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -88,7 +86,7 @@ static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount
auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx)), TxStateInactive{}));
assert(ret.second);
CWalletTx& wtx = (*ret.first).second;
- coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe);
+ coins.emplace_back(COutPoint(wtx.GetHash(), nInput), wtx.tx->vout.at(nInput), nAge, GetTxSpendSize(wallet, wtx, nInput), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate);
}
/** Check if SelectionResult a is equivalent to SelectionResult b.
@@ -311,13 +309,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 1);
+ add_coin(coins, *wallet, 1, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change));
// Test fees subtracted from output:
coins.clear();
- add_coin(coins, *wallet, 1 * CENT);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate);
coins.at(0).input_bytes = 40;
coin_selection_params_bnb.m_subtract_fee_outputs = true;
const auto result9 = SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change);
@@ -334,9 +332,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
std::vector<COutput> coins;
- add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
- add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
coin_control.Select(coins.at(0).outpoint);
@@ -344,6 +342,61 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(result10);
}
+ {
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+
+ // single coin should be selected when effective fee > long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(10 * CENT, 2, expected_result);
+ CCoinControl coin_control;
+ const auto result11 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result11));
+ coins.clear();
+
+ // more coins should be selected when effective fee < long term fee
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(5000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(9 * CENT, 2, expected_result);
+ add_coin(1 * CENT, 2, expected_result);
+ const auto result12 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result12));
+ coins.clear();
+
+ // pre selected coin should be selected even if disadvantageous
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
+ coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
+
+ add_coin(coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+
+ expected_result.Clear();
+ add_coin(9 * CENT, 2, expected_result);
+ add_coin(1 * CENT, 2, expected_result);
+ coin_control.fAllowOtherInputs = true;
+ coin_control.Select(coins.at(1).outpoint); // pre select 9 coin
+ const auto result13 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
+ BOOST_CHECK(EquivalentResult(expected_result, *result13));
+ }
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
@@ -367,7 +420,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// with an empty wallet we can't even pay one cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
- add_coin(coins, *wallet, 1*CENT, 4); // add a new 1 cent coin
+ add_coin(coins, *wallet, 1*CENT, CFeeRate(0), 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
BOOST_CHECK(!KnapsackSolver(KnapsackGroupOutputs(coins, *wallet, filter_standard), 1 * CENT, CENT));
@@ -388,7 +441,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 3 * CENT);
add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
- add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
+ add_coin(coins, *wallet, 10*CENT, CFeeRate(0), 3, true); // a new 10 cent coin sent from one of our own addresses
add_coin(coins, *wallet, 20*CENT); // and a mature 20 cent coin
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
@@ -815,5 +868,40 @@ BOOST_AUTO_TEST_CASE(waste_test)
BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
}
+BOOST_AUTO_TEST_CASE(effective_value_test)
+{
+ const int input_bytes = 148;
+ const CFeeRate feerate(1000);
+ const CAmount nValue = 10000;
+ const int nInput = 0;
+
+ CMutableTransaction tx;
+ tx.vout.resize(1);
+ tx.vout[nInput].nValue = nValue;
+
+ // standard case, pass feerate in constructor
+ COutput output1(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ const CAmount expected_ev1 = 9852; // 10000 - 148
+ BOOST_CHECK_EQUAL(output1.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass feerate in constructor
+ COutput output2(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
+ BOOST_CHECK_EQUAL(output2.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+
+ // negative effective value, pass feerate in constructor
+ COutput output3(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, CFeeRate(100000));
+ const CAmount expected_ev3 = -4800; // 10000 - 14800
+ BOOST_CHECK_EQUAL(output3.GetEffectiveValue(), expected_ev3);
+
+ // standard case, pass fees in constructor
+ const CAmount fees = 148;
+ COutput output4(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fees);
+ BOOST_CHECK_EQUAL(output4.GetEffectiveValue(), expected_ev1);
+
+ // input bytes unknown (input_bytes = -1), pass fees in constructor
+ COutput output5(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
+ BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index fbf1e0efd3..f61808c549 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -15,22 +15,22 @@
namespace wallet {
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
-static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, std::string& database_filename)
+static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
{
fs::path data_file = BDBDataFile(path);
- database_filename = fs::PathToString(data_file.filename());
+ database_filename = data_file.filename();
return GetBerkeleyEnv(data_file.parent_path(), false);
}
BOOST_AUTO_TEST_CASE(getwalletenv_file)
{
- std::string test_name = "test_name.dat";
+ fs::path test_name = "test_name.dat";
const fs::path datadir = m_args.GetDataDirNet();
fs::path file_path = datadir / test_name;
std::ofstream f{file_path};
f.close();
- std::string filename;
+ fs::path filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
BOOST_CHECK_EQUAL(filename, test_name);
BOOST_CHECK_EQUAL(env->Directory(), datadir);
@@ -38,10 +38,10 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
{
- std::string expected_name = "wallet.dat";
+ fs::path expected_name = "wallet.dat";
const fs::path datadir = m_args.GetDataDirNet();
- std::string filename;
+ fs::path filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
BOOST_CHECK_EQUAL(filename, expected_name);
BOOST_CHECK_EQUAL(env->Directory(), datadir);
@@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
{
fs::path datadir = m_args.GetDataDirNet() / "1";
fs::path datadir_2 = m_args.GetDataDirNet() / "2";
- std::string filename;
+ fs::path filename;
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
@@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
{
fs::path datadir = gArgs.GetDataDirNet() / "1";
fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
- std::string filename;
+ fs::path filename;
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 2693b68cca..3465f2f331 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -14,13 +14,13 @@
namespace wallet {
-static void AddCoin(const CAmount& value, int n_input, int n_input_bytes, int locktime, std::vector<COutput>& coins)
+static void AddCoin(const CAmount& value, int n_input, int n_input_bytes, int locktime, std::vector<COutput>& coins, CFeeRate fee_rate)
{
CMutableTransaction tx;
tx.vout.resize(n_input + 1);
tx.vout[n_input].nValue = value;
tx.nLockTime = locktime; // all transactions get different hashes
- coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true);
+ coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, fee_rate);
}
// Randomly distribute coins to instances of OutputGroup
@@ -70,7 +70,7 @@ FUZZ_TARGET(coinselection)
if (total_balance + amount >= MAX_MONEY) {
break;
}
- AddCoin(amount, n_input, n_input_bytes, ++next_locktime, utxo_pool);
+ AddCoin(amount, n_input, n_input_bytes, ++next_locktime, utxo_pool, coin_params.m_effective_feerate);
total_balance += amount;
}
diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp
index 1c16da25bd..9089c8ff46 100644
--- a/src/wallet/test/fuzz/notifications.cpp
+++ b/src/wallet/test/fuzz/notifications.cpp
@@ -64,7 +64,7 @@ struct FuzzedWallet {
assert(RemoveWallet(context, wallet, load_on_start, warnings));
assert(warnings.empty());
UnloadWallet(std::move(wallet));
- fs::remove_all(GetWalletDir() / name);
+ fs::remove_all(GetWalletDir() / fs::PathFromString(name));
}
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider)
{
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 34c22a9c0d..8eb7689c94 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -17,8 +17,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
{
m_wallet_loader = MakeWalletLoader(*m_node.chain, m_args);
- std::string sep;
- sep += fs::path::preferred_separator;
+ const auto sep = fs::path::preferred_separator;
m_datadir = m_args.GetDataDirNet();
m_cwd = fs::current_path();
@@ -27,8 +26,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";
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index 334bd5b8bc..bdc148afb4 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -27,9 +27,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
// instead of the miner.
auto check_tx = [&wallet](CAmount leftover_input_amount) {
CRecipient recipient{GetScriptForRawPubKey({}), 50 * COIN - leftover_input_amount, true /* subtract fee */};
- CTransactionRef tx;
- CAmount fee;
- int change_pos = -1;
+ constexpr int RANDOM_CHANGE_POSITION = -1;
bilingual_str error;
CCoinControl coin_control;
coin_control.m_feerate.emplace(10000);
@@ -37,11 +35,12 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
// We need to use a change type with high cost of change so that the leftover amount will be dropped to fee instead of added as a change output
coin_control.m_change_type = OutputType::LEGACY;
FeeCalculation fee_calc;
- BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
- BOOST_CHECK_EQUAL(tx->vout.size(), 1);
- BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
- BOOST_CHECK_GT(fee, 0);
- return fee;
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, coin_control, fee_calc);
+ BOOST_CHECK(txr.has_value());
+ BOOST_CHECK_EQUAL(txr->tx->vout.size(), 1);
+ BOOST_CHECK_EQUAL(txr->tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - txr->fee);
+ BOOST_CHECK_GT(txr->fee, 0);
+ return txr->fee;
};
// Send full input amount to recipient, check that only nonzero fee is
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 683f0eb327..70863f5464 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -521,13 +521,14 @@ public:
CWalletTx& AddTx(CRecipient recipient)
{
CTransactionRef tx;
- CAmount fee;
- int changePos = -1;
bilingual_str error;
CCoinControl dummy;
FeeCalculation fee_calc_out;
{
- BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
+ constexpr int RANDOM_CHANGE_POSITION = -1;
+ std::optional<CreatedTransactionResult> txr = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, error, dummy, fee_calc_out);
+ BOOST_CHECK(txr.has_value());
+ tx = txr->tx;
}
wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx;
@@ -583,7 +584,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -595,7 +596,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{
LOCK(wallet->cs_wallet);
std::vector<COutput> available;
- AvailableCoins(*wallet, available);
+ AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 489599e2a0..6c333c709b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -44,8 +44,6 @@
#include <assert.h>
#include <optional>
-#include <boost/algorithm/string/replace.hpp>
-
using interfaces::FoundBlock;
namespace wallet {
@@ -523,6 +521,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
void CWallet::chainStateFlushed(const CBlockLocator& loc)
{
+ // Don't update the best block until the chain is attached so that in case of a shutdown,
+ // the rescan will be restarted at next startup.
+ if (m_attaching_chain) {
+ return;
+ }
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(loc);
}
@@ -1013,14 +1016,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
if (!strCmd.empty())
{
- boost::replace_all(strCmd, "%s", hash.GetHex());
+ ReplaceAll(strCmd, "%s", hash.GetHex());
if (auto* conf = wtx.state<TxStateConfirmed>())
{
- boost::replace_all(strCmd, "%b", conf->confirmed_block_hash.GetHex());
- boost::replace_all(strCmd, "%h", ToString(conf->confirmed_block_height));
+ ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex());
+ ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height));
} else {
- boost::replace_all(strCmd, "%b", "unconfirmed");
- boost::replace_all(strCmd, "%h", "-1");
+ ReplaceAll(strCmd, "%b", "unconfirmed");
+ ReplaceAll(strCmd, "%h", "-1");
}
#ifndef WIN32
// Substituting the wallet name isn't currently supported on windows
@@ -1028,7 +1031,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
// A few ways it could be implemented in the future are described in:
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
- boost::replace_all(strCmd, "%w", ShellEscape(GetName()));
+ ReplaceAll(strCmd, "%w", ShellEscape(GetName()));
#endif
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
@@ -1704,8 +1707,10 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
*/
CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
{
- int64_t nNow = GetTime();
- int64_t start_time = GetTimeMillis();
+ using Clock = std::chrono::steady_clock;
+ constexpr auto LOG_INTERVAL{60s};
+ auto current_time{Clock::now()};
+ auto start_time{Clock::now()};
assert(reserver.isReserved());
@@ -1732,8 +1737,8 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
}
- if (GetTime() >= nNow + 60) {
- nNow = GetTime();
+ if (Clock::now() >= current_time + LOG_INTERVAL) {
+ current_time = Clock::now();
WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current);
}
@@ -1800,7 +1805,8 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height, progress_current);
result.status = ScanResult::USER_ABORT;
} else {
- WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - start_time);
+ auto duration_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
+ WalletLogPrintf("Rescan completed in %15dms\n", duration_milliseconds.count());
}
return result;
}
@@ -1835,6 +1841,8 @@ void CWallet::ReacceptWalletTransactions()
bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const
{
+ AssertLockHeld(cs_wallet);
+
// Can't relay if wallet is not broadcasting
if (!GetBroadcastTransactions()) return false;
// Don't relay abandoned transactions
@@ -1863,12 +1871,11 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string
std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
{
- std::set<uint256> result;
- {
- uint256 myHash = wtx.GetHash();
- result = GetConflicts(myHash);
- result.erase(myHash);
- }
+ AssertLockHeld(cs_wallet);
+
+ const uint256 myHash{wtx.GetHash()};
+ std::set<uint256> result{GetConflicts(myHash)};
+ result.erase(myHash);
return result;
}
@@ -2934,14 +2941,31 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
assert(!walletInstance->m_chain || walletInstance->m_chain == &chain);
walletInstance->m_chain = &chain;
+ // Unless allowed, ensure wallet files are not reused across chains:
+ if (!gArgs.GetBoolArg("-walletcrosschain", DEFAULT_WALLETCROSSCHAIN)) {
+ WalletBatch batch(walletInstance->GetDatabase());
+ CBlockLocator locator;
+ if (batch.ReadBestBlock(locator) && locator.vHave.size() > 0 && chain.getHeight()) {
+ // Wallet is assumed to be from another chain, if genesis block in the active
+ // chain differs from the genesis block known to the wallet.
+ if (chain.getBlockHash(0) != locator.vHave.back()) {
+ error = Untranslated("Wallet files should not be reused across chains. Restart bitcoind with -walletcrosschain to override.");
+ return false;
+ }
+ }
+ }
+
// Register wallet with validationinterface. It's done before rescan to avoid
// missing block connections between end of rescan and validation subscribing.
// Because of wallet lock being hold, block connection notifications are going to
// be pending on the validation-side until lock release. It's likely to have
// block processing duplicata (if rescan block range overlaps with notification one)
// but we guarantee at least than wallet state is correct after notifications delivery.
+ // However, chainStateFlushed notifications are ignored until the rescan is finished
+ // so that in case of a shutdown event, the rescan will be repeated at the next start.
// This is temporary until rescan and notifications delivery are unified under same
// interface.
+ walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
// If rescan_required = true, rescan_height remains equal to 0
@@ -3007,9 +3031,11 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
return false;
}
}
+ walletInstance->m_attaching_chain = false;
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->GetDatabase().IncrementUpdateCounter();
}
+ walletInstance->m_attaching_chain = false;
return true;
}
@@ -3103,8 +3129,11 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
{
- if (!wtx.IsCoinBase())
+ AssertLockHeld(cs_wallet);
+
+ if (!wtx.IsCoinBase()) {
return 0;
+ }
int chain_depth = GetTxDepthInMainChain(wtx);
assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
@@ -3112,6 +3141,8 @@ int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
{
+ AssertLockHeld(cs_wallet);
+
// note GetBlocksToMaturity is 0 for non-coinbase tx
return GetTxBlocksToMaturity(wtx) > 0;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 26b7f97b5f..7da601c3b7 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -102,6 +102,7 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+static const bool DEFAULT_WALLETCROSSCHAIN = false;
//! -maxtxfee default
constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
//! Discourage users to set fees higher than this amount (in satoshis) per kB
@@ -237,6 +238,7 @@ private:
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
+ std::atomic<bool> m_attaching_chain{false};
std::atomic<int64_t> m_scanning_start{0};
std::atomic<double> m_scanning_progress{0};
friend class WalletRescanReserver;
@@ -413,13 +415,7 @@ public:
const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- std::set<uint256> GetTxConflicts(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
+ std::set<uint256> GetTxConflicts(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return depth of transaction in blockchain:
@@ -427,22 +423,20 @@ public:
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- int GetTxDepthInMainChain(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
- bool IsTxInMainChain(const CWalletTx& wtx) const { return GetTxDepthInMainChain(wtx) > 0; }
+ int GetTxDepthInMainChain(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsTxInMainChain(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
+ {
+ AssertLockHeld(cs_wallet);
+ return GetTxDepthInMainChain(wtx) > 0;
+ }
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
- int GetTxBlocksToMaturity(const CWalletTx& wtx) const;
- bool IsTxImmatureCoinBase(const CWalletTx& wtx) const;
+ int GetTxBlocksToMaturity(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsTxImmatureCoinBase(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we support the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); }
@@ -582,7 +576,8 @@ public:
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
- bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const;
+ bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const
+ EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const
{
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 7bbed7973f..79e0a330b7 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -314,8 +314,7 @@ public:
std::map<uint160, CHDChain> m_hd_chains;
bool tx_corrupt{false};
- CWalletScanState() {
- }
+ CWalletScanState() = default;
};
static bool