Skip to content

Commit 0a50019

Browse files
committed
Walk pindexBestHeader back to ChainActive().Tip() if it is invalid
Instead of keeping pindexBestHeader set to the best header we've ever seen, reset it back to our validated tip if we find an ancestor of it turns out to be invalid. While the name is now a bit confusing, this matches much better with how it is used in practice, see below. Further, this opens up more use-cases for it in the future, namely aggressively searching for new peers in case we have discovered (possibly via some covert channel) headers which we do not know to be invalid, but which we cannot find block data for. Places pindexBestHeader is used: * Various GUI displays of the best header and getblockchaininfo["headers"], I don't think changing this is bad, and if anything this is less confusing in the presence of an invalid block. * IsCurrentForFeeEstimation(): If anything I think ensuring pindexBestHeader isn't some crazy invalid chain is better than the alternative, even in the case where you are rejecting the current chain due to hardware error (since hopefully in that case you won't get any new blocks anyway). * ConnectBlock assumevalid checks: We use pindexBestHeader to check that the block we're connecting leads to something with nMinimumChainWork (preventing a user-set assumevalid from having bogus work) and that the block we're connecting leads to pindexBestHeader (I'm not too worried about this one - it's nice to "disable" assumevalid if we have a long invalid headers chain, but I don't see it as a critical protection). * BlockRequestAllowed() uses pindexBestHeader as its target to ensure the requested block is within a month of the "current chain". I don't think this is a meaningful difference, if we're rejecting the current tip we're trivially fingerprintable anyway, and if the chain really does have a bunch of invalid crap near the tip, using the best not-invalid header is likely a better criteria. * ProcessGetBlockData uses pindexBestHeader as the "current chain" definition of whether a block request is "historical" for the purpose of bandwidth limiting. Similarly, I don't see why this is a meaningful change. * We use pindexBestHeader for requesting missing headers on receipt of a headers/compact block message or block inv as well as for initial getheaders. I think this is definitely wrong, using the best not-invalid header for such requests is much better. * We use pindexBestHeader to define the "current chain" for deciding when we're close to done with initial headers sync. I don't think this is a meaningful change. * We use pindexBestHeader to decide if initial headers sync has timed out. If we're rejecting the chain due to hardware error this may result in additional cases where we ban a peer, but this is already true, so I think its fine.
1 parent a6abc94 commit 0a50019

File tree

1 file changed

+6
-0
lines changed

1 file changed

+6
-0
lines changed

src/validation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,10 +1393,14 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) E
13931393
CheckForkWarningConditions();
13941394
}
13951395

1396+
// Called both upon regular invalid block discovery *and* InvalidateBlock
13961397
void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
13971398
{
13981399
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
13991400
pindexBestInvalid = pindexNew;
1401+
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
1402+
pindexBestHeader = ::ChainActive().Tip();
1403+
}
14001404

14011405
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
14021406
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
@@ -1409,6 +1413,8 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
14091413
CheckForkWarningConditions();
14101414
}
14111415

1416+
// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
1417+
// which does its own setBlockIndexCandidates manageent.
14121418
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) {
14131419
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
14141420
pindex->nStatus |= BLOCK_FAILED_VALID;

0 commit comments

Comments
 (0)