|
|
@@ -153,6 +153,26 @@ namespace { |
|
|
|
/** chainwork for the last block that preciousblock has been applied to. */ |
|
|
|
arith_uint256 nLastPreciousChainwork = 0; |
|
|
|
|
|
|
|
/** In order to efficiently track invalidity of headers, we keep the set of |
|
|
|
* blocks which we tried to connect and found to be invalid here (ie which |
|
|
|
* were set to BLOCK_FAILED_VALID since the last restart). We can then |
|
|
|
* walk this set and check if a new header is a descendant of something in |
|
|
|
* this set, preventing us from having to walk mapBlockIndex when we try |
|
|
|
* to connect a bad block and fail. |
|
|
|
* |
|
|
|
* While this is more complicated than marking everything which descends |
|
|
|
* from an invalid block as invalid at the time we discover it to be |
|
|
|
* invalid, doing so would require walking all of mapBlockIndex to find all |
|
|
|
* descendants. Since this case should be very rare, keeping track of all |
|
|
|
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as |
|
|
|
* well. |
|
|
|
* |
|
|
|
* Because we alreardy walk mapBlockIndex in height-order at startup, we go |
|
|
|
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, |
|
|
|
* instead of putting things in this set. |
|
|
|
*/ |
|
|
|
std::set<CBlockIndex*> g_failed_blocks; |
|
|
|
|
|
|
|
/** Dirty block index entries. */ |
|
|
|
std::set<CBlockIndex*> setDirtyBlockIndex; |
|
|
|
|
|
|
@@ -1168,6 +1188,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) |
|
|
|
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { |
|
|
|
if (!state.CorruptionPossible()) { |
|
|
|
pindex->nStatus |= BLOCK_FAILED_VALID; |
|
|
|
g_failed_blocks.insert(pindex); |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
setBlockIndexCandidates.erase(pindex); |
|
|
|
InvalidChainFound(pindex); |
|
|
@@ -2517,17 +2538,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C |
|
|
|
{ |
|
|
|
AssertLockHeld(cs_main); |
|
|
|
|
|
|
|
// Mark the block itself as invalid. |
|
|
|
pindex->nStatus |= BLOCK_FAILED_VALID; |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
setBlockIndexCandidates.erase(pindex); |
|
|
|
// We first disconnect backwards and then mark the blocks as invalid. |
|
|
|
// This prevents a case where pruned nodes may fail to invalidateblock |
|
|
|
// and be left unable to start as they have no tip candidates (as there |
|
|
|
// are no blocks that meet the "have data and are not invalid per |
|
|
|
// nStatus" criteria for inclusion in setBlockIndexCandidates). |
|
|
|
|
|
|
|
bool pindex_was_in_chain = false; |
|
|
|
CBlockIndex *invalid_walk_tip = chainActive.Tip(); |
|
|
|
|
|
|
|
DisconnectedBlockTransactions disconnectpool; |
|
|
|
while (chainActive.Contains(pindex)) { |
|
|
|
CBlockIndex *pindexWalk = chainActive.Tip(); |
|
|
|
pindexWalk->nStatus |= BLOCK_FAILED_CHILD; |
|
|
|
setDirtyBlockIndex.insert(pindexWalk); |
|
|
|
setBlockIndexCandidates.erase(pindexWalk); |
|
|
|
pindex_was_in_chain = true; |
|
|
|
// ActivateBestChain considers blocks already in chainActive |
|
|
|
// unconditionally valid already, so force disconnect away from it. |
|
|
|
if (!DisconnectTip(state, chainparams, &disconnectpool)) { |
|
|
@@ -2538,6 +2560,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Now mark the blocks we just disconnected as descendants invalid |
|
|
|
// (note this may not be all descendants). |
|
|
|
while (pindex_was_in_chain && invalid_walk_tip != pindex) { |
|
|
|
invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD; |
|
|
|
setDirtyBlockIndex.insert(invalid_walk_tip); |
|
|
|
setBlockIndexCandidates.erase(invalid_walk_tip); |
|
|
|
invalid_walk_tip = invalid_walk_tip->pprev; |
|
|
|
} |
|
|
|
|
|
|
|
// Mark the block itself as invalid. |
|
|
|
pindex->nStatus |= BLOCK_FAILED_VALID; |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
setBlockIndexCandidates.erase(pindex); |
|
|
|
g_failed_blocks.insert(pindex); |
|
|
|
|
|
|
|
// DisconnectTip will add transactions to disconnectpool; try to add these |
|
|
|
// back to the mempool. |
|
|
|
UpdateMempoolForReorg(disconnectpool, true); |
|
|
@@ -2575,6 +2612,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { |
|
|
|
// Reset invalid block marker if it was pointing to one of those. |
|
|
|
pindexBestInvalid = nullptr; |
|
|
|
} |
|
|
|
g_failed_blocks.erase(it->second); |
|
|
|
} |
|
|
|
it++; |
|
|
|
} |
|
|
@@ -3051,6 +3089,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state |
|
|
|
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); |
|
|
|
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) |
|
|
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); |
|
|
|
|
|
|
|
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { |
|
|
|
for (const CBlockIndex* failedit : g_failed_blocks) { |
|
|
|
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { |
|
|
|
assert(failedit->nStatus & BLOCK_FAILED_VALID); |
|
|
|
CBlockIndex* invalid_walk = pindexPrev; |
|
|
|
while (invalid_walk != failedit) { |
|
|
|
invalid_walk->nStatus |= BLOCK_FAILED_CHILD; |
|
|
|
setDirtyBlockIndex.insert(invalid_walk); |
|
|
|
invalid_walk = invalid_walk->pprev; |
|
|
|
} |
|
|
|
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (pindex == nullptr) |
|
|
|
pindex = AddToBlockIndex(block); |
|
|
@@ -3477,6 +3530,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) |
|
|
|
pindex->nChainTx = pindex->nTx; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) { |
|
|
|
pindex->nStatus |= BLOCK_FAILED_CHILD; |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
} |
|
|
|
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr)) |
|
|
|
setBlockIndexCandidates.insert(pindex); |
|
|
|
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) |
|
|
@@ -3867,6 +3924,7 @@ void UnloadBlockIndex() |
|
|
|
nLastBlockFile = 0; |
|
|
|
nBlockSequenceId = 1; |
|
|
|
setDirtyBlockIndex.clear(); |
|
|
|
g_failed_blocks.clear(); |
|
|
|
setDirtyFileInfo.clear(); |
|
|
|
versionbitscache.Clear(); |
|
|
|
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { |