-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Track best-possible-headers #12138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Track best-possible-headers #12138
Conversation
When we find an invalid block, instead of adding BLOCK_FAILED_CHILD to its descendants in FindMostWorkChain, iterate setBlockIndexCandidates to find candidate descendants and mark them as BLOCK_FAILED_CHILD immediately, removing them from setBlockIndexCandidates as we go. This keeps BLOCK_FAILED_MASK entries out of setBlockIndexCandidates. This also adds a few checks to CheckBlockIndex, including one which checks that blocks with BLOCK_FAILED_CHILD are not, themselves, marked invalid, but have an invalid parent. This should be fine for most block indexes, however InvalidateBlock previously violated this. Luckily most users shouldn't be running with -checkblockindex Note that this introduces a bug where a block who's header was received but data was not when a ancestor was found to be invalid will not be marked BLOCK_FAILED_CHILD. Thus, when that block is received, it will be added to setBlockIndexCandidates, violating the new invariant. This is fixed in the next commit.
This mirrors setBlockIndexCandidates but for BLOCK_VALID_TREE instead of BLOCK_VALID_TRANSACTIONS && nTx. There are a few differences between the two to keep the new set practical, see code comments for more.
Block index inconsistencies are one of the most critical types of bugs we could see. Making sure we get good coverage of CheckBlockIndex() is pretty important (and there is minimal, if any performance difference in a default run).
Technically, some internal datastructures may be in an inconsistent state if we do this, though there are no known bugs there. Still, for future safety, its much better to only unlock cs_main if we've made progress (not just tried a reorg which may make progress).
pindexBestHeader never considers the invalidity of a chain, only the validity of the header chain. Using it for sync estimation and GETHEADERS etc requests in net makes no sense. Instead, use the new setBlockIndexHeaderCandidates to find the best candidate tip.
Instead of allowing net_processing to inform AcceptBlock as to whether a block is interesting enough to commit to disk, use the new setBlockIndexHeadersCandidates to determine if they lead towards a chain with at least the same (or more) work as our current tip. This further decouples the maze in net_processing from our consensus anti-DoS logic.
e7aa9ce
to
ed5b277
Compare
Great work! |
Note that FindNextBlocksToDownload may want to grow a corresponding nMinimumChainWork check before downloading towards any headers which do not (yet) meet that requirement. Otherwise we'll have a (bandwidth-wasting, but otherwise harmless) infinite download loop on regtest nodes that manually-specify minimum chain work on the command line. |
// to do here. | ||
setBlockIndexHeaderCandidates.erase(it); | ||
break; | ||
parent_present = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, indeed, fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a review of: a9db3da
note that this appears to change the semantics of how often the UpdatedBlockTip() signal will be invoked. That seems like the desired intention but just wanted to mention
fInitialDownload = IsInitialBlockDownload(); | ||
if (fInvalidFound) { | ||
// Wipe cache, we may need another branch now. | ||
pindexMostWork = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FindMostWorkChain() isn't particularly expensive to call redundantly when you know you are on the tip (it amounts to two hashtable lookups)
If you don't mind calling it unnecessarily on your last pass through the loop you can substantially tighten the 3 if statements you have to implement this logic
arith_uint256 starting_work = chainActive.Tip() ? chainActive.Tip()->nChainWork : arith_uint256(0); | ||
bool blocks_connected = false; | ||
do { | ||
// We absolutely may not unlock cs_main until we've made forward progress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there always guaranteed to be progress? From reading this loop it seems like it could interrogate a potential alternate chain - find some invalidity and then roll back to the original tip
@TheBlueMatt did you move this work somewhere else? I guess this should be tagged "up for grabs, including possibly by the original author"? |
…_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes #13912. CC #12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…ks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from #12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…x extra_args fa31ca0 qa: Remove redundant checkmempool/checkblockindex extra_args (MarcoFalke) Pull request description: They are already enabled by default for regtest: https://github.com/bitcoin/bitcoin/blob/df9f71274645a917e2578c52a1c59745bce8112d/src/init.cpp#L1002-L1007 Closes bitcoin#13912. CC bitcoin#12138 Tree-SHA512: b11a3e8cc4715569f917ab89132f8c8dcae64aebcd7a34182675f86cf7f6e207e3187b7ea01a56c92c8c3af76122b6b995e84f533e134676863f8953dc1f0574
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
…ed_blocks in AcceptBlockHeader 66e15e8 Explain GetAncestor check for m_failed_blocks in AcceptBlockHeader (Sjors Provoost) Pull request description: Salvaged (but slightly modified) from bitcoin#12138, the comment there was really helpful to wrap my head around that part of the code. In addition, a naive reader like yours truly will first think `IsValid(BLOCK_VALID_SCRIPTS)` means the previous block was invalid. But IIUC that's not what it means. Instead, it means the block hasn't been checked for validity at the `BLOCK_VALID_SCRIPTS` level yet. So in that case the existing text "previous block index isn't valid" is wrong. Tree-SHA512: 442a319a83290d94697fdf51376463b70454e0f3909d4a45594ddc2e7c26cd19dc703808385a25e26d6d2dddab0aa35ca41722f2e65ee6fe57bbaf62652d3ec8
This adds a setBlockIndexHeaderCandidates which mimics setBlockIndexCandidates and is
The set of all leaf CBlockIndex entries with BLOCK_VALID_TREE (for itself and all ancestors) and
as good as our current tip or better. Entries here are potential future candidates for insertion
into setBlockIndexCandidates, once we get all the required block data. Thus, entries here
represent chains on which we should be actively downloading block data.
Note that we define "as good as our current tip or better" slightly differently here than in
setBlockIndexCandidates - we include things which will have a higher nSequence (but have the
same chain work) here, but do not include such entries in setBlockIndexCandidates. This is
because we prefer to also download towards chains which have the same total work as our current
chain (as an optimization since a reorg is very possible in such cases).
Note that, unlike setBlockIndexCandidates, we only store "leaf" entries here, as we are not as
aggressively prune-able (setBlockIndexCandidates are things which we can, and usually do, try to
connect immediately, and thus entries dont stick around for long). Thus, it may be the case that
chainActive.Tip() is NOT in setBlockIndexHeaderCandidates.
Additionally, unlike setBlockIndexCandidates, we are happy to store entries which are not
connectable due to pruning here.
This is useful as it (finally) disconnects net_processing logic from the "store on disk" logic in validation.cpp. More importantly, it represents what you'd need from the consensus logic to implement a spv-first sync mode, as this provides a best-header which will follow invalidity - always pointing to the best-possible header even after block(s) are found to be invalid.