Brain-dump of what I think happens (haven’t written a test) with InvalidateBlock calling InvalidChainFound in master:
InvalidChainFound will:
- update m_best_invalid
- call SetBlockFailureFlags which updates children to set BLOCK_FAILED_CHILD
- call RecalculateBestHeader
- call CheckForkWarningConditions
to_mark_failed can either be in-chain or out-of-chain. If it’s in-chain, then it will be set to the
last disconnected block. If it’s out-of-chain, it will be unchanged.
If to_mark_failed was in-chain:
- m_best_invalid might be changed to to_mark_failed. Since to_mark_failed was changed, m_best_invalid might not change when it should (what I mentioned above in the previous comment).
- SetBlockFailureFlags: Unnecessary because all of the children have been marked
BLOCK_FAILED_CHILD in the loop.
- RecalculateBestHeader: Unnecessary. If the best header was in-chain, then the
loop already updated it. If the best header is out-of-chain, it was not invalidated.
- CheckForkWarningConditions: Necessary because the tip definitely changed and
m_best_invalid may or may not have changed. Either way, the fork condition may be hit.
Else (to_mark_failed is out-of-chain):
- m_best_invalid might be changed to to_mark_failed. Even here, there may be children that could be
marked as m_best_invalid instead of to_mark_failed, depending on what block the user calls InvalidateBlock on.
- SetBlockFailureFlags: Necessary because if there are children, they need to be marked BLOCK_FAILED_CHILD.
- RecalculateBestHeader: Necessary because the loop above did not run.
- CheckForkWarningConditions: Necessary because m_best_invalid may have changed.
This PR changes things by only updating m_best_invalid and calling CheckForkWarningConditions. This is
fine for the in-chain case, but for the case where to_mark_failed is out-of-chain, I think it’s missing
SetBlockFailureFlags & RecalculateBestHeader?
It should be possible to fix this by calling UpdateBestInvalid also earlier in the process (which we could do more easily after this PR, because unlike old InvalidChainFound, we no longer iterate over the block index).
For the in-chain case of setting m_best_invalid improperly, we could call UpdateBestInvalid where it is and change the argument such that it’s either the old tip or to_mark_failed, depending on pindex_was_in_chain = true?
On the other hand, m_best_invalid is only used for logging of out-of-consensus warnings, and I’m not sure if these logged warnings are really helpful if the consensus failure is self-inflicted through RPC. What do you think?
If the logged warnings aren’t helpful, should they be removed? I don’t have a strong opinion here, I did find it a little confusing that m_best_invalid was being updated improperly in master, maybe a comment could suffice that it’s not being set to the actual best invalid? I didn’t mean to imply that this PR introduced that either btw.