@@ -2778,6 +2778,38 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
27782778 bool pindex_was_in_chain = false ;
27792779 int disconnected = 0 ;
27802780
2781+ // We do not allow ActivateBestChain() to run while InvalidateBlock() is
2782+ // running, as that could cause the tip to change while we disconnect
2783+ // blocks.
2784+ LOCK (m_cs_chainstate);
2785+
2786+ // We'll be acquiring and releasing cs_main below, to allow the validation
2787+ // callbacks to run. However, we should keep the block index in a
2788+ // consistent state as we disconnect blocks -- in particular we need to
2789+ // add equal-work blocks to setBlockIndexCandidates as we disconnect.
2790+ // To avoid walking the block index repeatedly in search of candidates,
2791+ // build a map once so that we can look up candidate blocks by chain
2792+ // work as we go.
2793+ std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work;
2794+
2795+ {
2796+ LOCK (cs_main);
2797+ for (const auto & entry : m_blockman.m_block_index ) {
2798+ CBlockIndex *candidate = entry.second ;
2799+ // We don't need to put anything in our active chain into the
2800+ // multimap, because those candidates will be found and considered
2801+ // as we disconnect.
2802+ // Instead, consider only non-active-chain blocks that have at
2803+ // least as much work as where we expect the new tip to end up.
2804+ if (!m_chain.Contains (candidate) &&
2805+ !CBlockIndexWorkComparator ()(candidate, pindex->pprev ) &&
2806+ candidate->IsValid (BLOCK_VALID_TRANSACTIONS) &&
2807+ candidate->HaveTxsDownloaded ()) {
2808+ candidate_blocks_by_work.insert (std::make_pair (candidate->nChainWork , candidate));
2809+ }
2810+ }
2811+ }
2812+
27812813 // Disconnect (descendants of) pindex, and mark them invalid.
27822814 while (true ) {
27832815 if (ShutdownRequested ()) break ;
@@ -2820,11 +2852,24 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
28202852 setDirtyBlockIndex.insert (to_mark_failed);
28212853 }
28222854
2855+ // Add any equal or more work headers to setBlockIndexCandidates
2856+ auto candidate_it = candidate_blocks_by_work.lower_bound (invalid_walk_tip->pprev ->nChainWork );
2857+ while (candidate_it != candidate_blocks_by_work.end ()) {
2858+ if (!CBlockIndexWorkComparator ()(candidate_it->second , invalid_walk_tip->pprev )) {
2859+ setBlockIndexCandidates.insert (candidate_it->second );
2860+ candidate_it = candidate_blocks_by_work.erase (candidate_it);
2861+ } else {
2862+ ++candidate_it;
2863+ }
2864+ }
2865+
28232866 // Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
28242867 // iterations, or, if it's the last one, call InvalidChainFound on it.
28252868 to_mark_failed = invalid_walk_tip;
28262869 }
28272870
2871+ CheckBlockIndex (chainparams.GetConsensus ());
2872+
28282873 {
28292874 LOCK (cs_main);
28302875 if (m_chain.Contains (to_mark_failed)) {
@@ -2838,8 +2883,13 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
28382883 setBlockIndexCandidates.erase (to_mark_failed);
28392884 m_blockman.m_failed_blocks .insert (to_mark_failed);
28402885
2841- // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2842- // add it again.
2886+ // If any new blocks somehow arrived while we were disconnecting
2887+ // (above), then the pre-calculation of what should go into
2888+ // setBlockIndexCandidates may have missed entries. This would
2889+ // technically be an inconsistency in the block index, but if we clean
2890+ // it up here, this should be an essentially unobservable error.
2891+ // Loop back over all block index entries and add any missing entries
2892+ // to setBlockIndexCandidates.
28432893 BlockMap::iterator it = m_blockman.m_block_index .begin ();
28442894 while (it != m_blockman.m_block_index .end ()) {
28452895 if (it->second ->IsValid (BLOCK_VALID_TRANSACTIONS) && it->second ->HaveTxsDownloaded () && !setBlockIndexCandidates.value_comp ()(it->second , m_chain.Tip ())) {
0 commit comments