Skip to content

Commit 9bb32eb

Browse files
committed
Release cs_main during InvalidateBlock iterations
1 parent 9b1ff5c commit 9bb32eb

File tree

1 file changed

+50
-40
lines changed

1 file changed

+50
-40
lines changed

src/validation.cpp

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2789,64 +2789,74 @@ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIn
27892789

27902790
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
27912791
{
2792-
LOCK(cs_main);
2793-
2794-
// We first disconnect backwards and then mark the blocks as invalid.
2795-
// This prevents a case where pruned nodes may fail to invalidateblock
2796-
// and be left unable to start as they have no tip candidates (as there
2797-
// are no blocks that meet the "have data and are not invalid per
2798-
// nStatus" criteria for inclusion in setBlockIndexCandidates).
2799-
2792+
CBlockIndex* to_mark_failed = pindex;
28002793
bool pindex_was_in_chain = false;
2801-
CBlockIndex *invalid_walk_tip = chainActive.Tip();
28022794

2803-
DisconnectedBlockTransactions disconnectpool;
2804-
while (chainActive.Contains(pindex)) {
2795+
// Disconnect (descendants of) pindex, and mark them invalid.
2796+
while (true) {
2797+
if (ShutdownRequested()) break;
2798+
2799+
LOCK(cs_main);
2800+
if (!chainActive.Contains(pindex)) break;
28052801
pindex_was_in_chain = true;
2802+
CBlockIndex *invalid_walk_tip = chainActive.Tip();
2803+
28062804
// ActivateBestChain considers blocks already in chainActive
28072805
// unconditionally valid already, so force disconnect away from it.
2808-
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
2809-
// It's probably hopeless to try to make the mempool consistent
2810-
// here if DisconnectTip failed, but we can try.
2811-
UpdateMempoolForReorg(disconnectpool, false);
2812-
return false;
2813-
}
2814-
}
2806+
DisconnectedBlockTransactions disconnectpool;
2807+
bool ret = DisconnectTip(state, chainparams, &disconnectpool);
2808+
// DisconnectTip will add transactions to disconnectpool.
2809+
// Adjust the mempool to be consistent with the new tip, adding
2810+
// transactions back to the mempool if disconnecting was succesful.
2811+
UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ ret);
2812+
if (!ret) return false;
2813+
assert(invalid_walk_tip->pprev == chainActive.Tip());
28152814

2816-
// Now mark the blocks we just disconnected as descendants invalid
2817-
// (note this may not be all descendants).
2818-
while (pindex_was_in_chain && invalid_walk_tip != pindex) {
2815+
// We immediately mark the disconnected blocks as invalid.
2816+
// This prevents a case where pruned nodes may fail to invalidateblock
2817+
// and be left unable to start as they have no tip candidates (as there
2818+
// are no blocks that meet the "have data and are not invalid per
2819+
// nStatus" criteria for inclusion in setBlockIndexCandidates).
28192820
invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
28202821
setDirtyBlockIndex.insert(invalid_walk_tip);
28212822
setBlockIndexCandidates.erase(invalid_walk_tip);
2822-
invalid_walk_tip = invalid_walk_tip->pprev;
2823+
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
2824+
2825+
// If we abort invalidation after this iteration, make sure
2826+
// the last disconnected block gets marked failed (rather than
2827+
// just child of failed)
2828+
to_mark_failed = invalid_walk_tip;
28232829
}
28242830

2825-
// Mark the block itself as invalid.
2826-
pindex->nStatus |= BLOCK_FAILED_VALID;
2827-
setDirtyBlockIndex.insert(pindex);
2828-
setBlockIndexCandidates.erase(pindex);
2829-
m_failed_blocks.insert(pindex);
2831+
{
2832+
// Mark pindex (or the last disconnected block) as invalid, regardless of whether it was in the main chain or not.
2833+
LOCK(cs_main);
2834+
if (chainActive.Contains(to_mark_failed)) {
2835+
// If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.
2836+
return false;
2837+
}
28302838

2831-
// DisconnectTip will add transactions to disconnectpool; try to add these
2832-
// back to the mempool.
2833-
UpdateMempoolForReorg(disconnectpool, true);
2839+
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
2840+
setDirtyBlockIndex.insert(to_mark_failed);
2841+
setBlockIndexCandidates.erase(to_mark_failed);
2842+
m_failed_blocks.insert(to_mark_failed);
28342843

2835-
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2836-
// add it again.
2837-
BlockMap::iterator it = mapBlockIndex.begin();
2838-
while (it != mapBlockIndex.end()) {
2839-
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
2840-
setBlockIndexCandidates.insert(it->second);
2844+
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2845+
// add it again.
2846+
BlockMap::iterator it = mapBlockIndex.begin();
2847+
while (it != mapBlockIndex.end()) {
2848+
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
2849+
setBlockIndexCandidates.insert(it->second);
2850+
}
2851+
it++;
28412852
}
2842-
it++;
2843-
}
28442853

2845-
InvalidChainFound(pindex);
2854+
InvalidChainFound(to_mark_failed);
2855+
}
28462856

28472857
// Only notify about a new block tip if the active chain was modified.
28482858
if (pindex_was_in_chain) {
2849-
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
2859+
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), to_mark_failed->pprev);
28502860
}
28512861
return true;
28522862
}

0 commit comments

Comments
 (0)