@@ -156,6 +156,26 @@ namespace {
156
156
/* * chainwork for the last block that preciousblock has been applied to. */
157
157
arith_uint256 nLastPreciousChainwork = 0 ;
158
158
159
+ /* * In order to efficiently track invalidity of headers, we keep the set of
160
+ * blocks which we tried to connect and found to be invalid here (ie which
161
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
162
+ * walk this set and check if a new header is a descendant of something in
163
+ * this set, preventing us from having to walk mapBlockIndex when we try
164
+ * to connect a bad block and fail.
165
+ *
166
+ * While this is more complicated than marking everything which descends
167
+ * from an invalid block as invalid at the time we discover it to be
168
+ * invalid, doing so would require walking all of mapBlockIndex to find all
169
+ * descendants. Since this case should be very rare, keeping track of all
170
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
171
+ * well.
172
+ *
173
+ * Because we alreardy walk mapBlockIndex in height-order at startup, we go
174
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
175
+ * instead of putting things in this set.
176
+ */
177
+ std::set<CBlockIndex*> g_failed_blocks;
178
+
159
179
/* * Dirty block index entries. */
160
180
std::set<CBlockIndex*> setDirtyBlockIndex;
161
181
@@ -1180,6 +1200,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
1180
1200
void static InvalidBlockFound (CBlockIndex *pindex, const CValidationState &state) {
1181
1201
if (!state.CorruptionPossible ()) {
1182
1202
pindex->nStatus |= BLOCK_FAILED_VALID;
1203
+ g_failed_blocks.insert (pindex);
1183
1204
setDirtyBlockIndex.insert (pindex);
1184
1205
setBlockIndexCandidates.erase (pindex);
1185
1206
InvalidChainFound (pindex);
@@ -2533,17 +2554,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2533
2554
{
2534
2555
AssertLockHeld (cs_main);
2535
2556
2536
- // Mark the block itself as invalid.
2537
- pindex->nStatus |= BLOCK_FAILED_VALID;
2538
- setDirtyBlockIndex.insert (pindex);
2539
- setBlockIndexCandidates.erase (pindex);
2557
+ // We first disconnect backwards and then mark the blocks as invalid.
2558
+ // This prevents a case where pruned nodes may fail to invalidateblock
2559
+ // and be left unable to start as they have no tip candidates (as there
2560
+ // are no blocks that meet the "have data and are not invalid per
2561
+ // nStatus" criteria for inclusion in setBlockIndexCandidates).
2562
+
2563
+ bool pindex_was_in_chain = false ;
2564
+ CBlockIndex *invalid_walk_tip = chainActive.Tip ();
2540
2565
2541
2566
DisconnectedBlockTransactions disconnectpool;
2542
2567
while (chainActive.Contains (pindex)) {
2543
- CBlockIndex *pindexWalk = chainActive.Tip ();
2544
- pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
2545
- setDirtyBlockIndex.insert (pindexWalk);
2546
- setBlockIndexCandidates.erase (pindexWalk);
2568
+ pindex_was_in_chain = true ;
2547
2569
// ActivateBestChain considers blocks already in chainActive
2548
2570
// unconditionally valid already, so force disconnect away from it.
2549
2571
if (!DisconnectTip (state, chainparams, &disconnectpool)) {
@@ -2554,6 +2576,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2554
2576
}
2555
2577
}
2556
2578
2579
+ // Now mark the blocks we just disconnected as descendants invalid
2580
+ // (note this may not be all descendants).
2581
+ while (pindex_was_in_chain && invalid_walk_tip != pindex) {
2582
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
2583
+ setDirtyBlockIndex.insert (invalid_walk_tip);
2584
+ setBlockIndexCandidates.erase (invalid_walk_tip);
2585
+ invalid_walk_tip = invalid_walk_tip->pprev ;
2586
+ }
2587
+
2588
+ // Mark the block itself as invalid.
2589
+ pindex->nStatus |= BLOCK_FAILED_VALID;
2590
+ setDirtyBlockIndex.insert (pindex);
2591
+ setBlockIndexCandidates.erase (pindex);
2592
+ g_failed_blocks.insert (pindex);
2593
+
2557
2594
// DisconnectTip will add transactions to disconnectpool; try to add these
2558
2595
// back to the mempool.
2559
2596
UpdateMempoolForReorg (disconnectpool, true );
@@ -2591,6 +2628,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
2591
2628
// Reset invalid block marker if it was pointing to one of those.
2592
2629
pindexBestInvalid = nullptr ;
2593
2630
}
2631
+ g_failed_blocks.erase (it->second );
2594
2632
}
2595
2633
it++;
2596
2634
}
@@ -3066,6 +3104,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
3066
3104
return state.DoS (100 , error (" %s: prev block invalid" , __func__), REJECT_INVALID, " bad-prevblk" );
3067
3105
if (!ContextualCheckBlockHeader (block, state, chainparams, pindexPrev, GetAdjustedTime ()))
3068
3106
return error (" %s: Consensus::ContextualCheckBlockHeader: %s, %s" , __func__, hash.ToString (), FormatStateMessage (state));
3107
+
3108
+ if (!pindexPrev->IsValid (BLOCK_VALID_SCRIPTS)) {
3109
+ for (const CBlockIndex* failedit : g_failed_blocks) {
3110
+ if (pindexPrev->GetAncestor (failedit->nHeight ) == failedit) {
3111
+ assert (failedit->nStatus & BLOCK_FAILED_VALID);
3112
+ CBlockIndex* invalid_walk = pindexPrev;
3113
+ while (invalid_walk != failedit) {
3114
+ invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
3115
+ setDirtyBlockIndex.insert (invalid_walk);
3116
+ invalid_walk = invalid_walk->pprev ;
3117
+ }
3118
+ return state.DoS (100 , error (" %s: prev block invalid" , __func__), REJECT_INVALID, " bad-prevblk" );
3119
+ }
3120
+ }
3121
+ }
3069
3122
}
3070
3123
if (pindex == nullptr )
3071
3124
pindex = AddToBlockIndex (block);
@@ -3117,7 +3170,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
3117
3170
// process an unrequested block if it's new and has enough work to
3118
3171
// advance our tip, and isn't too many blocks ahead.
3119
3172
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
3120
- bool fHasMoreWork = (chainActive.Tip () ? pindex->nChainWork > chainActive.Tip ()->nChainWork : true );
3173
+ bool fHasMoreOrSameWork = (chainActive.Tip () ? pindex->nChainWork >= chainActive.Tip ()->nChainWork : true );
3121
3174
// Blocks that are too out-of-order needlessly limit the effectiveness of
3122
3175
// pruning, because pruning will not delete block files that contain any
3123
3176
// blocks which are too close in height to the tip. Apply this test
@@ -3134,9 +3187,9 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
3134
3187
// and unrequested blocks.
3135
3188
if (fAlreadyHave ) return true ;
3136
3189
if (!fRequested ) { // If we didn't ask for it:
3137
- if (pindex->nTx != 0 ) return true ; // This is a previously-processed block that was pruned
3138
- if (!fHasMoreWork ) return true ; // Don't process less-work chains
3139
- if (fTooFarAhead ) return true ; // Block height is too high
3190
+ if (pindex->nTx != 0 ) return true ; // This is a previously-processed block that was pruned
3191
+ if (!fHasMoreOrSameWork ) return true ; // Don't process less-work chains
3192
+ if (fTooFarAhead ) return true ; // Block height is too high
3140
3193
3141
3194
// Protect against DoS attacks from low-work chains.
3142
3195
// If our tip is behind, a peer could try to send us
@@ -3494,6 +3547,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
3494
3547
pindex->nChainTx = pindex->nTx ;
3495
3548
}
3496
3549
}
3550
+ if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev ->nStatus & BLOCK_FAILED_MASK)) {
3551
+ pindex->nStatus |= BLOCK_FAILED_CHILD;
3552
+ setDirtyBlockIndex.insert (pindex);
3553
+ }
3497
3554
if (pindex->IsValid (BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr ))
3498
3555
setBlockIndexCandidates.insert (pindex);
3499
3556
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork ))
@@ -3884,6 +3941,7 @@ void UnloadBlockIndex()
3884
3941
nLastBlockFile = 0 ;
3885
3942
nBlockSequenceId = 1 ;
3886
3943
setDirtyBlockIndex.clear ();
3944
+ g_failed_blocks.clear ();
3887
3945
setDirtyFileInfo.clear ();
3888
3946
versionbitscache.Clear ();
3889
3947
for (int b = 0 ; b < VERSIONBITS_NUM_BITS; b++) {
0 commit comments