aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp507
1 files changed, 262 insertions, 245 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 3e9ba08bb1..1953a42df1 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1,9 +1,9 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2022 The Bitcoin Core developers
+// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <config/bitcoin-config.h> // IWYU pragma: keep
+#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <validation.h>
@@ -35,12 +35,11 @@
#include <policy/policy.h>
#include <policy/rbf.h>
#include <policy/settings.h>
-#include <policy/v3_policy.h>
+#include <policy/truc_policy.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
-#include <reverse_iterator.h>
#include <script/script.h>
#include <script/sigcache.h>
#include <signet.h>
@@ -70,6 +69,8 @@
#include <deque>
#include <numeric>
#include <optional>
+#include <ranges>
+#include <span>
#include <string>
#include <tuple>
#include <utility>
@@ -107,10 +108,6 @@ const std::vector<std::string> CHECKLEVEL_DOC {
* */
static constexpr int PRUNE_LOCK_BUFFER{10};
-GlobalMutex g_best_block_mutex;
-std::condition_variable g_best_block_cv;
-uint256 g_best_block;
-
const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -134,6 +131,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -268,7 +266,7 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
AssertLockHeld(pool.cs);
int expired = pool.Expire(GetTime<std::chrono::seconds>() - pool.m_opts.expiry);
if (expired != 0) {
- LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
+ LogDebug(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
}
std::vector<COutPoint> vNoSpendsRemaining;
@@ -336,7 +334,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
// Also updates valid entries' cached LockPoints if needed.
// If false, the tx is still valid and its lockpoints are updated.
// If true, the tx would be invalid in the next block; remove this entry and all of its descendants.
- // Note that v3 rules are not applied here, so reorgs may cause violations of v3 inheritance or
+ // Note that TRUC rules are not applied here, so reorgs may cause violations of TRUC inheritance or
// topology restrictions.
const auto filter_final_and_mature = [&](CTxMemPool::txiter it)
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
@@ -394,7 +392,8 @@ void Chainstate::MaybeUpdateMempoolForReorg(
* */
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& view, const CTxMemPool& pool,
- unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
+ unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
+ ValidationCache& validation_cache)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{
AssertLockHeld(cs_main);
@@ -426,7 +425,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
}
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
- return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
+ return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata, validation_cache);
}
namespace {
@@ -716,6 +715,11 @@ private:
return true;
}
+ ValidationCache& GetValidationCache()
+ {
+ return m_active_chainstate.m_chainman.m_validation_cache;
+ }
+
private:
CTxMemPool& m_pool;
CCoinsViewCache m_view;
@@ -829,7 +833,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// check all unconfirmed ancestors; otherwise an opt-in ancestor
// might be replaced, causing removal of this descendant.
//
- // All V3 transactions are considered replaceable.
+ // All TRUC transactions are considered replaceable.
//
// Replaceability signaling of the original transactions may be
// ignored due to node setting.
@@ -936,7 +940,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// while a tx could be package CPFP'd when entering the mempool, we do not have a DoS-resistant
// method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
// due to a replacement.
- // The only exception is v3 transactions.
+ // The only exception is TRUC transactions.
if (!bypass_limits && ws.m_ptx->version != TRUC_VERSION && ws.m_modified_fees < m_pool.m_opts.min_relay_feerate.GetFee(ws.m_vsize)) {
// Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
// TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
@@ -1005,7 +1009,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// If the new transaction is relatively small (up to 40k weight)
// and has at most one ancestor (ie ancestor limit of 2, including
// the new transaction), allow it if its parent has exactly the
- // descendant limit descendants. The transaction also cannot be v3,
+ // descendant limit descendants. The transaction also cannot be TRUC,
// as its topology restrictions do not allow a second child.
//
// This allows protocols which rely on distrusting counterparties
@@ -1032,7 +1036,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Even though just checking direct mempool parents for inheritance would be sufficient, we
// check using the full ancestor set here because it's more convenient to use what we have
// already calculated.
- if (const auto err{SingleV3Checks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
+ if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
// Single transaction contexts only.
if (args.m_allow_sibling_eviction && err->second != nullptr) {
// We should only be considering where replacement is considered valid as well.
@@ -1043,15 +1047,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
ws.m_conflicts.insert(err->second->GetHash());
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
- // the descendant count is done separately in SingleV3Checks for v3 transactions.
+ // the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
ws.m_sibling_eviction = true;
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
- // Note that we are not checking whether it opts in to replaceability via BIP125 or v3
- // (which is normally done in PreChecks). However, the only way a v3 transaction can
- // have a non-v3 and non-BIP125 descendant is due to a reorg.
+ // Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
+ // (which is normally done in PreChecks). However, the only way a TRUC transaction can
+ // have a non-TRUC and non-BIP125 descendant is due to a reorg.
} else {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "v3-rule-violation", err->first);
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
}
}
@@ -1103,7 +1107,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
}
// Enforce Rule #2.
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, m_subpackage.m_all_conflicts)}) {
- // Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors.
+ // Sibling eviction is only done for TRUC transactions, which cannot have multiple ancestors.
Assume(!ws.m_sibling_eviction);
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
@@ -1201,7 +1205,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
const CFeeRate package_feerate(m_subpackage.m_total_modified_fees, m_subpackage.m_total_vsize);
if (package_feerate <= parent_feerate) {
return package_state.Invalid(PackageValidationResult::PCKG_POLICY,
- "package RBF failed: package feerate is less than parent feerate",
+ "package RBF failed: package feerate is less than or equal to parent feerate",
strprintf("package feerate %s <= parent feerate is %s", package_feerate.ToString(), parent_feerate.ToString()));
}
@@ -1212,7 +1216,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
"package RBF failed: " + err_tup.value().second, "");
}
- LogPrint(BCLog::TXPACKAGES, "package RBF checks passed: parent %s (wtxid=%s), child %s (wtxid=%s)\n",
+ LogDebug(BCLog::TXPACKAGES, "package RBF checks passed: parent %s (wtxid=%s), child %s (wtxid=%s)\n",
txns.front()->GetHash().ToString(), txns.front()->GetWitnessHash().ToString(),
txns.back()->GetHash().ToString(), txns.back()->GetWitnessHash().ToString());
@@ -1231,13 +1235,13 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
// Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
+ if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
- if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata) &&
- !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata)) {
+ if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata, GetValidationCache()) &&
+ !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
// Only the witness is missing, so the transaction itself may be fine.
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
state.GetRejectReason(), state.GetDebugMessage());
@@ -1273,7 +1277,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
// transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
- ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
+ ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
return Assume(false);
}
@@ -1295,7 +1299,7 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// Remove conflicting transactions from the mempool
for (CTxMemPool::txiter it : m_subpackage.m_all_conflicts)
{
- LogPrint(BCLog::MEMPOOL, "replacing mempool tx %s (wtxid=%s, fees=%s, vsize=%s). New tx %s (wtxid=%s, fees=%s, vsize=%s)\n",
+ LogDebug(BCLog::MEMPOOL, "replacing mempool tx %s (wtxid=%s, fees=%s, vsize=%s). New tx %s (wtxid=%s, fees=%s, vsize=%s)\n",
it->GetTx().GetHash().ToString(),
it->GetTx().GetWitnessHash().ToString(),
it->GetFee(),
@@ -1398,7 +1402,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
[](const auto& ws) { return ws.m_ptx->GetWitnessHash(); });
if (!m_subpackage.m_replaced_transactions.empty()) {
- LogPrint(BCLog::MEMPOOL, "replaced %u mempool transactions with %u new one(s) for %s additional fees, %d delta bytes\n",
+ LogDebug(BCLog::MEMPOOL, "replaced %u mempool transactions with %u new one(s) for %s additional fees, %d delta bytes\n",
m_subpackage.m_replaced_transactions.size(), workspaces.size(),
m_subpackage.m_total_modified_fees - m_subpackage.m_conflicting_fees,
m_subpackage.m_total_vsize - static_cast<int>(m_subpackage.m_conflicting_size));
@@ -1486,7 +1490,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
}
if (!m_subpackage.m_replaced_transactions.empty()) {
- LogPrint(BCLog::MEMPOOL, "replaced %u mempool transactions with 1 new transaction for %s additional fees, %d delta bytes\n",
+ LogDebug(BCLog::MEMPOOL, "replaced %u mempool transactions with 1 new transaction for %s additional fees, %d delta bytes\n",
m_subpackage.m_replaced_transactions.size(),
ws.m_modified_fees - m_subpackage.m_conflicting_fees,
ws.m_vsize - static_cast<int>(m_subpackage.m_conflicting_size));
@@ -1545,10 +1549,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
}
// At this point we have all in-mempool ancestors, and we know every transaction's vsize.
- // Run the v3 checks on the package.
+ // Run the TRUC checks on the package.
for (Workspace& ws : workspaces) {
- if (auto err{PackageV3Checks(ws.m_ptx, ws.m_vsize, txns, ws.m_ancestors)}) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "v3-violation", err.value());
+ if (auto err{PackageTRUCChecks(ws.m_ptx, ws.m_vsize, txns, ws.m_ancestors)}) {
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value());
return PackageMempoolAcceptResult(package_state, {});
}
}
@@ -2016,7 +2020,8 @@ void Chainstate::CheckForkWarningConditions()
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
- if (m_chainman.IsInitialBlockDownload()) {
+ // Also not applicable to the background chainstate
+ if (m_chainman.IsInitialBlockDownload() || this->GetRole() == ChainstateRole::BACKGROUND) {
return;
}
@@ -2084,29 +2089,23 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
- return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);
+ return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error);
}
-static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
-static CSHA256 g_scriptExecutionCacheHasher;
-
-bool InitScriptExecutionCache(size_t max_size_bytes)
+ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, const size_t signature_cache_bytes)
+ : m_signature_cache{signature_cache_bytes}
{
// Setup the salted hasher
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
// this chunk, which makes later hash computations more efficient. We
// just write our 32-byte entropy twice to fill the 64 bytes.
- g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
- g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
-
- auto setup_results = g_scriptExecutionCache.setup_bytes(max_size_bytes);
- if (!setup_results) return false;
+ m_script_execution_cache_hasher.Write(nonce.begin(), 32);
+ m_script_execution_cache_hasher.Write(nonce.begin(), 32);
- const auto [num_elems, approx_size_bytes] = *setup_results;
+ const auto [num_elems, approx_size_bytes] = m_script_execution_cache.setup_bytes(script_execution_cache_bytes);
LogPrintf("Using %zu MiB out of %zu MiB requested for script execution cache, able to store %zu elements\n",
- approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
- return true;
+ approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
}
/**
@@ -2126,11 +2125,12 @@ bool InitScriptExecutionCache(size_t max_size_bytes)
* Note that we may set state.reason to NOT_STANDARD for extra soft-fork flags in flags, block-checking
* callers should probably reset it to CONSENSUS in such cases.
*
- * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
+ * Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp
*/
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks)
{
if (tx.IsCoinBase()) return true;
@@ -2145,10 +2145,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
- CSHA256 hasher = g_scriptExecutionCacheHasher;
+ CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
- if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ if (validation_cache.m_script_execution_cache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}
@@ -2175,7 +2175,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// spent being checked as a part of CScriptCheck.
// Verify signature
- CScriptCheck check(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
+ CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->emplace_back(std::move(check));
} else if (!check()) {
@@ -2188,7 +2188,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// splitting the network between upgraded and
// non-upgraded nodes by banning CONSENSUS-failing
// data providers.
- CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
+ CScriptCheck check2(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
@@ -2209,7 +2209,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
- g_scriptExecutionCache.insert(hashCacheEntry);
+ validation_cache.m_script_execution_cache.insert(hashCacheEntry);
}
return true;
@@ -2280,8 +2280,8 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
// Note: the blocks specified here are different than the ones used in ConnectBlock because DisconnectBlock
// unwinds the blocks in reverse. As a result, the inconsistency is not discovered until the earlier
// blocks with the duplicate coinbase transactions are disconnected.
- bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
- (pindex->nHeight==91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")));
+ bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256{"00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e"}) ||
+ (pindex->nHeight==91812 && pindex->GetBlockHash() == uint256{"00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"}));
// undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) {
@@ -2397,15 +2397,6 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
}
-static SteadyClock::duration time_check{};
-static SteadyClock::duration time_forks{};
-static SteadyClock::duration time_connect{};
-static SteadyClock::duration time_verify{};
-static SteadyClock::duration time_undo{};
-static SteadyClock::duration time_index{};
-static SteadyClock::duration time_total{};
-static int64_t num_blocks_total = 0;
-
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
* can fail if those validity checks fail (among other reasons). */
@@ -2450,7 +2441,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock());
- num_blocks_total++;
+ m_chainman.num_blocks_total++;
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
@@ -2492,11 +2483,11 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
}
const auto time_1{SteadyClock::now()};
- time_check += time_1 - time_start;
- LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_check += time_1 - time_start;
+ LogDebug(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_1 - time_start),
- Ticks<SecondsDouble>(time_check),
- Ticks<MillisecondsDouble>(time_check) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_check),
+ Ticks<MillisecondsDouble>(m_chainman.time_check) / m_chainman.num_blocks_total);
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
@@ -2594,11 +2585,11 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
const auto time_2{SteadyClock::now()};
- time_forks += time_2 - time_1;
- LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_forks += time_2 - time_1;
+ LogDebug(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_2 - time_1),
- Ticks<SecondsDouble>(time_forks),
- Ticks<MillisecondsDouble>(time_forks) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_forks),
+ Ticks<MillisecondsDouble>(m_chainman.time_forks) / m_chainman.num_blocks_total);
CBlockUndo blockundo;
@@ -2667,7 +2658,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@@ -2685,12 +2676,12 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
}
const auto time_3{SteadyClock::now()};
- time_connect += time_3 - time_2;
- LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(),
+ m_chainman.time_connect += time_3 - time_2;
+ LogDebug(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(),
Ticks<MillisecondsDouble>(time_3 - time_2), Ticks<MillisecondsDouble>(time_3 - time_2) / block.vtx.size(),
nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_3 - time_2) / (nInputs - 1),
- Ticks<SecondsDouble>(time_connect),
- Ticks<MillisecondsDouble>(time_connect) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_connect),
+ Ticks<MillisecondsDouble>(m_chainman.time_connect) / m_chainman.num_blocks_total);
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, params.GetConsensus());
if (block.vtx[0]->GetValueOut() > blockReward) {
@@ -2703,12 +2694,12 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "block-validation-failed");
}
const auto time_4{SteadyClock::now()};
- time_verify += time_4 - time_2;
- LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1,
+ m_chainman.time_verify += time_4 - time_2;
+ LogDebug(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1,
Ticks<MillisecondsDouble>(time_4 - time_2),
nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_4 - time_2) / (nInputs - 1),
- Ticks<SecondsDouble>(time_verify),
- Ticks<MillisecondsDouble>(time_verify) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_verify),
+ Ticks<MillisecondsDouble>(m_chainman.time_verify) / m_chainman.num_blocks_total);
if (fJustCheck)
return true;
@@ -2718,11 +2709,11 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
}
const auto time_5{SteadyClock::now()};
- time_undo += time_5 - time_4;
- LogPrint(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_undo += time_5 - time_4;
+ LogDebug(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_5 - time_4),
- Ticks<SecondsDouble>(time_undo),
- Ticks<MillisecondsDouble>(time_undo) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_undo),
+ Ticks<MillisecondsDouble>(m_chainman.time_undo) / m_chainman.num_blocks_total);
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
@@ -2733,11 +2724,11 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
view.SetBestBlock(pindex->GetBlockHash());
const auto time_6{SteadyClock::now()};
- time_index += time_6 - time_5;
- LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_index += time_6 - time_5;
+ LogDebug(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_6 - time_5),
- Ticks<SecondsDouble>(time_index),
- Ticks<MillisecondsDouble>(time_index) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_index),
+ Ticks<MillisecondsDouble>(m_chainman.time_index) / m_chainman.num_blocks_total);
TRACE6(validation, block_connected,
block_hash.data(),
@@ -2820,7 +2811,7 @@ bool Chainstate::FlushStateToDisk(
}
if (limiting_lock) {
- LogPrint(BCLog::PRUNE, "%s limited pruning to height %d\n", limiting_lock.value(), last_prune);
+ LogDebug(BCLog::PRUNE, "%s limited pruning to height %d\n", limiting_lock.value(), last_prune);
}
if (nManualPruneHeight > 0) {
@@ -2909,7 +2900,7 @@ bool Chainstate::FlushStateToDisk(
return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
- const auto empty_cache{(mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fFlushForPrune};
+ const auto empty_cache{(mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical};
if (empty_cache ? !CoinsTip().Flush() : !CoinsTip().Sync()) {
return FatalError(m_chainman.GetNotifications(), state, _("Failed to write to coin database."));
}
@@ -2963,7 +2954,7 @@ static void UpdateTipLog(
LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
prefix, func_name,
tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
- log(tip->nChainWork.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
+ log(tip->nChainWork.getdouble()) / log(2.0), tip->m_chain_tx_count,
FormatISO8601DateTime(tip->GetBlockTime()),
GuessVerificationProgress(params.TxData(), tip),
coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
@@ -2994,12 +2985,6 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
m_mempool->AddTransactionsUpdated(1);
}
- {
- LOCK(g_best_block_mutex);
- g_best_block = pindexNew->GetBlockHash();
- g_best_block_cv.notify_all();
- }
-
std::vector<bilingual_str> warning_messages;
if (!m_chainman.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
@@ -3057,7 +3042,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
bool flushed = view.Flush();
assert(flushed);
}
- LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n",
+ LogDebug(BCLog::BENCH, "- Disconnect block: %.2fms\n",
Ticks<MillisecondsDouble>(SteadyClock::now() - time_start));
{
@@ -3067,7 +3052,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
if (prune_lock.second.height_first <= max_height_first) continue;
prune_lock.second.height_first = max_height_first;
- LogPrint(BCLog::PRUNE, "%s prune lock moved back to %d\n", prune_lock.first, max_height_first);
+ LogDebug(BCLog::PRUNE, "%s prune lock moved back to %d\n", prune_lock.first, max_height_first);
}
}
@@ -3095,11 +3080,6 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
return true;
}
-static SteadyClock::duration time_connect_total{};
-static SteadyClock::duration time_flush{};
-static SteadyClock::duration time_chainstate{};
-static SteadyClock::duration time_post_connect{};
-
struct PerBlockConnectTrace {
CBlockIndex* pindex = nullptr;
std::shared_ptr<const CBlock> pblock;
@@ -3162,7 +3142,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
}
pthisBlock = pblockNew;
} else {
- LogPrint(BCLog::BENCH, " - Using cached block\n");
+ LogDebug(BCLog::BENCH, " - Using cached block\n");
pthisBlock = pblock;
}
const CBlock& blockConnecting = *pthisBlock;
@@ -3171,7 +3151,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
SteadyClock::time_point time_3;
// When adding aggregate statistics in the future, keep in mind that
// num_blocks_total may be zero until the ConnectBlock() call below.
- LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms\n",
+ LogDebug(BCLog::BENCH, " - Load block from disk: %.2fms\n",
Ticks<MillisecondsDouble>(time_2 - time_1));
{
CCoinsViewCache view(&CoinsTip());
@@ -3186,31 +3166,31 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
return false;
}
time_3 = SteadyClock::now();
- time_connect_total += time_3 - time_2;
- assert(num_blocks_total > 0);
- LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_connect_total += time_3 - time_2;
+ assert(m_chainman.num_blocks_total > 0);
+ LogDebug(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_3 - time_2),
- Ticks<SecondsDouble>(time_connect_total),
- Ticks<MillisecondsDouble>(time_connect_total) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_connect_total),
+ Ticks<MillisecondsDouble>(m_chainman.time_connect_total) / m_chainman.num_blocks_total);
bool flushed = view.Flush();
assert(flushed);
}
const auto time_4{SteadyClock::now()};
- time_flush += time_4 - time_3;
- LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_flush += time_4 - time_3;
+ LogDebug(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_4 - time_3),
- Ticks<SecondsDouble>(time_flush),
- Ticks<MillisecondsDouble>(time_flush) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_flush),
+ Ticks<MillisecondsDouble>(m_chainman.time_flush) / m_chainman.num_blocks_total);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
return false;
}
const auto time_5{SteadyClock::now()};
- time_chainstate += time_5 - time_4;
- LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_chainstate += time_5 - time_4;
+ LogDebug(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_5 - time_4),
- Ticks<SecondsDouble>(time_chainstate),
- Ticks<MillisecondsDouble>(time_chainstate) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_chainstate),
+ Ticks<MillisecondsDouble>(m_chainman.time_chainstate) / m_chainman.num_blocks_total);
// Remove conflicting transactions from the mempool.;
if (m_mempool) {
m_mempool->removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
@@ -3221,16 +3201,16 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
UpdateTip(pindexNew);
const auto time_6{SteadyClock::now()};
- time_post_connect += time_6 - time_5;
- time_total += time_6 - time_1;
- LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",
+ m_chainman.time_post_connect += time_6 - time_5;
+ m_chainman.time_total += time_6 - time_1;
+ LogDebug(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_6 - time_5),
- Ticks<SecondsDouble>(time_post_connect),
- Ticks<MillisecondsDouble>(time_post_connect) / num_blocks_total);
- LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",
+ Ticks<SecondsDouble>(m_chainman.time_post_connect),
+ Ticks<MillisecondsDouble>(m_chainman.time_post_connect) / m_chainman.num_blocks_total);
+ LogDebug(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",
Ticks<MillisecondsDouble>(time_6 - time_1),
- Ticks<SecondsDouble>(time_total),
- Ticks<MillisecondsDouble>(time_total) / num_blocks_total);
+ Ticks<SecondsDouble>(m_chainman.time_total),
+ Ticks<MillisecondsDouble>(m_chainman.time_total) / m_chainman.num_blocks_total);
// If we are the background validation chainstate, check to see if we are done
// validating the snapshot (i.e. our tip has reached the snapshot's base block).
@@ -3369,7 +3349,7 @@ bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex*
nHeight = nTargetHeight;
// Connect new blocks.
- for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) {
+ for (CBlockIndex* pindexConnect : vpindexToConnect | std::views::reverse) {
if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
@@ -3417,25 +3397,24 @@ static SynchronizationState GetSynchronizationState(bool init, bool blockfiles_i
return SynchronizationState::INIT_DOWNLOAD;
}
-static bool NotifyHeaderTip(ChainstateManager& chainman) LOCKS_EXCLUDED(cs_main)
+bool ChainstateManager::NotifyHeaderTip()
{
bool fNotify = false;
bool fInitialBlockDownload = false;
- static CBlockIndex* pindexHeaderOld = nullptr;
CBlockIndex* pindexHeader = nullptr;
{
- LOCK(cs_main);
- pindexHeader = chainman.m_best_header;
+ LOCK(GetMutex());
+ pindexHeader = m_best_header;
- if (pindexHeader != pindexHeaderOld) {
+ if (pindexHeader != m_last_notified_header) {
fNotify = true;
- fInitialBlockDownload = chainman.IsInitialBlockDownload();
- pindexHeaderOld = pindexHeader;
+ fInitialBlockDownload = IsInitialBlockDownload();
+ m_last_notified_header = pindexHeader;
}
}
- // Send block tip changed notifications without cs_main
+ // Send block tip changed notifications without the lock held
if (fNotify) {
- chainman.GetNotifications().headerTip(GetSynchronizationState(fInitialBlockDownload, chainman.m_blockman.m_blockfiles_indexed), pindexHeader->nHeight, pindexHeader->nTime, false);
+ GetNotifications().headerTip(GetSynchronizationState(fInitialBlockDownload, m_blockman.m_blockfiles_indexed), pindexHeader->nHeight, pindexHeader->nTime, false);
}
return fNotify;
}
@@ -3486,6 +3465,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
{
LOCK(cs_main);
+ {
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
LOCK(MempoolMutex());
const bool was_in_ibd = m_chainman.IsInitialBlockDownload();
@@ -3553,7 +3533,6 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
m_chainman.m_options.signals->UpdatedBlockTip(pindexNewTip, pindexFork, still_in_ibd);
}
- // Always notify the UI if a new block tip was connected
if (kernel::IsInterrupted(m_chainman.GetNotifications().blockTip(GetSynchronizationState(still_in_ibd, m_chainman.m_blockman.m_blockfiles_indexed), *pindexNewTip))) {
// Just breaking and returning success for now. This could
// be changed to bubble up the kernel::Interrupted value to
@@ -3562,7 +3541,12 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
break;
}
}
- }
+ } // release MempoolMutex
+ // Notify external listeners about the new tip, even if pindexFork == pindexNewTip.
+ if (m_chainman.m_options.signals && this == &m_chainman.ActiveChainstate()) {
+ m_chainman.m_options.signals->ActiveTipChange(*Assert(pindexNewTip), m_chainman.IsInitialBlockDownload());
+ }
+ } // release cs_main
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
if (exited_ibd) {
@@ -3581,8 +3565,8 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
//
// This cannot be done while holding cs_main (within
// MaybeCompleteSnapshotValidation) or a cs_main deadlock will occur.
- if (m_chainman.restart_indexes) {
- m_chainman.restart_indexes();
+ if (m_chainman.snapshot_download_completed) {
+ m_chainman.snapshot_download_completed();
}
break;
}
@@ -3781,6 +3765,12 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// distinguish user-initiated invalidateblock changes from other
// changes.
(void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_blockfiles_indexed), *to_mark_failed->pprev);
+
+ // Fire ActiveTipChange now for the current chain tip to make sure clients are notified.
+ // ActivateBestChain may call this as well, but not necessarily.
+ if (m_chainman.m_options.signals) {
+ m_chainman.m_options.signals->ActiveTipChange(*Assert(m_chain.Tip()), m_chainman.IsInitialBlockDownload());
+ }
}
return true;
}
@@ -3847,17 +3837,17 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd
{
AssertLockHeld(cs_main);
pindexNew->nTx = block.vtx.size();
- // Typically nChainTx will be 0 at this point, but it can be nonzero if this
+ // Typically m_chain_tx_count will be 0 at this point, but it can be nonzero if this
// is a pruned block which is being downloaded again, or if this is an
- // assumeutxo snapshot block which has a hardcoded nChainTx value from the
+ // assumeutxo snapshot block which has a hardcoded m_chain_tx_count value from the
// snapshot metadata. If the pindex is not the snapshot block and the
- // nChainTx value is not zero, assert that value is actually correct.
- auto prev_tx_sum = [](CBlockIndex& block) { return block.nTx + (block.pprev ? block.pprev->nChainTx : 0); };
- if (!Assume(pindexNew->nChainTx == 0 || pindexNew->nChainTx == prev_tx_sum(*pindexNew) ||
+ // m_chain_tx_count value is not zero, assert that value is actually correct.
+ auto prev_tx_sum = [](CBlockIndex& block) { return block.nTx + (block.pprev ? block.pprev->m_chain_tx_count : 0); };
+ if (!Assume(pindexNew->m_chain_tx_count == 0 || pindexNew->m_chain_tx_count == prev_tx_sum(*pindexNew) ||
pindexNew == GetSnapshotBaseBlock())) {
- LogWarning("Internal bug detected: block %d has unexpected nChainTx %i that should be %i (%s %s). Please report this issue here: %s\n",
- pindexNew->nHeight, pindexNew->nChainTx, prev_tx_sum(*pindexNew), PACKAGE_NAME, FormatFullVersion(), PACKAGE_BUGREPORT);
- pindexNew->nChainTx = 0;
+ LogWarning("Internal bug detected: block %d has unexpected m_chain_tx_count %i that should be %i (%s %s). Please report this issue here: %s\n",
+ pindexNew->nHeight, pindexNew->m_chain_tx_count, prev_tx_sum(*pindexNew), PACKAGE_NAME, FormatFullVersion(), PACKAGE_BUGREPORT);
+ pindexNew->m_chain_tx_count = 0;
}
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
@@ -3878,15 +3868,15 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd
while (!queue.empty()) {
CBlockIndex *pindex = queue.front();
queue.pop_front();
- // Before setting nChainTx, assert that it is 0 or already set to
+ // Before setting m_chain_tx_count, assert that it is 0 or already set to
// the correct value. This assert will fail after receiving the
// assumeutxo snapshot block if assumeutxo snapshot metadata has an
- // incorrect hardcoded AssumeutxoData::nChainTx value.
- if (!Assume(pindex->nChainTx == 0 || pindex->nChainTx == prev_tx_sum(*pindex))) {
- LogWarning("Internal bug detected: block %d has unexpected nChainTx %i that should be %i (%s %s). Please report this issue here: %s\n",
- pindex->nHeight, pindex->nChainTx, prev_tx_sum(*pindex), PACKAGE_NAME, FormatFullVersion(), PACKAGE_BUGREPORT);
+ // incorrect hardcoded AssumeutxoData::m_chain_tx_count value.
+ if (!Assume(pindex->m_chain_tx_count == 0 || pindex->m_chain_tx_count == prev_tx_sum(*pindex))) {
+ LogWarning("Internal bug detected: block %d has unexpected m_chain_tx_count %i that should be %i (%s %s). Please report this issue here: %s\n",
+ pindex->nHeight, pindex->m_chain_tx_count, prev_tx_sum(*pindex), PACKAGE_NAME, FormatFullVersion(), PACKAGE_BUGREPORT);
}
- pindex->nChainTx = prev_tx_sum(*pindex);
+ pindex->m_chain_tx_count = prev_tx_sum(*pindex);
pindex->nSequenceId = nBlockSequenceId++;
for (Chainstate *c : GetAll()) {
c->TryAddBlockIndexCandidate(pindex);
@@ -4137,7 +4127,7 @@ bool IsBlockMutated(const CBlock& block, bool check_witness_root)
return false;
}
-arith_uint256 CalculateClaimedHeadersWork(const std::vector<CBlockHeader>& headers)
+arith_uint256 CalculateClaimedHeadersWork(std::span<const CBlockHeader> headers)
{
arith_uint256 total_work{0};
for (const CBlockHeader& header : headers) {
@@ -4183,6 +4173,18 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
+ // Testnet4 and regtest only: Check timestamp against prev for difficulty-adjustment
+ // blocks to prevent timewarp attacks (see https://github.com/bitcoin/bitcoin/pull/15482).
+ if (consensusParams.enforce_BIP94) {
+ // Check timestamp for the first block of each difficulty adjustment
+ // interval, except the genesis block.
+ if (nHeight % consensusParams.DifficultyAdjustmentInterval() == 0) {
+ if (block.GetBlockTime() < pindexPrev->GetBlockTime() - MAX_TIMEWARP) {
+ return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-timewarp-attack", "block's timestamp is too early on diff adjustment block");
+ }
+ }
+ }
+
// Check timestamp
if (block.Time() > NodeClock::now() + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) {
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
@@ -4276,14 +4278,14 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK) {
- LogPrint(BCLog::VALIDATION, "%s: block %s is marked invalid\n", __func__, hash.ToString());
+ LogDebug(BCLog::VALIDATION, "%s: block %s is marked invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate");
}
return true;
}
if (!CheckBlockHeader(block, state, GetConsensus())) {
- LogPrint(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
+ LogDebug(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -4291,16 +4293,16 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
CBlockIndex* pindexPrev = nullptr;
BlockMap::iterator mi{m_blockman.m_block_index.find(block.hashPrevBlock)};
if (mi == m_blockman.m_block_index.end()) {
- LogPrint(BCLog::VALIDATION, "header %s has prev block not found: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
+ LogDebug(BCLog::VALIDATION, "header %s has prev block not found: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
}
pindexPrev = &((*mi).second);
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
- LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
+ LogDebug(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev)) {
- LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
+ LogDebug(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -4337,14 +4339,14 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
m_blockman.m_dirty_blockindex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
- LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
+ LogDebug(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
}
}
}
if (!min_pow_checked) {
- LogPrint(BCLog::VALIDATION, "%s: not adding new block header %s, missing anti-dos proof-of-work validation\n", __func__, hash.ToString());
+ LogDebug(BCLog::VALIDATION, "%s: not adding new block header %s, missing anti-dos proof-of-work validation\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_HEADER_LOW_WORK, "too-little-chainwork");
}
CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, m_best_header)};
@@ -4373,7 +4375,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
// Exposed wrapper for AcceptBlockHeader
-bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex)
+bool ChainstateManager::ProcessNewBlockHeaders(std::span<const CBlockHeader> headers, bool min_pow_checked, BlockValidationState& state, const CBlockIndex** ppindex)
{
AssertLockNotHeld(cs_main);
{
@@ -4391,7 +4393,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
}
}
- if (NotifyHeaderTip(*this)) {
+ if (NotifyHeaderTip()) {
if (IsInitialBlockDownload() && ppindex && *ppindex) {
const CBlockIndex& last_accepted{**ppindex};
int64_t blocks_left{(NodeClock::now() - last_accepted.Time()) / GetConsensus().PowTargetSpacing()};
@@ -4562,7 +4564,7 @@ bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& blo
}
}
- NotifyHeaderTip(*this);
+ NotifyHeaderTip();
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
if (!ActiveChainstate().ActivateBestChain(state, block)) {
@@ -5073,7 +5075,7 @@ void ChainstateManager::LoadExternalBlockFile(
LOCK(cs_main);
// detect out of order blocks, and store them for later
if (hash != params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(header.hashPrevBlock)) {
- LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
+ LogDebug(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
header.hashPrevBlock.ToString());
if (dbp && blocks_with_unknown_parent) {
blocks_with_unknown_parent->emplace(header.hashPrevBlock, *dbp);
@@ -5098,7 +5100,7 @@ void ChainstateManager::LoadExternalBlockFile(
break;
}
} else if (hash != params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
- LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
+ LogDebug(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
}
}
@@ -5129,7 +5131,7 @@ void ChainstateManager::LoadExternalBlockFile(
for (auto c : GetAll()) {
BlockValidationState state;
if (!c->ActivateBestChain(state, pblock)) {
- LogPrint(BCLog::REINDEX, "failed to activate chain (%s)\n", state.ToString());
+ LogDebug(BCLog::REINDEX, "failed to activate chain (%s)\n", state.ToString());
activation_failure = true;
break;
}
@@ -5139,7 +5141,7 @@ void ChainstateManager::LoadExternalBlockFile(
}
}
- NotifyHeaderTip(*this);
+ NotifyHeaderTip();
if (!blocks_with_unknown_parent) continue;
@@ -5154,7 +5156,7 @@ void ChainstateManager::LoadExternalBlockFile(
std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
if (m_blockman.ReadBlockFromDisk(*pblockrecursive, it->second)) {
- LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(),
+ LogDebug(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(),
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
@@ -5165,7 +5167,7 @@ void ChainstateManager::LoadExternalBlockFile(
}
range.first++;
blocks_with_unknown_parent->erase(it);
- NotifyHeaderTip(*this);
+ NotifyHeaderTip();
}
}
} catch (const std::exception& e) {
@@ -5180,7 +5182,7 @@ void ChainstateManager::LoadExternalBlockFile(
// the reindex process is not the place to attempt to clean and/or compact the block files. if so desired, a studious node operator
// may use knowledge of the fact that the block files are not entirely pristine in order to prepare a set of pristine, and
// perhaps ordered, block files for later reindexing.
- LogPrint(BCLog::REINDEX, "%s: unexpected data at file offset 0x%x - %s. continuing\n", __func__, (nRewind - 1), e.what());
+ LogDebug(BCLog::REINDEX, "%s: unexpected data at file offset 0x%x - %s. continuing\n", __func__, (nRewind - 1), e.what());
}
}
} catch (const std::runtime_error& e) {
@@ -5193,7 +5195,7 @@ bool ChainstateManager::ShouldCheckBlockIndex() const
{
// Assert to verify Flatten() has been called.
if (!*Assert(m_options.check_block_index)) return false;
- if (GetRand(*m_options.check_block_index) >= 1) return false;
+ if (FastRandomContext().randrange(*m_options.check_block_index) >= 1) return false;
return true;
}
@@ -5334,17 +5336,17 @@ void ChainstateManager::CheckBlockIndex()
// Checks for not-invalid blocks.
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
}
- // Make sure nChainTx sum is correctly computed.
+ // Make sure m_chain_tx_count sum is correctly computed.
if (!pindex->pprev) {
- // If no previous block, nTx and nChainTx must be the same.
- assert(pindex->nChainTx == pindex->nTx);
- } else if (pindex->pprev->nChainTx > 0 && pindex->nTx > 0) {
- // If previous nChainTx is set and number of transactions in block is known, sum must be set.
- assert(pindex->nChainTx == pindex->nTx + pindex->pprev->nChainTx);
+ // If no previous block, nTx and m_chain_tx_count must be the same.
+ assert(pindex->m_chain_tx_count == pindex->nTx);
+ } else if (pindex->pprev->m_chain_tx_count > 0 && pindex->nTx > 0) {
+ // If previous m_chain_tx_count is set and number of transactions in block is known, sum must be set.
+ assert(pindex->m_chain_tx_count == pindex->nTx + pindex->pprev->m_chain_tx_count);
} else {
- // Otherwise nChainTx should only be set if this is a snapshot
+ // Otherwise m_chain_tx_count should only be set if this is a snapshot
// block, and must be set if it is.
- assert((pindex->nChainTx != 0) == (pindex == snap_base));
+ assert((pindex->m_chain_tx_count != 0) == (pindex == snap_base));
}
// Chainstate-specific checks on setBlockIndexCandidates
@@ -5373,7 +5375,7 @@ void ChainstateManager::CheckBlockIndex()
// and the transactions were not pruned (pindexFirstMissing
// is null), it is a potential candidate. The check
// excludes pruned blocks, because if any blocks were
- // pruned between pindex the current chain tip, pindex will
+ // pruned between pindex and the current chain tip, pindex will
// only temporarily be added to setBlockIndexCandidates,
// before being moved to m_blocks_unlinked. This check
// could be improved to verify that if all blocks between
@@ -5549,13 +5551,13 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
}
//! Guess how far we are in the verification process at the given block index
-//! require cs_main if pindex has not been validated yet (because nChainTx might be unset)
+//! require cs_main if pindex has not been validated yet (because m_chain_tx_count might be unset)
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) {
if (pindex == nullptr)
return 0.0;
- if (!Assume(pindex->nChainTx > 0)) {
- LogWarning("Internal bug detected: block %d has unset nChainTx (%s %s). Please report this issue here: %s\n",
+ if (!Assume(pindex->m_chain_tx_count > 0)) {
+ LogWarning("Internal bug detected: block %d has unset m_chain_tx_count (%s %s). Please report this issue here: %s\n",
pindex->nHeight, PACKAGE_NAME, FormatFullVersion(), PACKAGE_BUGREPORT);
return 0.0;
}
@@ -5564,13 +5566,13 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
double fTxTotal;
- if (pindex->nChainTx <= data.nTxCount) {
- fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate;
+ if (pindex->m_chain_tx_count <= data.tx_count) {
+ fTxTotal = data.tx_count + (nNow - data.nTime) * data.dTxRate;
} else {
- fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate;
+ fTxTotal = pindex->m_chain_tx_count + (nNow - pindex->GetBlockTime()) * data.dTxRate;
}
- return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
+ return std::min<double>(pindex->m_chain_tx_count / fTxTotal, 1.0);
}
std::optional<uint256> ChainstateManager::SnapshotBlockhash() const
@@ -5646,7 +5648,7 @@ Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
return destroyed && !fs::exists(db_path);
}
-bool ChainstateManager::ActivateSnapshot(
+util::Result<CBlockIndex*> ChainstateManager::ActivateSnapshot(
AutoFile& coins_file,
const SnapshotMetadata& metadata,
bool in_memory)
@@ -5654,15 +5656,40 @@ bool ChainstateManager::ActivateSnapshot(
uint256 base_blockhash = metadata.m_base_blockhash;
if (this->SnapshotBlockhash()) {
- LogPrintf("[snapshot] can't activate a snapshot-based chainstate more than once\n");
- return false;
+ return util::Error{Untranslated("Can't activate a snapshot-based chainstate more than once")};
}
+ CBlockIndex* snapshot_start_block{};
+
{
LOCK(::cs_main);
- if (Assert(m_active_chainstate->GetMempool())->size() > 0) {
- LogPrintf("[snapshot] can't activate a snapshot when mempool not empty\n");
- return false;
+
+ if (!GetParams().AssumeutxoForBlockhash(base_blockhash).has_value()) {
+ auto available_heights = GetParams().GetAvailableSnapshotHeights();
+ std::string heights_formatted = util::Join(available_heights, ", ", [&](const auto& i) { return util::ToString(i); });
+ return util::Error{strprintf(Untranslated("assumeutxo block hash in snapshot metadata not recognized (hash: %s). The following snapshot heights are available: %s"),
+ base_blockhash.ToString(),
+ heights_formatted)};
+ }
+
+ snapshot_start_block = m_blockman.LookupBlockIndex(base_blockhash);
+ if (!snapshot_start_block) {
+ return util::Error{strprintf(Untranslated("The base block header (%s) must appear in the headers chain. Make sure all headers are syncing, and call loadtxoutset again"),
+ base_blockhash.ToString())};
+ }
+
+ bool start_block_invalid = snapshot_start_block->nStatus & BLOCK_FAILED_MASK;
+ if (start_block_invalid) {
+ return util::Error{strprintf(Untranslated("The base block header (%s) is part of an invalid chain"), base_blockhash.ToString())};
+ }
+
+ if (!m_best_header || m_best_header->GetAncestor(snapshot_start_block->nHeight) != snapshot_start_block) {
+ return util::Error{Untranslated("A forked headers-chain with more work than the chain with the snapshot base block header exists. Please proceed to sync without AssumeUtxo.")};
+ }
+
+ auto mempool{m_active_chainstate->GetMempool()};
+ if (mempool && mempool->size() > 0) {
+ return util::Error{Untranslated("Can't activate a snapshot when mempool not empty")};
}
}
@@ -5711,8 +5738,7 @@ bool ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
}
- auto cleanup_bad_snapshot = [&](const char* reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
- LogPrintf("[snapshot] activation failed - %s\n", reason);
+ auto cleanup_bad_snapshot = [&](bilingual_str reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
this->MaybeRebalanceCaches();
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
@@ -5728,12 +5754,12 @@ bool ChainstateManager::ActivateSnapshot(
"Manually remove it before restarting.\n"), fs::PathToString(*snapshot_datadir)));
}
}
- return false;
+ return util::Error{std::move(reason)};
};
- if (!this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)) {
+ if (auto res{this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)}; !res) {
LOCK(::cs_main);
- return cleanup_bad_snapshot("population failed");
+ return cleanup_bad_snapshot(strprintf(Untranslated("Population failed: %s"), util::ErrorString(res)));
}
LOCK(::cs_main); // cs_main required for rest of snapshot activation.
@@ -5742,13 +5768,13 @@ bool ChainstateManager::ActivateSnapshot(
// work chain than the active chainstate; a user could have loaded a snapshot
// very late in the IBD process, and we wouldn't want to load a useless chainstate.
if (!CBlockIndexWorkComparator()(ActiveTip(), snapshot_chainstate->m_chain.Tip())) {
- return cleanup_bad_snapshot("work does not exceed active chainstate");
+ return cleanup_bad_snapshot(Untranslated("work does not exceed active chainstate"));
}
// If not in-memory, persist the base blockhash for use during subsequent
// initialization.
if (!in_memory) {
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
- return cleanup_bad_snapshot("could not write base blockhash");
+ return cleanup_bad_snapshot(Untranslated("could not write base blockhash"));
}
}
@@ -5771,7 +5797,7 @@ bool ChainstateManager::ActivateSnapshot(
m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
this->MaybeRebalanceCaches();
- return true;
+ return snapshot_start_block;
}
static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_loaded)
@@ -5798,7 +5824,7 @@ static void SnapshotUTXOHashBreakpoint(const util::SignalInterrupt& interrupt)
if (interrupt) throw StopHashingException();
}
-bool ChainstateManager::PopulateAndValidateSnapshot(
+util::Result<void> ChainstateManager::PopulateAndValidateSnapshot(
Chainstate& snapshot_chainstate,
AutoFile& coins_file,
const SnapshotMetadata& metadata)
@@ -5814,18 +5840,16 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
if (!snapshot_start_block) {
// Needed for ComputeUTXOStats to determine the
// height and to avoid a crash when base_blockhash.IsNull()
- LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
- base_blockhash.ToString());
- return false;
+ return util::Error{strprintf(Untranslated("Did not find snapshot start blockheader %s"),
+ base_blockhash.ToString())};
}
int base_height = snapshot_start_block->nHeight;
const auto& maybe_au_data = GetParams().AssumeutxoForHeight(base_height);
if (!maybe_au_data) {
- LogPrintf("[snapshot] assumeutxo height in snapshot metadata not recognized "
- "(%d) - refusing to load snapshot\n", base_height);
- return false;
+ return util::Error{strprintf(Untranslated("Assumeutxo height in snapshot metadata not recognized "
+ "(%d) - refusing to load snapshot"), base_height)};
}
const AssumeutxoData& au_data = *maybe_au_data;
@@ -5834,8 +5858,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// ActivateSnapshot(), but is done so that we avoid doing the long work of staging
// a snapshot that isn't actually usable.
if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) {
- LogPrintf("[snapshot] activation failed - work does not exceed active chainstate\n");
- return false;
+ return util::Error{Untranslated("Work does not exceed active chainstate")};
}
const uint64_t coins_count = metadata.m_coins_count;
@@ -5852,8 +5875,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
coins_per_txid = ReadCompactSize(coins_file);
if (coins_per_txid > coins_left) {
- LogPrintf("[snapshot] mismatch in coins count in snapshot metadata and actual snapshot data\n");
- return false;
+ return util::Error{Untranslated("Mismatch in coins count in snapshot metadata and actual snapshot data")};
}
for (size_t i = 0; i < coins_per_txid; i++) {
@@ -5865,14 +5887,12 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
if (coin.nHeight > base_height ||
outpoint.n >= std::numeric_limits<decltype(outpoint.n)>::max() // Avoid integer wrap-around in coinstats.cpp:ApplyHash
) {
- LogPrintf("[snapshot] bad snapshot data after deserializing %d coins\n",
- coins_count - coins_left);
- return false;
+ return util::Error{strprintf(Untranslated("Bad snapshot data after deserializing %d coins"),
+ coins_count - coins_left)};
}
if (!MoneyRange(coin.out.nValue)) {
- LogPrintf("[snapshot] bad snapshot data after deserializing %d coins - bad tx out value\n",
- coins_count - coins_left);
- return false;
+ return util::Error{strprintf(Untranslated("Bad snapshot data after deserializing %d coins - bad tx out value"),
+ coins_count - coins_left)};
}
coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
@@ -5892,7 +5912,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// means <5MB of memory imprecision.
if (coins_processed % 120000 == 0) {
if (m_interrupt) {
- return false;
+ return util::Error{Untranslated("Aborting after an interrupt was requested")};
}
const auto snapshot_cache_state = WITH_LOCK(::cs_main,
@@ -5910,9 +5930,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
}
}
} catch (const std::ios_base::failure&) {
- LogPrintf("[snapshot] bad snapshot format or truncated snapshot after deserializing %d coins\n",
- coins_processed);
- return false;
+ return util::Error{strprintf(Untranslated("Bad snapshot format or truncated snapshot after deserializing %d coins"),
+ coins_processed)};
}
}
@@ -5932,9 +5951,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
out_of_coins = true;
}
if (!out_of_coins) {
- LogPrintf("[snapshot] bad snapshot - coins left over after deserializing %d coins\n",
- coins_count);
- return false;
+ return util::Error{strprintf(Untranslated("Bad snapshot - coins left over after deserializing %d coins"),
+ coins_count)};
}
LogPrintf("[snapshot] loaded %d (%.2f MB) coins from snapshot %s\n",
@@ -5957,18 +5975,16 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
maybe_stats = ComputeUTXOStats(
CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
} catch (StopHashingException const&) {
- return false;
+ return util::Error{Untranslated("Aborting after an interrupt was requested")};
}
if (!maybe_stats.has_value()) {
- LogPrintf("[snapshot] failed to generate coins stats\n");
- return false;
+ return util::Error{Untranslated("Failed to generate coins stats")};
}
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
- LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
- au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
- return false;
+ return util::Error{strprintf(Untranslated("Bad snapshot content hash: expected %s, got %s"),
+ au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString())};
}
snapshot_chainstate.m_chain.SetTip(*snapshot_start_block);
@@ -5988,7 +6004,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
index = snapshot_chainstate.m_chain[i];
// Fake BLOCK_OPT_WITNESS so that Chainstate::NeedsRedownload()
- // won't ask to rewind the entire assumed-valid chain on startup.
+ // won't ask for -reindex on startup.
if (DeploymentActiveAt(*index, *this, Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
@@ -6003,12 +6019,12 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(index);
assert(index == snapshot_start_block);
- index->nChainTx = au_data.nChainTx;
+ index->m_chain_tx_count = au_data.m_chain_tx_count;
snapshot_chainstate.setBlockIndexCandidates.insert(snapshot_start_block);
LogPrintf("[snapshot] validated snapshot (%.2f MB)\n",
coins_cache.DynamicMemoryUsage() / (1000 * 1000));
- return true;
+ return {};
}
// Currently, this function holds cs_main for its duration, which could be for
@@ -6229,7 +6245,8 @@ ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Opt
: m_script_check_queue{/*batch_size=*/128, options.worker_threads_num},
m_interrupt{interrupt},
m_options{Flatten(std::move(options))},
- m_blockman{interrupt, std::move(blockman_options)}
+ m_blockman{interrupt, std::move(blockman_options)},
+ m_validation_cache{m_options.script_execution_cache_bytes, m_options.signature_cache_bytes}
{
}
@@ -6276,14 +6293,14 @@ Chainstate& ChainstateManager::ActivateExistingSnapshot(uint256 base_blockhash)
bool IsBIP30Repeat(const CBlockIndex& block_index)
{
- return (block_index.nHeight==91842 && block_index.GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
- (block_index.nHeight==91880 && block_index.GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"));
+ return (block_index.nHeight==91842 && block_index.GetBlockHash() == uint256{"00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec"}) ||
+ (block_index.nHeight==91880 && block_index.GetBlockHash() == uint256{"00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"});
}
bool IsBIP30Unspendable(const CBlockIndex& block_index)
{
- return (block_index.nHeight==91722 && block_index.GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
- (block_index.nHeight==91812 && block_index.GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"));
+ return (block_index.nHeight==91722 && block_index.GetBlockHash() == uint256{"00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e"}) ||
+ (block_index.nHeight==91812 && block_index.GetBlockHash() == uint256{"00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"});
}
static fs::path GetSnapshotCoinsDBPath(Chainstate& cs) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)