aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am19
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qt.include4
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/Makefile.test_fuzz.include2
-rw-r--r--src/Makefile.test_util.include2
-rw-r--r--src/addrman.cpp37
-rw-r--r--src/addrman.h34
-rw-r--r--src/addrman_impl.h6
-rw-r--r--src/bitcoin-tx.cpp6
-rw-r--r--src/blockfilter.cpp31
-rw-r--r--src/core_write.cpp23
-rw-r--r--src/index/coinstatsindex.cpp2
-rw-r--r--src/init.cpp64
-rw-r--r--src/net.cpp38
-rw-r--r--src/net.h13
-rw-r--r--src/net_processing.cpp84
-rw-r--r--src/node/blockstorage.cpp370
-rw-r--r--src/node/blockstorage.h84
-rw-r--r--src/node/chainstate.cpp2
-rw-r--r--src/node/chainstate.h2
-rw-r--r--src/node/coinstats.cpp11
-rw-r--r--src/node/coinstats.h10
-rw-r--r--src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java6
-rw-r--r--src/qt/forms/receivecoinsdialog.ui2
-rw-r--r--src/qt/receivecoinsdialog.cpp2
-rw-r--r--src/rpc/blockchain.cpp66
-rw-r--r--src/rpc/blockchain.h1
-rw-r--r--src/rpc/rawtransaction.cpp9
-rw-r--r--src/rpc/server_util.cpp13
-rw-r--r--src/rpc/server_util.h7
-rw-r--r--src/test/addrman_tests.cpp476
-rw-r--r--src/test/fuzz/addition_overflow.cpp1
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp1
-rw-r--r--src/test/fuzz/golomb_rice.cpp14
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/pow.cpp1
-rw-r--r--src/test/fuzz/util.cpp17
-rw-r--r--src/test/fuzz/util.h13
-rw-r--r--src/test/util/net.h18
-rw-r--r--src/test/util/setup_common.cpp32
-rw-r--r--src/test/util_tests.cpp33
-rw-r--r--src/txmempool.cpp15
-rw-r--r--src/txmempool.h10
-rw-r--r--src/util/golombrice.h31
-rw-r--r--src/util/overflow.h31
-rw-r--r--src/util/sock.cpp27
-rw-r--r--src/util/sock.h9
-rw-r--r--src/validation.cpp374
-rw-r--r--src/validation.h82
-rw-r--r--src/wallet/dump.cpp5
52 files changed, 1161 insertions, 988 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 4199f7e89d..b946a325c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@ EXTRA_LIBRARIES =
BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
-LIBBITCOIN_SERVER=libbitcoin_server.a
+LIBBITCOIN_NODE=libbitcoin_node.a
LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a
LIBBITCOIN_CLI=libbitcoin_cli.a
@@ -61,7 +61,7 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_IPC) \
$(LIBBITCOIN_WALLET) \
@@ -248,6 +248,7 @@ BITCOIN_CORE_H = \
util/macros.h \
util/message.h \
util/moneystr.h \
+ util/overflow.h \
util/overloaded.h \
util/rbf.h \
util/readwritefile.h \
@@ -316,9 +317,9 @@ ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
# Contains code accessing mempool and chain state that is meant to be separated
# from wallet and gui code (see node/README.md). Shared code should go in
# libbitcoin_common or libbitcoin_util libraries, instead.
-libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
-libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-libbitcoin_server_a_SOURCES = \
+libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+libbitcoin_node_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
banman.cpp \
@@ -381,10 +382,10 @@ libbitcoin_server_a_SOURCES = \
$(BITCOIN_CORE_H)
if ENABLE_WALLET
-libbitcoin_server_a_SOURCES += wallet/init.cpp
+libbitcoin_node_a_SOURCES += wallet/init.cpp
endif
if !ENABLE_WALLET
-libbitcoin_server_a_SOURCES += dummywallet.cpp
+libbitcoin_node_a_SOURCES += dummywallet.cpp
endif
if ENABLE_ZMQ
@@ -669,13 +670,13 @@ bitcoind_SOURCES = $(bitcoin_daemon_sources) init/bitcoind.cpp
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoind_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoind_LDFLAGS = $(bitcoin_bin_ldflags)
-bitcoind_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
+bitcoind_LDADD = $(LIBBITCOIN_NODE) $(bitcoin_bin_ldadd)
bitcoin_node_SOURCES = $(bitcoin_daemon_sources) init/bitcoin-node.cpp
bitcoin_node_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_node_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_node_LDFLAGS = $(bitcoin_bin_ldflags)
-bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
+bitcoin_node_LDADD = $(LIBBITCOIN_NODE) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 2a8e4a0aac..2feb31a9e9 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -51,7 +51,7 @@ nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 6c0e6f7c6f..f4d4641feb 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -323,7 +323,7 @@ bitcoin_qt_sources = qt/main.cpp
if TARGET_WINDOWS
bitcoin_qt_sources += $(BITCOIN_QT_RC)
endif
-bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
+bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_NODE)
if ENABLE_WALLET
bitcoin_qt_ldadd += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
@@ -357,7 +357,7 @@ SECONDARY: $(QT_QM)
$(srcdir)/qt/bitcoinstrings.cpp: FORCE
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
- $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
+ $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index a0900f2691..b92d2cb6e2 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -44,7 +44,7 @@ endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
-qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBTEST_UTIL)
+qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_NODE) $(LIBTEST_UTIL)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 9fe2a3cf8a..801745d0c6 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -38,7 +38,7 @@ BITCOIN_TEST_SUITE = \
FUZZ_SUITE_LD_COMMON = \
$(LIBTEST_UTIL) \
$(LIBTEST_FUZZ) \
- $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_NODE) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
@@ -197,7 +197,7 @@ if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
-test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
+test_test_bitcoin_LDADD += $(LIBBITCOIN_NODE) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) $(MINISKETCH_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include
index 2d772f2fca..9574454fd2 100644
--- a/src/Makefile.test_fuzz.include
+++ b/src/Makefile.test_fuzz.include
@@ -19,7 +19,7 @@ libtest_fuzz_a_SOURCES = \
test/fuzz/util.cpp \
$(TEST_FUZZ_H)
-LIBTEST_FUZZ += $(LIBBITCOIN_SERVER)
+LIBTEST_FUZZ += $(LIBBITCOIN_NODE)
LIBTEST_FUZZ += $(LIBBITCOIN_COMMON)
LIBTEST_FUZZ += $(LIBBITCOIN_UTIL)
LIBTEST_FUZZ += $(LIBBITCOIN_CRYPTO_BASE)
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 0a3b99e7d2..92cb8a5ce6 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -35,7 +35,7 @@ libtest_util_a_SOURCES = \
test/util/wallet.cpp \
$(TEST_UTIL_H)
-LIBTEST_UTIL += $(LIBBITCOIN_SERVER)
+LIBTEST_UTIL += $(LIBBITCOIN_NODE)
LIBTEST_UTIL += $(LIBBITCOIN_COMMON)
LIBTEST_UTIL += $(LIBBITCOIN_UTIL)
LIBTEST_UTIL += $(LIBBITCOIN_CRYPTO_BASE)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 15c6f2943c..3a845b5b6e 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -930,6 +930,29 @@ std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision_()
return {info_old, info_old.nLastTry};
}
+std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& addr)
+{
+ AssertLockHeld(cs);
+
+ AddrInfo* addr_info = Find(addr);
+
+ if (!addr_info) return std::nullopt;
+
+ if(addr_info->fInTried) {
+ int bucket{addr_info->GetTriedBucket(nKey, m_asmap)};
+ return AddressPosition(/*tried=*/true,
+ /*multiplicity=*/1,
+ /*bucket=*/bucket,
+ /*position=*/addr_info->GetBucketPosition(nKey, false, bucket));
+ } else {
+ int bucket{addr_info->GetNewBucket(nKey, m_asmap)};
+ return AddressPosition(/*tried=*/false,
+ /*multiplicity=*/addr_info->nRefCount,
+ /*bucket=*/bucket,
+ /*position=*/addr_info->GetBucketPosition(nKey, true, bucket));
+ }
+}
+
void AddrManImpl::Check() const
{
AssertLockHeld(cs);
@@ -1116,6 +1139,15 @@ void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
Check();
}
+std::optional<AddressPosition> AddrManImpl::FindAddressEntry(const CAddress& addr)
+{
+ LOCK(cs);
+ Check();
+ auto entry = FindAddressEntry_(addr);
+ Check();
+ return entry;
+}
+
const std::vector<bool>& AddrManImpl::GetAsmap() const
{
return m_asmap;
@@ -1201,3 +1233,8 @@ const std::vector<bool>& AddrMan::GetAsmap() const
{
return m_impl->GetAsmap();
}
+
+std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
+{
+ return m_impl->FindAddressEntry(addr);
+}
diff --git a/src/addrman.h b/src/addrman.h
index 7f0936be8c..0646ef368d 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -22,6 +22,31 @@ class AddrManImpl;
/** Default for -checkaddrman */
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
+/** Test-only struct, capturing info about an address in AddrMan */
+struct AddressPosition {
+ // Whether the address is in the new or tried table
+ const bool tried;
+
+ // Addresses in the tried table should always have a multiplicity of 1.
+ // Addresses in the new table can have multiplicity between 1 and
+ // ADDRMAN_NEW_BUCKETS_PER_ADDRESS
+ const int multiplicity;
+
+ // If the address is in the new table, the bucket and position are
+ // populated based on the first source who sent the address.
+ // In certain edge cases, this may not be where the address is currently
+ // located.
+ const int bucket;
+ const int position;
+
+ bool operator==(AddressPosition other) {
+ return std::tie(tried, multiplicity, bucket, position) ==
+ std::tie(other.tried, other.multiplicity, other.bucket, other.position);
+ }
+ explicit AddressPosition(bool tried_in, int multiplicity_in, int bucket_in, int position_in)
+ : tried{tried_in}, multiplicity{multiplicity_in}, bucket{bucket_in}, position{position_in} {}
+};
+
/** Stochastic address manager
*
* Design goals:
@@ -142,6 +167,15 @@ public:
void SetServices(const CService& addr, ServiceFlags nServices);
const std::vector<bool>& GetAsmap() const;
+
+ /** Test-only function
+ * Find the address record in AddrMan and return information about its
+ * position.
+ * @param[in] addr The address record to look up.
+ * @return Information about the address record in AddrMan
+ * or nullopt if address is not found.
+ */
+ std::optional<AddressPosition> FindAddressEntry(const CAddress& addr);
};
#endif // BITCOIN_ADDRMAN_H
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
index bd7caf473b..5e76f72342 100644
--- a/src/addrman_impl.h
+++ b/src/addrman_impl.h
@@ -137,9 +137,11 @@ public:
void SetServices(const CService& addr, ServiceFlags nServices)
EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ std::optional<AddressPosition> FindAddressEntry(const CAddress& addr)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
const std::vector<bool>& GetAsmap() const;
- friend class AddrManTest;
friend class AddrManDeterministic;
private:
@@ -266,6 +268,8 @@ private:
std::pair<CAddress, int64_t> SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ std::optional<AddressPosition> FindAddressEntry_(const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
//! Consistency check, taking into account m_consistency_check_ratio.
//! Will std::abort if an inconsistency is detected.
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 8602e362c8..edec883264 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -630,7 +630,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
}
Coin newcoin;
newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = 0;
+ newcoin.out.nValue = MAX_MONEY;
if (prevOut.exists("amount")) {
newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
}
@@ -669,6 +669,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata);
+ if (amount == MAX_MONEY && !sigdata.scriptWitness.IsNull()) {
+ throw std::runtime_error(strprintf("Missing amount for CTxOut with scriptPubKey=%s", HexStr(prevPubKey)));
+ }
+
UpdateInput(txin, sigdata);
}
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index 68cc6462d0..566254d839 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -24,37 +24,6 @@ static const std::map<BlockFilterType, std::string> g_filter_types = {
{BlockFilterType::BASIC, "basic"},
};
-// Map a value x that is uniformly distributed in the range [0, 2^64) to a
-// value uniformly distributed in [0, n) by returning the upper 64 bits of
-// x * n.
-//
-// See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-static uint64_t MapIntoRange(uint64_t x, uint64_t n)
-{
-#ifdef __SIZEOF_INT128__
- return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
-#else
- // To perform the calculation on 64-bit numbers without losing the
- // result to overflow, split the numbers into the most significant and
- // least significant 32 bits and perform multiplication piece-wise.
- //
- // See: https://stackoverflow.com/a/26855440
- uint64_t x_hi = x >> 32;
- uint64_t x_lo = x & 0xFFFFFFFF;
- uint64_t n_hi = n >> 32;
- uint64_t n_lo = n & 0xFFFFFFFF;
-
- uint64_t ac = x_hi * n_hi;
- uint64_t ad = x_hi * n_lo;
- uint64_t bc = x_lo * n_hi;
- uint64_t bd = x_lo * n_lo;
-
- uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
- uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
- return upper64;
-#endif
-}
-
uint64_t GCSFilter::HashToRange(const Element& element) const
{
uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 358f736a13..067f1e4f4e 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -208,22 +208,17 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
const CTxOut& prev_txout = prev_coin.out;
amt_total_in += prev_txout.nValue;
- switch (verbosity) {
- case TxVerbosity::SHOW_TXID:
- case TxVerbosity::SHOW_DETAILS:
- break;
- case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
- UniValue o_script_pub_key(UniValue::VOBJ);
- ScriptPubKeyToUniv(prev_txout.scriptPubKey, o_script_pub_key, /* includeHex */ true);
+ if (verbosity == TxVerbosity::SHOW_DETAILS_AND_PREVOUT) {
+ UniValue o_script_pub_key(UniValue::VOBJ);
+ ScriptPubKeyToUniv(prev_txout.scriptPubKey, o_script_pub_key, /*include_hex=*/ true);
- UniValue p(UniValue::VOBJ);
- p.pushKV("generated", bool(prev_coin.fCoinBase));
- p.pushKV("height", uint64_t(prev_coin.nHeight));
- p.pushKV("value", ValueFromAmount(prev_txout.nValue));
- p.pushKV("scriptPubKey", o_script_pub_key);
- in.pushKV("prevout", p);
- break;
+ UniValue p(UniValue::VOBJ);
+ p.pushKV("generated", bool(prev_coin.fCoinBase));
+ p.pushKV("height", uint64_t(prev_coin.nHeight));
+ p.pushKV("value", ValueFromAmount(prev_txout.nValue));
+ p.pushKV("scriptPubKey", o_script_pub_key);
+ in.pushKV("prevout", p);
}
}
in.pushKV("sequence", (int64_t)txin.nSequence);
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 9ab9209ca4..e4ee0e674b 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -321,7 +321,7 @@ bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& co
coins_stats.hashSerialized = entry.muhash;
coins_stats.nTransactionOutputs = entry.transaction_output_count;
coins_stats.nBogoSize = entry.bogo_size;
- coins_stats.nTotalAmount = entry.total_amount;
+ 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;
diff --git a/src/init.cpp b/src/init.cpp
index b11eb762f2..ab5a53d6e9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1404,31 +1404,31 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
uiInterface.InitMessage(_("Loading block index…").translated);
const int64_t load_block_index_start_time = GetTimeMillis();
- std::optional<ChainstateLoadingError> rv;
+ std::optional<ChainstateLoadingError> maybe_load_error;
try {
- rv = LoadChainstate(fReset,
- chainman,
- Assert(node.mempool.get()),
- fPruneMode,
- chainparams.GetConsensus(),
- fReindexChainState,
- cache_sizes.block_tree_db,
- cache_sizes.coins_db,
- cache_sizes.coins,
- false,
- false,
- ShutdownRequested,
- []() {
- uiInterface.ThreadSafeMessageBox(
- _("Error reading from database, shutting down."),
- "", CClientUIInterface::MSG_ERROR);
- });
+ maybe_load_error = LoadChainstate(fReset,
+ chainman,
+ Assert(node.mempool.get()),
+ fPruneMode,
+ chainparams.GetConsensus(),
+ fReindexChainState,
+ cache_sizes.block_tree_db,
+ cache_sizes.coins_db,
+ cache_sizes.coins,
+ /*block_tree_db_in_memory=*/false,
+ /*coins_db_in_memory=*/false,
+ /*shutdown_requested=*/ShutdownRequested,
+ /*coins_error_cb=*/[]() {
+ uiInterface.ThreadSafeMessageBox(
+ _("Error reading from database, shutting down."),
+ "", CClientUIInterface::MSG_ERROR);
+ });
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- rv = ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
+ maybe_load_error = ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
}
- if (rv.has_value()) {
- switch (rv.value()) {
+ if (maybe_load_error.has_value()) {
+ switch (maybe_load_error.value()) {
case ChainstateLoadingError::ERROR_LOADING_BLOCK_DB:
strLoadError = _("Error loading block database");
break;
@@ -1462,7 +1462,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
break;
}
} else {
- std::optional<ChainstateLoadVerifyError> rv2;
+ std::optional<ChainstateLoadVerifyError> maybe_verify_error;
try {
uiInterface.InitMessage(_("Verifying blocks…").translated);
auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
@@ -1470,19 +1470,19 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
- rv2 = VerifyLoadedChainstate(chainman,
- fReset,
- fReindexChainState,
- chainparams.GetConsensus(),
- check_blocks,
- args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- static_cast<int64_t(*)()>(GetTime));
+ maybe_verify_error = VerifyLoadedChainstate(chainman,
+ fReset,
+ fReindexChainState,
+ chainparams.GetConsensus(),
+ check_blocks,
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
+ /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- rv2 = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
+ maybe_verify_error = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
}
- if (rv2.has_value()) {
- switch (rv2.value()) {
+ if (maybe_verify_error.has_value()) {
+ switch (maybe_verify_error.value()) {
case ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE:
strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. "
diff --git a/src/net.cpp b/src/net.cpp
index 019e77fd7a..89a4aee5d9 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1099,10 +1099,10 @@ bool CConnman::AttemptToEvictConnection()
void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
+ auto sock = hListenSocket.sock->Accept((struct sockaddr*)&sockaddr, &len);
CAddress addr;
- if (hSocket == INVALID_SOCKET) {
+ if (!sock) {
const int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK) {
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
@@ -1116,15 +1116,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}
- const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(hSocket)), NODE_NONE};
+ const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(sock->Get())), NODE_NONE};
NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
- CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr);
+ CreateNodeFromAcceptedSocket(std::move(sock), permissionFlags, addr_bind, addr);
}
-void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
+void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NetPermissionFlags permissionFlags,
const CAddress& addr_bind,
const CAddress& addr)
@@ -1150,27 +1150,24 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
if (!fNetworkActive) {
LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString());
- CloseSocket(hSocket);
return;
}
- if (!IsSelectableSocket(hSocket))
+ if (!IsSelectableSocket(sock->Get()))
{
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
- CloseSocket(hSocket);
return;
}
// According to the internet TCP_NODELAY is not carried into accepted sockets
// on all platforms. Set it again here just to be sure.
- SetSocketNoDelay(hSocket);
+ SetSocketNoDelay(sock->Get());
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && banned)
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
- CloseSocket(hSocket);
return;
}
@@ -1179,7 +1176,6 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && nInbound + 1 >= nMaxInbound && discouraged)
{
LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
- CloseSocket(hSocket);
return;
}
@@ -1188,7 +1184,6 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
if (!AttemptToEvictConnection()) {
// No connection to evict, disconnect the new connection
LogPrint(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
- CloseSocket(hSocket);
return;
}
}
@@ -1202,7 +1197,7 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
}
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
- CNode* pnode = new CNode(id, nodeServices, hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
+ CNode* pnode = new CNode(id, nodeServices, sock->Release(), addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
pnode->AddRef();
pnode->m_permissionFlags = permissionFlags;
pnode->m_prefer_evict = discouraged;
@@ -1364,7 +1359,7 @@ bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
std::set<SOCKET>& error_set)
{
for (const ListenSocket& hListenSocket : vhListenSocket) {
- recv_set.insert(hListenSocket.socket);
+ recv_set.insert(hListenSocket.sock->Get());
}
for (CNode* pnode : nodes) {
@@ -1646,7 +1641,7 @@ void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set)
if (interruptNet) {
return;
}
- if (recv_set.count(listen_socket.socket) > 0) {
+ if (recv_set.count(listen_socket.sock->Get()) > 0) {
AcceptConnection(listen_socket);
}
}
@@ -2335,7 +2330,7 @@ void CConnman::ThreadI2PAcceptIncoming()
continue;
}
- CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::None,
+ CreateNodeFromAcceptedSocket(std::move(conn.sock), NetPermissionFlags::None,
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
}
}
@@ -2397,7 +2392,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
return false;
}
- vhListenSocket.push_back(ListenSocket(sock->Release(), permissions));
+ vhListenSocket.emplace_back(std::move(sock), permissions);
return true;
}
@@ -2706,15 +2701,6 @@ void CConnman::StopNodes()
DeleteNode(pnode);
}
- // Close listening sockets.
- for (ListenSocket& hListenSocket : vhListenSocket) {
- if (hListenSocket.socket != INVALID_SOCKET) {
- if (!CloseSocket(hListenSocket.socket)) {
- LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
- }
- }
- }
-
for (CNode* pnode : m_nodes_disconnected) {
DeleteNode(pnode);
}
diff --git a/src/net.h b/src/net.h
index 977e6502ce..80fc93a5d0 100644
--- a/src/net.h
+++ b/src/net.h
@@ -25,6 +25,7 @@
#include <threadinterrupt.h>
#include <uint256.h>
#include <util/check.h>
+#include <util/sock.h>
#include <atomic>
#include <condition_variable>
@@ -947,9 +948,13 @@ public:
private:
struct ListenSocket {
public:
- SOCKET socket;
+ std::shared_ptr<Sock> sock;
inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
- ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
+ ListenSocket(std::shared_ptr<Sock> sock_, NetPermissionFlags permissions_)
+ : sock{sock_}, m_permissions{permissions_}
+ {
+ }
+
private:
NetPermissionFlags m_permissions;
};
@@ -969,12 +974,12 @@ private:
/**
* Create a `CNode` object from a socket that has just been accepted and add the node to
* the `m_nodes` member.
- * @param[in] hSocket Connected socket to communicate with the peer.
+ * @param[in] sock Connected socket to communicate with the peer.
* @param[in] permissionFlags The peer's permissions.
* @param[in] addr_bind The address and port at our side of the connection.
* @param[in] addr The address and port at the peer's side of the connection.
*/
- void CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ void CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NetPermissionFlags permissionFlags,
const CAddress& addr_bind,
const CAddress& addr);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 51aa498fb0..43843624a1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -57,14 +57,14 @@ static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
* behind headers chain.
*/
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
-/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
-static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** Timeout for (unprotected) outbound peers to sync to our chainwork */
+static constexpr auto CHAIN_SYNC_TIMEOUT{20min};
/** How frequently to check for stale tips */
static constexpr auto STALE_CHECK_INTERVAL{10min};
/** How frequently to check for extra outbound peers and disconnect */
static constexpr auto EXTRA_PEER_CHECK_INTERVAL{45s};
/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict */
-static constexpr std::chrono::seconds MINIMUM_CONNECT_TIME{30};
+static constexpr auto MINIMUM_CONNECT_TIME{30s};
/** SHA256("main address relay")[0:8] */
static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
/// Age after which a stale block will no longer be served if requested as
@@ -74,7 +74,7 @@ static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
/// limiting block relay. Set to one week, denominated in seconds.
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
/** Time between pings automatically sent out for latency probing and keepalive */
-static constexpr std::chrono::minutes PING_INTERVAL{2};
+static constexpr auto PING_INTERVAL{2min};
/** The maximum number of entries in a locator */
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of entries in an 'inv' protocol message */
@@ -88,19 +88,19 @@ static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
* the actual transaction (from any peer) in response to requests for them. */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
-static constexpr auto TXID_RELAY_DELAY = std::chrono::seconds{2};
+static constexpr auto TXID_RELAY_DELAY{2s};
/** How long to delay requesting transactions from non-preferred peers */
-static constexpr auto NONPREF_PEER_TX_DELAY = std::chrono::seconds{2};
+static constexpr auto NONPREF_PEER_TX_DELAY{2s};
/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
-static constexpr auto OVERLOADED_PEER_TX_DELAY = std::chrono::seconds{2};
-/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
-static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}};
+static constexpr auto OVERLOADED_PEER_TX_DELAY{2s};
+/** How long to wait before downloading a transaction from an additional peer */
+static constexpr auto GETDATA_TX_INTERVAL{60s};
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
static const unsigned int MAX_GETDATA_SZ = 1000;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
/** Time during which a peer must stall block download progress before being disconnected. */
-static constexpr auto BLOCK_STALLING_TIMEOUT = 2s;
+static constexpr auto BLOCK_STALLING_TIMEOUT{2s};
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
@@ -125,16 +125,16 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
/** Average delay between local address broadcasts */
-static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h;
+static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24h};
/** Average delay between peer address broadcasts */
-static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL = 30s;
+static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL{30s};
/** Average delay between trickled inventory transmissions for inbound peers.
* Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */
-static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL = 5s;
+static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s};
/** Average delay between trickled inventory transmissions for outbound peers.
* Use a smaller delay as there is less privacy concern for them.
* Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */
-static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL = 2s;
+static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s};
/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
@@ -148,9 +148,9 @@ static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
* peers, and random variations in the broadcast mechanism. */
static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
-static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL = 10min;
+static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL{10min};
/** Maximum feefilter broadcast delay after significant change. */
-static constexpr auto MAX_FEEFILTER_CHANGE_DELAY = 5min;
+static constexpr auto MAX_FEEFILTER_CHANGE_DELAY{5min};
/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
@@ -329,7 +329,7 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
- void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(std::chrono::seconds now) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -727,7 +727,7 @@ struct CNodeState {
* - its chain tip has at least as much work as ours
*
* CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip,
- * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
+ * set a timeout CHAIN_SYNC_TIMEOUT in the future:
* - If at timeout their best known block now has more work than our tip
* when the timeout was set, then either reset the timeout or clear it
* (after comparing against our current tip's work)
@@ -742,7 +742,7 @@ struct CNodeState {
*/
struct ChainSyncTimeoutState {
//! A timeout used for checking whether our peer has sufficiently synced
- int64_t m_timeout{0};
+ std::chrono::seconds m_timeout{0s};
//! A header with the work we require on our peer's chain
const CBlockIndex* m_work_header{nullptr};
//! After timeout is reached, set to true after sending getheaders
@@ -949,10 +949,10 @@ bool PeerManagerImpl::TipMayBeStale()
{
AssertLockHeld(cs_main);
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
- if (count_seconds(m_last_tip_update) == 0) {
+ if (m_last_tip_update.load() == 0s) {
m_last_tip_update = GetTime<std::chrono::seconds>();
}
- return count_seconds(m_last_tip_update) < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+ return m_last_tip_update.load() < GetTime<std::chrono::seconds>() - std::chrono::seconds{consensusParams.nPowTargetSpacing * 3} && mapBlocksInFlight.empty();
}
bool PeerManagerImpl::CanDirectFetch()
@@ -1137,7 +1137,7 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
// MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
- auto delay = std::chrono::microseconds{0};
+ auto delay{0us};
const bool preferred = state->fPreferredDownload;
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
@@ -1191,7 +1191,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
// Schedule next run for 10-15 minutes in the future.
// We add randomness on every cycle to avoid the possibility of P2P fingerprinting.
- const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
+ const std::chrono::milliseconds delta = 10min + GetRandMillis(5min);
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1296,7 +1296,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
- std::chrono::microseconds ping_wait{0};
+ auto ping_wait{0us};
if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) {
ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
}
@@ -1496,7 +1496,7 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
// schedule next run for 10-15 minutes in the future
- const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
+ const std::chrono::milliseconds delta = 10min + GetRandMillis(5min);
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
@@ -1746,8 +1746,8 @@ void PeerManagerImpl::RelayAddress(NodeId originator,
// Relay to a limited number of other nodes
// Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the m_addr_knowns of the chosen nodes prevent repeats
- uint64_t hashAddr = addr.GetHash();
- const CSipHasher hasher = m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24 * 60 * 60));
+ const uint64_t hashAddr{addr.GetHash()};
+ const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr).Write((GetTime() + hashAddr) / (24 * 60 * 60))};
FastRandomContext insecure_rand;
// Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers.
@@ -1963,10 +1963,9 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
- const std::chrono::seconds now = GetTime<std::chrono::seconds>();
+ const auto now{GetTime<std::chrono::seconds>()};
// Get last mempool request time
- const std::chrono::seconds mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load()
- : std::chrono::seconds::min();
+ const auto mempool_req = pfrom.m_tx_relay != nullptr ? pfrom.m_tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min();
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
@@ -2890,7 +2889,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
int64_t nSince = nNow - 10 * 60;
// Update/increment addr rate limiting bucket.
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) {
// Don't increment bucket if it's already full
const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us);
@@ -2976,7 +2975,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
uint256* best_block{nullptr};
for (CInv& inv : vInv) {
@@ -3354,7 +3353,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
}
if (!fRejectedParents) {
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
for (const uint256& parent_txid : unique_parents) {
// Here, we only have the txid (and not wtxid) of the
@@ -4180,7 +4179,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
return fMoreWork;
}
-void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
+void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_seconds)
{
AssertLockHeld(cs_main);
@@ -4195,12 +4194,12 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {
- if (state.m_chain_sync.m_timeout != 0) {
- state.m_chain_sync.m_timeout = 0;
+ if (state.m_chain_sync.m_timeout != 0s) {
+ state.m_chain_sync.m_timeout = 0s;
state.m_chain_sync.m_work_header = nullptr;
state.m_chain_sync.m_sent_getheaders = false;
}
- } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
+ } else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {
// Our best block known by this peer is behind our tip, and we're either noticing
// that for the first time, OR this peer was able to catch up to some earlier point
// where we checked against our tip.
@@ -4208,7 +4207,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();
state.m_chain_sync.m_sent_getheaders = false;
- } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
+ } else if (state.m_chain_sync.m_timeout > 0s && time_in_seconds > state.m_chain_sync.m_timeout) {
// No evidence yet that our peer has synced to a chain with work equal to that
// of our tip, when we first detected it was behind. Send a single getheaders
// message to give the peer a chance to update us.
@@ -4221,7 +4220,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
- constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
+ constexpr auto HEADERS_RESPONSE_TIME{2min};
// Bump the timeout to allow a response, which could clear the timeout
// (if the response shows the peer has synced), reset the timeout (if
// the peer syncs to the required work but not to our tip), or result
@@ -4348,7 +4347,8 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) {
- LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", count_seconds(now) - count_seconds(m_last_tip_update));
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n",
+ count_seconds(now - m_last_tip_update.load()));
m_connman.SetTryNewOutboundPeer(true);
} else if (m_connman.GetTryNewOutboundPeer()) {
m_connman.SetTryNewOutboundPeer(false);
@@ -4570,7 +4570,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// If we get here, the outgoing message serialization version is set and can't change.
const CNetMsgMaker msgMaker(pto->GetCommonVersion());
- const auto current_time = GetTime<std::chrono::microseconds>();
+ const auto current_time{GetTime<std::chrono::microseconds>()};
if (pto->IsAddrFetchConn() && current_time - pto->m_connected > 10 * AVG_ADDRESS_BROADCAST_INTERVAL) {
LogPrint(BCLog::NET, "addrfetch connection timeout; disconnecting peer=%d\n", pto->GetId());
@@ -4969,7 +4969,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Check that outbound peers have reasonable chains
// GetTime() is used by this anti-DoS logic so we can test this using mocktime
- ConsiderEviction(*pto, GetTime());
+ ConsiderEviction(*pto, GetTime<std::chrono::seconds>());
//
// Message: getdata (blocks)
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 53bc2b5069..e9f78c2870 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -12,6 +12,7 @@
#include <fs.h>
#include <hash.h>
#include <pow.h>
+#include <reverse_iterator.h>
#include <shutdown.h>
#include <signet.h>
#include <streams.h>
@@ -47,6 +48,375 @@ static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();
+CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
+{
+ AssertLockHeld(cs_main);
+ BlockMap::const_iterator it = m_block_index.find(hash);
+ return it == m_block_index.end() ? nullptr : it->second;
+}
+
+CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
+{
+ AssertLockHeld(cs_main);
+
+ // Check for duplicate
+ uint256 hash = block.GetHash();
+ BlockMap::iterator it = m_block_index.find(hash);
+ if (it != m_block_index.end()) {
+ return it->second;
+ }
+
+ // Construct new block index object
+ CBlockIndex* pindexNew = new CBlockIndex(block);
+ // We assign the sequence id to blocks only when the full data is available,
+ // to avoid miners withholding blocks but broadcasting headers, to get a
+ // competitive advantage.
+ pindexNew->nSequenceId = 0;
+ BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
+ pindexNew->phashBlock = &((*mi).first);
+ BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
+ if (miPrev != m_block_index.end()) {
+ pindexNew->pprev = (*miPrev).second;
+ pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
+ pindexNew->BuildSkip();
+ }
+ pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
+ pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
+ pindexNew->RaiseValidity(BLOCK_VALID_TREE);
+ if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork)
+ pindexBestHeader = pindexNew;
+
+ setDirtyBlockIndex.insert(pindexNew);
+
+ return pindexNew;
+}
+
+void BlockManager::PruneOneBlockFile(const int fileNumber)
+{
+ AssertLockHeld(cs_main);
+ LOCK(cs_LastBlockFile);
+
+ for (const auto& entry : m_block_index) {
+ CBlockIndex* pindex = entry.second;
+ if (pindex->nFile == fileNumber) {
+ pindex->nStatus &= ~BLOCK_HAVE_DATA;
+ pindex->nStatus &= ~BLOCK_HAVE_UNDO;
+ pindex->nFile = 0;
+ pindex->nDataPos = 0;
+ pindex->nUndoPos = 0;
+ setDirtyBlockIndex.insert(pindex);
+
+ // Prune from m_blocks_unlinked -- any block we prune would have
+ // to be downloaded again in order to consider its chain, at which
+ // point it would be considered as a candidate for
+ // m_blocks_unlinked or setBlockIndexCandidates.
+ auto range = m_blocks_unlinked.equal_range(pindex->pprev);
+ while (range.first != range.second) {
+ std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;
+ range.first++;
+ if (_it->second == pindex) {
+ m_blocks_unlinked.erase(_it);
+ }
+ }
+ }
+ }
+
+ vinfoBlockFile[fileNumber].SetNull();
+ setDirtyFileInfo.insert(fileNumber);
+}
+
+void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
+{
+ assert(fPruneMode && nManualPruneHeight > 0);
+
+ LOCK2(cs_main, cs_LastBlockFile);
+ if (chain_tip_height < 0) {
+ return;
+ }
+
+ // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
+ unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
+ int count = 0;
+ for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
+ if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
+ continue;
+ }
+ PruneOneBlockFile(fileNumber);
+ setFilesToPrune.insert(fileNumber);
+ count++;
+ }
+ LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
+}
+
+void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
+{
+ LOCK2(cs_main, cs_LastBlockFile);
+ if (chain_tip_height < 0 || nPruneTarget == 0) {
+ return;
+ }
+ if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
+ return;
+ }
+
+ unsigned int nLastBlockWeCanPrune{(unsigned)std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP))};
+ uint64_t nCurrentUsage = CalculateCurrentUsage();
+ // We don't check to prune until after we've allocated new space for files
+ // So we should leave a buffer under our target to account for another allocation
+ // before the next pruning.
+ uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
+ uint64_t nBytesToPrune;
+ int count = 0;
+
+ if (nCurrentUsage + nBuffer >= nPruneTarget) {
+ // On a prune event, the chainstate DB is flushed.
+ // To avoid excessive prune events negating the benefit of high dbcache
+ // values, we should not prune too rapidly.
+ // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
+ if (is_ibd) {
+ // Since this is only relevant during IBD, we use a fixed 10%
+ nBuffer += nPruneTarget / 10;
+ }
+
+ for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
+ nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
+
+ if (vinfoBlockFile[fileNumber].nSize == 0) {
+ continue;
+ }
+
+ if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
+ break;
+ }
+
+ // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
+ if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
+ continue;
+ }
+
+ PruneOneBlockFile(fileNumber);
+ // Queue up the files for removal
+ setFilesToPrune.insert(fileNumber);
+ nCurrentUsage -= nBytesToPrune;
+ count++;
+ }
+ }
+
+ LogPrint(BCLog::PRUNE, "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);
+}
+
+CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
+{
+ AssertLockHeld(cs_main);
+
+ if (hash.IsNull()) {
+ return nullptr;
+ }
+
+ // Return existing
+ BlockMap::iterator mi = m_block_index.find(hash);
+ if (mi != m_block_index.end()) {
+ return (*mi).second;
+ }
+
+ // Create new
+ CBlockIndex* pindexNew = new CBlockIndex();
+ mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
+ pindexNew->phashBlock = &((*mi).first);
+
+ return pindexNew;
+}
+
+bool BlockManager::LoadBlockIndex(
+ const Consensus::Params& consensus_params,
+ ChainstateManager& chainman)
+{
+ if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
+ return false;
+ }
+
+ // Calculate nChainWork
+ std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
+ vSortedByHeight.reserve(m_block_index.size());
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
+ CBlockIndex* pindex = item.second;
+ vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
+ }
+ sort(vSortedByHeight.begin(), vSortedByHeight.end());
+
+ // Find start of assumed-valid region.
+ int first_assumed_valid_height = std::numeric_limits<int>::max();
+
+ for (const auto& [height, block] : vSortedByHeight) {
+ if (block->IsAssumedValid()) {
+ auto chainstates = chainman.GetAll();
+
+ // If we encounter an assumed-valid block index entry, ensure that we have
+ // one chainstate that tolerates assumed-valid entries and another that does
+ // not (i.e. the background validation chainstate), since assumed-valid
+ // entries should always be pending validation by a fully-validated chainstate.
+ auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
+ assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
+ assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
+
+ first_assumed_valid_height = height;
+ break;
+ }
+ }
+
+ for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
+ if (ShutdownRequested()) return false;
+ CBlockIndex* pindex = item.second;
+ pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
+ pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
+
+ // We can link the chain of blocks for which we've received transactions at some point, or
+ // blocks that are assumed-valid on the basis of snapshot load (see
+ // PopulateAndValidateSnapshot()).
+ // Pruned nodes may have deleted the block.
+ if (pindex->nTx > 0) {
+ if (pindex->pprev) {
+ if (pindex->pprev->nChainTx > 0) {
+ pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
+ } else {
+ pindex->nChainTx = 0;
+ m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
+ }
+ } else {
+ pindex->nChainTx = pindex->nTx;
+ }
+ }
+ if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
+ pindex->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(pindex);
+ }
+ if (pindex->IsAssumedValid() ||
+ (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
+ (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
+
+ // Fill each chainstate's block candidate set. Only add assumed-valid
+ // blocks to the tip candidate set if the chainstate is allowed to rely on
+ // assumed-valid blocks.
+ //
+ // If all setBlockIndexCandidates contained the assumed-valid blocks, the
+ // background chainstate's ActivateBestChain() call would add assumed-valid
+ // blocks to the chain (based on how FindMostWorkChain() works). Obviously
+ // we don't want this since the purpose of the background validation chain
+ // is to validate assued-valid blocks.
+ //
+ // Note: This is considering all blocks whose height is greater or equal to
+ // the first assumed-valid block to be assumed-valid blocks, and excluding
+ // them from the background chainstate's setBlockIndexCandidates set. This
+ // does mean that some blocks which are not technically assumed-valid
+ // (later blocks on a fork beginning before the first assumed-valid block)
+ // might not get added to the the background chainstate, but this is ok,
+ // because they will still be attached to the active chainstate if they
+ // actually contain more work.
+ //
+ // Instead of this height-based approach, an earlier attempt was made at
+ // detecting "holistically" whether the block index under consideration
+ // relied on an assumed-valid ancestor, but this proved to be too slow to
+ // be practical.
+ for (CChainState* chainstate : chainman.GetAll()) {
+ if (chainstate->reliesOnAssumedValid() ||
+ pindex->nHeight < first_assumed_valid_height) {
+ chainstate->setBlockIndexCandidates.insert(pindex);
+ }
+ }
+ }
+ if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
+ chainman.m_best_invalid = pindex;
+ }
+ if (pindex->pprev) {
+ pindex->BuildSkip();
+ }
+ if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
+ pindexBestHeader = pindex;
+ }
+
+ return true;
+}
+
+void BlockManager::Unload()
+{
+ m_blocks_unlinked.clear();
+
+ for (const BlockMap::value_type& entry : m_block_index) {
+ delete entry.second;
+ }
+
+ m_block_index.clear();
+}
+
+bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
+{
+ if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
+ return false;
+ }
+
+ // Load block file info
+ m_block_tree_db->ReadLastBlockFile(nLastBlockFile);
+ vinfoBlockFile.resize(nLastBlockFile + 1);
+ LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
+ for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
+ m_block_tree_db->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
+ }
+ LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
+ for (int nFile = nLastBlockFile + 1; true; nFile++) {
+ CBlockFileInfo info;
+ if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
+ vinfoBlockFile.push_back(info);
+ } else {
+ break;
+ }
+ }
+
+ // Check presence of blk files
+ LogPrintf("Checking all blk files are present...\n");
+ std::set<int> setBlkDataFiles;
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
+ CBlockIndex* pindex = item.second;
+ if (pindex->nStatus & BLOCK_HAVE_DATA) {
+ setBlkDataFiles.insert(pindex->nFile);
+ }
+ }
+ for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
+ FlatFilePos pos(*it, 0);
+ if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
+ return false;
+ }
+ }
+
+ // Check whether we have ever pruned block & undo files
+ m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
+ if (fHavePruned) {
+ LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
+ }
+
+ // Check whether we need to continue reindexing
+ bool fReindexing = false;
+ m_block_tree_db->ReadReindexing(fReindexing);
+ if (fReindexing) fReindex = true;
+
+ return true;
+}
+
+CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
+{
+ const MapCheckpoints& checkpoints = data.mapCheckpoints;
+
+ for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
+ const uint256& hash = i.second;
+ CBlockIndex* pindex = LookupBlockIndex(hash);
+ if (pindex) {
+ return pindex;
+ }
+ }
+ return nullptr;
+}
+
bool IsBlockPruned(const CBlockIndex* pblockindex)
{
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 7c7bf68178..a18203f48d 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -7,6 +7,7 @@
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <txdb.h>
#include <atomic>
#include <cstdint>
@@ -20,7 +21,9 @@ class CBlockIndex;
class CBlockUndo;
class CChain;
class CChainParams;
+class CChainState;
class ChainstateManager;
+struct CCheckpointData;
struct FlatFilePos;
namespace Consensus {
struct Params;
@@ -45,6 +48,87 @@ extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
+typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
+
+struct CBlockIndexWorkComparator {
+ bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
+};
+
+/**
+ * Maintains a tree of blocks (stored in `m_block_index`) which is consulted
+ * to determine where the most-work tip is.
+ *
+ * This data is used mostly in `CChainState` - information about, e.g.,
+ * candidate tips is not maintained here.
+ */
+class BlockManager
+{
+ friend CChainState;
+
+private:
+ /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
+ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
+
+ /**
+ * Prune block and undo files (blk???.dat and rev???.dat) so that the disk space used is less than a user-defined target.
+ * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
+ * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+ * (which in this case means the blockchain must be re-downloaded.)
+ *
+ * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+ * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+ * A db flag records the fact that at least some block files have been pruned.
+ *
+ * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
+ */
+ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
+
+public:
+ BlockMap m_block_index GUARDED_BY(cs_main);
+
+ /**
+ * All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
+ * Pruned nodes may have entries where B is missing data.
+ */
+ std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
+
+ std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
+
+ bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ /**
+ * Load the blocktree off disk and into memory. Populate certain metadata
+ * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
+ * collections like setDirtyBlockIndex.
+ */
+ bool LoadBlockIndex(
+ const Consensus::Params& consensus_params,
+ ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Clear all data members. */
+ void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** Create a new block index entry for a given block hash */
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Mark one block file as pruned (modify associated database entries)
+ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Returns last CBlockIndex* that is a checkpoint
+ CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ ~BlockManager()
+ {
+ Unload();
+ }
+};
+
//! Check whether the block associated with this index entry is pruned or not.
bool IsBlockPruned(const CBlockIndex* pblockindex);
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index abeebad1a6..0274587e17 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -141,7 +141,7 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
const CBlockIndex* tip = chainstate->m_chain.Tip();
- if (tip && tip->nTime > get_unix_time_seconds() + 2 * 60 * 60) {
+ if (tip && tip->nTime > get_unix_time_seconds() + MAX_FUTURE_BLOCK_TIME) {
return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE;
}
diff --git a/src/node/chainstate.h b/src/node/chainstate.h
index 11278a0991..4df917a281 100644
--- a/src/node/chainstate.h
+++ b/src/node/chainstate.h
@@ -50,7 +50,7 @@ enum class ChainstateLoadingError {
* this sequence, when we explicitly checked shutdown_requested() at
* arbitrary points, one of those calls returned true". Therefore, a
* return value other than SHUTDOWN_PROBED does not guarantee that
- * shutdown_requested() hasn't been called indirectly.
+ * shutdown hasn't been called indirectly.
* - else
* - Success!
*/
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index b538474d8d..68cc65d3ed 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -11,6 +11,7 @@
#include <index/coinstatsindex.h>
#include <serialize.h>
#include <uint256.h>
+#include <util/overflow.h>
#include <util/system.h>
#include <validation.h>
@@ -82,7 +83,9 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<u
stats.nTransactions++;
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
stats.nTransactionOutputs++;
- stats.nTotalAmount += it->second.out.nValue;
+ if (stats.total_amount.has_value()) {
+ stats.total_amount = CheckedAdd(*stats.total_amount, it->second.out.nValue);
+ }
stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
}
}
@@ -95,10 +98,8 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
assert(pcursor);
if (!pindex) {
- {
- LOCK(cs_main);
- pindex = blockman.LookupBlockIndex(view->GetBestBlock());
- }
+ LOCK(cs_main);
+ pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
stats.nHeight = Assert(pindex)->nHeight;
stats.hashBlock = pindex->GetBlockHash();
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 4a5d17b3fd..3b641200ad 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -24,9 +24,10 @@ enum class CoinStatsHashType {
NONE,
};
-struct CCoinsStats
-{
- CoinStatsHashType m_hash_type;
+struct CCoinsStats {
+ //! Which hash type to use
+ const CoinStatsHashType m_hash_type;
+
int nHeight{0};
uint256 hashBlock{};
uint64_t nTransactions{0};
@@ -34,7 +35,8 @@ struct CCoinsStats
uint64_t nBogoSize{0};
uint256 hashSerialized{};
uint64_t nDiskSize{0};
- CAmount nTotalAmount{0};
+ //! The total amount, or nullopt if an overflow occurred calculating it
+ std::optional<CAmount> total_amount{0};
//! The number of coins contained.
uint64_t coins_count{0};
diff --git a/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java b/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java
index cf3b4f6668..2cba489242 100644
--- a/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java
+++ b/src/qt/android/src/org/bitcoincore/qt/BitcoinQtActivity.java
@@ -18,12 +18,6 @@ public class BitcoinQtActivity extends QtActivity
bitcoinDir.mkdir();
}
- try {
- Os.setenv("QT_QPA_PLATFORM", "android", true);
- } catch (ErrnoException e) {
- e.printStackTrace();
- }
-
super.onCreate(savedInstanceState);
}
}
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 4b2c3ed434..7590dd524d 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -302,7 +302,7 @@
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
- <normaloff>:/icons/edit</normaloff>:/icons/edit</iconset>
+ <normaloff>:/icons/eye</normaloff>:/icons/eye</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index ba386a97ae..3c80c01160 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -38,7 +38,7 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
} else {
ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
ui->receiveButton->setIcon(_platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
- ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit"));
+ ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/eye"));
ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 689879ae97..073c28b5e8 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2010 Satoshi Nakamoto
-// Copyright (c) 2009-2021 The Bitcoin Core developers
+// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -19,10 +19,10 @@
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
+#include <logging/timer.h>
#include <net.h>
#include <net_processing.h>
#include <node/blockstorage.h>
-#include <logging/timer.h>
#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
@@ -185,6 +185,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo, verbosity);
txs.push_back(objTx);
}
+ break;
}
result.pushKV("tx", txs);
@@ -967,7 +968,7 @@ static RPCHelpMan getblock()
"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
+ {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs"},
},
{
RPCResult{"for verbosity = 0",
@@ -1009,6 +1010,37 @@ static RPCHelpMan getblock()
}},
}},
}},
+ RPCResult{"for verbosity = 3",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ELISION, "", "Same output as verbosity = 2"},
+ {RPCResult::Type::ARR, "tx", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "vin", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"},
+ {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)",
+ {
+ {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
+ {RPCResult::Type::NUM, "height", "The height of the prevout"},
+ {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR, "asm", "The asm"},
+ {RPCResult::Type::STR, "hex", "The hex"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
+ {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
+ }},
+ }},
+ }},
+ }},
+ }},
+ }},
+ }},
},
RPCExamples{
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
@@ -1242,9 +1274,10 @@ static RPCHelpMan gettxoutsetinfo()
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
}
if (hash_type == CoinStatsHashType::MUHASH) {
- ret.pushKV("muhash", stats.hashSerialized.GetHex());
+ ret.pushKV("muhash", stats.hashSerialized.GetHex());
}
- ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
+ CHECK_NONFATAL(stats.total_amount.has_value());
+ ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value()));
if (!stats.index_used) {
ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
ret.pushKV("disk_size", stats.nDiskSize);
@@ -1512,6 +1545,7 @@ RPCHelpMan getblockchaininfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ const ArgsManager& args{EnsureAnyArgsman(request.context)};
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
CChainState& active_chainstate = chainman.ActiveChainstate();
@@ -1542,7 +1576,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("pruneheight", block->nHeight);
// if 0, execution bypasses the whole if block.
- bool automatic_pruning = (gArgs.GetIntArg("-prune", 0) != 1);
+ bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
obj.pushKV("prune_target_size", nPruneTarget);
@@ -1709,7 +1743,7 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::NUM, "size", "Current tx count"},
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
- {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritizetransaction"},
+ {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
@@ -2238,10 +2272,9 @@ static RPCHelpMan savemempool()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ const ArgsManager& args{EnsureAnyArgsman(request.context)};
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
- const NodeContext& node = EnsureAnyNodeContext(request.context);
-
if (!mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
}
@@ -2251,7 +2284,7 @@ static RPCHelpMan savemempool()
}
UniValue ret(UniValue::VOBJ);
- ret.pushKV("filename", fs::path((node.args->GetDataDirNet() / "mempool.dat")).u8string());
+ ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).u8string());
return ret;
},
@@ -2576,13 +2609,9 @@ static RPCHelpMan dumptxoutset()
{
return RPCHelpMan{
"dumptxoutset",
- "\nWrite the serialized UTXO set to disk.\n",
+ "Write the serialized UTXO set to disk.",
{
- {"path",
- RPCArg::Type::STR,
- RPCArg::Optional::NO,
- /* default_val */ "",
- "path to the output file. If relative, will be prefixed by datadir."},
+ {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2600,10 +2629,11 @@ static RPCHelpMan dumptxoutset()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str()));
+ const ArgsManager& args{EnsureAnyArgsman(request.context)};
+ const fs::path path = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str()));
// Write to a temporary path and then move into `path` on completion
// to avoid confusion due to an interruption.
- const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
+ const fs::path temppath = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
if (fs::exists(path)) {
throw JSONRPCError(
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index e487accb66..2176b5997e 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -21,7 +21,6 @@ class CBlock;
class CBlockIndex;
class CChainState;
class CTxMemPool;
-class ChainstateManager;
class UniValue;
struct NodeContext;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 3c9e0625e6..f4790b007c 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1066,8 +1066,9 @@ static RPCHelpMan testmempoolaccept()
static RPCHelpMan decodepsbt()
{
- return RPCHelpMan{"decodepsbt",
- "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
+ return RPCHelpMan{
+ "decodepsbt",
+ "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
},
@@ -1176,7 +1177,7 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
- {RPCResult::Type::ARR, "proprietary", "The input proprietary map",
+ {RPCResult::Type::ARR, "proprietary", /*optional=*/true, "The input proprietary map",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -1217,7 +1218,7 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
- {RPCResult::Type::ARR, "proprietary", "The output proprietary map",
+ {RPCResult::Type::ARR, "proprietary", /*optional=*/true, "The output proprietary map",
{
{RPCResult::Type::OBJ, "", "",
{
diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp
index 3fc35222e1..2978051574 100644
--- a/src/rpc/server_util.cpp
+++ b/src/rpc/server_util.cpp
@@ -37,6 +37,19 @@ CTxMemPool& EnsureAnyMemPool(const std::any& context)
return EnsureMemPool(EnsureAnyNodeContext(context));
}
+ArgsManager& EnsureArgsman(const NodeContext& node)
+{
+ if (!node.args) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Node args not found");
+ }
+ return *node.args;
+}
+
+ArgsManager& EnsureAnyArgsman(const std::any& context)
+{
+ return EnsureArgsman(EnsureAnyNodeContext(context));
+}
+
ChainstateManager& EnsureChainman(const NodeContext& node)
{
if (!node.chainman) {
diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h
index ad3c149c90..3b0df2d651 100644
--- a/src/rpc/server_util.h
+++ b/src/rpc/server_util.h
@@ -7,16 +7,19 @@
#include <any>
+class ArgsManager;
class CBlockPolicyEstimator;
class CConnman;
-class ChainstateManager;
class CTxMemPool;
-struct NodeContext;
+class ChainstateManager;
class PeerManager;
+struct NodeContext;
NodeContext& EnsureAnyNodeContext(const std::any& context);
CTxMemPool& EnsureMemPool(const NodeContext& node);
CTxMemPool& EnsureAnyMemPool(const std::any& context);
+ArgsManager& EnsureArgsman(const NodeContext& node);
+ArgsManager& EnsureAnyArgsman(const std::any& context);
ChainstateManager& EnsureChainman(const NodeContext& node);
ChainstateManager& EnsureAnyChainman(const std::any& context);
CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node);
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index fd365e703f..752bd0af9e 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -22,59 +22,6 @@
using namespace std::literals;
-class AddrManTest : public AddrMan
-{
-public:
- explicit AddrManTest(std::vector<bool> asmap = std::vector<bool>())
- : AddrMan(asmap, /*deterministic=*/true, /*consistency_check_ratio=*/100)
- {}
-
- AddrInfo* Find(const CService& addr)
- {
- LOCK(m_impl->cs);
- return m_impl->Find(addr);
- }
-
- AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
- {
- LOCK(m_impl->cs);
- return m_impl->Create(addr, addrSource, pnId);
- }
-
- void Delete(int nId)
- {
- LOCK(m_impl->cs);
- m_impl->Delete(nId);
- }
-
- // Used to test deserialization
- std::pair<int, int> GetBucketAndEntry(const CAddress& addr)
- {
- LOCK(m_impl->cs);
- int nId = m_impl->mapAddr[addr];
- for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
- for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) {
- if (nId == m_impl->vvNew[bucket][entry]) {
- return std::pair<int, int>(bucket, entry);
- }
- }
- }
- return std::pair<int, int>(-1, -1);
- }
-
- // Simulates connection failure so that we can test eviction of offline nodes
- void SimConnFail(const CService& addr)
- {
- int64_t nLastSuccess = 1;
- // Set last good connection in the deep past.
- Good(addr, nLastSuccess);
-
- bool count_failure = false;
- int64_t nLastTry = GetAdjustedTime() - 61;
- Attempt(addr, count_failure, nLastTry);
- }
-};
-
static CNetAddr ResolveIP(const std::string& ip)
{
CNetAddr addr;
@@ -102,12 +49,17 @@ static std::vector<bool> FromBytes(const unsigned char* source, int vector_size)
return result;
}
+/* Utility function to create a deterministic addrman, as used in most tests */
+static std::unique_ptr<AddrMan> TestAddrMan(std::vector<bool> asmap = std::vector<bool>())
+{
+ return std::make_unique<AddrMan>(asmap, /*deterministic=*/true, /*consistency_check_ratio=*/100);
+}
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple)
{
- auto addrman = std::make_unique<AddrManTest>();
+ auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2");
@@ -141,7 +93,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_CHECK(addrman->size() >= 1);
// Test: reset addrman and test AddrMan::Add multiple addresses works as expected
- addrman = std::make_unique<AddrManTest>();
+ addrman = TestAddrMan();
std::vector<CAddress> vAddr;
vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
@@ -151,58 +103,58 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_AUTO_TEST_CASE(addrman_ports)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2");
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
+ BOOST_CHECK_EQUAL(addrman->size(), 0U);
// Test 7; Addr with same IP but diff port does not replace existing addr.
CService addr1 = ResolveService("250.1.1.1", 8333);
- BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334);
- BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), 2U);
- auto addr_ret2 = addrman.Select().first;
+ BOOST_CHECK(addrman->Add({CAddress(addr1_port, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), 2U);
+ auto addr_ret2 = addrman->Select().first;
BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
// Test: Add same IP but diff port to tried table; this converts the entry with
// the specified port to tried, but not the other.
- addrman.Good(CAddress(addr1_port, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.size(), 2U);
+ addrman->Good(CAddress(addr1_port, NODE_NONE));
+ BOOST_CHECK_EQUAL(addrman->size(), 2U);
bool newOnly = true;
- auto addr_ret3 = addrman.Select(newOnly).first;
+ auto addr_ret3 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_select)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2");
// Test: Select from new with 1 addr in new.
CService addr1 = ResolveService("250.1.1.1", 8333);
- BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
bool newOnly = true;
- auto addr_ret1 = addrman.Select(newOnly).first;
+ auto addr_ret1 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
// Test: move addr to tried, select from new expected nothing returned.
- BOOST_CHECK(addrman.Good(CAddress(addr1, NODE_NONE)));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
- auto addr_ret2 = addrman.Select(newOnly).first;
+ BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
+ auto addr_ret2 = addrman->Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0");
- auto addr_ret3 = addrman.Select().first;
+ auto addr_ret3 = addrman->Select().first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
// Add three addresses to new table.
@@ -210,65 +162,97 @@ BOOST_AUTO_TEST_CASE(addrman_select)
CService addr3 = ResolveService("250.3.2.2", 9999);
CService addr4 = ResolveService("250.3.3.3", 9999);
- BOOST_CHECK(addrman.Add({CAddress(addr2, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
- BOOST_CHECK(addrman.Add({CAddress(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
- BOOST_CHECK(addrman.Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr3, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Add({CAddress(addr4, NODE_NONE)}, ResolveService("250.4.1.1", 8333)));
// Add three addresses to tried table.
CService addr5 = ResolveService("250.4.4.4", 8333);
CService addr6 = ResolveService("250.4.5.5", 7777);
CService addr7 = ResolveService("250.4.6.6", 8333);
- BOOST_CHECK(addrman.Add({CAddress(addr5, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
- BOOST_CHECK(addrman.Good(CAddress(addr5, NODE_NONE)));
- BOOST_CHECK(addrman.Add({CAddress(addr6, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
- BOOST_CHECK(addrman.Good(CAddress(addr6, NODE_NONE)));
- BOOST_CHECK(addrman.Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333)));
- BOOST_CHECK(addrman.Good(CAddress(addr7, NODE_NONE)));
+ BOOST_CHECK(addrman->Add({CAddress(addr5, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Good(CAddress(addr5, NODE_NONE)));
+ BOOST_CHECK(addrman->Add({CAddress(addr6, NODE_NONE)}, ResolveService("250.3.1.1", 8333)));
+ BOOST_CHECK(addrman->Good(CAddress(addr6, NODE_NONE)));
+ BOOST_CHECK(addrman->Add({CAddress(addr7, NODE_NONE)}, ResolveService("250.1.1.3", 8333)));
+ BOOST_CHECK(addrman->Good(CAddress(addr7, NODE_NONE)));
// Test: 6 addrs + 1 addr from last test = 7.
- BOOST_CHECK_EQUAL(addrman.size(), 7U);
+ BOOST_CHECK_EQUAL(addrman->size(), 7U);
// Test: Select pulls from new and tried regardless of port number.
std::set<uint16_t> ports;
for (int i = 0; i < 20; ++i) {
- ports.insert(addrman.Select().first.GetPort());
+ ports.insert(addrman->Select().first.GetPort());
}
BOOST_CHECK_EQUAL(ports.size(), 3U);
}
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2");
uint32_t num_addrs{0};
- BOOST_CHECK_EQUAL(addrman.size(), num_addrs);
+ BOOST_CHECK_EQUAL(addrman->size(), num_addrs);
while (num_addrs < 22) { // Magic number! 250.1.1.1 - 250.1.1.22 do not collide with deterministic key = 1
CService addr = ResolveService("250.1.1." + ToString(++num_addrs));
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// Test: No collision in new table yet.
- BOOST_CHECK_EQUAL(addrman.size(), num_addrs);
+ BOOST_CHECK_EQUAL(addrman->size(), num_addrs);
}
// Test: new table collision!
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
uint32_t collisions{1};
- BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions);
+ BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), num_addrs - collisions);
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
- BOOST_CHECK(addrman.Add({CAddress(addr2, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions);
+ BOOST_CHECK(addrman->Add({CAddress(addr2, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), num_addrs - collisions);
+}
+
+BOOST_AUTO_TEST_CASE(addrman_new_multiplicity)
+{
+ auto addrman = TestAddrMan();
+ CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)};
+ int64_t start_time{GetAdjustedTime()};
+ addr.nTime = start_time;
+
+ // test that multiplicity stays at 1 if nTime doesn't increase
+ for (unsigned int i = 1; i < 20; ++i) {
+ std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
+ CNetAddr source{ResolveIP(addr_ip)};
+ addrman->Add({addr}, source);
+ }
+ AddressPosition addr_pos = addrman->FindAddressEntry(addr).value();
+ BOOST_CHECK_EQUAL(addr_pos.multiplicity, 1U);
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
+
+ // if nTime increases, an addr can occur in up to 8 buckets
+ // The acceptance probability decreases exponentially with existing multiplicity -
+ // choose number of iterations such that it gets to 8 with deterministic addrman.
+ for (unsigned int i = 1; i < 400; ++i) {
+ std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"};
+ CNetAddr source{ResolveIP(addr_ip)};
+ addr.nTime = start_time + i;
+ addrman->Add({addr}, source);
+ }
+ AddressPosition addr_pos_multi = addrman->FindAddressEntry(addr).value();
+ BOOST_CHECK_EQUAL(addr_pos_multi.multiplicity, 8U);
+ // multiplicity doesn't affect size
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
}
BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
{
- auto addrman = std::make_unique<AddrMan>(std::vector<bool>(), /*deterministic=*/true, /*consistency_check_ratio=*/100);
+ auto addrman = TestAddrMan();
CNetAddr source = ResolveIP("252.2.2.2");
@@ -296,87 +280,15 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
}
-BOOST_AUTO_TEST_CASE(addrman_find)
-{
- AddrManTest addrman;
-
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
-
- CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
- CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
- CAddress addr3 = CAddress(ResolveService("251.255.2.1", 8333), NODE_NONE);
-
- CNetAddr source1 = ResolveIP("250.1.2.1");
- CNetAddr source2 = ResolveIP("250.1.2.2");
-
- BOOST_CHECK(addrman.Add({addr1}, source1));
- BOOST_CHECK(addrman.Add({addr2}, source2));
- BOOST_CHECK(addrman.Add({addr3}, source1));
-
- // Test: ensure Find returns an IP/port matching what we searched on.
- AddrInfo* info1 = addrman.Find(addr1);
- BOOST_REQUIRE(info1);
- BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
-
- // Test; Find discriminates by port number.
- AddrInfo* info2 = addrman.Find(addr2);
- BOOST_REQUIRE(info2);
- BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:9999");
-
- // Test: Find returns another IP matching what we searched on.
- AddrInfo* info3 = addrman.Find(addr3);
- BOOST_REQUIRE(info3);
- BOOST_CHECK_EQUAL(info3->ToString(), "251.255.2.1:8333");
-}
-
-BOOST_AUTO_TEST_CASE(addrman_create)
-{
- AddrManTest addrman;
-
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
-
- CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
- CNetAddr source1 = ResolveIP("250.1.2.1");
-
- int nId;
- AddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
-
- // Test: The result should be the same as the input addr.
- BOOST_CHECK_EQUAL(pinfo->ToString(), "250.1.2.1:8333");
-
- AddrInfo* info2 = addrman.Find(addr1);
- BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:8333");
-}
-
-
-BOOST_AUTO_TEST_CASE(addrman_delete)
-{
- AddrManTest addrman;
-
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
-
- CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
- CNetAddr source1 = ResolveIP("250.1.2.1");
-
- int nId;
- addrman.Create(addr1, source1, &nId);
-
- // Test: Delete should actually delete the addr.
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
- addrman.Delete(nId);
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
- AddrInfo* info2 = addrman.Find(addr1);
- BOOST_CHECK(info2 == nullptr);
-}
BOOST_AUTO_TEST_CASE(addrman_getaddr)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
// Test: Sanity check, GetAddr should never return anything if addrman
// is empty.
- BOOST_CHECK_EQUAL(addrman.size(), 0U);
- std::vector<CAddress> vAddr1 = addrman.GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
+ BOOST_CHECK_EQUAL(addrman->size(), 0U);
+ std::vector<CAddress> vAddr1 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
@@ -393,18 +305,18 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
CNetAddr source2 = ResolveIP("250.2.3.3");
// Test: Ensure GetAddr works with new addresses.
- BOOST_CHECK(addrman.Add({addr1, addr3, addr5}, source1));
- BOOST_CHECK(addrman.Add({addr2, addr4}, source2));
+ BOOST_CHECK(addrman->Add({addr1, addr3, addr5}, source1));
+ BOOST_CHECK(addrman->Add({addr2, addr4}, source2));
- BOOST_CHECK_EQUAL(addrman.GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
+ BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
// Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down.
- BOOST_CHECK_EQUAL(addrman.GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
+ BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
// Test: Ensure GetAddr works with new and tried addresses.
- addrman.Good(CAddress(addr1, NODE_NONE));
- addrman.Good(CAddress(addr2, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
- BOOST_CHECK_EQUAL(addrman.GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
+ BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
+ BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));
+ BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 5U);
+ BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
// Test: Ensure GetAddr still returns 23% when addrman has many addrs.
for (unsigned int i = 1; i < (8 * 256); i++) {
@@ -415,24 +327,22 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
// Ensure that for all addrs in addrman, isTerrible == false.
addr.nTime = GetAdjustedTime();
- addrman.Add({addr}, ResolveIP(strAddr));
+ addrman->Add({addr}, ResolveIP(strAddr));
if (i % 8 == 0)
- addrman.Good(addr);
+ addrman->Good(addr);
}
- std::vector<CAddress> vAddr = addrman.GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
+ std::vector<CAddress> vAddr = addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
- size_t percent23 = (addrman.size() * 23) / 100;
+ size_t percent23 = (addrman->size() * 23) / 100;
BOOST_CHECK_EQUAL(vAddr.size(), percent23);
BOOST_CHECK_EQUAL(vAddr.size(), 461U);
// (Addrman.size() < number of addresses added) due to address collisions.
- BOOST_CHECK_EQUAL(addrman.size(), 2006U);
+ BOOST_CHECK_EQUAL(addrman->size(), 2006U);
}
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
{
- AddrManTest addrman;
-
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
@@ -486,8 +396,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
{
- AddrManTest addrman;
-
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
@@ -564,8 +472,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
// 101.8.0.0/16 AS8
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
{
- AddrManTest addrman;
-
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
@@ -619,8 +525,6 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
{
- AddrManTest addrman;
-
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
@@ -700,72 +604,69 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
{
std::vector<bool> asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
- auto addrman_asmap1 = std::make_unique<AddrManTest>(asmap1);
- auto addrman_asmap1_dup = std::make_unique<AddrManTest>(asmap1);
- auto addrman_noasmap = std::make_unique<AddrManTest>();
+ auto addrman_asmap1 = TestAddrMan(asmap1);
+ auto addrman_asmap1_dup = TestAddrMan(asmap1);
+ auto addrman_noasmap = TestAddrMan();
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CNetAddr default_source;
-
addrman_asmap1->Add({addr}, default_source);
stream << *addrman_asmap1;
// serizalizing/deserializing addrman with the same asmap
stream >> *addrman_asmap1_dup;
- std::pair<int, int> bucketAndEntry_asmap1 = addrman_asmap1->GetBucketAndEntry(addr);
- std::pair<int, int> bucketAndEntry_asmap1_dup = addrman_asmap1_dup->GetBucketAndEntry(addr);
- BOOST_CHECK(bucketAndEntry_asmap1.second != -1);
- BOOST_CHECK(bucketAndEntry_asmap1_dup.second != -1);
+ AddressPosition addr_pos1 = addrman_asmap1->FindAddressEntry(addr).value();
+ AddressPosition addr_pos2 = addrman_asmap1_dup->FindAddressEntry(addr).value();
+ BOOST_CHECK(addr_pos1.multiplicity != 0);
+ BOOST_CHECK(addr_pos2.multiplicity != 0);
- BOOST_CHECK(bucketAndEntry_asmap1.first == bucketAndEntry_asmap1_dup.first);
- BOOST_CHECK(bucketAndEntry_asmap1.second == bucketAndEntry_asmap1_dup.second);
+ BOOST_CHECK(addr_pos1 == addr_pos2);
// deserializing asmaped peers.dat to non-asmaped addrman
stream << *addrman_asmap1;
stream >> *addrman_noasmap;
- std::pair<int, int> bucketAndEntry_noasmap = addrman_noasmap->GetBucketAndEntry(addr);
- BOOST_CHECK(bucketAndEntry_noasmap.second != -1);
- BOOST_CHECK(bucketAndEntry_asmap1.first != bucketAndEntry_noasmap.first);
- BOOST_CHECK(bucketAndEntry_asmap1.second != bucketAndEntry_noasmap.second);
+ AddressPosition addr_pos3 = addrman_noasmap->FindAddressEntry(addr).value();
+ BOOST_CHECK(addr_pos3.multiplicity != 0);
+ BOOST_CHECK(addr_pos1.bucket != addr_pos3.bucket);
+ BOOST_CHECK(addr_pos1.position != addr_pos3.position);
// deserializing non-asmaped peers.dat to asmaped addrman
- addrman_asmap1 = std::make_unique<AddrManTest>(asmap1);
- addrman_noasmap = std::make_unique<AddrManTest>();
+ addrman_asmap1 = TestAddrMan(asmap1);
+ addrman_noasmap = TestAddrMan();
addrman_noasmap->Add({addr}, default_source);
stream << *addrman_noasmap;
stream >> *addrman_asmap1;
- std::pair<int, int> bucketAndEntry_asmap1_deser = addrman_asmap1->GetBucketAndEntry(addr);
- BOOST_CHECK(bucketAndEntry_asmap1_deser.second != -1);
- BOOST_CHECK(bucketAndEntry_asmap1_deser.first != bucketAndEntry_noasmap.first);
- BOOST_CHECK(bucketAndEntry_asmap1_deser.first == bucketAndEntry_asmap1_dup.first);
- BOOST_CHECK(bucketAndEntry_asmap1_deser.second == bucketAndEntry_asmap1_dup.second);
+
+ AddressPosition addr_pos4 = addrman_asmap1->FindAddressEntry(addr).value();
+ BOOST_CHECK(addr_pos4.multiplicity != 0);
+ BOOST_CHECK(addr_pos4.bucket != addr_pos3.bucket);
+ BOOST_CHECK(addr_pos4 == addr_pos2);
// used to map to different buckets, now maps to the same bucket.
- addrman_asmap1 = std::make_unique<AddrManTest>(asmap1);
- addrman_noasmap = std::make_unique<AddrManTest>();
+ addrman_asmap1 = TestAddrMan(asmap1);
+ addrman_noasmap = TestAddrMan();
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
addrman_noasmap->Add({addr, addr2}, default_source);
- std::pair<int, int> bucketAndEntry_noasmap_addr1 = addrman_noasmap->GetBucketAndEntry(addr1);
- std::pair<int, int> bucketAndEntry_noasmap_addr2 = addrman_noasmap->GetBucketAndEntry(addr2);
- BOOST_CHECK(bucketAndEntry_noasmap_addr1.first != bucketAndEntry_noasmap_addr2.first);
- BOOST_CHECK(bucketAndEntry_noasmap_addr1.second != bucketAndEntry_noasmap_addr2.second);
+ AddressPosition addr_pos5 = addrman_noasmap->FindAddressEntry(addr1).value();
+ AddressPosition addr_pos6 = addrman_noasmap->FindAddressEntry(addr2).value();
+ BOOST_CHECK(addr_pos5.bucket != addr_pos6.bucket);
stream << *addrman_noasmap;
stream >> *addrman_asmap1;
- std::pair<int, int> bucketAndEntry_asmap1_deser_addr1 = addrman_asmap1->GetBucketAndEntry(addr1);
- std::pair<int, int> bucketAndEntry_asmap1_deser_addr2 = addrman_asmap1->GetBucketAndEntry(addr2);
- BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.first == bucketAndEntry_asmap1_deser_addr2.first);
- BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.second != bucketAndEntry_asmap1_deser_addr2.second);
+ AddressPosition addr_pos7 = addrman_asmap1->FindAddressEntry(addr1).value();
+ AddressPosition addr_pos8 = addrman_asmap1->FindAddressEntry(addr2).value();
+ BOOST_CHECK(addr_pos7.bucket == addr_pos8.bucket);
+ BOOST_CHECK(addr_pos7.position != addr_pos8.position);
}
BOOST_AUTO_TEST_CASE(remove_invalid)
{
// Confirm that invalid addresses are ignored in unserialization.
- auto addrman = std::make_unique<AddrManTest>();
+ auto addrman = TestAddrMan();
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
@@ -797,29 +698,29 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
- addrman = std::make_unique<AddrManTest>();
+ addrman = TestAddrMan();
stream >> *addrman;
BOOST_CHECK_EQUAL(addrman->size(), 2);
}
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK(addrman->size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Add twenty two addresses.
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 23; i++) {
CService addr = ResolveService("250.1.1." + ToString(i));
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collisions in tried.
- BOOST_CHECK(addrman.Good(addr));
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->Good(addr));
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
}
// Ensure Good handles duplicates well.
@@ -828,114 +729,125 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
CService addr = ResolveService("250.1.1." + ToString(i));
// Unable to add duplicate address to tried table.
- BOOST_CHECK(!addrman.Good(addr));
+ BOOST_CHECK(!addrman->Good(addr));
// Verify duplicate address not marked as a collision.
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
}
}
BOOST_AUTO_TEST_CASE(addrman_noevict)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
// Add 35 addresses.
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 36; i++) {
CService addr = ResolveService("250.1.1." + ToString(i));
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collision yet.
- BOOST_CHECK(addrman.Good(addr));
+ BOOST_CHECK(addrman->Good(addr));
}
// Collision in tried table between 36 and 19.
CService addr36 = ResolveService("250.1.1.36");
- BOOST_CHECK(addrman.Add({CAddress(addr36, NODE_NONE)}, source));
- BOOST_CHECK(!addrman.Good(addr36));
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.19:0");
+ BOOST_CHECK(addrman->Add({CAddress(addr36, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman->Good(addr36));
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.19:0");
// 36 should be discarded and 19 not evicted.
// This means we keep 19 in the tried table and
// 36 stays in the new table.
- addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ addrman->ResolveCollisions();
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Lets create two collisions.
for (unsigned int i = 37; i < 59; i++) {
CService addr = ResolveService("250.1.1." + ToString(i));
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
- BOOST_CHECK(addrman.Good(addr));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(addrman->Good(addr));
}
// Cause a collision in the tried table.
CService addr59 = ResolveService("250.1.1.59");
- BOOST_CHECK(addrman.Add({CAddress(addr59, NODE_NONE)}, source));
- BOOST_CHECK(!addrman.Good(addr59));
+ BOOST_CHECK(addrman->Add({CAddress(addr59, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman->Good(addr59));
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.10:0");
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.10:0");
// Cause a second collision in the new table.
- BOOST_CHECK(!addrman.Add({CAddress(addr36, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman->Add({CAddress(addr36, NODE_NONE)}, source));
// 36 still cannot be moved from new to tried due to colliding with 19
- BOOST_CHECK(!addrman.Good(addr36));
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() != "[::]:0");
+ BOOST_CHECK(!addrman->Good(addr36));
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() != "[::]:0");
// Resolve all collisions.
- addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ addrman->ResolveCollisions();
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
}
BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{
- AddrManTest addrman;
+ auto addrman = TestAddrMan();
- BOOST_CHECK(addrman.size() == 0);
+ BOOST_CHECK(addrman->size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// Add 35 addresses
CNetAddr source = ResolveIP("252.2.2.2");
for (unsigned int i = 1; i < 36; i++) {
CService addr = ResolveService("250.1.1." + ToString(i));
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
// No collision yet.
- BOOST_CHECK(addrman.Good(addr));
+ BOOST_CHECK(addrman->Good(addr));
}
// Collision between 36 and 19.
CService addr = ResolveService("250.1.1.36");
- BOOST_CHECK(addrman.Add({CAddress(addr, NODE_NONE)}, source));
- BOOST_CHECK(!addrman.Good(addr));
+ BOOST_CHECK(addrman->Add({CAddress(addr, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman->Good(addr));
- auto info = addrman.SelectTriedCollision().first;
+ auto info = addrman->SelectTriedCollision().first;
BOOST_CHECK_EQUAL(info.ToString(), "250.1.1.19:0");
// Ensure test of address fails, so that it is evicted.
- addrman.SimConnFail(info);
+ // Update entry in tried by setting last good connection in the deep past.
+ BOOST_CHECK(!addrman->Good(info, /*nTime=*/1));
+ addrman->Attempt(info, /*fCountFailure=*/false, /*nTime=*/GetAdjustedTime() - 61);
// Should swap 36 for 19.
- addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ addrman->ResolveCollisions();
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ AddressPosition addr_pos{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
+ BOOST_CHECK(addr_pos.tried);
// If 36 was swapped for 19, then adding 36 to tried should fail because we
// are attempting to add a duplicate.
// We check this by verifying Good() returns false and also verifying that
// we have no collisions.
- BOOST_CHECK(!addrman.Good(addr));
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(!addrman->Good(addr));
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
// 19 should fail as a collision (not a duplicate) if we now attempt to move
// it to the tried table.
CService addr19 = ResolveService("250.1.1.19");
- BOOST_CHECK(!addrman.Good(addr19));
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.36:0");
-
- addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
+ BOOST_CHECK(!addrman->Good(addr19));
+ BOOST_CHECK_EQUAL(addrman->SelectTriedCollision().first.ToString(), "250.1.1.36:0");
+
+ // Eviction is also successful if too much time has passed since last try
+ SetMockTime(GetTime() + 4 * 60 *60);
+ addrman->ResolveCollisions();
+ BOOST_CHECK(addrman->SelectTriedCollision().first.ToString() == "[::]:0");
+ //Now 19 is in tried again, and 36 back to new
+ AddressPosition addr_pos19{addrman->FindAddressEntry(CAddress(addr19, NODE_NONE)).value()};
+ BOOST_CHECK(addr_pos19.tried);
+ AddressPosition addr_pos36{addrman->FindAddressEntry(CAddress(addr, NODE_NONE)).value()};
+ BOOST_CHECK(!addr_pos36.tried);
}
static CDataStream AddrmanToStream(const AddrMan& addrman)
@@ -1044,5 +956,35 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
+BOOST_AUTO_TEST_CASE(addrman_update_address)
+{
+ // Tests updating nTime via Connected() and nServices via SetServices()
+ auto addrman = TestAddrMan();
+ CNetAddr source{ResolveIP("252.2.2.2")};
+ CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)};
+
+ int64_t start_time{GetAdjustedTime() - 10000};
+ addr.nTime = start_time;
+ BOOST_CHECK(addrman->Add({addr}, source));
+ BOOST_CHECK_EQUAL(addrman->size(), 1U);
+
+ // Updating an addrman entry with a different port doesn't change it
+ CAddress addr_diff_port{CAddress(ResolveService("250.1.1.1", 8334), NODE_NONE)};
+ addr_diff_port.nTime = start_time;
+ addrman->Connected(addr_diff_port);
+ addrman->SetServices(addr_diff_port, NODE_NETWORK_LIMITED);
+ std::vector<CAddress> vAddr1{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)};
+ BOOST_CHECK_EQUAL(vAddr1.size(), 1U);
+ BOOST_CHECK_EQUAL(vAddr1.at(0).nTime, start_time);
+ BOOST_CHECK_EQUAL(vAddr1.at(0).nServices, NODE_NONE);
+
+ // Updating an addrman entry with the correct port is successful
+ addrman->Connected(addr);
+ addrman->SetServices(addr, NODE_NETWORK_LIMITED);
+ std::vector<CAddress> vAddr2 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt);
+ BOOST_CHECK_EQUAL(vAddr2.size(), 1U);
+ BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000);
+ BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/addition_overflow.cpp b/src/test/fuzz/addition_overflow.cpp
index c6cfbd8d30..cfad41659e 100644
--- a/src/test/fuzz/addition_overflow.cpp
+++ b/src/test/fuzz/addition_overflow.cpp
@@ -5,6 +5,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/overflow.h>
#include <cstdint>
#include <string>
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
index 5e60b0f25b..596614a71b 100644
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/overflow.h>
#include <cassert>
#include <cstdint>
diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp
index d6879067b8..2d0b29953c 100644
--- a/src/test/fuzz/golomb_rice.cpp
+++ b/src/test/fuzz/golomb_rice.cpp
@@ -19,20 +19,6 @@
#include <vector>
namespace {
-uint64_t MapIntoRange(const uint64_t x, const uint64_t n)
-{
- const uint64_t x_hi = x >> 32;
- const uint64_t x_lo = x & 0xFFFFFFFF;
- const uint64_t n_hi = n >> 32;
- const uint64_t n_lo = n & 0xFFFFFFFF;
- const uint64_t ac = x_hi * n_hi;
- const uint64_t ad = x_hi * n_lo;
- const uint64_t bc = x_lo * n_hi;
- const uint64_t bd = x_lo * n_lo;
- const uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
- const uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
- return upper64;
-}
uint64_t HashToRange(const std::vector<uint8_t>& element, const uint64_t f)
{
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 33b0d7325b..3087f11771 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -26,6 +26,7 @@
#include <univalue.h>
#include <util/check.h>
#include <util/moneystr.h>
+#include <util/overflow.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index 1123c8c170..0004d82d66 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -9,6 +9,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/overflow.h>
#include <cstdint>
#include <optional>
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 843b29b911..f89b597eed 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -8,10 +8,13 @@
#include <pubkey.h>
#include <test/fuzz/util.h>
#include <test/util/script.h>
+#include <util/overflow.h>
#include <util/rbf.h>
#include <util/time.h>
#include <version.h>
+#include <memory>
+
FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
: m_fuzzed_data_provider{fuzzed_data_provider}
{
@@ -157,6 +160,20 @@ int FuzzedSock::Connect(const sockaddr*, socklen_t) const
return 0;
}
+std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) const
+{
+ constexpr std::array accept_errnos{
+ ECONNABORTED,
+ EINTR,
+ ENOMEM,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos);
+ return std::unique_ptr<FuzzedSock>();
+ }
+ return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
+}
+
int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
constexpr std::array getsockopt_errnos{
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 7937315822..fd7f40c01d 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -193,17 +193,6 @@ template <typename T>
}
}
-template <class T>
-[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept
-{
- static_assert(std::is_integral<T>::value, "Integral required.");
- if (std::numeric_limits<T>::is_signed) {
- return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
- (i < 0 && j < std::numeric_limits<T>::min() - i);
- }
- return std::numeric_limits<T>::max() - i < j;
-}
-
[[nodiscard]] bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept;
/**
@@ -412,6 +401,8 @@ public:
int Connect(const sockaddr*, socklen_t) const override;
+ std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override;
+
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 006e876c1a..20c45058a1 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -13,6 +13,7 @@
#include <array>
#include <cassert>
#include <cstring>
+#include <memory>
#include <string>
struct ConnmanTestMsg : public CConnman {
@@ -126,6 +127,23 @@ public:
int Connect(const sockaddr*, socklen_t) const override { return 0; }
+ std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override
+ {
+ if (addr != nullptr) {
+ // Pretend all connections come from 5.5.5.5:6789
+ memset(addr, 0x00, *addr_len);
+ const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in));
+ if (*addr_len >= write_len) {
+ *addr_len = write_len;
+ sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr);
+ addr_in->sin_family = AF_INET;
+ memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr));
+ addr_in->sin_port = htons(6789);
+ }
+ }
+ return std::make_unique<StaticContentsSock>("");
+ };
+
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
{
std::memset(opt_val, 0x0, *opt_len);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 3a37aeb531..c48b2025ad 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -182,28 +182,28 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
- auto rv = LoadChainstate(fReindex.load(),
- *Assert(m_node.chainman.get()),
- Assert(m_node.mempool.get()),
- fPruneMode,
- chainparams.GetConsensus(),
- m_args.GetBoolArg("-reindex-chainstate", false),
- m_cache_sizes.block_tree_db,
- m_cache_sizes.coins_db,
- m_cache_sizes.coins,
- true,
- true);
- assert(!rv.has_value());
-
- auto maybe_verify_failure = VerifyLoadedChainstate(
+ auto maybe_load_error = LoadChainstate(fReindex.load(),
+ *Assert(m_node.chainman.get()),
+ Assert(m_node.mempool.get()),
+ fPruneMode,
+ chainparams.GetConsensus(),
+ m_args.GetBoolArg("-reindex-chainstate", false),
+ m_cache_sizes.block_tree_db,
+ m_cache_sizes.coins_db,
+ m_cache_sizes.coins,
+ /*block_tree_db_in_memory=*/true,
+ /*coins_db_in_memory=*/true);
+ assert(!maybe_load_error.has_value());
+
+ auto maybe_verify_error = VerifyLoadedChainstate(
*Assert(m_node.chainman),
fReindex.load(),
m_args.GetBoolArg("-reindex-chainstate", false),
chainparams.GetConsensus(),
m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS),
m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- static_cast<int64_t(*)()>(GetTime));
- assert(!maybe_verify_failure.has_value());
+ /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
+ assert(!maybe_verify_error.has_value());
BlockValidationState state;
if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) {
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 077d6e2d37..04c4524911 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -15,6 +15,7 @@
#include <util/getuniquepath.h>
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
+#include <util/overflow.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -1463,6 +1464,38 @@ BOOST_AUTO_TEST_CASE(test_IsDigit)
BOOST_CHECK_EQUAL(IsDigit(9), false);
}
+/* Check for overflow */
+template <typename T>
+static void TestAddMatrixOverflow()
+{
+ constexpr T MAXI{std::numeric_limits<T>::max()};
+ BOOST_CHECK(!CheckedAdd(T{1}, MAXI));
+ BOOST_CHECK(!CheckedAdd(MAXI, MAXI));
+ BOOST_CHECK_EQUAL(0, CheckedAdd(T{0}, T{0}).value());
+ BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{0}, MAXI).value());
+ BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{1}, MAXI - 1).value());
+}
+
+/* Check for overflow or underflow */
+template <typename T>
+static void TestAddMatrix()
+{
+ TestAddMatrixOverflow<T>();
+ constexpr T MINI{std::numeric_limits<T>::min()};
+ constexpr T MAXI{std::numeric_limits<T>::max()};
+ BOOST_CHECK(!CheckedAdd(T{-1}, MINI));
+ BOOST_CHECK(!CheckedAdd(MINI, MINI));
+ BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{0}, MINI).value());
+ BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{-1}, MINI + 1).value());
+ BOOST_CHECK_EQUAL(-1, CheckedAdd(MINI, MAXI).value());
+}
+
+BOOST_AUTO_TEST_CASE(util_overflow)
+{
+ TestAddMatrixOverflow<unsigned>();
+ TestAddMatrix<signed>();
+}
+
BOOST_AUTO_TEST_CASE(test_ParseInt32)
{
int32_t n;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 66beb0a9b3..dc2769b81e 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -64,16 +64,6 @@ private:
int64_t feeDelta;
};
-struct update_lock_points
-{
- explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
-
-private:
- const LockPoints& lp;
-};
-
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
{
AssertLockHeld(cs_main);
@@ -649,10 +639,7 @@ void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check
}
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
- const LockPoints lp{it->GetLockPoints()};
- if (!TestLockPointValidity(chain, lp)) {
- mapTx.modify(it, update_lock_points(lp));
- }
+ assert(TestLockPointValidity(chain, it->GetLockPoints()));
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index df578d5111..b8c508fd90 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -312,6 +312,16 @@ public:
}
};
+struct update_lock_points
+{
+ explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
+
+ void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
+
+private:
+ const LockPoints& lp;
+};
+
// Multi_index tag names
struct descendant_score {};
struct entry_time {};
diff --git a/src/util/golombrice.h b/src/util/golombrice.h
index 67d262406f..f14cb594db 100644
--- a/src/util/golombrice.h
+++ b/src/util/golombrice.h
@@ -40,4 +40,35 @@ uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P)
return (q << P) + r;
}
+// Map a value x that is uniformly distributed in the range [0, 2^64) to a
+// value uniformly distributed in [0, n) by returning the upper 64 bits of
+// x * n.
+//
+// See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
+static inline uint64_t MapIntoRange(uint64_t x, uint64_t n)
+{
+#ifdef __SIZEOF_INT128__
+ return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
+#else
+ // To perform the calculation on 64-bit numbers without losing the
+ // result to overflow, split the numbers into the most significant and
+ // least significant 32 bits and perform multiplication piece-wise.
+ //
+ // See: https://stackoverflow.com/a/26855440
+ const uint64_t x_hi = x >> 32;
+ const uint64_t x_lo = x & 0xFFFFFFFF;
+ const uint64_t n_hi = n >> 32;
+ const uint64_t n_lo = n & 0xFFFFFFFF;
+
+ const uint64_t ac = x_hi * n_hi;
+ const uint64_t ad = x_hi * n_lo;
+ const uint64_t bc = x_lo * n_hi;
+ const uint64_t bd = x_lo * n_lo;
+
+ const uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
+ const uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
+ return upper64;
+#endif
+}
+
#endif // BITCOIN_UTIL_GOLOMBRICE_H
diff --git a/src/util/overflow.h b/src/util/overflow.h
new file mode 100644
index 0000000000..5982af8d04
--- /dev/null
+++ b/src/util/overflow.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef BITCOIN_UTIL_OVERFLOW_H
+#define BITCOIN_UTIL_OVERFLOW_H
+
+#include <limits>
+#include <type_traits>
+
+template <class T>
+[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept
+{
+ static_assert(std::is_integral<T>::value, "Integral required.");
+ if (std::numeric_limits<T>::is_signed) {
+ return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
+ (i < 0 && j < std::numeric_limits<T>::min() - i);
+ }
+ return std::numeric_limits<T>::max() - i < j;
+}
+
+template <class T>
+[[nodiscard]] std::optional<T> CheckedAdd(const T i, const T j) noexcept
+{
+ if (AdditionOverflow(i, j)) {
+ return std::nullopt;
+ }
+ return i + j;
+}
+
+#endif // BITCOIN_UTIL_OVERFLOW_H
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 1a4d67a65e..2029d70a37 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -10,6 +10,7 @@
#include <util/system.h>
#include <util/time.h>
+#include <memory>
#include <stdexcept>
#include <string>
@@ -73,6 +74,32 @@ int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
return connect(m_socket, addr, addr_len);
}
+std::unique_ptr<Sock> Sock::Accept(sockaddr* addr, socklen_t* addr_len) const
+{
+#ifdef WIN32
+ static constexpr auto ERR = INVALID_SOCKET;
+#else
+ static constexpr auto ERR = SOCKET_ERROR;
+#endif
+
+ std::unique_ptr<Sock> sock;
+
+ const auto socket = accept(m_socket, addr, addr_len);
+ if (socket != ERR) {
+ try {
+ sock = std::make_unique<Sock>(socket);
+ } catch (const std::exception&) {
+#ifdef WIN32
+ closesocket(socket);
+#else
+ close(socket);
+#endif
+ }
+ }
+
+ return sock;
+}
+
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
diff --git a/src/util/sock.h b/src/util/sock.h
index 59cc8c0b1d..7510482857 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -10,6 +10,7 @@
#include <util/time.h>
#include <chrono>
+#include <memory>
#include <string>
/**
@@ -97,6 +98,14 @@ public:
[[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
/**
+ * accept(2) wrapper. Equivalent to `std::make_unique<Sock>(accept(this->Get(), addr, addr_len))`.
+ * Code that uses this wrapper can be unit tested if this method is overridden by a mock Sock
+ * implementation.
+ * The returned unique_ptr is empty if `accept()` failed in which case errno will be set.
+ */
+ [[nodiscard]] virtual std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const;
+
+ /**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
diff --git a/src/validation.cpp b/src/validation.cpp
index cb2b60b481..a98ffe006d 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -143,13 +143,6 @@ extern std::set<int> setDirtyFileInfo;
void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
// ... TODO move fully to blockstorage
-CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
-{
- AssertLockHeld(cs_main);
- BlockMap::const_iterator it = m_block_index.find(hash);
- return it == m_block_index.end() ? nullptr : it->second;
-}
-
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -375,6 +368,8 @@ void CChainState::MaybeUpdateMempoolForReorg(
}
}
}
+ // CheckSequenceLocks updates lp. Update the mempool entry LockPoints.
+ if (!validLP) m_mempool->mapTx.modify(it, update_lock_points(lp));
return should_remove;
};
@@ -3123,42 +3118,6 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
}
-CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
-{
- AssertLockHeld(cs_main);
-
- // Check for duplicate
- uint256 hash = block.GetHash();
- BlockMap::iterator it = m_block_index.find(hash);
- if (it != m_block_index.end())
- return it->second;
-
- // Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(block);
- // We assign the sequence id to blocks only when the full data is available,
- // to avoid miners withholding blocks but broadcasting headers, to get a
- // competitive advantage.
- pindexNew->nSequenceId = 0;
- BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
- BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
- if (miPrev != m_block_index.end())
- {
- pindexNew->pprev = (*miPrev).second;
- pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
- pindexNew->BuildSkip();
- }
- pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
- pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
- pindexNew->RaiseValidity(BLOCK_VALID_TREE);
- if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork)
- pindexBestHeader = pindexNew;
-
- setDirtyBlockIndex.insert(pindexNew);
-
- return pindexNew;
-}
-
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
{
@@ -3325,21 +3284,6 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
-{
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
- {
- const uint256& hash = i.second;
- CBlockIndex* pindex = LookupBlockIndex(hash);
- if (pindex) {
- return pindex;
- }
- }
- return nullptr;
-}
-
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3761,67 +3705,6 @@ bool TestBlockValidity(BlockValidationState& state,
return true;
}
-/**
- * BLOCK PRUNING CODE
- */
-
-void BlockManager::PruneOneBlockFile(const int fileNumber)
-{
- AssertLockHeld(cs_main);
- LOCK(cs_LastBlockFile);
-
- for (const auto& entry : m_block_index) {
- CBlockIndex* pindex = entry.second;
- if (pindex->nFile == fileNumber) {
- pindex->nStatus &= ~BLOCK_HAVE_DATA;
- pindex->nStatus &= ~BLOCK_HAVE_UNDO;
- pindex->nFile = 0;
- pindex->nDataPos = 0;
- pindex->nUndoPos = 0;
- setDirtyBlockIndex.insert(pindex);
-
- // Prune from m_blocks_unlinked -- any block we prune would have
- // to be downloaded again in order to consider its chain, at which
- // point it would be considered as a candidate for
- // m_blocks_unlinked or setBlockIndexCandidates.
- auto range = m_blocks_unlinked.equal_range(pindex->pprev);
- while (range.first != range.second) {
- std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
- range.first++;
- if (_it->second == pindex) {
- m_blocks_unlinked.erase(_it);
- }
- }
- }
- }
-
- vinfoBlockFile[fileNumber].SetNull();
- setDirtyFileInfo.insert(fileNumber);
-}
-
-void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
-{
- assert(fPruneMode && nManualPruneHeight > 0);
-
- LOCK2(cs_main, cs_LastBlockFile);
- if (chain_tip_height < 0) {
- return;
- }
-
- // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
- int count = 0;
- for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
- continue;
- }
- PruneOneBlockFile(fileNumber);
- setFilesToPrune.insert(fileNumber);
- count++;
- }
- LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
-}
-
/* This function is called from the RPC code for pruneblockchain */
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
{
@@ -3832,259 +3715,6 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
}
}
-void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
-{
- LOCK2(cs_main, cs_LastBlockFile);
- if (chain_tip_height < 0 || nPruneTarget == 0) {
- return;
- }
- if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
- return;
- }
-
- unsigned int nLastBlockWeCanPrune{(unsigned)std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP))};
- uint64_t nCurrentUsage = CalculateCurrentUsage();
- // We don't check to prune until after we've allocated new space for files
- // So we should leave a buffer under our target to account for another allocation
- // before the next pruning.
- uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
- uint64_t nBytesToPrune;
- int count = 0;
-
- if (nCurrentUsage + nBuffer >= nPruneTarget) {
- // On a prune event, the chainstate DB is flushed.
- // To avoid excessive prune events negating the benefit of high dbcache
- // values, we should not prune too rapidly.
- // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
- if (is_ibd) {
- // Since this is only relevant during IBD, we use a fixed 10%
- nBuffer += nPruneTarget / 10;
- }
-
- for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
-
- if (vinfoBlockFile[fileNumber].nSize == 0) {
- continue;
- }
-
- if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
- break;
- }
-
- // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
- continue;
- }
-
- PruneOneBlockFile(fileNumber);
- // Queue up the files for removal
- setFilesToPrune.insert(fileNumber);
- nCurrentUsage -= nBytesToPrune;
- count++;
- }
- }
-
- LogPrint(BCLog::PRUNE, "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);
-}
-
-CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
-{
- AssertLockHeld(cs_main);
-
- if (hash.IsNull())
- return nullptr;
-
- // Return existing
- BlockMap::iterator mi = m_block_index.find(hash);
- if (mi != m_block_index.end())
- return (*mi).second;
-
- // Create new
- CBlockIndex* pindexNew = new CBlockIndex();
- mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
-
- return pindexNew;
-}
-
-bool BlockManager::LoadBlockIndex(
- const Consensus::Params& consensus_params,
- ChainstateManager& chainman)
-{
- if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
- return false;
- }
-
- // Calculate nChainWork
- std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
- vSortedByHeight.reserve(m_block_index.size());
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index)
- {
- CBlockIndex* pindex = item.second;
- vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
- }
- sort(vSortedByHeight.begin(), vSortedByHeight.end());
-
- // Find start of assumed-valid region.
- int first_assumed_valid_height = std::numeric_limits<int>::max();
-
- for (const auto& [height, block] : vSortedByHeight) {
- if (block->IsAssumedValid()) {
- auto chainstates = chainman.GetAll();
-
- // If we encounter an assumed-valid block index entry, ensure that we have
- // one chainstate that tolerates assumed-valid entries and another that does
- // not (i.e. the background validation chainstate), since assumed-valid
- // entries should always be pending validation by a fully-validated chainstate.
- auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
- assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
- assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
-
- first_assumed_valid_height = height;
- break;
- }
- }
-
- for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
- {
- if (ShutdownRequested()) return false;
- CBlockIndex* pindex = item.second;
- pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
- pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
-
- // We can link the chain of blocks for which we've received transactions at some point, or
- // blocks that are assumed-valid on the basis of snapshot load (see
- // PopulateAndValidateSnapshot()).
- // Pruned nodes may have deleted the block.
- if (pindex->nTx > 0) {
- if (pindex->pprev) {
- if (pindex->pprev->nChainTx > 0) {
- pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
- } else {
- pindex->nChainTx = 0;
- m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
- }
- } else {
- pindex->nChainTx = pindex->nTx;
- }
- }
- if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
- pindex->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindex);
- }
- if (pindex->IsAssumedValid() ||
- (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
- (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
-
- // Fill each chainstate's block candidate set. Only add assumed-valid
- // blocks to the tip candidate set if the chainstate is allowed to rely on
- // assumed-valid blocks.
- //
- // If all setBlockIndexCandidates contained the assumed-valid blocks, the
- // background chainstate's ActivateBestChain() call would add assumed-valid
- // blocks to the chain (based on how FindMostWorkChain() works). Obviously
- // we don't want this since the purpose of the background validation chain
- // is to validate assued-valid blocks.
- //
- // Note: This is considering all blocks whose height is greater or equal to
- // the first assumed-valid block to be assumed-valid blocks, and excluding
- // them from the background chainstate's setBlockIndexCandidates set. This
- // does mean that some blocks which are not technically assumed-valid
- // (later blocks on a fork beginning before the first assumed-valid block)
- // might not get added to the the background chainstate, but this is ok,
- // because they will still be attached to the active chainstate if they
- // actually contain more work.
- //
- // Instad of this height-based approach, an earlier attempt was made at
- // detecting "holistically" whether the block index under consideration
- // relied on an assumed-valid ancestor, but this proved to be too slow to
- // be practical.
- for (CChainState* chainstate : chainman.GetAll()) {
- if (chainstate->reliesOnAssumedValid() ||
- pindex->nHeight < first_assumed_valid_height) {
- chainstate->setBlockIndexCandidates.insert(pindex);
- }
- }
- }
- if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
- chainman.m_best_invalid = pindex;
- }
- if (pindex->pprev)
- pindex->BuildSkip();
- if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
- pindexBestHeader = pindex;
- }
-
- return true;
-}
-
-void BlockManager::Unload() {
- m_blocks_unlinked.clear();
-
- for (const BlockMap::value_type& entry : m_block_index) {
- delete entry.second;
- }
-
- m_block_index.clear();
-}
-
-bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
-{
- if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
- return false;
- }
-
- // Load block file info
- m_block_tree_db->ReadLastBlockFile(nLastBlockFile);
- vinfoBlockFile.resize(nLastBlockFile + 1);
- LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
- for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
- m_block_tree_db->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
- }
- LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
- for (int nFile = nLastBlockFile + 1; true; nFile++) {
- CBlockFileInfo info;
- if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
- vinfoBlockFile.push_back(info);
- } else {
- break;
- }
- }
-
- // Check presence of blk files
- LogPrintf("Checking all blk files are present...\n");
- std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
- CBlockIndex* pindex = item.second;
- if (pindex->nStatus & BLOCK_HAVE_DATA) {
- setBlkDataFiles.insert(pindex->nFile);
- }
- }
- for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++)
- {
- FlatFilePos pos(*it, 0);
- if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
- return false;
- }
- }
-
- // Check whether we have ever pruned block & undo files
- m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
- if (fHavePruned)
- LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
-
- // Check whether we need to continue reindexing
- bool fReindexing = false;
- m_block_tree_db->ReadReindexing(fReindexing);
- if(fReindexing) fReindex = true;
-
- return true;
-}
-
void CChainState::LoadMempool(const ArgsManager& args)
{
if (!m_mempool) return;
diff --git a/src/validation.h b/src/validation.h
index 68649a3d23..16f4bfe741 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -15,6 +15,7 @@
#include <chain.h>
#include <consensus/amount.h>
#include <fs.h>
+#include <node/blockstorage.h>
#include <policy/feerate.h>
#include <policy/packages.h>
#include <script/script_error.h>
@@ -40,7 +41,6 @@
class CChainState;
class CBlockTreeDB;
class CChainParams;
-struct CCheckpointData;
class CTxMemPool;
class ChainstateManager;
class SnapshotMetadata;
@@ -107,7 +107,6 @@ enum class SynchronizationState {
};
extern RecursiveMutex cs_main;
-typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
/** Used to notify getblocktemplate RPC of new tips. */
@@ -381,85 +380,6 @@ enum class FlushStateMode {
ALWAYS
};
-struct CBlockIndexWorkComparator
-{
- bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const;
-};
-
-/**
- * Maintains a tree of blocks (stored in `m_block_index`) which is consulted
- * to determine where the most-work tip is.
- *
- * This data is used mostly in `CChainState` - information about, e.g.,
- * candidate tips is not maintained here.
- */
-class BlockManager
-{
- friend CChainState;
-
-private:
- /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
- void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
-
- /**
- * Prune block and undo files (blk???.dat and rev???.dat) so that the disk space used is less than a user-defined target.
- * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
- * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
- * (which in this case means the blockchain must be re-downloaded.)
- *
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
- * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
- * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
- * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
- * A db flag records the fact that at least some block files have been pruned.
- *
- * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
- */
- void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
-
-public:
- BlockMap m_block_index GUARDED_BY(cs_main);
-
- /**
- * All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
- * Pruned nodes may have entries where B is missing data.
- */
- std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
-
- std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
-
- bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-
- /**
- * Load the blocktree off disk and into memory. Populate certain metadata
- * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
- * collections like setDirtyBlockIndex.
- */
- bool LoadBlockIndex(
- const Consensus::Params& consensus_params,
- ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- /** Clear all data members. */
- void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Create a new block index entry for a given block hash */
- CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- //! Mark one block file as pruned (modify associated database entries)
- void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- //! Returns last CBlockIndex* that is a checkpoint
- CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- ~BlockManager() {
- Unload();
- }
-};
-
/**
* A convenience class for constructing the CCoinsView* hierarchy used
* to facilitate access to the UTXO set.
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 645576ef91..9a4c201c4a 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -214,6 +214,11 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
if (key == "checksum") {
std::vector<unsigned char> parsed_checksum = ParseHex(value);
+ if (parsed_checksum.size() != checksum.size()) {
+ error = Untranslated("Error: Checksum is not the correct size");
+ ret = false;
+ break;
+ }
std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
break;
}