@@ -2778,6 +2778,38 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
2778
2778
bool pindex_was_in_chain = false ;
2779
2779
int disconnected = 0 ;
2780
2780
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
+
2781
2813
// Disconnect (descendants of) pindex, and mark them invalid.
2782
2814
while (true ) {
2783
2815
if (ShutdownRequested ()) break ;
@@ -2820,11 +2852,24 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
2820
2852
setDirtyBlockIndex.insert (to_mark_failed);
2821
2853
}
2822
2854
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
+
2823
2866
// Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
2824
2867
// iterations, or, if it's the last one, call InvalidChainFound on it.
2825
2868
to_mark_failed = invalid_walk_tip;
2826
2869
}
2827
2870
2871
+ CheckBlockIndex (chainparams.GetConsensus ());
2872
+
2828
2873
{
2829
2874
LOCK (cs_main);
2830
2875
if (m_chain.Contains (to_mark_failed)) {
@@ -2838,8 +2883,13 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
2838
2883
setBlockIndexCandidates.erase (to_mark_failed);
2839
2884
m_blockman.m_failed_blocks .insert (to_mark_failed);
2840
2885
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.
2843
2893
BlockMap::iterator it = m_blockman.m_block_index .begin ();
2844
2894
while (it != m_blockman.m_block_index .end ()) {
2845
2895
if (it->second ->IsValid (BLOCK_VALID_TRANSACTIONS) && it->second ->HaveTxsDownloaded () && !setBlockIndexCandidates.value_comp ()(it->second , m_chain.Tip ())) {
0 commit comments