Merge 891e4bf374 into merged_master (Bitcoin PR bitcoin/bitcoin#28339)

This commit is contained in:
ikripaka 2026-02-26 12:59:57 +00:00
commit 6bee2dd79d
No known key found for this signature in database
8 changed files with 73 additions and 19 deletions

View file

@ -23,6 +23,7 @@ bench_bench_bitcoin_SOURCES = \
bench/ccoins_caching.cpp \
bench/chacha20.cpp \
bench/checkblock.cpp \
bench/checkblockindex.cpp \
bench/checkqueue.cpp \
bench/crypto_hash.cpp \
bench/data.cpp \

View file

@ -0,0 +1,20 @@
// Copyright (c) 2023-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 <bench/bench.h>
#include <test/util/setup_common.h>
#include <validation.h>
static void CheckBlockIndex(benchmark::Bench& bench)
{
auto testing_setup{MakeNoLogFileContext<TestChain100Setup>()};
// Mine some more blocks
testing_setup->mineBlocks(1000);
bench.run([&] {
testing_setup->m_node.chainman->CheckBlockIndex();
});
}
BENCHMARK(CheckBlockIndex, benchmark::PriorityLevel::HIGH);

View file

@ -611,7 +611,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures every <n> operations. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkaddrman=<n>", strprintf("Run addrman consistency checks every <n> operations. Use 0 to disable. (default: %u)", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkmempool=<n>", strprintf("Run mempool consistency checks every <n> transactions. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);

View file

@ -33,7 +33,7 @@ namespace kernel {
struct ChainstateManagerOpts {
const CChainParams& chainparams;
fs::path datadir;
std::optional<bool> check_block_index{};
std::optional<int32_t> check_block_index{};
bool checkpoints_enabled{DEFAULT_CHECKPOINTS_ENABLED};
//! If set, it will override the minimum work we will assume exists on some valid chain.
std::optional<arith_uint256> minimum_chain_work{};

View file

@ -24,7 +24,10 @@
namespace node {
util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts)
{
if (auto value{args.GetBoolArg("-checkblockindex")}) opts.check_block_index = *value;
if (auto value{args.GetIntArg("-checkblockindex")}) {
// Interpret bare -checkblockindex argument as 1 instead of 0.
opts.check_block_index = args.GetArg("-checkblockindex")->empty() ? 1 : *value;
}
if (auto value{args.GetBoolArg("-checkpoints")}) opts.checkpoints_enabled = *value;

View file

@ -258,7 +258,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
.datadir = m_args.GetDataDirNet(),
.check_block_index = true,
.check_block_index = 1,
.minimum_chain_work = std::nullopt,
.assumed_valid_block = std::nullopt,
.notifications = *m_node.notifications,

View file

@ -5706,6 +5706,14 @@ void ChainstateManager::LoadExternalBlockFile(
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
}
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;
return true;
}
void ChainstateManager::CheckBlockIndex()
{
if (!ShouldCheckBlockIndex()) {
@ -5722,19 +5730,28 @@ void ChainstateManager::CheckBlockIndex()
return;
}
// Build forward-pointing map of the entire block tree.
// Build forward-pointing data structure for the entire block tree.
// For performance reasons, indexes of the best header chain are stored in a vector (within CChain).
// All remaining blocks are stored in a multimap.
// The best header chain can differ from the active chain: E.g. its entries may belong to blocks that
// are not yet validated.
CChain best_hdr_chain;
assert(m_best_header);
best_hdr_chain.SetTip(*m_best_header);
std::multimap<CBlockIndex*,CBlockIndex*> forward;
for (auto& [_, block_index] : m_blockman.m_block_index) {
forward.emplace(block_index.pprev, &block_index);
// Only save indexes in forward that are not part of the best header chain.
if (!best_hdr_chain.Contains(&block_index)) {
// Only genesis, which must be part of the best header chain, can have a nullptr parent.
assert(block_index.pprev);
forward.emplace(block_index.pprev, &block_index);
}
}
assert(forward.size() + best_hdr_chain.Height() + 1 == m_blockman.m_block_index.size());
assert(forward.size() == m_blockman.m_block_index.size());
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(nullptr);
CBlockIndex *pindex = rangeGenesis.first->second;
rangeGenesis.first++;
assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent nullptr.
CBlockIndex* pindex = best_hdr_chain[0];
assert(pindex);
// Iterate over the entire block tree, using depth-first search.
// Along the way, remember whether there are blocks on the path from genesis
// block being explored which are the first to have certain properties.
@ -5946,14 +5963,21 @@ void ChainstateManager::CheckBlockIndex()
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
// End: actual consistency checks.
// Try descending into the first subnode.
// Try descending into the first subnode. Always process forks first and the best header chain after.
snap_update_firsts();
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> range = forward.equal_range(pindex);
if (range.first != range.second) {
// A subnode was found.
// A subnode not part of the best header chain was found.
pindex = range.first->second;
nHeight++;
continue;
} else if (best_hdr_chain.Contains(pindex)) {
// Descend further into best header chain.
nHeight++;
pindex = best_hdr_chain[nHeight];
if (!pindex) break; // we are finished, since the best header chain is always processed last
continue;
}
// This is a leaf node.
// Move upwards until we reach a node of which we have not yet visited the last child.
@ -5979,9 +6003,15 @@ void ChainstateManager::CheckBlockIndex()
// Proceed to the next one.
rangePar.first++;
if (rangePar.first != rangePar.second) {
// Move to the sibling.
// Move to a sibling not part of the best header chain.
pindex = rangePar.first->second;
break;
} else if (pindexPar == best_hdr_chain[nHeight - 1]) {
// Move to pindex's sibling on the best-chain, if it has one.
pindex = best_hdr_chain[nHeight];
// There will not be a next block if (and only if) parent block is the best header.
assert((pindex == nullptr) == (pindexPar == best_hdr_chain.Tip()));
break;
} else {
// Move up further.
pindex = pindexPar;
@ -5991,8 +6021,8 @@ void ChainstateManager::CheckBlockIndex()
}
}
// Check that we actually traversed the entire map.
assert(nNodes == forward.size());
// Check that we actually traversed the entire block index.
assert(nNodes == forward.size() + best_hdr_chain.Height() + 1);
}
std::string Chainstate::ToString()

View file

@ -948,7 +948,7 @@ public:
const CChainParams& GetParams() const { return m_options.chainparams; }
const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); }
bool ShouldCheckBlockIndex() const { return *Assert(m_options.check_block_index); }
bool ShouldCheckBlockIndex() const;
const arith_uint256& MinimumChainWork() const { return *Assert(m_options.minimum_chain_work); }
const uint256& AssumedValidBlock() const { return *Assert(m_options.assumed_valid_block); }
kernel::Notifications& GetNotifications() const { return m_options.notifications; };