@ -153,6 +153,26 @@ namespace {
/** chainwork for the last block that preciousblock has been applied to. */
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0 ;
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. */
/** Dirty block index entries. */
std : : set < CBlockIndex * > setDirtyBlockIndex ;
std : : set < CBlockIndex * > setDirtyBlockIndex ;
@ -1168,6 +1188,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void static InvalidBlockFound ( CBlockIndex * pindex , const CValidationState & state ) {
void static InvalidBlockFound ( CBlockIndex * pindex , const CValidationState & state ) {
if ( ! state . CorruptionPossible ( ) ) {
if ( ! state . CorruptionPossible ( ) ) {
pindex - > nStatus | = BLOCK_FAILED_VALID ;
pindex - > nStatus | = BLOCK_FAILED_VALID ;
g_failed_blocks . insert ( pindex ) ;
setDirtyBlockIndex . insert ( pindex ) ;
setDirtyBlockIndex . insert ( pindex ) ;
setBlockIndexCandidates . erase ( pindex ) ;
setBlockIndexCandidates . erase ( pindex ) ;
InvalidChainFound ( pindex ) ;
InvalidChainFound ( pindex ) ;
@ -2517,17 +2538,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
{
{
AssertLockHeld ( cs_main ) ;
AssertLockHeld ( cs_main ) ;
// Mark the block itself as invalid.
// We first disconnect backwards and then mark the blocks as invalid.
pindex - > nStatus | = BLOCK_FAILED_VALID ;
// This prevents a case where pruned nodes may fail to invalidateblock
setDirtyBlockIndex . insert ( pindex ) ;
// and be left unable to start as they have no tip candidates (as there
setBlockIndexCandidates . erase ( pindex ) ;
// 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 ;
DisconnectedBlockTransactions disconnectpool ;
while ( chainActive . Contains ( pindex ) ) {
while ( chainActive . Contains ( pindex ) ) {
CBlockIndex * pindexWalk = chainActive . Tip ( ) ;
pindex_was_in_chain = true ;
pindexWalk - > nStatus | = BLOCK_FAILED_CHILD ;
setDirtyBlockIndex . insert ( pindexWalk ) ;
setBlockIndexCandidates . erase ( pindexWalk ) ;
// ActivateBestChain considers blocks already in chainActive
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
// unconditionally valid already, so force disconnect away from it.
if ( ! DisconnectTip ( state , chainparams , & disconnectpool ) ) {
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
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
// back to the mempool.
UpdateMempoolForReorg ( disconnectpool , true ) ;
UpdateMempoolForReorg ( disconnectpool , true ) ;
@ -2575,6 +2612,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr ;
pindexBestInvalid = nullptr ;
}
}
g_failed_blocks . erase ( it - > second ) ;
}
}
it + + ;
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 " ) ;
return state . DoS ( 100 , error ( " %s: prev block invalid " , __func__ ) , REJECT_INVALID , " bad-prevblk " ) ;
if ( ! ContextualCheckBlockHeader ( block , state , chainparams , pindexPrev , GetAdjustedTime ( ) ) )
if ( ! ContextualCheckBlockHeader ( block , state , chainparams , pindexPrev , GetAdjustedTime ( ) ) )
return error ( " %s: Consensus::ContextualCheckBlockHeader: %s, %s " , __func__ , hash . ToString ( ) , FormatStateMessage ( state ) ) ;
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 )
if ( pindex = = nullptr )
pindex = AddToBlockIndex ( block ) ;
pindex = AddToBlockIndex ( block ) ;
@ -3477,6 +3530,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindex - > nChainTx = pindex - > nTx ;
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 ) )
if ( pindex - > IsValid ( BLOCK_VALID_TRANSACTIONS ) & & ( pindex - > nChainTx | | pindex - > pprev = = nullptr ) )
setBlockIndexCandidates . insert ( pindex ) ;
setBlockIndexCandidates . insert ( pindex ) ;
if ( pindex - > nStatus & BLOCK_FAILED_MASK & & ( ! pindexBestInvalid | | pindex - > nChainWork > pindexBestInvalid - > nChainWork ) )
if ( pindex - > nStatus & BLOCK_FAILED_MASK & & ( ! pindexBestInvalid | | pindex - > nChainWork > pindexBestInvalid - > nChainWork ) )
@ -3867,6 +3924,7 @@ void UnloadBlockIndex()
nLastBlockFile = 0 ;
nLastBlockFile = 0 ;
nBlockSequenceId = 1 ;
nBlockSequenceId = 1 ;
setDirtyBlockIndex . clear ( ) ;
setDirtyBlockIndex . clear ( ) ;
g_failed_blocks . clear ( ) ;
setDirtyFileInfo . clear ( ) ;
setDirtyFileInfo . clear ( ) ;
versionbitscache . Clear ( ) ;
versionbitscache . Clear ( ) ;
for ( int b = 0 ; b < VERSIONBITS_NUM_BITS ; b + + ) {
for ( int b = 0 ; b < VERSIONBITS_NUM_BITS ; b + + ) {