Skip to content

validation: Add eligible ancestors of reconsidered block to setBlockIndexCandidates

When we call reconsiderblock for some block,  Chainstate::ResetBlockFailureFlags puts the descendants of that block into setBlockIndexCandidates (if they meet the criteria, i.e. have more work than the tip etc.), but never put any ancestors into the set even though we do clear their failure flags.

I think that this is wrong, because setBlockIndexCandidates should always contain all eligible indexes that have at least as much work as the current tip, which can include ancestors of the reconsidered block. This is being checked by CheckBlockIndex(), which could fail if it was invoked after ActivateBestChain connects a block and releases cs_main:

diff --git a/src/validation.cpp b/src/validation.cpp
index 7b04bd9a5b..ff0c3c9f58 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3551,6 +3551,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
             }
         }
         // When we reach this point, we switched to a new tip (stored in pindexNewTip).
+        m_chainman.CheckBlockIndex();
 
         if (exited_ibd) {
             // If a background chainstate is in use, we may need to rebalance our

makes rpc_invalidateblock.py fail on master.

Even though we don't currently have a CheckBlockIndex() in that place, after cs_main is released other threads could invoke it, which is happening in the rare failures of #16444 where an invalid header received from another peer could trigger a CheckBlockIndex() call that would fail.

Fix this by adding eligible ancestors to setBlockIndexCandidates in Chainstate::ResetBlockFailureFlags (also simplifying that function a bit).

Fixes #16444

Merge request reports

Loading