@@ -96,7 +96,7 @@ namespace {
96
96
97
97
struct CBlockIndexWorkComparator
98
98
{
99
- bool operator ()(CBlockIndex *pa, CBlockIndex *pb) const {
99
+ bool operator ()(const CBlockIndex *pa, const CBlockIndex *pb) const {
100
100
// First sort by most total work, ...
101
101
if (pa->nChainWork > pb->nChainWork ) return false ;
102
102
if (pa->nChainWork < pb->nChainWork ) return true ;
@@ -1331,17 +1331,19 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
1331
1331
return DISCONNECT_FAILED; // adding output for transaction without known metadata
1332
1332
}
1333
1333
}
1334
- view.AddCoin (out, std::move (undo), undo.fCoinBase );
1334
+ // The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
1335
+ // sure that the coin did not already exist in the cache. As we have queried for that above
1336
+ // using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
1337
+ // it is an overwrite.
1338
+ view.AddCoin (out, std::move (undo), !fClean );
1335
1339
1336
1340
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
1337
1341
}
1338
1342
1339
1343
/* * Undo the effects of this block (with given index) on the UTXO set represented by coins.
1340
- * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */
1344
+ * When FAILED is returned, view is left in an indeterminate state. */
1341
1345
static DisconnectResult DisconnectBlock (const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
1342
1346
{
1343
- assert (pindex->GetBlockHash () == view.GetBestBlock ());
1344
-
1345
1347
bool fClean = true ;
1346
1348
1347
1349
CBlockUndo blockUndo;
@@ -1946,6 +1948,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
1946
1948
int64_t nStart = GetTimeMicros ();
1947
1949
{
1948
1950
CCoinsViewCache view (pcoinsTip);
1951
+ assert (view.GetBestBlock () == pindexDelete->GetBlockHash ());
1949
1952
if (DisconnectBlock (block, pindexDelete, view) != DISCONNECT_OK)
1950
1953
return error (" DisconnectTip(): DisconnectBlock %s failed" , pindexDelete->GetBlockHash ().ToString ());
1951
1954
bool flushed = view.Flush ();
@@ -3417,20 +3420,26 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
3417
3420
pblocktree->ReadFlag (" txindex" , fTxIndex );
3418
3421
LogPrintf (" %s: transaction index %s\n " , __func__, fTxIndex ? " enabled" : " disabled" );
3419
3422
3423
+ LoadChainTip (chainparams);
3424
+ return true ;
3425
+ }
3426
+
3427
+ void LoadChainTip (const CChainParams& chainparams)
3428
+ {
3429
+ if (chainActive.Tip () && chainActive.Tip ()->GetBlockHash () == pcoinsTip->GetBestBlock ()) return ;
3430
+
3420
3431
// Load pointer to end of best chain
3421
3432
BlockMap::iterator it = mapBlockIndex.find (pcoinsTip->GetBestBlock ());
3422
3433
if (it == mapBlockIndex.end ())
3423
- return true ;
3434
+ return ;
3424
3435
chainActive.SetTip (it->second );
3425
3436
3426
3437
PruneBlockIndexCandidates ();
3427
3438
3428
- LogPrintf (" %s : hashBestChain=%s height=%d date=%s progress=%f\n " , __func__ ,
3439
+ LogPrintf (" Loaded best chain : hashBestChain=%s height=%d date=%s progress=%f\n " ,
3429
3440
chainActive.Tip ()->GetBlockHash ().ToString (), chainActive.Height (),
3430
3441
DateTimeStrFormat (" %Y-%m-%d %H:%M:%S" , chainActive.Tip ()->GetBlockTime ()),
3431
3442
GuessVerificationProgress (chainparams.TxData (), chainActive.Tip ()));
3432
-
3433
- return true ;
3434
3443
}
3435
3444
3436
3445
CVerifyDB::CVerifyDB ()
@@ -3499,6 +3508,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
3499
3508
}
3500
3509
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
3501
3510
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage () + pcoinsTip->DynamicMemoryUsage ()) <= nCoinCacheUsage) {
3511
+ assert (coins.GetBestBlock () == pindex->GetBlockHash ());
3502
3512
DisconnectResult res = DisconnectBlock (block, pindex, coins);
3503
3513
if (res == DISCONNECT_FAILED) {
3504
3514
return error (" VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s" , pindex->nHeight , pindex->GetBlockHash ().ToString ());
@@ -3538,6 +3548,92 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
3538
3548
return true ;
3539
3549
}
3540
3550
3551
+ /* * Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
3552
+ static bool RollforwardBlock (const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
3553
+ {
3554
+ // TODO: merge with ConnectBlock
3555
+ CBlock block;
3556
+ if (!ReadBlockFromDisk (block, pindex, params.GetConsensus ())) {
3557
+ return error (" ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s" , pindex->nHeight , pindex->GetBlockHash ().ToString ());
3558
+ }
3559
+
3560
+ for (const CTransactionRef& tx : block.vtx ) {
3561
+ if (!tx->IsCoinBase ()) {
3562
+ for (const CTxIn &txin : tx->vin ) {
3563
+ inputs.SpendCoin (txin.prevout );
3564
+ }
3565
+ }
3566
+ // Pass check = true as every addition may be an overwrite.
3567
+ AddCoins (inputs, *tx, pindex->nHeight , true );
3568
+ }
3569
+ return true ;
3570
+ }
3571
+
3572
+ bool ReplayBlocks (const CChainParams& params, CCoinsView* view)
3573
+ {
3574
+ LOCK (cs_main);
3575
+
3576
+ CCoinsViewCache cache (view);
3577
+
3578
+ std::vector<uint256> hashHeads = view->GetHeadBlocks ();
3579
+ if (hashHeads.empty ()) return true ; // We're already in a consistent state.
3580
+ if (hashHeads.size () != 2 ) return error (" ReplayBlocks(): unknown inconsistent state" );
3581
+
3582
+ uiInterface.ShowProgress (_ (" Replaying blocks..." ), 0 );
3583
+ LogPrintf (" Replaying blocks\n " );
3584
+
3585
+ const CBlockIndex* pindexOld = nullptr ; // Old tip during the interrupted flush.
3586
+ const CBlockIndex* pindexNew; // New tip during the interrupted flush.
3587
+ const CBlockIndex* pindexFork = nullptr ; // Latest block common to both the old and the new tip.
3588
+
3589
+ if (mapBlockIndex.count (hashHeads[0 ]) == 0 ) {
3590
+ return error (" ReplayBlocks(): reorganization to unknown block requested" );
3591
+ }
3592
+ pindexNew = mapBlockIndex[hashHeads[0 ]];
3593
+
3594
+ if (!hashHeads[1 ].IsNull ()) { // The old tip is allowed to be 0, indicating it's the first flush.
3595
+ if (mapBlockIndex.count (hashHeads[1 ]) == 0 ) {
3596
+ return error (" ReplayBlocks(): reorganization from unknown block requested" );
3597
+ }
3598
+ pindexOld = mapBlockIndex[hashHeads[1 ]];
3599
+ pindexFork = LastCommonAncestor (pindexOld, pindexNew);
3600
+ assert (pindexFork != nullptr );
3601
+ }
3602
+
3603
+ // Rollback along the old branch.
3604
+ while (pindexOld != pindexFork) {
3605
+ if (pindexOld->nHeight > 0 ) { // Never disconnect the genesis block.
3606
+ CBlock block;
3607
+ if (!ReadBlockFromDisk (block, pindexOld, params.GetConsensus ())) {
3608
+ return error (" RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s" , pindexOld->nHeight , pindexOld->GetBlockHash ().ToString ());
3609
+ }
3610
+ LogPrintf (" Rolling back %s (%i)\n " , pindexOld->GetBlockHash ().ToString (), pindexOld->nHeight );
3611
+ DisconnectResult res = DisconnectBlock (block, pindexOld, cache);
3612
+ if (res == DISCONNECT_FAILED) {
3613
+ return error (" RollbackBlock(): DisconnectBlock failed at %d, hash=%s" , pindexOld->nHeight , pindexOld->GetBlockHash ().ToString ());
3614
+ }
3615
+ // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was
3616
+ // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations
3617
+ // applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations,
3618
+ // the result is still a version of the UTXO set with the effects of that block undone.
3619
+ }
3620
+ pindexOld = pindexOld->pprev ;
3621
+ }
3622
+
3623
+ // Roll forward from the forking point to the new tip.
3624
+ int nForkHeight = pindexFork ? pindexFork->nHeight : 0 ;
3625
+ for (int nHeight = nForkHeight + 1 ; nHeight <= pindexNew->nHeight ; ++nHeight) {
3626
+ const CBlockIndex* pindex = pindexNew->GetAncestor (nHeight);
3627
+ LogPrintf (" Rolling forward %s (%i)\n " , pindex->GetBlockHash ().ToString (), nHeight);
3628
+ if (!RollforwardBlock (pindex, cache, params)) return false ;
3629
+ }
3630
+
3631
+ cache.SetBestBlock (pindexNew->GetBlockHash ());
3632
+ cache.Flush ();
3633
+ uiInterface.ShowProgress (" " , 100 );
3634
+ return true ;
3635
+ }
3636
+
3541
3637
bool RewindBlockIndex (const CChainParams& params)
3542
3638
{
3543
3639
LOCK (cs_main);
0 commit comments