Skip to content

Commit 141fbe4

Browse files
committed
Merge bitcoin/bitcoin#34884: validation: remove unused code in FindMostWorkChain
ba01b00 refactor: use for loops in FindMostWorkChain (stratospher) aa0eef7 test: add InvalidateBlock/ReconsiderBlock asymmetry test (stratospher) 1b0b3e2 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 bitcoin/bitcoin#32950 (comment)) the second commit is unrelated and adds a unit test for the situation in bitcoin/bitcoin#32173 ACKs for top commit: fjahr: re-ACK ba01b00 optout21: crACK ba01b00 w0xlt: ACK ba01b00 ryanofsky: Code review ACK ba01b00, just tweaking comment and for loop condition since last review. Tree-SHA512: a8be3c30b1c41b76690d16d850e87e9e71fa6a1ecaa8b90ec997ffee1aace48b336a7009a480cd016103759d79c964b3d761a13ae936523808b2930beb68dae5
2 parents 2b541ee + ba01b00 commit 141fbe4

File tree

2 files changed

+94
-13
lines changed

2 files changed

+94
-13
lines changed

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,94 @@ BOOST_FIXTURE_TEST_CASE(loadblockindex_invalid_descendants, TestChain100Setup)
618618
BOOST_CHECK(child->nStatus & BLOCK_FAILED_VALID);
619619
}
620620

621+
//! Verify that ReconsiderBlock clears failure flags for the target block, its ancestors, and descendants,
622+
//! but not for sibling forks that diverge from a shared ancestor.
623+
BOOST_FIXTURE_TEST_CASE(invalidate_block_and_reconsider_fork, TestChain100Setup)
624+
{
625+
ChainstateManager& chainman = *Assert(m_node.chainman);
626+
Chainstate& chainstate = chainman.ActiveChainstate();
627+
628+
// we have a chain of 100 blocks: genesis(0) <- ... <- block98 <- block99 <- block100
629+
CBlockIndex* block98;
630+
CBlockIndex* block99;
631+
CBlockIndex* block100;
632+
{
633+
LOCK(chainman.GetMutex());
634+
block98 = chainman.ActiveChain()[98];
635+
block99 = chainman.ActiveChain()[99];
636+
block100 = chainman.ActiveChain()[100];
637+
}
638+
639+
// create the following block constellation:
640+
// genesis(0) <- ... <- block98 <- block99 <- block100
641+
// <- block99' <- block100'
642+
// by temporarily invalidating block99. the chain tip now falls to block98,
643+
// mine 2 new blocks on top of block 98 (block99' and block100') and then restore block99 and block 100.
644+
BlockValidationState state;
645+
BOOST_REQUIRE(chainstate.InvalidateBlock(state, block99));
646+
BOOST_REQUIRE(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()) == block98);
647+
CScript coinbase_script = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
648+
for (int i = 0; i < 2; ++i) {
649+
CreateAndProcessBlock({}, coinbase_script);
650+
}
651+
const CBlockIndex* fork_block99;
652+
const CBlockIndex* fork_block100;
653+
{
654+
LOCK(chainman.GetMutex());
655+
fork_block99 = chainman.ActiveChain()[99];
656+
BOOST_REQUIRE(fork_block99->pprev == block98);
657+
fork_block100 = chainman.ActiveChain()[100];
658+
BOOST_REQUIRE(fork_block100->pprev == fork_block99);
659+
}
660+
// Restore original block99 and block100
661+
{
662+
LOCK(chainman.GetMutex());
663+
chainstate.ResetBlockFailureFlags(block99);
664+
chainman.RecalculateBestHeader();
665+
}
666+
chainstate.ActivateBestChain(state);
667+
BOOST_REQUIRE(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()) == block100);
668+
669+
{
670+
LOCK(chainman.GetMutex());
671+
BOOST_CHECK(!(block100->nStatus & BLOCK_FAILED_VALID));
672+
BOOST_CHECK(!(block99->nStatus & BLOCK_FAILED_VALID));
673+
BOOST_CHECK(!(fork_block100->nStatus & BLOCK_FAILED_VALID));
674+
BOOST_CHECK(!(fork_block99->nStatus & BLOCK_FAILED_VALID));
675+
}
676+
677+
// Invalidate block98
678+
BOOST_REQUIRE(chainstate.InvalidateBlock(state, block98));
679+
680+
{
681+
LOCK(chainman.GetMutex());
682+
// block98 and all descendants of block98 are marked BLOCK_FAILED_VALID
683+
BOOST_CHECK(block98->nStatus & BLOCK_FAILED_VALID);
684+
BOOST_CHECK(block99->nStatus & BLOCK_FAILED_VALID);
685+
BOOST_CHECK(block100->nStatus & BLOCK_FAILED_VALID);
686+
BOOST_CHECK(fork_block99->nStatus & BLOCK_FAILED_VALID);
687+
BOOST_CHECK(fork_block100->nStatus & BLOCK_FAILED_VALID);
688+
}
689+
690+
// Reconsider block99. ResetBlockFailureFlags clears BLOCK_FAILED_VALID from
691+
// block99 and its ancestors (block98) and descendants (block100)
692+
// but NOT from block99' and block100' (not a direct ancestor/descendant)
693+
{
694+
LOCK(chainman.GetMutex());
695+
chainstate.ResetBlockFailureFlags(block99);
696+
chainman.RecalculateBestHeader();
697+
}
698+
chainstate.ActivateBestChain(state);
699+
{
700+
LOCK(chainman.GetMutex());
701+
BOOST_CHECK(!(block98->nStatus & BLOCK_FAILED_VALID));
702+
BOOST_CHECK(!(block99->nStatus & BLOCK_FAILED_VALID));
703+
BOOST_CHECK(!(block100->nStatus & BLOCK_FAILED_VALID));
704+
BOOST_CHECK(fork_block99->nStatus & BLOCK_FAILED_VALID);
705+
BOOST_CHECK(fork_block100->nStatus & BLOCK_FAILED_VALID);
706+
}
707+
}
708+
621709
//! Ensure that snapshot chainstate can be loaded when found on disk after a
622710
//! restart, and that new blocks can be connected to both chainstates.
623711
BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)

src/validation.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3127,9 +3127,8 @@ CBlockIndex* Chainstate::FindMostWorkChain()
31273127

31283128
// Check whether all blocks on the path between the currently active chain and the candidate are valid.
31293129
// Just going until the active chain is an optimization, as we know all blocks in it are valid already.
3130-
CBlockIndex *pindexTest = pindexNew;
31313130
bool fInvalidAncestor = false;
3132-
while (pindexTest && !m_chain.Contains(pindexTest)) {
3131+
for (CBlockIndex *pindexTest = pindexNew; pindexTest && !m_chain.Contains(pindexTest); pindexTest = pindexTest->pprev) {
31333132
assert(pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0);
31343133

31353134
// Pruned nodes may have entries in setBlockIndexCandidates for
@@ -3143,27 +3142,21 @@ CBlockIndex* Chainstate::FindMostWorkChain()
31433142
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork)) {
31443143
m_chainman.m_best_invalid = pindexNew;
31453144
}
3146-
CBlockIndex *pindexFailed = pindexNew;
31473145
// Remove the entire chain from the set.
3148-
while (pindexTest != pindexFailed) {
3149-
if (fFailedChain) {
3150-
pindexFailed->nStatus |= BLOCK_FAILED_VALID;
3151-
m_blockman.m_dirty_blockindex.insert(pindexFailed);
3152-
} else if (fMissingData) {
3153-
// If we're missing data, then add back to m_blocks_unlinked,
3154-
// so that if the block arrives in the future we can try adding
3155-
// to setBlockIndexCandidates again.
3146+
for (CBlockIndex *pindexFailed = pindexNew; pindexFailed != pindexTest; pindexFailed = pindexFailed->pprev) {
3147+
if (fMissingData && !fFailedChain) {
3148+
// If we're missing data and not a descendant of an invalid block,
3149+
// then add back to m_blocks_unlinked, so that if the block arrives in the future
3150+
// we can try adding to setBlockIndexCandidates again.
31563151
m_blockman.m_blocks_unlinked.insert(
31573152
std::make_pair(pindexFailed->pprev, pindexFailed));
31583153
}
31593154
setBlockIndexCandidates.erase(pindexFailed);
3160-
pindexFailed = pindexFailed->pprev;
31613155
}
31623156
setBlockIndexCandidates.erase(pindexTest);
31633157
fInvalidAncestor = true;
31643158
break;
31653159
}
3166-
pindexTest = pindexTest->pprev;
31673160
}
31683161
if (!fInvalidAncestor)
31693162
return pindexNew;

0 commit comments

Comments
 (0)