diff options
Diffstat (limited to 'src/validation.cpp')
-rw-r--r-- | src/validation.cpp | 507 |
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) |