Skip to content

Commit 3556b85

Browse files
committed
Move CheckBlockIndex() from Chainstate to ChainstateManager
Also rewrite CheckBlockIndex() to perform tests on all chainstates. This increases sanity-check coverage, as any place in our code where we were invoke CheckBlockIndex() on a single chainstate will now invoke the sanity checks on all chainstates. This change also tightens up the checks on setBlockIndexCandidates and mapBlocksUnlinked, to more precisely match what we aim for even in the presence of assumed-valid blocks.
1 parent 0ce805b commit 3556b85

File tree

2 files changed

+55
-45
lines changed

2 files changed

+55
-45
lines changed

src/validation.cpp

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,7 +3193,8 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
31933193
// that the best block hash is non-null.
31943194
if (m_chainman.m_interrupt) break;
31953195
} while (pindexNewTip != pindexMostWork);
3196-
CheckBlockIndex();
3196+
3197+
m_chainman.CheckBlockIndex();
31973198

31983199
// Write changes periodically to disk, after relay.
31993200
if (!FlushStateToDisk(state, FlushStateMode::PERIODIC)) {
@@ -3339,7 +3340,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
33393340
to_mark_failed = invalid_walk_tip;
33403341
}
33413342

3342-
CheckBlockIndex();
3343+
m_chainman.CheckBlockIndex();
33433344

33443345
{
33453346
LOCK(cs_main);
@@ -3882,7 +3883,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
38823883
for (const CBlockHeader& header : headers) {
38833884
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
38843885
bool accepted{AcceptBlockHeader(header, state, &pindex, min_pow_checked)};
3885-
ActiveChainstate().CheckBlockIndex();
3886+
CheckBlockIndex();
38863887

38873888
if (!accepted) {
38883889
return false;
@@ -3940,7 +3941,7 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
39403941
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
39413942

39423943
bool accepted_header{AcceptBlockHeader(block, state, &pindex, min_pow_checked)};
3943-
ActiveChainstate().CheckBlockIndex();
3944+
CheckBlockIndex();
39443945

39453946
if (!accepted_header)
39463947
return false;
@@ -4017,7 +4018,7 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
40174018
// background validation yet).
40184019
ActiveChainstate().FlushStateToDisk(state, FlushStateMode::NONE);
40194020

4020-
ActiveChainstate().CheckBlockIndex();
4021+
CheckBlockIndex();
40214022

40224023
return true;
40234024
}
@@ -4676,9 +4677,9 @@ void ChainstateManager::LoadExternalBlockFile(
46764677
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
46774678
}
46784679

4679-
void Chainstate::CheckBlockIndex()
4680+
void ChainstateManager::CheckBlockIndex()
46804681
{
4681-
if (!m_chainman.ShouldCheckBlockIndex()) {
4682+
if (!ShouldCheckBlockIndex()) {
46824683
return;
46834684
}
46844685

@@ -4687,7 +4688,7 @@ void Chainstate::CheckBlockIndex()
46874688
// During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain,
46884689
// so we have the genesis block in m_blockman.m_block_index but no active chain. (A few of the
46894690
// tests when iterating the block tree require that m_chain has been initialized.)
4690-
if (m_chain.Height() < 0) {
4691+
if (ActiveChain().Height() < 0) {
46914692
assert(m_blockman.m_block_index.size() <= 1);
46924693
return;
46934694
}
@@ -4751,8 +4752,12 @@ void Chainstate::CheckBlockIndex()
47514752
// Begin: actual consistency checks.
47524753
if (pindex->pprev == nullptr) {
47534754
// Genesis block checks.
4754-
assert(pindex->GetBlockHash() == m_chainman.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
4755-
assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
4755+
assert(pindex->GetBlockHash() == GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
4756+
for (auto c : GetAll()) {
4757+
if (c->m_chain.Genesis() != nullptr) {
4758+
assert(pindex == c->m_chain.Genesis()); // The chain's genesis block must be this block.
4759+
}
4760+
}
47564761
}
47574762
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
47584763
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
@@ -4798,27 +4803,32 @@ void Chainstate::CheckBlockIndex()
47984803
// Checks for not-invalid blocks.
47994804
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
48004805
}
4801-
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
4802-
if (pindexFirstInvalid == nullptr) {
4803-
const bool is_active = this == &m_chainman.ActiveChainstate();
4804-
4805-
// If this block sorts at least as good as the current tip and
4806-
// is valid and we have all data for its parents, it must be in
4807-
// setBlockIndexCandidates. m_chain.Tip() must also be there
4808-
// even if some data has been pruned.
4809-
//
4810-
// Don't perform this check for the background chainstate since
4811-
// its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
4812-
// snapshot block) which do exist in the block index for the active chainstate.
4813-
if (is_active && (pindexFirstMissing == nullptr || pindex == m_chain.Tip())) {
4814-
assert(setBlockIndexCandidates.count(pindex));
4806+
// Chainstate-specific checks on setBlockIndexCandidates
4807+
for (auto c : GetAll()) {
4808+
if (c->m_chain.Tip() == nullptr) continue;
4809+
if (!CBlockIndexWorkComparator()(pindex, c->m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
4810+
if (pindexFirstInvalid == nullptr) {
4811+
const bool is_active = c == &ActiveChainstate();
4812+
// If this block sorts at least as good as the current tip and
4813+
// is valid and we have all data for its parents, it must be in
4814+
// setBlockIndexCandidates. m_chain.Tip() must also be there
4815+
// even if some data has been pruned.
4816+
//
4817+
if ((pindexFirstMissing == nullptr || pindex == c->m_chain.Tip())) {
4818+
// The active chainstate should always have this block
4819+
// as a candidate, but a background chainstate should
4820+
// only have it if it is an ancestor of the snapshot base.
4821+
if (is_active || GetSnapshotBaseBlock()->GetAncestor(pindex->nHeight) == pindex) {
4822+
assert(c->setBlockIndexCandidates.count(pindex));
4823+
}
4824+
}
4825+
// If some parent is missing, then it could be that this block was in
4826+
// setBlockIndexCandidates but had to be removed because of the missing data.
4827+
// In this case it must be in m_blocks_unlinked -- see test below.
48154828
}
4816-
// If some parent is missing, then it could be that this block was in
4817-
// setBlockIndexCandidates but had to be removed because of the missing data.
4818-
// In this case it must be in m_blocks_unlinked -- see test below.
4829+
} else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates.
4830+
assert(c->setBlockIndexCandidates.count(pindex) == 0);
48194831
}
4820-
} else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates.
4821-
assert(setBlockIndexCandidates.count(pindex) == 0);
48224832
}
48234833
// Check whether this block is in m_blocks_unlinked.
48244834
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
@@ -4846,16 +4856,16 @@ void Chainstate::CheckBlockIndex()
48464856
// - we tried switching to that descendant but were missing
48474857
// data for some intermediate block between m_chain and the
48484858
// tip.
4849-
// So if this block is itself better than m_chain.Tip() and it wasn't in
4859+
// So if this block is itself better than any m_chain.Tip() and it wasn't in
48504860
// setBlockIndexCandidates, then it must be in m_blocks_unlinked.
4851-
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
4852-
if (pindexFirstInvalid == nullptr && pindexFirstAssumeValid == nullptr) {
4853-
// If this is a chain based on an assumeutxo snapshot, then
4854-
// this block could either be in mapBlocksUnlinked or in
4855-
// setBlockIndexCandidates; it may take a call to
4856-
// FindMostWorkChain() to figure out whether all the blocks
4857-
// between the tip and this block are actually available.
4858-
assert(foundInUnlinked);
4861+
for (auto c : GetAll()) {
4862+
const bool is_active = c == &ActiveChainstate();
4863+
if (!CBlockIndexWorkComparator()(pindex, c->m_chain.Tip()) && c->setBlockIndexCandidates.count(pindex) == 0) {
4864+
if (pindexFirstInvalid == nullptr) {
4865+
if (is_active || GetSnapshotBaseBlock()->GetAncestor(pindex->nHeight) == pindex) {
4866+
assert(foundInUnlinked);
4867+
}
4868+
}
48594869
}
48604870
}
48614871
}

src/validation.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -706,13 +706,6 @@ class Chainstate
706706
/** Find the last common block of this chain and a locator. */
707707
const CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
708708

709-
/**
710-
* Make various assertions about the state of the block index.
711-
*
712-
* By default this only executes fully when using the Regtest chain; see: m_options.check_block_index.
713-
*/
714-
void CheckBlockIndex();
715-
716709
/** Load the persisted mempool from disk */
717710
void LoadMempool(const fs::path& load_path, fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
718711

@@ -927,6 +920,13 @@ class ChainstateManager
927920
const uint256& AssumedValidBlock() const { return *Assert(m_options.assumed_valid_block); }
928921
kernel::Notifications& GetNotifications() const { return m_options.notifications; };
929922

923+
/**
924+
* Make various assertions about the state of the block index.
925+
*
926+
* By default this only executes fully when using the Regtest chain; see: m_options.check_block_index.
927+
*/
928+
void CheckBlockIndex();
929+
930930
/**
931931
* Alias for ::cs_main.
932932
* Should be used in new code to make it easier to make ::cs_main a member

0 commit comments

Comments
 (0)