Merge bitcoin/bitcoin#34884: validation: remove unused code in FindMostWorkChain

ba01b00d45 refactor: use for loops in FindMostWorkChain (stratospher)
aa0eef735b test: add InvalidateBlock/ReconsiderBlock asymmetry test (stratospher)
1b0b3e2c2c validation: remove redundant marking in FindMostWorkChain (stratospher)

Pull request description:

  recent PRs like #31405, #30666 mark all `m_block_index` descendants as invalid immediately whenever an invalid block is encountered in `SetBlockFailureFlags`. so by the time we reach `FindMostWorkChain`, the block in `setBlockIndexCandidates` already has `BLOCK_FAILED_VALID` set on it - not just on its ancestor. this means `pindexTest = pindexFailed` whenever `fFailedChain` fires, and the inner `while (pindexTest != pindexFailed)` loop body is never reached!

  I think we can remove it but I've just replaced it with `Assume` in this PR for safety + good to document this invariant in case the code changes in future. (noticed by @ stickies-v in https://github.com/bitcoin/bitcoin/pull/32950#discussion_r2815053885)

  the second commit is unrelated and adds a unit test for the situation in https://github.com/bitcoin/bitcoin/issues/32173

ACKs for top commit:
  fjahr:
    re-ACK ba01b00d45
  optout21:
    crACK ba01b00d45
  w0xlt:
    ACK ba01b00d45
  ryanofsky:
    Code review ACK ba01b00d45, just tweaking comment and for loop condition since last review.

Tree-SHA512: a8be3c30b1c41b76690d16d850e87e9e71fa6a1ecaa8b90ec997ffee1aace48b336a7009a480cd016103759d79c964b3d761a13ae936523808b2930beb68dae5
This commit is contained in:
Ryan Ofsky
2026-04-09 08:31:05 -04:00
2 changed files with 94 additions and 13 deletions

View File

@@ -3127,9 +3127,8 @@ CBlockIndex* Chainstate::FindMostWorkChain()
// Check whether all blocks on the path between the currently active chain and the candidate are valid.
// Just going until the active chain is an optimization, as we know all blocks in it are valid already.
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !m_chain.Contains(pindexTest)) {
for (CBlockIndex *pindexTest = pindexNew; pindexTest && !m_chain.Contains(pindexTest); pindexTest = pindexTest->pprev) {
assert(pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
@@ -3143,27 +3142,21 @@ CBlockIndex* Chainstate::FindMostWorkChain()
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork)) {
m_chainman.m_best_invalid = pindexNew;
}
CBlockIndex *pindexFailed = pindexNew;
// Remove the entire chain from the set.
while (pindexTest != pindexFailed) {
if (fFailedChain) {
pindexFailed->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(pindexFailed);
} else if (fMissingData) {
// If we're missing data, then add back to m_blocks_unlinked,
// so that if the block arrives in the future we can try adding
// to setBlockIndexCandidates again.
for (CBlockIndex *pindexFailed = pindexNew; pindexFailed != pindexTest; pindexFailed = pindexFailed->pprev) {
if (fMissingData && !fFailedChain) {
// If we're missing data and not a descendant of an invalid block,
// then add back to m_blocks_unlinked, so that if the block arrives in the future
// we can try adding to setBlockIndexCandidates again.
m_blockman.m_blocks_unlinked.insert(
std::make_pair(pindexFailed->pprev, pindexFailed));
}
setBlockIndexCandidates.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
setBlockIndexCandidates.erase(pindexTest);
fInvalidAncestor = true;
break;
}
pindexTest = pindexTest->pprev;
}
if (!fInvalidAncestor)
return pindexNew;