aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy2
-rw-r--r--src/Makefile.am37
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/addrdb.cpp3
-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.cpp3
-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/bitcoin-chainstate.cpp28
-rw-r--r--src/bitcoin-cli.cpp74
-rw-r--r--src/bitcoin-tx.cpp2
-rw-r--r--src/bitcoin-util.cpp1
-rw-r--r--src/bitcoind.cpp5
-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/core_io.h1
-rw-r--r--src/dbwrapper.cpp6
-rw-r--r--src/deploymentstatus.cpp2
-rw-r--r--src/deploymentstatus.h15
-rw-r--r--src/external_signer.cpp5
-rw-r--r--src/hash.h1
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/httpserver.cpp35
-rw-r--r--src/i2p.cpp2
-rw-r--r--src/i2p.h6
-rw-r--r--src/index/base.cpp12
-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.cpp56
-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.cpp57
-rw-r--r--src/net.h49
-rw-r--r--src/net_processing.cpp603
-rw-r--r--src/net_processing.h2
-rw-r--r--src/net_types.cpp8
-rw-r--r--src/netaddress.cpp2
-rw-r--r--src/netaddress.h5
-rw-r--r--src/node/blockstorage.cpp6
-rw-r--r--src/node/blockstorage.h2
-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.h4
-rw-r--r--src/node/transaction.h1
-rw-r--r--src/outputtype.h1
-rw-r--r--src/policy/feerate.cpp2
-rw-r--r--src/policy/feerate.h3
-rw-r--r--src/policy/fees.cpp22
-rw-r--r--src/policy/fees.h6
-rw-r--r--src/policy/packages.cpp6
-rw-r--r--src/policy/packages.h2
-rw-r--r--src/policy/policy.cpp14
-rw-r--r--src/policy/policy.h9
-rw-r--r--src/policy/rbf.cpp10
-rw-r--r--src/policy/rbf.h9
-rw-r--r--src/policy/settings.cpp1
-rw-r--r--src/policy/settings.h7
-rw-r--r--src/prevector.h15
-rw-r--r--src/protocol.h8
-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/android/res/values/libs.xml3
-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.cpp8
-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/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp198
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/fees.cpp2
-rw-r--r--src/rpc/mempool.cpp109
-rw-r--r--src/rpc/mining.cpp56
-rw-r--r--src/rpc/net.cpp8
-rw-r--r--src/rpc/node.cpp4
-rw-r--r--src/rpc/output_script.cpp6
-rw-r--r--src/rpc/rawtransaction.cpp6
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/rpc/request.cpp2
-rw-r--r--src/rpc/server.cpp2
-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.cpp20
-rw-r--r--src/scheduler.h35
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/script/miniscript.cpp95
-rw-r--r--src/script/miniscript.h415
-rw-r--r--src/script/sign.cpp2
-rw-r--r--src/script/standard.h1
-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.cpp4
-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.cpp17
-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/miniscript.cpp167
-rw-r--r--src/test/fuzz/miniscript_decode.cpp72
-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/signature_checker.cpp2
-rw-r--r--src/test/fuzz/tx_out.cpp1
-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/key_io_tests.cpp2
-rw-r--r--src/test/logging_tests.cpp120
-rw-r--r--src/test/miner_tests.cpp5
-rw-r--r--src/test/miniscript_tests.cpp36
-rw-r--r--src/test/random_tests.cpp18
-rw-r--r--src/test/rpc_tests.cpp30
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/scheduler_tests.cpp28
-rw-r--r--src/test/script_standard_tests.cpp4
-rw-r--r--src/test/script_tests.cpp14
-rw-r--r--src/test/settings_tests.cpp8
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/transaction_tests.cpp8
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-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.cpp33
-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.cpp39
-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/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/strencodings.cpp10
-rw-r--r--src/util/strencodings.h5
-rw-r--r--src/util/system.cpp59
-rw-r--r--src/util/system.h19
-rw-r--r--src/util/time.cpp17
-rw-r--r--src/util/time.h49
-rw-r--r--src/validation.cpp142
-rw-r--r--src/validation.h69
-rw-r--r--src/validationinterface.cpp31
-rw-r--r--src/validationinterface.h6
-rw-r--r--src/versionbits.h8
-rw-r--r--src/wallet/bdb.cpp4
-rw-r--r--src/wallet/bdb.h4
-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/interfaces.cpp16
-rw-r--r--src/wallet/receive.cpp8
-rw-r--r--src/wallet/receive.h16
-rw-r--r--src/wallet/rpc/addresses.cpp10
-rw-r--r--src/wallet/rpc/backup.cpp6
-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/fuzz/coinselection.cpp6
-rw-r--r--src/wallet/test/spend_tests.cpp15
-rw-r--r--src/wallet/test/wallet_tests.cpp11
-rw-r--r--src/wallet/wallet.cpp49
-rw-r--r--src/wallet/wallet.h29
-rw-r--r--src/wallet/walletdb.cpp3
-rw-r--r--src/zmq/zmqnotificationinterface.cpp12
-rw-r--r--src/zmq/zmqpublishnotifier.cpp24
-rw-r--r--src/zmq/zmqutil.cpp2
255 files changed, 3654 insertions, 2610 deletions
diff --git a/src/.clang-tidy b/src/.clang-tidy
index b1f543f610..e9807d4cb7 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -1,11 +1,13 @@
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 357e562c69..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 \
@@ -329,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
@@ -356,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 \
@@ -364,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 \
@@ -407,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
@@ -426,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 \
@@ -629,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 \
@@ -852,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 \
@@ -870,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 \
@@ -905,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 \
@@ -1018,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.test.include b/src/Makefile.test.include
index 02a3f9ae7d..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) \
@@ -269,7 +270,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/locale.cpp \
test/fuzz/merkleblock.cpp \
test/fuzz/message.cpp \
- test/fuzz/miniscript_decode.cpp \
+ test/fuzz/miniscript.cpp \
test/fuzz/minisketch.cpp \
test/fuzz/muhash.cpp \
test/fuzz/multiplication_overflow.cpp \
@@ -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 b9eae292d7..31f8eadf98 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -49,8 +49,7 @@ 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
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 53591f8905..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));
}
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/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 580cc5839a..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");
diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp
index a2f3fca26c..1aeac3cef0 100644
--- a/src/bitcoin-util.cpp
+++ b/src/bitcoin-util.cpp
@@ -15,6 +15,7 @@
#include <streams.h>
#include <util/system.h>
#include <util/translation.h>
+#include <version.h>
#include <atomic>
#include <cstdio>
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index bc063faed1..92e73d7c2a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -188,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");
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/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/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/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/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 3bf165495c..4e7e72b037 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -75,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;
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.cpp b/src/i2p.cpp
index e08b5461fe..9b8f83caef 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -242,7 +242,7 @@ std::string Session::Reply::Get(const std::string& key) const
template <typename... Args>
void Session::Log(const std::string& fmt, const Args&... args) const
{
- LogPrint(BCLog::I2P, "I2P: %s\n", tfm::format(fmt, args...));
+ LogPrint(BCLog::I2P, "%s\n", tfm::format(fmt, args...));
}
Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
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 f3c9395928..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)
@@ -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.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 713598f411..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>
@@ -86,7 +87,6 @@
#include <vector>
#ifndef WIN32
-#include <attributes.h>
#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
@@ -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__);
}
@@ -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());
@@ -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 3820ee1fd0..82b5a69eb5 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>
@@ -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) :
@@ -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};
}
@@ -1864,7 +1873,13 @@ bool CConnman::GetTryNewOutboundPeer() const
void CConnman::SetTryNewOutboundPeer(bool flag)
{
m_try_another_outbound_peer = flag;
- LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
+ LogPrint(BCLog::NET, "setting try another outbound peer=%s\n", flag ? "true" : "false");
+}
+
+void CConnman::StartExtraBlockRelayPeers()
+{
+ LogPrint(BCLog::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
@@ -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()
{
diff --git a/src/net.h b/src/net.h
index 5cdddf1fee..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>
@@ -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
{
@@ -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 478368b673..30d5548385 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -21,6 +21,7 @@
#include <node/blockstorage.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
@@ -175,6 +176,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 +240,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,10 +355,17 @@ 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>;
@@ -365,23 +401,12 @@ struct CNodeState {
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 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};
- //! 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.
*
@@ -435,35 +460,43 @@ struct CNodeState {
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:
@@ -474,15 +507,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
@@ -495,14 +528,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.
*
@@ -512,13 +547,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
@@ -545,7 +583,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);
@@ -615,7 +653,8 @@ private:
/** 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);
+ 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.
@@ -683,11 +722,10 @@ private:
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
- RecursiveMutex 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);
- bool m_most_recent_compact_block_has_witnesses GUARDED_BY(m_most_recent_block_mutex){false};
/** Height of the highest block announced using BIP 152 high-bandwidth mode. */
int m_highest_fast_announce{0};
@@ -722,7 +760,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);
@@ -773,7 +812,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.
@@ -880,10 +920,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);
- }
+ auto tx_relay = peer.GetTxRelay();
+ if (!tx_relay) return;
+
+ 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,
@@ -974,54 +1015,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()
@@ -1110,7 +1149,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
@@ -1140,7 +1178,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;
}
@@ -1186,7 +1224,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)
@@ -1241,7 +1279,7 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
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);
@@ -1377,9 +1415,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;
@@ -1550,17 +1588,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),
@@ -1632,7 +1670,7 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
*/
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);
@@ -1641,7 +1679,8 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
return;
m_highest_fast_announce = pindex->nHeight;
- bool fWitnessEnabled = DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);
+ if (!DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) return;
+
uint256 hashBlock(pblock->GetHash());
const std::shared_future<CSerializedNetMsg> lazy_ser{
std::async(std::launch::deferred, [&] { return msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock); })};
@@ -1651,10 +1690,9 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
m_most_recent_block_hash = hashBlock;
m_most_recent_block = pblock;
m_most_recent_compact_block = pcmpctblock;
- m_most_recent_compact_block_has_witnesses = fWitnessEnabled;
}
- 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)
@@ -1663,8 +1701,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());
@@ -1795,12 +1832,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);
}
};
}
@@ -1859,12 +1897,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(m_most_recent_block_mutex);
a_recent_block = m_most_recent_block;
a_recent_compact_block = m_most_recent_compact_block;
- fWitnessesPresentInARecentCompactBlock = m_most_recent_compact_block_has_witnesses;
}
bool need_activate_chain = false;
@@ -1951,11 +1987,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) {
@@ -1977,17 +2013,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));
}
}
}
@@ -2038,13 +2072,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
@@ -2057,8 +2093,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;
}
@@ -2085,7 +2122,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);
}
@@ -2146,10 +2183,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,
@@ -2214,7 +2250,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;
@@ -2258,7 +2294,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);
}
@@ -2292,7 +2328,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)) {
@@ -2591,7 +2627,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 {
@@ -2721,10 +2757,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;
}
@@ -2862,16 +2904,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;
@@ -2884,26 +2922,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;
}
@@ -3054,7 +3086,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)) {
@@ -3252,9 +3284,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;
@@ -3271,9 +3301,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;
}
@@ -3329,9 +3373,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;
@@ -3569,7 +3613,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;
@@ -3629,12 +3673,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) {
@@ -3943,9 +3981,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;
}
@@ -4038,16 +4076,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;
}
@@ -4066,10 +4101,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;
}
@@ -4086,14 +4121,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;
@@ -4104,8 +4138,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());
}
@@ -4479,10 +4513,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) {
@@ -4566,10 +4600,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}};
@@ -4580,27 +4616,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);
}
}
@@ -4682,10 +4718,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 || (m_num_preferred_download_peers == 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 +
(
@@ -4724,7 +4781,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
@@ -4777,33 +4834,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(m_most_recent_block_mutex);
if (m_most_recent_block_hash == pBestIndex->GetBlockHash()) {
- if (state.fWantsCmpctWitness || !m_most_recent_compact_block_has_witnesses)
- m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *m_most_recent_compact_block));
- else {
- CBlockHeaderAndShortTxIDs cmpctblock(*m_most_recent_block, state.fWantsCmpctWitness);
- m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- }
- fGotBlockFromCache = true;
+ 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) {
@@ -4868,45 +4919,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) {
@@ -4914,18 +4965,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);
@@ -4933,7 +4984,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);
@@ -4942,9 +4993,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.
@@ -4958,7 +5009,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);
@@ -4985,14 +5036,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);
}
}
}
@@ -5064,7 +5115,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 c982b919a6..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() { }
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 9717f7abce..ca148bfa51 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -98,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/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 17ab226a30..cadafcaa8d 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -226,7 +226,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
}
}
- LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
+ LogPrint(BCLog::PRUNE, "target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
nLastBlockWeCanPrune, count);
@@ -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;
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 488713dbd8..2e52716649 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -153,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 */
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 678df815c0..7cf8e3fb9e 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -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/feerate.cpp b/src/policy/feerate.cpp
index 0ea56d8db7..82b767793d 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <policy/feerate.h>
-
#include <tinyformat.h>
#include <cmath>
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 50fd6fd11b..a8d4d2fc63 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -9,7 +9,10 @@
#include <consensus/amount.h>
#include <serialize.h>
+
+#include <cstdint>
#include <string>
+#include <type_traits>
const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 6499dbd97f..b39632364f 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -6,12 +6,30 @@
#include <policy/fees.h>
#include <clientversion.h>
+#include <consensus/amount.h>
#include <fs.h>
#include <logging.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <random.h>
+#include <serialize.h>
#include <streams.h>
+#include <sync.h>
+#include <tinyformat.h>
#include <txmempool.h>
+#include <uint256.h>
#include <util/serfloat.h>
#include <util/system.h>
+#include <util/time.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <exception>
+#include <stdexcept>
+#include <utility>
static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
@@ -537,9 +555,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
}
}
-CBlockPolicyEstimator::~CBlockPolicyEstimator()
-{
-}
+CBlockPolicyEstimator::~CBlockPolicyEstimator() = default;
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
{
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 6e25bb42b8..dea1e1d31b 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -7,20 +7,20 @@
#include <consensus/amount.h>
#include <policy/feerate.h>
-#include <uint256.h>
#include <random.h>
#include <sync.h>
+#include <threadsafety.h>
+#include <uint256.h>
#include <array>
#include <map>
#include <memory>
+#include <set>
#include <string>
#include <vector>
class CAutoFile;
-class CFeeRate;
class CTxMemPoolEntry;
-class CTxMemPool;
class TxConfirmStats;
/* Identifier for each of the 3 different TxConfirmStats which will track
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
index 21f5488816..67918c9dec 100644
--- a/src/policy/packages.cpp
+++ b/src/policy/packages.cpp
@@ -2,12 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <consensus/validation.h>
#include <policy/packages.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <uint256.h>
#include <util/hasher.h>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
#include <numeric>
#include <unordered_set>
diff --git a/src/policy/packages.h b/src/policy/packages.h
index 9f274f6b7d..ba6a3a9a06 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -5,10 +5,12 @@
#ifndef BITCOIN_POLICY_PACKAGES_H
#define BITCOIN_POLICY_PACKAGES_H
+#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <cstdint>
#include <vector>
/** Default maximum number of transactions in a package. */
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 6aba6a4a5b..f6452266b7 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -7,10 +7,22 @@
#include <policy/policy.h>
-#include <consensus/validation.h>
#include <coins.h>
+#include <consensus/amount.h>
+#include <consensus/consensus.h>
+#include <consensus/validation.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <script/interpreter.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <serialize.h>
#include <span.h>
+#include <algorithm>
+#include <cstddef>
+#include <vector>
+
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
// "Dust" is defined in terms of dustRelayFee,
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 89f6e72618..94f9623b8a 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -6,15 +6,18 @@
#ifndef BITCOIN_POLICY_POLICY_H
#define BITCOIN_POLICY_POLICY_H
+#include <consensus/amount.h>
#include <consensus/consensus.h>
-#include <policy/feerate.h>
+#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <script/standard.h>
+#include <cstdint>
#include <string>
class CCoinsViewCache;
-class CTxOut;
+class CFeeRate;
+class CScript;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
@@ -52,6 +55,8 @@ static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
static const unsigned int DUST_RELAY_TX_FEE = 3000;
+/** Default for -minrelaytxfee, minimum relay fee for transactions */
+static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 8fe4dc35b8..e25f5c7c5b 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -4,11 +4,19 @@
#include <policy/rbf.h>
-#include <policy/settings.h>
+#include <consensus/amount.h>
+#include <policy/feerate.h>
+#include <primitives/transaction.h>
+#include <sync.h>
#include <tinyformat.h>
+#include <txmempool.h>
+#include <uint256.h>
#include <util/moneystr.h>
#include <util/rbf.h>
+#include <limits>
+#include <vector>
+
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
AssertLockHeld(pool.cs);
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index fcec7052ed..07f68c8fd4 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -5,13 +5,20 @@
#ifndef BITCOIN_POLICY_RBF_H
#define BITCOIN_POLICY_RBF_H
+#include <consensus/amount.h>
#include <primitives/transaction.h>
+#include <threadsafety.h>
#include <txmempool.h>
-#include <uint256.h>
+#include <cstddef>
+#include <cstdint>
#include <optional>
+#include <set>
#include <string>
+class CFeeRate;
+class uint256;
+
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
* mempool conflicts and their descendants. */
static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100};
diff --git a/src/policy/settings.cpp b/src/policy/settings.cpp
index eb2ec56850..0b67d274ce 100644
--- a/src/policy/settings.cpp
+++ b/src/policy/settings.cpp
@@ -11,4 +11,5 @@
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
+CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
diff --git a/src/policy/settings.h b/src/policy/settings.h
index 0b4fc1e770..2311d01fe8 100644
--- a/src/policy/settings.h
+++ b/src/policy/settings.h
@@ -6,14 +6,19 @@
#ifndef BITCOIN_POLICY_SETTINGS_H
#define BITCOIN_POLICY_SETTINGS_H
+#include <policy/feerate.h>
#include <policy/policy.h>
-class CFeeRate;
+#include <cstdint>
+#include <string>
+
class CTransaction;
// Policy settings which are configurable at runtime.
extern CFeeRate incrementalRelayFee;
extern CFeeRate dustRelayFee;
+/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
+extern CFeeRate minRelayTxFee;
extern unsigned int nBytesPerSigOp;
extern bool fIsBareMultisigStd;
diff --git a/src/prevector.h b/src/prevector.h
index 830b31e315..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;
}
@@ -465,9 +459,6 @@ public:
}
~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/protocol.h b/src/protocol.h
index fdeaa9a9c5..da2d24aff3 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -3,10 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef __cplusplus
-#error This header can only be compiled as C++.
-#endif
-
#ifndef BITCOIN_PROTOCOL_H
#define BITCOIN_PROTOCOL_H
@@ -15,10 +11,9 @@
#include <serialize.h>
#include <streams.h>
#include <uint256.h>
-#include <version.h>
+#include <cstdint>
#include <limits>
-#include <stdint.h>
#include <string>
/** Message header.
@@ -420,7 +415,6 @@ public:
use_v2 = s.GetVersion() & ADDRV2_FORMAT;
}
- SER_READ(obj, obj.nTime = TIME_INIT);
READWRITE(obj.nTime);
// nServices is serialized as CompactSize in V2; as uint64_t in V1.
if (use_v2) {
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/android/res/values/libs.xml b/src/qt/android/res/values/libs.xml
index 0f20df4eb0..b4b77b1c7b 100644
--- a/src/qt/android/res/values/libs.xml
+++ b/src/qt/android/res/values/libs.xml
@@ -10,8 +10,5 @@
<item>
x86_64;libbitcoin-qt_x86_64.so
</item>
- <item>
- x86;libbitcoin-qt_x86.so
- </item>
</array>
</resources>
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 706cf2f7db..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)});
@@ -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/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 57b5178d78..d682a7d3e8 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,7 +759,7 @@ static RPCHelpMan pruneblockchain()
CChainState& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
- int heightParam = request.params[0].get_int();
+ int heightParam = request.params[0].getInt<int>();
if (heightParam < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
}
@@ -777,11 +777,11 @@ 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;
}
@@ -790,7 +790,7 @@ static RPCHelpMan pruneblockchain()
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);
+ return static_cast<int64_t>(last_block->nHeight - 1);
},
};
}
@@ -808,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",
@@ -862,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);
@@ -884,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()};
@@ -903,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);
@@ -922,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);
@@ -993,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();
@@ -1051,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 {
@@ -1099,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);
@@ -1142,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);
@@ -1150,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",
{},
@@ -1178,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{
@@ -1203,14 +1223,14 @@ RPCHelpMan getblockchaininfo()
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", GetDifficulty(&tip));
obj.pushKV("time", tip.GetBlockTime());
obj.pushKV("mediantime", tip.GetMedianTimePast());
- obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip));
+ obj.pushKV("verificationprogress", GuessVerificationProgress(chainman.GetParams().TxData(), &tip));
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip.nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
@@ -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,7 +1619,7 @@ 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");
@@ -1900,7 +1913,7 @@ static RPCHelpMan getblockstats()
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);
@@ -1969,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);
@@ -2072,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;
@@ -2281,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);
@@ -2300,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;
{
@@ -2320,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;
@@ -2354,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
index bfec0780aa..1873bc1587 100644
--- a/src/rpc/fees.cpp
+++ b/src/rpc/fees.cpp
@@ -7,6 +7,7 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/server.h>
@@ -16,7 +17,6 @@
#include <univalue.h>
#include <util/fees.h>
#include <util/system.h>
-#include <validation.h>
#include <algorithm>
#include <array>
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 27080d3881..97ec95a166 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -8,6 +8,7 @@
#include <core_io.h>
#include <fs.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
#include <rpc/server_util.h>
@@ -15,7 +16,6 @@
#include <txmempool.h>
#include <univalue.h>
#include <util/moneystr.h>
-#include <validation.h>
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
using node::NodeContext;
@@ -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 b552528951..8fb6daf0cb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -27,6 +27,7 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <shutdown.h>
+#include <timedata.h>
#include <txmempool.h>
#include <univalue.h>
#include <util/strencodings.h>
@@ -109,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());
},
};
}
@@ -119,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;
}
@@ -133,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");
}
@@ -145,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;
@@ -217,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;
@@ -264,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)) {
@@ -349,7 +348,6 @@ static RPCHelpMan generateblock()
}
}
- CChainParams chainparams(Params());
CBlock block;
ChainstateManager& chainman = EnsureChainman(node);
@@ -357,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");
}
@@ -374,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()));
}
}
@@ -429,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;
},
@@ -462,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.");
@@ -643,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);
}
@@ -657,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>();
}
}
}
@@ -665,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!");
@@ -726,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) {
@@ -755,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");
@@ -770,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");
@@ -836,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:
@@ -844,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:
{
@@ -853,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;
@@ -930,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 {
@@ -993,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";
@@ -1042,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());
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 09dc8eb3eb..0a061f2451 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -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/node.cpp b/src/rpc/node.cpp
index 24697a606e..5475662b82 100644
--- a/src/rpc/node.cpp
+++ b/src/rpc/node.cpp
@@ -53,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));
}
@@ -108,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)");
}
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index 1cf952d0c8..f4bb76f50f 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -124,7 +124,7 @@ static RPCHelpMan createmultisig()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- int required = request.params[0].get_int();
+ int required = request.params[0].getInt<int>();
// Get the public keys
const UniValue& keys = request.params[1].get_array();
@@ -163,11 +163,11 @@ static RPCHelpMan createmultisig()
result.pushKV("descriptor", descriptor->ToString());
UniValue warnings(UniValue::VARR);
- if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ if (descriptor->GetOutputType() != 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);
+ if (!warnings.empty()) result.pushKV("warnings", warnings);
return result;
},
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index e8713fbd2e..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) {
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/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/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 3e7ee7d370..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_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::system_clock::now());
+ m_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::steady_clock::now());
}
void SingleThreadedSchedulerClient::ProcessQueue()
diff --git a/src/scheduler.h b/src/scheduler.h
index eb350e4bc3..749e5442b0 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -46,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);
}
/**
@@ -60,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();
@@ -93,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};
@@ -128,8 +129,8 @@ private:
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& scheduler LIFETIMEBOUND) : m_scheduler{scheduler} {}
@@ -140,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/miniscript.cpp b/src/script/miniscript.cpp
index 019f02f159..cb4d4cb783 100644
--- a/src/script/miniscript.cpp
+++ b/src/script/miniscript.cpp
@@ -17,69 +17,67 @@ Type SanitizeType(Type e) {
int num_types = (e << "K"_mst) + (e << "V"_mst) + (e << "B"_mst) + (e << "W"_mst);
if (num_types == 0) return ""_mst; // No valid type, don't care about the rest
assert(num_types == 1); // K, V, B, W all conflict with each other
- bool ok = // Work around a GCC 4.8 bug that breaks user-defined literals in macro calls.
- (!(e << "z"_mst) || !(e << "o"_mst)) && // z conflicts with o
- (!(e << "n"_mst) || !(e << "z"_mst)) && // n conflicts with z
- (!(e << "n"_mst) || !(e << "W"_mst)) && // n conflicts with W
- (!(e << "V"_mst) || !(e << "d"_mst)) && // V conflicts with d
- (!(e << "K"_mst) || (e << "u"_mst)) && // K implies u
- (!(e << "V"_mst) || !(e << "u"_mst)) && // V conflicts with u
- (!(e << "e"_mst) || !(e << "f"_mst)) && // e conflicts with f
- (!(e << "e"_mst) || (e << "d"_mst)) && // e implies d
- (!(e << "V"_mst) || !(e << "e"_mst)) && // V conflicts with e
- (!(e << "d"_mst) || !(e << "f"_mst)) && // d conflicts with f
- (!(e << "V"_mst) || (e << "f"_mst)) && // V implies f
- (!(e << "K"_mst) || (e << "s"_mst)) && // K implies s
- (!(e << "z"_mst) || (e << "m"_mst)); // z implies m
- assert(ok);
+ assert(!(e << "z"_mst) || !(e << "o"_mst)); // z conflicts with o
+ assert(!(e << "n"_mst) || !(e << "z"_mst)); // n conflicts with z
+ assert(!(e << "n"_mst) || !(e << "W"_mst)); // n conflicts with W
+ assert(!(e << "V"_mst) || !(e << "d"_mst)); // V conflicts with d
+ assert(!(e << "K"_mst) || (e << "u"_mst)); // K implies u
+ assert(!(e << "V"_mst) || !(e << "u"_mst)); // V conflicts with u
+ assert(!(e << "e"_mst) || !(e << "f"_mst)); // e conflicts with f
+ assert(!(e << "e"_mst) || (e << "d"_mst)); // e implies d
+ assert(!(e << "V"_mst) || !(e << "e"_mst)); // V conflicts with e
+ assert(!(e << "d"_mst) || !(e << "f"_mst)); // d conflicts with f
+ assert(!(e << "V"_mst) || (e << "f"_mst)); // V implies f
+ assert(!(e << "K"_mst) || (e << "s"_mst)); // K implies s
+ assert(!(e << "z"_mst) || (e << "m"_mst)); // z implies m
return e;
}
-Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) {
+Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) {
// Sanity check on data
- if (nodetype == Fragment::SHA256 || nodetype == Fragment::HASH256) {
+ if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) {
assert(data_size == 32);
- } else if (nodetype == Fragment::RIPEMD160 || nodetype == Fragment::HASH160) {
+ } else if (fragment == Fragment::RIPEMD160 || fragment == Fragment::HASH160) {
assert(data_size == 20);
} else {
assert(data_size == 0);
}
// Sanity check on k
- if (nodetype == Fragment::OLDER || nodetype == Fragment::AFTER) {
+ if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) {
assert(k >= 1 && k < 0x80000000UL);
- } else if (nodetype == Fragment::MULTI) {
+ } else if (fragment == Fragment::MULTI) {
assert(k >= 1 && k <= n_keys);
- } else if (nodetype == Fragment::THRESH) {
+ } else if (fragment == Fragment::THRESH) {
assert(k >= 1 && k <= n_subs);
} else {
assert(k == 0);
}
// Sanity check on subs
- if (nodetype == Fragment::AND_V || nodetype == Fragment::AND_B || nodetype == Fragment::OR_B ||
- nodetype == Fragment::OR_C || nodetype == Fragment::OR_I || nodetype == Fragment::OR_D) {
+ if (fragment == Fragment::AND_V || fragment == Fragment::AND_B || fragment == Fragment::OR_B ||
+ fragment == Fragment::OR_C || fragment == Fragment::OR_I || fragment == Fragment::OR_D) {
assert(n_subs == 2);
- } else if (nodetype == Fragment::ANDOR) {
+ } else if (fragment == Fragment::ANDOR) {
assert(n_subs == 3);
- } else if (nodetype == Fragment::WRAP_A || nodetype == Fragment::WRAP_S || nodetype == Fragment::WRAP_C ||
- nodetype == Fragment::WRAP_D || nodetype == Fragment::WRAP_V || nodetype == Fragment::WRAP_J ||
- nodetype == Fragment::WRAP_N) {
+ } else if (fragment == Fragment::WRAP_A || fragment == Fragment::WRAP_S || fragment == Fragment::WRAP_C ||
+ fragment == Fragment::WRAP_D || fragment == Fragment::WRAP_V || fragment == Fragment::WRAP_J ||
+ fragment == Fragment::WRAP_N) {
assert(n_subs == 1);
- } else if (nodetype != Fragment::THRESH) {
+ } else if (fragment != Fragment::THRESH) {
assert(n_subs == 0);
}
// Sanity check on keys
- if (nodetype == Fragment::PK_K || nodetype == Fragment::PK_H) {
+ if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) {
assert(n_keys == 1);
- } else if (nodetype == Fragment::MULTI) {
+ } else if (fragment == Fragment::MULTI) {
assert(n_keys >= 1 && n_keys <= 20);
} else {
assert(n_keys == 0);
}
- // Below is the per-nodetype logic for computing the expression types.
+ // Below is the per-fragment logic for computing the expression types.
// It heavily relies on Type's << operator (where "X << a_mst" means
// "X has all properties listed in a").
- switch (nodetype) {
+ switch (fragment) {
case Fragment::PK_K: return "Konudemsxk"_mst;
case Fragment::PK_H: return "Knudemsxk"_mst;
case Fragment::OLDER: return
@@ -247,11 +245,10 @@ Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Ty
}
}
assert(false);
- return ""_mst;
}
-size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) {
- switch (nodetype) {
+size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys) {
+ switch (fragment) {
case Fragment::JUST_1:
case Fragment::JUST_0: return 1;
case Fragment::PK_K: return 34;
@@ -262,7 +259,7 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
case Fragment::SHA256: return 4 + 2 + 33;
case Fragment::HASH160:
case Fragment::RIPEMD160: return 4 + 2 + 21;
- case Fragment::MULTI: return 3 + (n_keys > 16) + (k > 16) + 34 * n_keys;
+ case Fragment::MULTI: return 1 + BuildScript(n_keys).size() + BuildScript(k).size() + 34 * n_keys;
case Fragment::AND_V: return subsize;
case Fragment::WRAP_V: return subsize + (sub0typ << "x"_mst);
case Fragment::WRAP_S:
@@ -280,19 +277,17 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
case Fragment::THRESH: return subsize + n_subs + BuildScript(k).size();
}
assert(false);
- return 0;
}
-bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out)
+std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script)
{
- out.clear();
+ std::vector<Opcode> out;
CScript::const_iterator it = script.begin(), itend = script.end();
while (it != itend) {
std::vector<unsigned char> push_data;
opcodetype opcode;
if (!script.GetOp(it, opcode, push_data)) {
- out.clear();
- return false;
+ return {};
} else if (opcode >= OP_1 && opcode <= OP_16) {
// Deal with OP_n (GetOp does not turn them into pushes).
push_data.assign(1, CScript::DecodeOP_N(opcode));
@@ -309,30 +304,28 @@ bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, st
out.emplace_back(OP_EQUAL, std::vector<unsigned char>());
opcode = OP_VERIFY;
} else if (IsPushdataOp(opcode)) {
- if (!CheckMinimalPush(push_data, opcode)) return false;
+ if (!CheckMinimalPush(push_data, opcode)) return {};
} else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) {
// Rule out non minimal VERIFY sequences
- return false;
+ return {};
}
out.emplace_back(opcode, std::move(push_data));
}
std::reverse(out.begin(), out.end());
- return true;
+ return out;
}
-bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k) {
+std::optional<int64_t> ParseScriptNumber(const Opcode& in) {
if (in.first == OP_0) {
- k = 0;
- return true;
+ return 0;
}
if (!in.second.empty()) {
- if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return false;
+ if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return {};
try {
- k = CScriptNum(in.second, true).GetInt64();
- return true;
+ return CScriptNum(in.second, true).GetInt64();
} catch(const scriptnum_error&) {}
}
- return false;
+ return {};
}
int FindNextChar(Span<const char> sp, const char m)
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 5c1cc316dc..2c239c2678 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -6,6 +6,7 @@
#define BITCOIN_SCRIPT_MINISCRIPT_H
#include <algorithm>
+#include <functional>
#include <numeric>
#include <memory>
#include <optional>
@@ -40,7 +41,7 @@ namespace miniscript {
* - For example: older(n) = <n> OP_CHECKSEQUENCEVERIFY.
* - "V" Verify:
* - Takes its inputs from the top of the stack.
- * - When satisfactied, pushes nothing.
+ * - When satisfied, pushes nothing.
* - Cannot be dissatisfied.
* - This can be obtained by adding an OP_VERIFY to a B, modifying the last opcode
* of a B to its -VERIFY version (only for OP_CHECKSIG, OP_CHECKSIGVERIFY
@@ -179,6 +180,8 @@ inline constexpr Type operator"" _mst(const char* c, size_t l) {
return typ;
}
+using Opcode = std::pair<opcodetype, std::vector<unsigned char>>;
+
template<typename Key> struct Node;
template<typename Key> using NodeRef = std::shared_ptr<const Node<Key>>;
@@ -224,10 +227,10 @@ enum class Fragment {
namespace internal {
//! Helper function for Node::CalcType.
-Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys);
+Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys);
//! Helper function for Node::CalcScriptLen.
-size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys);
+size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_t k, size_t n_subs, size_t n_keys);
//! A helper sanitizer/checker for the output of CalcType.
Type SanitizeType(Type x);
@@ -279,7 +282,7 @@ struct StackSize {
template<typename Key>
struct Node {
//! What node type this node is.
- const Fragment nodetype;
+ const Fragment fragment;
//! The k parameter (time for OLDER/AFTER, threshold for THRESH(_M))
const uint32_t k = 0;
//! The keys used by this expression (only for PK_K/PK_H/MULTI)
@@ -298,6 +301,8 @@ private:
const Type typ;
//! Cached script length (computed by CalcScriptLen).
const size_t scriptlen;
+ //! Whether a public key appears more than once in this node.
+ const bool duplicate_key;
//! Compute the length of the script for this miniscript (including children).
size_t CalcScriptLen() const {
@@ -306,7 +311,7 @@ private:
subsize += sub->ScriptSize();
}
Type sub0type = subs.size() > 0 ? subs[0]->GetType() : ""_mst;
- return internal::ComputeScriptLen(nodetype, sub0type, subsize, k, subs.size(), keys.size());
+ return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size());
}
/* Apply a recursive algorithm to a Miniscript tree, without actual recursive calls.
@@ -329,6 +334,8 @@ private:
* computes the result of the node. If std::nullopt is returned by upfn,
* TreeEvalMaybe() immediately returns std::nullopt.
* The return value of TreeEvalMaybe is the result of the root node.
+ *
+ * Result type cannot be bool due to the std::vector<bool> specialization.
*/
template<typename Result, typename State, typename DownFn, typename UpFn>
std::optional<Result> TreeEvalMaybe(State root_state, DownFn downfn, UpFn upfn) const
@@ -393,6 +400,20 @@ private:
return std::move(results[0]);
}
+ /** Like TreeEvalMaybe, but without downfn or State type.
+ * upfn takes (const Node&, Span<Result>) and returns std::optional<Result>. */
+ template<typename Result, typename UpFn>
+ std::optional<Result> TreeEvalMaybe(UpFn upfn) const
+ {
+ struct DummyState {};
+ return TreeEvalMaybe<Result>(DummyState{},
+ [](DummyState, const Node&, size_t) { return DummyState{}; },
+ [&upfn](DummyState, const Node& node, Span<Result> subs) {
+ return upfn(node, subs);
+ }
+ );
+ }
+
/** Like TreeEvalMaybe, but always produces a result. upfn must return Result. */
template<typename Result, typename State, typename DownFn, typename UpFn>
Result TreeEval(State root_state, DownFn&& downfn, UpFn upfn) const
@@ -408,13 +429,33 @@ private:
));
}
+ /** Compare two miniscript subtrees, using a non-recursive algorithm. */
+ friend int Compare(const Node<Key>& node1, const Node<Key>& node2)
+ {
+ std::vector<std::pair<const Node<Key>&, const Node<Key>&>> queue;
+ queue.emplace_back(node1, node2);
+ while (!queue.empty()) {
+ const auto& [a, b] = queue.back();
+ queue.pop_back();
+ if (std::tie(a.fragment, a.k, a.keys, a.data) < std::tie(b.fragment, b.k, b.keys, b.data)) return -1;
+ if (std::tie(b.fragment, b.k, b.keys, b.data) < std::tie(a.fragment, a.k, a.keys, a.data)) return 1;
+ if (a.subs.size() < b.subs.size()) return -1;
+ if (b.subs.size() < a.subs.size()) return 1;
+ size_t n = a.subs.size();
+ for (size_t i = 0; i < n; ++i) {
+ queue.emplace_back(*a.subs[n - 1 - i], *b.subs[n - 1 - i]);
+ }
+ }
+ return 0;
+ }
+
//! Compute the type for this miniscript.
Type CalcType() const {
using namespace internal;
// THRESH has a variable number of subexpressions
std::vector<Type> sub_types;
- if (nodetype == Fragment::THRESH) {
+ if (fragment == Fragment::THRESH) {
for (const auto& sub : subs) sub_types.push_back(sub->GetType());
}
// All other nodes than THRESH can be computed just from the types of the 0-3 subexpressions.
@@ -422,7 +463,7 @@ private:
Type y = subs.size() > 1 ? subs[1]->GetType() : ""_mst;
Type z = subs.size() > 2 ? subs[2]->GetType() : ""_mst;
- return SanitizeType(ComputeType(nodetype, x, y, z, sub_types, k, data.size(), subs.size(), keys.size()));
+ return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size()));
}
public:
@@ -434,17 +475,17 @@ public:
// by an OP_VERIFY (which may need to be combined with the last script opcode).
auto downfn = [](bool verify, const Node& node, size_t index) {
// For WRAP_V, the subexpression is certainly followed by OP_VERIFY.
- if (node.nodetype == Fragment::WRAP_V) return true;
+ if (node.fragment == Fragment::WRAP_V) return true;
// The subexpression of WRAP_S, and the last subexpression of AND_V
// inherit the followed-by-OP_VERIFY property from the parent.
- if (node.nodetype == Fragment::WRAP_S ||
- (node.nodetype == Fragment::AND_V && index == 1)) return verify;
+ if (node.fragment == Fragment::WRAP_S ||
+ (node.fragment == Fragment::AND_V && index == 1)) return verify;
return false;
};
// The upward function computes for a node, given its followed-by-OP_VERIFY status
// and the CScripts of its child nodes, the CScript of the node.
auto upfn = [&ctx](bool verify, const Node& node, Span<CScript> subs) -> CScript {
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::PK_K: return BuildScript(ctx.ToPKBytes(node.keys[0]));
case Fragment::PK_H: return BuildScript(OP_DUP, OP_HASH160, ctx.ToPKHBytes(node.keys[0]), OP_EQUALVERIFY);
case Fragment::OLDER: return BuildScript(node.k, OP_CHECKSEQUENCEVERIFY);
@@ -491,45 +532,44 @@ public:
}
}
assert(false);
- return {};
};
return TreeEval<CScript>(false, downfn, upfn);
}
template<typename CTx>
- bool ToString(const CTx& ctx, std::string& ret) const {
+ std::optional<std::string> ToString(const CTx& ctx) const {
// To construct the std::string representation for a Miniscript object, we use
// the TreeEvalMaybe algorithm. The State is a boolean: whether the parent node is a
// wrapper. If so, non-wrapper expressions must be prefixed with a ":".
auto downfn = [](bool, const Node& node, size_t) {
- return (node.nodetype == Fragment::WRAP_A || node.nodetype == Fragment::WRAP_S ||
- node.nodetype == Fragment::WRAP_D || node.nodetype == Fragment::WRAP_V ||
- node.nodetype == Fragment::WRAP_J || node.nodetype == Fragment::WRAP_N ||
- node.nodetype == Fragment::WRAP_C ||
- (node.nodetype == Fragment::AND_V && node.subs[1]->nodetype == Fragment::JUST_1) ||
- (node.nodetype == Fragment::OR_I && node.subs[0]->nodetype == Fragment::JUST_0) ||
- (node.nodetype == Fragment::OR_I && node.subs[1]->nodetype == Fragment::JUST_0));
+ return (node.fragment == Fragment::WRAP_A || node.fragment == Fragment::WRAP_S ||
+ node.fragment == Fragment::WRAP_D || node.fragment == Fragment::WRAP_V ||
+ node.fragment == Fragment::WRAP_J || node.fragment == Fragment::WRAP_N ||
+ node.fragment == Fragment::WRAP_C ||
+ (node.fragment == Fragment::AND_V && node.subs[1]->fragment == Fragment::JUST_1) ||
+ (node.fragment == Fragment::OR_I && node.subs[0]->fragment == Fragment::JUST_0) ||
+ (node.fragment == Fragment::OR_I && node.subs[1]->fragment == Fragment::JUST_0));
};
// The upward function computes for a node, given whether its parent is a wrapper,
// and the string representations of its child nodes, the string representation of the node.
auto upfn = [&ctx](bool wrapped, const Node& node, Span<std::string> subs) -> std::optional<std::string> {
std::string ret = wrapped ? ":" : "";
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::WRAP_A: return "a" + std::move(subs[0]);
case Fragment::WRAP_S: return "s" + std::move(subs[0]);
case Fragment::WRAP_C:
- if (node.subs[0]->nodetype == Fragment::PK_K) {
+ if (node.subs[0]->fragment == Fragment::PK_K) {
// pk(K) is syntactic sugar for c:pk_k(K)
- std::string key_str;
- if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
- return std::move(ret) + "pk(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.subs[0]->keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk(" + std::move(*key_str) + ")";
}
- if (node.subs[0]->nodetype == Fragment::PK_H) {
+ if (node.subs[0]->fragment == Fragment::PK_H) {
// pkh(K) is syntactic sugar for c:pk_h(K)
- std::string key_str;
- if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
- return std::move(ret) + "pkh(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.subs[0]->keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pkh(" + std::move(*key_str) + ")";
}
return "c" + std::move(subs[0]);
case Fragment::WRAP_D: return "d" + std::move(subs[0]);
@@ -538,24 +578,24 @@ public:
case Fragment::WRAP_N: return "n" + std::move(subs[0]);
case Fragment::AND_V:
// t:X is syntactic sugar for and_v(X,1).
- if (node.subs[1]->nodetype == Fragment::JUST_1) return "t" + std::move(subs[0]);
+ if (node.subs[1]->fragment == Fragment::JUST_1) return "t" + std::move(subs[0]);
break;
case Fragment::OR_I:
- if (node.subs[0]->nodetype == Fragment::JUST_0) return "l" + std::move(subs[1]);
- if (node.subs[1]->nodetype == Fragment::JUST_0) return "u" + std::move(subs[0]);
+ if (node.subs[0]->fragment == Fragment::JUST_0) return "l" + std::move(subs[1]);
+ if (node.subs[1]->fragment == Fragment::JUST_0) return "u" + std::move(subs[0]);
break;
default: break;
}
- switch (node.nodetype) {
+ switch (node.fragment) {
case Fragment::PK_K: {
- std::string key_str;
- if (!ctx.ToString(node.keys[0], key_str)) return {};
- return std::move(ret) + "pk_k(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk_k(" + std::move(*key_str) + ")";
}
case Fragment::PK_H: {
- std::string key_str;
- if (!ctx.ToString(node.keys[0], key_str)) return {};
- return std::move(ret) + "pk_h(" + std::move(key_str) + ")";
+ auto key_str = ctx.ToString(node.keys[0]);
+ if (!key_str) return {};
+ return std::move(ret) + "pk_h(" + std::move(*key_str) + ")";
}
case Fragment::AFTER: return std::move(ret) + "after(" + ::ToString(node.k) + ")";
case Fragment::OLDER: return std::move(ret) + "older(" + ::ToString(node.k) + ")";
@@ -573,14 +613,14 @@ public:
case Fragment::OR_I: return std::move(ret) + "or_i(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
case Fragment::ANDOR:
// and_n(X,Y) is syntactic sugar for andor(X,Y,0).
- if (node.subs[2]->nodetype == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
+ if (node.subs[2]->fragment == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";
return std::move(ret) + "andor(" + std::move(subs[0]) + "," + std::move(subs[1]) + "," + std::move(subs[2]) + ")";
case Fragment::MULTI: {
auto str = std::move(ret) + "multi(" + ::ToString(node.k);
for (const auto& key : node.keys) {
- std::string key_str;
- if (!ctx.ToString(key, key_str)) return {};
- str += "," + std::move(key_str);
+ auto key_str = ctx.ToString(key);
+ if (!key_str) return {};
+ str += "," + std::move(*key_str);
}
return std::move(str) + ")";
}
@@ -591,18 +631,16 @@ public:
}
return std::move(str) + ")";
}
- default: assert(false);
+ default: break;
}
- return ""; // Should never be reached.
+ assert(false);
};
- auto res = TreeEvalMaybe<std::string>(false, downfn, upfn);
- if (res.has_value()) ret = std::move(*res);
- return res.has_value();
+ return TreeEvalMaybe<std::string>(false, downfn, upfn);
}
internal::Ops CalcOps() const {
- switch (nodetype) {
+ switch (fragment) {
case Fragment::JUST_1: return {0, 0, {}};
case Fragment::JUST_0: return {0, {}, 0};
case Fragment::PK_K: return {0, 0, 0};
@@ -672,11 +710,10 @@ public:
}
}
assert(false);
- return {0, {}, {}};
}
internal::StackSize CalcStackSize() const {
- switch (nodetype) {
+ switch (fragment) {
case Fragment::JUST_0: return {{}, 0};
case Fragment::JUST_1:
case Fragment::OLDER:
@@ -723,7 +760,42 @@ public:
}
}
assert(false);
- return {{}, {}};
+ }
+
+ /** Check whether any key is repeated.
+ * This uses a custom key comparator provided by the context in order to still detect duplicates
+ * for more complicated types.
+ */
+ template<typename Ctx> bool ContainsDuplicateKey(const Ctx& ctx) const {
+ // We cannot use a lambda here, as lambdas are non assignable, and the set operations
+ // below require moving the comparators around.
+ struct Comp {
+ const Ctx* ctx_ptr;
+ Comp(const Ctx& ctx) : ctx_ptr(&ctx) {}
+ bool operator()(const Key& a, const Key& b) const { return ctx_ptr->KeyCompare(a, b); }
+ };
+ using set = std::set<Key, Comp>;
+
+ auto upfn = [this, &ctx](const Node& node, Span<set> subs) -> std::optional<set> {
+ if (&node != this && node.duplicate_key) return {};
+
+ size_t keys_count = node.keys.size();
+ set key_set{node.keys.begin(), node.keys.end(), Comp(ctx)};
+ if (key_set.size() != keys_count) return {};
+
+ for (auto& sub: subs) {
+ keys_count += sub.size();
+ // Small optimization: std::set::merge is linear in the size of the second arg but
+ // logarithmic in the size of the first.
+ if (key_set.size() < sub.size()) std::swap(key_set, sub);
+ key_set.merge(sub);
+ if (key_set.size() != keys_count) return {};
+ }
+
+ return key_set;
+ };
+
+ return !TreeEvalMaybe<set>(upfn);
}
public:
@@ -758,35 +830,31 @@ public:
//! Check whether this script always needs a signature.
bool NeedsSignature() const { return GetType() << "s"_mst; }
- //! Do all sanity checks.
- bool IsSane() const { return IsValid() && GetType() << "mk"_mst && CheckOpsLimit() && CheckStackSize(); }
+ //! Check whether there is no satisfaction path that contains both timelocks and heightlocks
+ bool CheckTimeLocksMix() const { return GetType() << "k"_mst; }
+
+ //! Check whether there is no duplicate key across this fragment and all its sub-fragments.
+ bool CheckDuplicateKey() const { return !duplicate_key; }
+
+ //! Whether successful non-malleable satisfactions are guaranteed to be valid.
+ bool ValidSatisfactions() const { return IsValid() && CheckOpsLimit() && CheckStackSize(); }
+
+ //! Whether the apparent policy of this node matches its script semantics. Doesn't guarantee it is a safe script on its own.
+ bool IsSaneSubexpression() const { return ValidSatisfactions() && IsNonMalleable() && CheckTimeLocksMix() && CheckDuplicateKey(); }
//! Check whether this node is safe as a script on its own.
- bool IsSaneTopLevel() const { return IsValidTopLevel() && IsSane() && NeedsSignature(); }
+ bool IsSane() const { return IsValidTopLevel() && IsSaneSubexpression() && NeedsSignature(); }
//! Equality testing.
- bool operator==(const Node<Key>& arg) const
- {
- if (nodetype != arg.nodetype) return false;
- if (k != arg.k) return false;
- if (data != arg.data) return false;
- if (keys != arg.keys) return false;
- if (subs.size() != arg.subs.size()) return false;
- for (size_t i = 0; i < subs.size(); ++i) {
- if (!(*subs[i] == *arg.subs[i])) return false;
- }
- assert(scriptlen == arg.scriptlen);
- assert(typ == arg.typ);
- return true;
- }
+ bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; }
// Constructors with various argument combinations.
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : nodetype(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : nodetype(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : nodetype(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<Key> key, uint32_t val = 0) : nodetype(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : nodetype(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
- Node(Fragment nt, uint32_t val = 0) : nodetype(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
+ template <typename Ctx> Node(const Ctx& ctx, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()), duplicate_key(ContainsDuplicateKey(ctx)) {}
};
namespace internal {
@@ -847,15 +915,15 @@ enum class ParseContext {
int FindNextChar(Span<const char> in, const char m);
-/** Parse a key string ending with a ')' or ','. */
+/** Parse a key string ending at the end of the fragment's text representation. */
template<typename Key, typename Ctx>
std::optional<std::pair<Key, int>> ParseKeyEnd(Span<const char> in, const Ctx& ctx)
{
- Key key;
int key_size = FindNextChar(in, ')');
if (key_size < 1) return {};
- if (!ctx.FromString(in.begin(), in.begin() + key_size, key)) return {};
- return {{std::move(key), key_size}};
+ auto key = ctx.FromString(in.begin(), in.begin() + key_size);
+ if (!key) return {};
+ return {{std::move(*key), key_size}};
}
/** Parse a hex string ending at the end of the fragment's text representation. */
@@ -873,15 +941,15 @@ std::optional<std::pair<std::vector<unsigned char>, int>> ParseHexStrEnd(Span<co
}
/** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */
-template<typename Key>
-void BuildBack(Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
+template<typename Key, typename Ctx>
+void BuildBack(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
{
NodeRef<Key> child = std::move(constructed.back());
constructed.pop_back();
if (reverse) {
- constructed.back() = MakeNodeRef<Key>(nt, Vector(std::move(child), std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(child), std::move(constructed.back())));
} else {
- constructed.back() = MakeNodeRef<Key>(nt, Vector(std::move(constructed.back()), std::move(child)));
+ constructed.back() = MakeNodeRef<Key>(ctx, nt, Vector(std::move(constructed.back()), std::move(child)));
}
}
@@ -934,7 +1002,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
to_parse.emplace_back(ParseContext::WRAP_T, -1, -1);
} else if (in[j] == 'l') {
// The l: wrapper is equivalent to or_i(0,X)
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
to_parse.emplace_back(ParseContext::OR_I, -1, -1);
} else {
return {};
@@ -946,56 +1014,56 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
}
case ParseContext::EXPR: {
if (Const("0", in)) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
} else if (Const("1", in)) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
} else if (Const("pk(", in)) {
auto res = ParseKeyEnd<Key, Ctx>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::WRAP_C, Vector(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
} else if (Const("pkh(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::WRAP_C, Vector(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))))));
in = in.subspan(key_size + 1);
} else if (Const("pk_k(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(key))));
in = in.subspan(key_size + 1);
} else if (Const("pk_h(", in)) {
auto res = ParseKeyEnd<Key>(in, ctx);
if (!res) return {};
auto& [key, key_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(key))));
in = in.subspan(key_size + 1);
} else if (Const("sha256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::SHA256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("ripemd160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::RIPEMD160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("hash256(", in)) {
auto res = ParseHexStrEnd(in, 32, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH256, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("hash160(", in)) {
auto res = ParseHexStrEnd(in, 20, ctx);
if (!res) return {};
auto& [hash, hash_size] = *res;
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH160, std::move(hash)));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, std::move(hash)));
in = in.subspan(hash_size + 1);
} else if (Const("after(", in)) {
int arg_size = FindNextChar(in, ')');
@@ -1003,7 +1071,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, num));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, num));
in = in.subspan(arg_size + 1);
} else if (Const("older(", in)) {
int arg_size = FindNextChar(in, ')');
@@ -1011,7 +1079,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
int64_t num;
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
if (num < 1 || num >= 0x80000000L) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, num));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, num));
in = in.subspan(arg_size + 1);
} else if (Const("multi(", in)) {
// Get threshold
@@ -1022,17 +1090,17 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
// Get keys
std::vector<Key> keys;
while (next_comma != -1) {
- Key key;
next_comma = FindNextChar(in, ',');
int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma;
if (key_length < 1) return {};
- if (!ctx.FromString(in.begin(), in.begin() + key_length, key)) return {};
- keys.push_back(std::move(key));
+ auto key = ctx.FromString(in.begin(), in.begin() + key_length);
+ if (!key) return {};
+ keys.push_back(std::move(*key));
in = in.subspan(key_length + 1);
}
if (keys.size() < 1 || keys.size() > 20) return {};
if (k < 1 || k > (int64_t)keys.size()) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), k));
} else if (Const("thresh(", in)) {
int next_comma = FindNextChar(in, ',');
if (next_comma < 1) return {};
@@ -1076,69 +1144,69 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
break;
}
case ParseContext::ALT: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case ParseContext::SWAP: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case ParseContext::CHECK: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case ParseContext::DUP_IF: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case ParseContext::NON_ZERO: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case ParseContext::ZERO_NOTEQUAL: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case ParseContext::VERIFY: {
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case ParseContext::WRAP_U: {
- constructed.back() = MakeNodeRef<Key>(Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::OR_I, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
break;
}
case ParseContext::WRAP_T: {
- constructed.back() = MakeNodeRef<Key>(Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(Fragment::JUST_1)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::AND_V, Vector(std::move(constructed.back()), MakeNodeRef<Key>(ctx, Fragment::JUST_1)));
break;
}
case ParseContext::AND_B: {
- BuildBack(Fragment::AND_B, constructed);
+ BuildBack(ctx, Fragment::AND_B, constructed);
break;
}
case ParseContext::AND_N: {
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(Fragment::JUST_0)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), MakeNodeRef<Key>(ctx, Fragment::JUST_0)));
break;
}
case ParseContext::AND_V: {
- BuildBack(Fragment::AND_V, constructed);
+ BuildBack(ctx, Fragment::AND_V, constructed);
break;
}
case ParseContext::OR_B: {
- BuildBack(Fragment::OR_B, constructed);
+ BuildBack(ctx, Fragment::OR_B, constructed);
break;
}
case ParseContext::OR_C: {
- BuildBack(Fragment::OR_C, constructed);
+ BuildBack(ctx, Fragment::OR_C, constructed);
break;
}
case ParseContext::OR_D: {
- BuildBack(Fragment::OR_D, constructed);
+ BuildBack(ctx, Fragment::OR_D, constructed);
break;
}
case ParseContext::OR_I: {
- BuildBack(Fragment::OR_I, constructed);
+ BuildBack(ctx, Fragment::OR_I, constructed);
break;
}
case ParseContext::ANDOR: {
@@ -1146,7 +1214,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
auto mid = std::move(constructed.back());
constructed.pop_back();
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(constructed.back()), std::move(mid), std::move(right)));
break;
}
case ParseContext::THRESH: {
@@ -1165,7 +1233,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
constructed.pop_back();
}
std::reverse(subs.begin(), subs.end());
- constructed.push_back(MakeNodeRef<Key>(Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
} else {
return {};
}
@@ -1200,10 +1268,10 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
* and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, OP_EQUAL
* respectively, plus OP_VERIFY.
*/
-bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out);
+std::optional<std::vector<Opcode>> DecomposeScript(const CScript& script);
/** Determine whether the passed pair (created by DecomposeScript) is pushing a number. */
-bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k);
+std::optional<int64_t> ParseScriptNumber(const Opcode& in);
enum class DecodeContext {
/** A single expression of type B, K, or V. Specifically, this can't be an
@@ -1300,58 +1368,59 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
// Constants
if (in[0].first == OP_1) {
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_1));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_1));
break;
}
if (in[0].first == OP_0) {
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::JUST_0));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::JUST_0));
break;
}
// Public keys
if (in[0].second.size() == 33) {
- Key key;
- if (!ctx.FromPKBytes(in[0].second.begin(), in[0].second.end(), key)) return {};
+ auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end());
+ if (!key) return {};
++in;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_K, Vector(std::move(*key))));
break;
}
if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) {
- Key key;
- if (!ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end(), key)) return {};
+ auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end());
+ if (!key) return {};
in += 5;
- constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::PK_H, Vector(std::move(*key))));
break;
}
// Time locks
- if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && ParseScriptNumber(in[1], k)) {
+ std::optional<int64_t> num;
+ if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
- if (k < 1 || k > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, k));
+ if (*num < 1 || *num > 0x7FFFFFFFL) return {};
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::OLDER, *num));
break;
}
- if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) {
+ if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && (num = ParseScriptNumber(in[1]))) {
in += 2;
- if (k < 1 || k > 0x7FFFFFFFL) return {};
- constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, k));
+ if (num < 1 || num > 0x7FFFFFFFL) return {};
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::AFTER, *num));
break;
}
// Hashes
- if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) {
+ if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && (num = ParseScriptNumber(in[5])) && num == 32 && in[6].first == OP_SIZE) {
if (in[2].first == OP_SHA256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::SHA256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::SHA256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_RIPEMD160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::RIPEMD160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::RIPEMD160, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH256 && in[1].second.size() == 32) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH256, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH256, in[1].second));
in += 7;
break;
} else if (in[2].first == OP_HASH160 && in[1].second.size() == 20) {
- constructed.push_back(MakeNodeRef<Key>(Fragment::HASH160, in[1].second));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::HASH160, in[1].second));
in += 7;
break;
}
@@ -1359,20 +1428,20 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
// Multi
if (last - in >= 3 && in[0].first == OP_CHECKMULTISIG) {
std::vector<Key> keys;
- if (!ParseScriptNumber(in[1], n)) return {};
- if (last - in < 3 + n) return {};
- if (n < 1 || n > 20) return {};
- for (int i = 0; i < n; ++i) {
- Key key;
+ const auto n = ParseScriptNumber(in[1]);
+ if (!n || last - in < 3 + *n) return {};
+ if (*n < 1 || *n > 20) return {};
+ for (int i = 0; i < *n; ++i) {
if (in[2 + i].second.size() != 33) return {};
- if (!ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end(), key)) return {};
- keys.push_back(std::move(key));
+ auto key = ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end());
+ if (!key) return {};
+ keys.push_back(std::move(*key));
}
- if (!ParseScriptNumber(in[2 + n], k)) return {};
- if (k < 1 || k > n) return {};
- in += 3 + n;
+ const auto k = ParseScriptNumber(in[2 + *n]);
+ if (!k || *k < 1 || *k > *n) return {};
+ in += 3 + *n;
std::reverse(keys.begin(), keys.end());
- constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::MULTI, std::move(keys), *k));
break;
}
/** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather
@@ -1400,10 +1469,10 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
break;
}
// Thresh
- if (last - in >= 3 && in[0].first == OP_EQUAL && ParseScriptNumber(in[1], k)) {
- if (k < 1) return {};
+ if (last - in >= 3 && in[0].first == OP_EQUAL && (num = ParseScriptNumber(in[1]))) {
+ if (*num < 1) return {};
in += 2;
- to_parse.emplace_back(DecodeContext::THRESH_W, 0, k);
+ to_parse.emplace_back(DecodeContext::THRESH_W, 0, *num);
break;
}
// OP_ENDIF can be WRAP_J, WRAP_D, ANDOR, OR_C, OR_D, or OR_I
@@ -1467,63 +1536,63 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
case DecodeContext::SWAP: {
if (in >= last || in[0].first != OP_SWAP || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_S, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_S, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ALT: {
if (in >= last || in[0].first != OP_TOALTSTACK || constructed.empty()) return {};
++in;
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_A, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_A, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::CHECK: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_C, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_C, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::DUP_IF: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_D, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_D, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::VERIFY: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_V, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_V, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::NON_ZERO: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_J, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_J, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::ZERO_NOTEQUAL: {
if (constructed.empty()) return {};
- constructed.back() = MakeNodeRef<Key>(Fragment::WRAP_N, Vector(std::move(constructed.back())));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::WRAP_N, Vector(std::move(constructed.back())));
break;
}
case DecodeContext::AND_V: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::AND_V, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::AND_V, constructed, /*reverse=*/true);
break;
}
case DecodeContext::AND_B: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::AND_B, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::AND_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_B: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_B, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_B, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_C: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_C, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_C, constructed, /*reverse=*/true);
break;
}
case DecodeContext::OR_D: {
if (constructed.size() < 2) return {};
- BuildBack(Fragment::OR_D, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_D, constructed, /*reverse=*/true);
break;
}
case DecodeContext::ANDOR: {
@@ -1533,7 +1602,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
NodeRef<Key> right = std::move(constructed.back());
constructed.pop_back();
NodeRef<Key> mid = std::move(constructed.back());
- constructed.back() = MakeNodeRef<Key>(Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
+ constructed.back() = MakeNodeRef<Key>(ctx, Fragment::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)));
break;
}
case DecodeContext::THRESH_W: {
@@ -1557,7 +1626,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
constructed.pop_back();
subs.push_back(std::move(sub));
}
- constructed.push_back(MakeNodeRef<Key>(Fragment::THRESH, std::move(subs), k));
+ constructed.push_back(MakeNodeRef<Key>(ctx, Fragment::THRESH, std::move(subs), k));
break;
}
case DecodeContext::ENDIF: {
@@ -1607,7 +1676,7 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
if (in >= last) return {};
if (in[0].first == OP_IF) {
++in;
- BuildBack(Fragment::OR_I, constructed, /*reverse=*/true);
+ BuildBack(ctx, Fragment::OR_I, constructed, /*reverse=*/true);
} else if (in[0].first == OP_NOTIF) {
++in;
to_parse.emplace_back(DecodeContext::ANDOR, -1, -1);
@@ -1638,12 +1707,12 @@ inline NodeRef<typename Ctx::Key> FromString(const std::string& str, const Ctx&
template<typename Ctx>
inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) {
using namespace internal;
- std::vector<std::pair<opcodetype, std::vector<unsigned char>>> decomposed;
- if (!DecomposeScript(script, decomposed)) return {};
- auto it = decomposed.begin();
- auto ret = DecodeScript<typename Ctx::Key>(it, decomposed.end(), ctx);
+ auto decomposed = DecomposeScript(script);
+ if (!decomposed) return {};
+ auto it = decomposed->begin();
+ auto ret = DecodeScript<typename Ctx::Key>(it, decomposed->end(), ctx);
if (!ret) return {};
- if (it != decomposed.end()) return {};
+ if (it != decomposed->end()) return {};
return ret;
}
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 7328e8d1ad..2d569d674a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -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; }
};
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/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 79d6b94dff..875522d744 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -95,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
@@ -129,7 +129,7 @@ struct FrozenCleanupCheck {
{
return true;
}
- FrozenCleanupCheck() {}
+ FrozenCleanupCheck() = default;
~FrozenCleanupCheck()
{
if (should_freeze) {
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 7b492fd1da..3b4a6f2637 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -44,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
@@ -134,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;
@@ -147,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;
@@ -212,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};
@@ -274,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;
@@ -390,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/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/miniscript.cpp b/src/test/fuzz/miniscript.cpp
new file mode 100644
index 0000000000..6be75322b4
--- /dev/null
+++ b/src/test/fuzz/miniscript.cpp
@@ -0,0 +1,167 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <core_io.h>
+#include <hash.h>
+#include <key.h>
+#include <script/miniscript.h>
+#include <script/script.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <util/strencodings.h>
+
+namespace {
+
+//! Some pre-computed data for more efficient string roundtrips.
+struct TestData {
+ typedef CPubKey Key;
+
+ // Precomputed public keys.
+ std::vector<Key> dummy_keys;
+ std::map<Key, int> dummy_key_idx_map;
+ std::map<CKeyID, Key> dummy_keys_map;
+
+ //! Set the precomputed data.
+ void Init() {
+ unsigned char keydata[32] = {1};
+ for (size_t i = 0; i < 256; i++) {
+ keydata[31] = i;
+ CKey privkey;
+ privkey.Set(keydata, keydata + 32, true);
+ const Key pubkey = privkey.GetPubKey();
+
+ dummy_keys.push_back(pubkey);
+ dummy_key_idx_map.emplace(pubkey, i);
+ dummy_keys_map.insert({pubkey.GetID(), pubkey});
+ }
+ }
+} TEST_DATA;
+
+/**
+ * Context to parse a Miniscript node to and from Script or text representation.
+ * Uses an integer (an index in the dummy keys array from the test data) as keys in order
+ * to focus on fuzzing the Miniscript nodes' test representation, not the key representation.
+ */
+struct ParserContext {
+ typedef CPubKey Key;
+
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a < b;
+ }
+
+ std::optional<std::string> ToString(const Key& key) const
+ {
+ auto it = TEST_DATA.dummy_key_idx_map.find(key);
+ if (it == TEST_DATA.dummy_key_idx_map.end()) return {};
+ uint8_t idx = it->second;
+ return HexStr(Span{&idx, 1});
+ }
+
+ template<typename I>
+ std::optional<Key> FromString(I first, I last) const {
+ if (last - first != 2) return {};
+ auto idx = ParseHex(std::string(first, last));
+ if (idx.size() != 1) return {};
+ return TEST_DATA.dummy_keys[idx[0]];
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKBytes(I first, I last) const {
+ Key key;
+ key.Set(first, last);
+ if (!key.IsValid()) return {};
+ return key;
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKHBytes(I first, I last) const {
+ assert(last - first == 20);
+ CKeyID keyid;
+ std::copy(first, last, keyid.begin());
+ const auto it = TEST_DATA.dummy_keys_map.find(keyid);
+ if (it == TEST_DATA.dummy_keys_map.end()) return {};
+ return it->second;
+ }
+} PARSER_CTX;
+
+//! Context that implements naive conversion from/to script only, for roundtrip testing.
+struct ScriptParserContext {
+ //! For Script roundtrip we never need the key from a key hash.
+ struct Key {
+ bool is_hash;
+ std::vector<unsigned char> data;
+ };
+
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a.data < b.data;
+ }
+
+ const std::vector<unsigned char>& ToPKBytes(const Key& key) const
+ {
+ assert(!key.is_hash);
+ return key.data;
+ }
+
+ const std::vector<unsigned char> ToPKHBytes(const Key& key) const
+ {
+ if (key.is_hash) return key.data;
+ const auto h = Hash160(key.data);
+ return {h.begin(), h.end()};
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKBytes(I first, I last) const
+ {
+ Key key;
+ key.data.assign(first, last);
+ key.is_hash = false;
+ return key;
+ }
+
+ template<typename I>
+ std::optional<Key> FromPKHBytes(I first, I last) const
+ {
+ Key key;
+ key.data.assign(first, last);
+ key.is_hash = true;
+ return key;
+ }
+} SCRIPT_PARSER_CONTEXT;
+
+} // namespace
+
+void FuzzInit()
+{
+ ECC_Start();
+ TEST_DATA.Init();
+}
+
+/* Fuzz tests that test parsing from a string, and roundtripping via string. */
+FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+ auto str = provider.ConsumeRemainingBytesAsString();
+ auto parsed = miniscript::FromString(str, PARSER_CTX);
+ if (!parsed) return;
+
+ const auto str2 = parsed->ToString(PARSER_CTX);
+ assert(str2);
+ auto parsed2 = miniscript::FromString(*str2, PARSER_CTX);
+ assert(parsed2);
+ assert(*parsed == *parsed2);
+}
+
+/* Fuzz tests that test parsing from a script, and roundtripping via script. */
+FUZZ_TARGET(miniscript_script)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
+ if (!script) return;
+
+ const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT);
+ if (!ms) return;
+
+ assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script);
+}
diff --git a/src/test/fuzz/miniscript_decode.cpp b/src/test/fuzz/miniscript_decode.cpp
deleted file mode 100644
index 4cc0a1be8f..0000000000
--- a/src/test/fuzz/miniscript_decode.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2022 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <core_io.h>
-#include <hash.h>
-#include <key.h>
-#include <script/miniscript.h>
-#include <script/script.h>
-#include <span.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <util/strencodings.h>
-
-#include <optional>
-
-using miniscript::operator""_mst;
-
-
-struct Converter {
- typedef CPubKey Key;
-
- bool ToString(const Key& key, std::string& ret) const {
- ret = HexStr(key);
- return true;
- }
- const std::vector<unsigned char> ToPKBytes(const Key& key) const {
- return {key.begin(), key.end()};
- }
- const std::vector<unsigned char> ToPKHBytes(const Key& key) const {
- const auto h = Hash160(key);
- return {h.begin(), h.end()};
- }
-
- template<typename I>
- bool FromString(I first, I last, Key& key) const {
- const auto bytes = ParseHex(std::string(first, last));
- key.Set(bytes.begin(), bytes.end());
- return key.IsValid();
- }
- template<typename I>
- bool FromPKBytes(I first, I last, CPubKey& key) const {
- key.Set(first, last);
- return key.IsValid();
- }
- template<typename I>
- bool FromPKHBytes(I first, I last, CPubKey& key) const {
- assert(last - first == 20);
- return false;
- }
-};
-
-const Converter CONVERTER;
-
-FUZZ_TARGET(miniscript_decode)
-{
- FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
- if (!script) return;
-
- const auto ms = miniscript::FromScript(*script, CONVERTER);
- if (!ms) return;
-
- // We can roundtrip it to its string representation.
- std::string ms_str;
- assert(ms->ToString(CONVERTER, ms_str));
- assert(*miniscript::FromString(ms_str, CONVERTER) == *ms);
- // The Script representation must roundtrip since we parsed it this way the first time.
- const CScript ms_script = ms->ToScript(CONVERTER);
- assert(ms_script == *script);
-}
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/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/tx_out.cpp b/src/test/fuzz/tx_out.cpp
index 39a50b6c80..a2421ff582 100644
--- a/src/test/fuzz/tx_out.cpp
+++ b/src/test/fuzz/tx_out.cpp
@@ -4,6 +4,7 @@
#include <consensus/validation.h>
#include <core_memusage.h>
+#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <streams.h>
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/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/miner_tests.cpp b/src/test/miner_tests.cpp
index cafa5710fa..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>
@@ -51,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 {
@@ -587,7 +588,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
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));
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 930582ea24..3877fea907 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -71,6 +71,10 @@ std::unique_ptr<const TestData> g_testdata;
struct KeyConverter {
typedef CPubKey Key;
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return a < b;
+ }
+
//! Convert a public key to bytes.
std::vector<unsigned char> ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; }
@@ -84,27 +88,28 @@ struct KeyConverter {
//! Parse a public key from a range of hex characters.
template<typename I>
- bool FromString(I first, I last, CPubKey& key) const {
+ std::optional<Key> FromString(I first, I last) const {
auto bytes = ParseHex(std::string(first, last));
- key.Set(bytes.begin(), bytes.end());
- return key.IsValid();
+ Key key{bytes.begin(), bytes.end()};
+ if (key.IsValid()) return key;
+ return {};
}
template<typename I>
- bool FromPKBytes(I first, I last, CPubKey& key) const {
- key.Set(first, last);
- return key.IsValid();
+ std::optional<Key> FromPKBytes(I first, I last) const {
+ Key key{first, last};
+ if (key.IsValid()) return key;
+ return {};
}
template<typename I>
- bool FromPKHBytes(I first, I last, CPubKey& key) const {
+ std::optional<Key> FromPKHBytes(I first, I last) const {
assert(last - first == 20);
CKeyID keyid;
std::copy(first, last, keyid.begin());
auto it = g_testdata->pkmap.find(keyid);
assert(it != g_testdata->pkmap.end());
- key = it->second;
- return true;
+ return it->second;
}
};
@@ -272,6 +277,19 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
// its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341).
const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER);
BOOST_CHECK(!ms_minimalif);
+ // A Miniscript with duplicate keys is not sane
+ const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ BOOST_CHECK(ms_dup1);
+ BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey());
+ // Same with a disjunction, and different key nodes (pk and pkh)
+ const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey());
+ // Same when the duplicates are leaves or a larger tree
+ const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey());
+ // Same when the duplicates are on different levels in the tree
+ const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey());
// Timelock tests
Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
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 7cec287e8f..3e9e04da25 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -66,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);
@@ -90,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();
@@ -264,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
@@ -279,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);
@@ -337,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 6e089de0c1..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}));
@@ -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 19e32d0c36..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) {
@@ -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());
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 c3a2a66027..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)
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index d41b54af20..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,
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 3b2aca5887..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);
@@ -265,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)
@@ -1488,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>());
}
@@ -2449,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 05dbc6057f..82b8529d76 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -94,7 +94,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
self->reply_handlers.front()(*self, self->message);
self->reply_handlers.pop_front();
} else {
- LogPrint(BCLog::TOR, "tor: Received unexpected sync reply %i\n", self->message.code);
+ LogPrint(BCLog::TOR, "Received unexpected sync reply %i\n", self->message.code);
}
}
self->message.Clear();
@@ -113,13 +113,13 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
{
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
if (what & BEV_EVENT_CONNECTED) {
- LogPrint(BCLog::TOR, "tor: Successfully connected!\n");
+ LogPrint(BCLog::TOR, "Successfully connected!\n");
self->connected(*self);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
if (what & BEV_EVENT_ERROR) {
- LogPrint(BCLog::TOR, "tor: Error connecting to Tor control socket\n");
+ LogPrint(BCLog::TOR, "Error connecting to Tor control socket\n");
} else {
- LogPrint(BCLog::TOR, "tor: End of stream\n");
+ LogPrint(BCLog::TOR, "End of stream\n");
}
self->Disconnect();
self->disconnected(*self);
@@ -304,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);
@@ -319,7 +318,7 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
- LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
+ LogPrint(BCLog::TOR, "Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
private_key = pkf.second;
}
}
@@ -360,7 +359,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
}
if (!socks_location.empty()) {
- LogPrint(BCLog::TOR, "tor: Get SOCKS port command yielded %s\n", socks_location);
+ LogPrint(BCLog::TOR, "Get SOCKS port command yielded %s\n", socks_location);
} else {
LogPrintf("tor: Get SOCKS port command returned nothing\n");
}
@@ -381,7 +380,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
Assume(resolved.IsValid());
- LogPrint(BCLog::TOR, "tor: Configuring onion proxy for %s\n", resolved.ToStringIPPort());
+ LogPrint(BCLog::TOR, "Configuring onion proxy for %s\n", resolved.ToStringIPPort());
Proxy addrOnion = Proxy(resolved, true);
SetProxy(NET_ONION, addrOnion);
@@ -405,7 +404,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n");
+ LogPrint(BCLog::TOR, "ADD_ONION successful\n");
for (const std::string &s : reply.lines) {
std::map<std::string,std::string> m = ParseTorReplyMapping(s);
std::map<std::string,std::string>::iterator i;
@@ -424,7 +423,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
- LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
+ LogPrint(BCLog::TOR, "Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
} else {
LogPrintf("tor: Error writing service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
}
@@ -440,7 +439,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: Authentication successful\n");
+ LogPrint(BCLog::TOR, "Authentication successful\n");
// Now that we know Tor is running setup the proxy for onion addresses
// if -onion isn't set to something else.
@@ -491,7 +490,7 @@ static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::v
void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply)
{
if (reply.code == 250) {
- LogPrint(BCLog::TOR, "tor: SAFECOOKIE authentication challenge successful\n");
+ LogPrint(BCLog::TOR, "SAFECOOKIE authentication challenge successful\n");
std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]);
if (l.first == "AUTHCHALLENGE") {
std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
@@ -501,7 +500,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr
}
std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
- LogPrint(BCLog::TOR, "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce));
+ LogPrint(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce));
if (serverNonce.size() != 32) {
LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n");
return;
@@ -548,12 +547,12 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
std::map<std::string,std::string>::iterator i;
if ((i = m.find("Tor")) != m.end()) {
- LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n", i->second);
+ LogPrint(BCLog::TOR, "Connected to Tor version %s\n", i->second);
}
}
}
for (const std::string &s : methods) {
- LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n", s);
+ LogPrint(BCLog::TOR, "Supported authentication method: %s\n", s);
}
// Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD
/* Authentication:
@@ -563,18 +562,18 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
std::string torpassword = gArgs.GetArg("-torpassword", "");
if (!torpassword.empty()) {
if (methods.count("HASHEDPASSWORD")) {
- LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n");
+ LogPrint(BCLog::TOR, "Using HASHEDPASSWORD authentication\n");
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");
}
} else if (methods.count("NULL")) {
- LogPrint(BCLog::TOR, "tor: Using NULL authentication\n");
+ LogPrint(BCLog::TOR, "Using NULL authentication\n");
_conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
- LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
+ LogPrint(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
@@ -616,7 +615,7 @@ void TorController::disconnected_cb(TorControlConnection& _conn)
if (!reconnect)
return;
- LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center);
+ LogPrint(BCLog::TOR, "Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center);
// Single-shot timer for reconnect. Use exponential backoff.
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
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/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/strencodings.cpp b/src/util/strencodings.cpp
index bcedd4f517..675fe7d2d7 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -77,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)) {
@@ -90,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)
{
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/system.cpp b/src/util/system.cpp
index 44ebf5cb3e..f88b0fac77 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -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
diff --git a/src/util/system.h b/src/util/system.h
index a7f4d16911..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>
@@ -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/validation.cpp b/src/validation.cpp
index b5d6a66088..23ad221ffe 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>
@@ -60,17 +58,18 @@
#include <optional>
#include <string>
+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;
@@ -135,8 +134,6 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;
-CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-
const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -262,7 +259,7 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
}
// 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)
@@ -815,7 +812,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");
}
@@ -1027,7 +1023,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,
@@ -1044,7 +1039,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());
@@ -1457,7 +1452,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) {
@@ -1515,7 +1510,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) {}
@@ -1909,10 +1904,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(); }
@@ -1923,15 +1919,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
@@ -1947,22 +1945,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;
}
@@ -2005,7 +2003,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
@@ -2153,12 +2151,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);
@@ -2454,9 +2452,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);
}
}
@@ -2556,7 +2554,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);
@@ -2653,7 +2651,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
@@ -3280,7 +3278,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);
@@ -3398,11 +3396,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;
@@ -3410,7 +3408,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);
@@ -3433,7 +3431,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;
}
@@ -3446,14 +3444,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");
@@ -3462,7 +3460,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");
@@ -3478,9 +3476,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));
}
@@ -3494,13 +3492,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;
}
@@ -3517,7 +3515,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() ||
@@ -3535,7 +3533,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;
@@ -3576,13 +3574,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);
@@ -3595,7 +3593,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;
}
@@ -3612,7 +3610,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;
}
@@ -3665,14 +3663,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) {
@@ -3686,7 +3684,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);
}
@@ -3705,7 +3703,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)
@@ -3745,7 +3743,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);
@@ -3778,7 +3776,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);
@@ -3796,7 +3794,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);
@@ -3836,6 +3834,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -3849,11 +3848,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;
@@ -4133,7 +4132,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;
@@ -4157,7 +4156,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()};
@@ -4986,14 +4985,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 */
@@ -5094,22 +5094,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;
}
@@ -5147,7 +5147,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;
}
@@ -5216,9 +5216,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 42e41502f9..cc94add3cb 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,9 +54,10 @@ 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;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
@@ -120,8 +124,6 @@ extern bool g_parallel_script_checks;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
-/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
-extern CFeeRate minRelayTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
extern int64_t nMaxTipAge;
@@ -356,15 +358,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:
@@ -495,6 +492,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
@@ -834,6 +832,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,
@@ -847,11 +849,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.
@@ -932,6 +942,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;
@@ -960,7 +975,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.
@@ -970,10 +985,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.
@@ -991,9 +1005,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 edc4633c01..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& scheduler LIFETIMEBOUND) : m_schedulerClient(scheduler) {}
+ 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 = std::make_unique<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.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 1aa0339445..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();
}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 1c99e1f9af..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;
};
@@ -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/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..f25ad59528 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();
@@ -302,11 +302,11 @@ RPCHelpMan addmultisigaddress()
result.pushKV("descriptor", descriptor->ToString());
UniValue warnings(UniValue::VARR);
- if (!request.params[3].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ if (descriptor->GetOutputType() != 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);
+ if (!warnings.empty()) result.pushKV("warnings", warnings);
return result;
},
@@ -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..aa9f23c886 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");
@@ -1621,7 +1621,7 @@ RPCHelpMan importdescriptors()
},
RPCExamples{
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
- "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
+ "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
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/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/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 356d0ce5a0..0c83c3d954 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1707,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());
@@ -1735,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);
}
@@ -1803,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;
}
@@ -1838,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
@@ -1866,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;
}
@@ -2102,7 +2106,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
- AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
+ CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
CHECK_NONFATAL(wtx.mapValue.empty());
CHECK_NONFATAL(wtx.vOrderForm.empty());
wtx.mapValue = std::move(mapValue);
@@ -2112,6 +2116,11 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
return true;
});
+ // wtx can only be null if the db write failed.
+ if (!wtx) {
+ throw std::runtime_error(std::string(__func__) + ": Wallet db error, transaction commit failed");
+ }
+
// Notify that old coins are spent
for (const CTxIn& txin : tx->vin) {
CWalletTx &coin = mapWallet.at(txin.prevout.hash);
@@ -2119,17 +2128,13 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
NotifyTransactionChanged(coin.GetHash(), CT_UPDATED);
}
- // Get the inserted-CWalletTx from mapWallet so that the
- // wtx cached mempool state is updated correctly
- CWalletTx& wtx = mapWallet.at(tx->GetHash());
-
if (!fBroadcastTransactions) {
// Don't submit tx to the mempool
return;
}
std::string err_string;
- if (!SubmitTxMemoryPoolAndRelay(wtx, err_string, true)) {
+ if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
@@ -2961,6 +2966,7 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
// 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
@@ -2987,7 +2993,6 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
if (tip_height && *tip_height != rescan_height)
{
- walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
if (chain.havePruned()) {
int block_height = *tip_height;
while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
@@ -3031,6 +3036,7 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->GetDatabase().IncrementUpdateCounter();
}
+ walletInstance->m_attaching_chain = false;
return true;
}
@@ -3124,8 +3130,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);
@@ -3133,6 +3142,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 4e81a2b957..7da601c3b7 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -415,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:
@@ -429,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); }
@@ -584,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
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index a53de34db4..26618735f6 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -70,9 +70,9 @@ bool CZMQNotificationInterface::Initialize()
{
int major = 0, minor = 0, patch = 0;
zmq_version(&major, &minor, &patch);
- LogPrint(BCLog::ZMQ, "zmq: version %d.%d.%d\n", major, minor, patch);
+ LogPrint(BCLog::ZMQ, "version %d.%d.%d\n", major, minor, patch);
- LogPrint(BCLog::ZMQ, "zmq: Initialize notification interface\n");
+ LogPrint(BCLog::ZMQ, "Initialize notification interface\n");
assert(!pcontext);
pcontext = zmq_ctx_new();
@@ -85,9 +85,9 @@ bool CZMQNotificationInterface::Initialize()
for (auto& notifier : notifiers) {
if (notifier->Initialize(pcontext)) {
- LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
} else {
- LogPrint(BCLog::ZMQ, "zmq: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
return false;
}
}
@@ -98,11 +98,11 @@ bool CZMQNotificationInterface::Initialize()
// Called during shutdown sequence
void CZMQNotificationInterface::Shutdown()
{
- LogPrint(BCLog::ZMQ, "zmq: Shutdown notification interface\n");
+ LogPrint(BCLog::ZMQ, "Shutdown notification interface\n");
if (pcontext)
{
for (auto& notifier : notifiers) {
- LogPrint(BCLog::ZMQ, "zmq: Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
notifier->Shutdown();
}
zmq_ctx_term(pcontext);
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 2c6f24a239..011336bf72 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -106,7 +106,7 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
- LogPrint(BCLog::ZMQ, "zmq: Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
+ LogPrint(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
int rc = zmq_setsockopt(psocket, ZMQ_SNDHWM, &outbound_message_high_water_mark, sizeof(outbound_message_high_water_mark));
if (rc != 0)
@@ -147,8 +147,8 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
}
else
{
- LogPrint(BCLog::ZMQ, "zmq: Reusing socket for address %s\n", address);
- LogPrint(BCLog::ZMQ, "zmq: Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
+ LogPrint(BCLog::ZMQ, "Reusing socket for address %s\n", address);
+ LogPrint(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
psocket = i->second->psocket;
mapPublishNotifiers.insert(std::make_pair(address, this));
@@ -179,7 +179,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
if (count == 1)
{
- LogPrint(BCLog::ZMQ, "zmq: Close socket at address %s\n", address);
+ LogPrint(BCLog::ZMQ, "Close socket at address %s\n", address);
int linger = 0;
zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger));
zmq_close(psocket);
@@ -208,7 +208,7 @@ bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashblock %s to %s\n", hash.GetHex(), this->address);
uint8_t data[32];
for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
@@ -219,7 +219,7 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx %s to %s\n", hash.GetHex(), this->address);
uint8_t data[32];
for (unsigned int i = 0; i < 32; i++) {
data[31 - i] = hash.begin()[i];
@@ -229,7 +229,7 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
- LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
@@ -251,7 +251,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish rawtx %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ss << transaction;
return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
@@ -273,27 +273,27 @@ static bool SendSequenceMsg(CZMQAbstractPublishNotifier& notifier, uint256 hash,
bool CZMQPublishSequenceNotifier::NotifyBlockConnect(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish sequence block connect %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish sequence block connect %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Block (C)onnect */ 'C');
}
bool CZMQPublishSequenceNotifier::NotifyBlockDisconnect(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish sequence block disconnect %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish sequence block disconnect %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Block (D)isconnect */ 'D');
}
bool CZMQPublishSequenceNotifier::NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool acceptance %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx mempool acceptance %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Mempool (A)cceptance */ 'A', mempool_sequence);
}
bool CZMQPublishSequenceNotifier::NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool removal %s to %s\n", hash.GetHex(), this->address);
+ LogPrint(BCLog::ZMQ, "Publish hashtx mempool removal %s to %s\n", hash.GetHex(), this->address);
return SendSequenceMsg(*this, hash, /* Mempool (R)emoval */ 'R', mempool_sequence);
}
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
index f0568634d4..cf3a0b2d71 100644
--- a/src/zmq/zmqutil.cpp
+++ b/src/zmq/zmqutil.cpp
@@ -12,5 +12,5 @@
void zmqError(const std::string& str)
{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, msg: %s\n", str, zmq_strerror(errno));
+ LogPrint(BCLog::ZMQ, "Error: %s, msg: %s\n", str, zmq_strerror(errno));
}