Do not allow descendants of BLOCK_FAILED_VALID blocks to be BLOCK_FAILED_VALID #16856
pull TheBlueMatt wants to merge 4 commits into bitcoin:master from TheBlueMatt:2019-09-invalidate-block-child-invalid changing 1 files +77 −8-
TheBlueMatt commented at 9:15 pm on September 11, 2019: memberBased on #16849, this was intended primarily to fix n invalid-direction comparison (see the first commit) but discovered there were further issues preventing the addition of a new rule in CheckBlockIndex. We can either go this way, or just delete the code that attempts (incorrectly) to mark blocks BLOCK_FAILED_CHILD in InvalidateBlock and not require this as an invariant (I don’t have a strong opinion here, just PR’ing this as I wrote it during testing and figured I’d demonstrate).
-
Fix block index inconsistency in InvalidateBlock()
Previously, we could release cs_main while leaving the block index in a state that would fail CheckBlockIndex, because setBlockIndexCandidates was not being fully populated before releasing cs_main.
-
Fix invalid_walk_tip comparison in InvalidateBlock 2075bd7774
-
Clarify the final |= BLOCK_FAILED_VALID in InvalidateBlock
This has no functional affect, as the any CBlockIndex*s which to_mark_failed is set to will already have been marked failed.
-
Add a checkfor the previous invalid comparison of invalid_walk_tip
This adds a new test in CheckBlockIndex which tests that any descendants of a block marked BLOCK_FAILED_VALID will not be marked BLOCK_FAILED_VALID as they should never have been connected in the first place. Note that this check requires further changes in InvalidateBlock as if you iteratively call InvalidateBlock to walk the chain backwards (instead of calling InvalidateBlock on an old block and allowing it to do the walking), the later-in-the-chain blocks will violate this.
-
DrahtBot added the label Validation on Sep 11, 2019
-
in src/validation.cpp:2851 in 0ae728197c
2847@@ -2813,33 +2848,62 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c 2848 setDirtyBlockIndex.insert(invalid_walk_tip); 2849 setBlockIndexCandidates.erase(invalid_walk_tip); 2850 setBlockIndexCandidates.insert(invalid_walk_tip->pprev); 2851- if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) { 2852+ if (invalid_walk_tip == to_mark_failed->pprev && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
Sjors commented at 11:35 am on September 30, 2019:I’m confused by this fix. Let’s say we calledinvalidateblock
on block 10. In that caseto_mark_failed
is block 10,to_mark_failed->pprev
is block 9. Let’s say we’re almost done and we just disconnected the tip at height 11. Theninvalid_walk_tip
is block 11. So this comparison always returnsfalse
?
TheBlueMatt commented at 2:57 am on October 3, 2019:If we’re almost done, to_mark_failed got set to block 12 in the last iteration of the loop (yes, these variable names make no sense).
Sjors commented at 4:24 pm on October 3, 2019:Ah, it’s actually
pindex
that stays at block 10 throughout this function.to_mark_failed
is set toinvalid_walk_tip
. By the time we reach this line we chipped one block off of the tip.Looking at the first iteration: let’s say the original tip is block 20.
to_mark_failed
starts at block 10. We disconnect block 20 and mark itBLOCK_FAILED_VALID
. Theif
statement here returnsfalse
, so block 20 temporarily remainsBLOCK_FAILED_VALID
. The second iterationto_mark_failed
points to block 20, so then we set it toBLOCK_FAILED_CHILD
.Do I understand correctly that the reason for this “we fix it the next iteration” is because we might get shut down? So we need to wait with
BLOCK_FAILED_CHILD
until we’re sure the parent isBLOCK_FAILED_VALID
.
TheBlueMatt commented at 4:42 pm on October 3, 2019:Right, I mean its not strictly because we want to be safe in case of shutdown (though that’s nice), and more that this hunk previously never did anything at all (though its intent was “descendants get marked FAILED_CHILD”).laanwj added the label Bug on Sep 30, 2019Sjors commented at 12:08 pm on September 30, 2019: memberLooking at thepindexFirstInvalid
check in isolation (0ae7281), that makes sense to me. If I drop everything except that check, then a whole bunch of functional tests throw an assert:((pindex->nStatus & BLOCK_FAILED_VALID) == 0), function CheckBlockIndex, file validation.cpp, line 4669.
. But I don’t understand the fix itself, at least not the bit above my inline comment.laanwj referenced this in commit 30c2b0b1cb on Oct 2, 2019sidhujag referenced this in commit a7b114133b on Oct 2, 2019jnewbery commented at 4:25 pm on December 13, 2019: memberCan you give a bit of motivation for why we want to mark descendants of
BLOCK_FAILED_VALID
asBLOCK_FAILED_CHILD
? I can’t see any functional difference in the way that aCBlockIndex
is treated when itsnStatus
isBLOCK_FAILED_CHILD
. The only comparisons seem to be againstBLOCK_FAILED_MASK
.If that’s the case, then I think we should just mark all descendants of the block to invalidate as
BLOCK_FAILED_VALID
. Ifreconsiderblock
is later called, thenStatus
of the block and all its descendants are reset anyway. It seems to me thatInvalidateBlock()
would be much simpler if we did that.TheBlueMatt commented at 10:14 pm on December 13, 2019: memberRight, I don’t think we really use it anywhere, I just figured as long as we make the distinction, make the distinction. Maybe @sipa recalls why it was there in the first place?sipa commented at 10:38 pm on December 13, 2019: memberI don’t recall exactly why they’re separate. One point is that BLOCK_FAILED_CHILD doesn’t actually need to be stored on disk, as it is recomputed at db load time. So an advantage of keeping them separate is that if only FAILED_CHILD gets set on a blockindex object, it doesn’t need to be made dirty and written to disk. I’m not sure that optimization is ever taken advantage of (and it probably can be achieved in other ways too).jnewbery commented at 11:05 pm on December 13, 2019: memberI’m not sure that optimization is ever taken advantage of
Looks to me like it isn’t used. I’d vote to just use
BLOCK_FAILED_VALID
and keepInvalidateBlock()
simple.jnewbery commented at 8:16 pm on December 20, 2019: memberAlternative here https://github.com/jnewbery/bitcoin/tree/2019-12-block-failed-valid-descendants just removes tracking ofBLOCK_FAILED_CHILD
. Let me know what you think, @TheBlueMatt .fanquake deleted a comment on Dec 21, 2019BrannonKing commented at 3:35 am on September 6, 2020: none@jnewbery , that branch doesn’t remove all references to BLOCK_FAILED_CHILD. Was the intent to remove them all?jnewbery commented at 8:27 am on September 6, 2020: member@BrannonKing - The intent was to demonstrate how removingBLOCK_FAILED_CHILD
fromInvalidateBlock()
makes it simpler (and therefore less likely to hide bugs). I agree that we should removeBLOCK_FAILED_CHILD
everywhere.jnewbery commented at 10:06 am on October 1, 2020: member@BrannonKing - I’ve rebased my branch on master and removed all the remaining instances of BLOCK_FAILED_CHILD. @TheBlueMatt - This PR hasn’t had any activity for about a year. Are you happy to close it?TheBlueMatt closed this on Oct 28, 2020
UdjinM6 referenced this in commit fba2a546f6 on Oct 29, 2021pravblockc referenced this in commit 37d1b95013 on Nov 18, 2021DrahtBot locked this on Feb 15, 2022
TheBlueMatt Sjors jnewbery sipa BrannonKingLabels
Bug Validation
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-22 21:12 UTC
More mirrored repositories can be found on mirror.b10c.me