@@ -94,7 +94,7 @@ namespace {
9494
9595 struct CBlockIndexWorkComparator
9696 {
97- bool operator ()(CBlockIndex *pa, CBlockIndex *pb) const {
97+ bool operator ()(const CBlockIndex *pa, const CBlockIndex *pb) const {
9898 // First sort by most total work, ...
9999 if (pa->nChainWork > pb->nChainWork ) return false ;
100100 if (pa->nChainWork < pb->nChainWork ) return true ;
@@ -1506,7 +1506,6 @@ bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint
15061506 if (undo.nHeight != 0 ) {
15071507 // undo data contains height: this is the last output of the prevout tx being spent
15081508 if (!coins->IsPruned ()) fClean = false ;
1509- coins->Clear ();
15101509 coins->fCoinBase = undo.fCoinBase ;
15111510 coins->nHeight = undo.nHeight ;
15121511 coins->nVersion = undo.nVersion ;
@@ -1968,7 +1967,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
19681967 nLastSetChain = nNow;
19691968 }
19701969 int64_t nMempoolSizeMax = GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 ;
1971- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage () * DB_PEAK_USAGE_FACTOR ;
1970+ int64_t cacheSize = pcoinsTip->DynamicMemoryUsage ();
19721971 int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t >(nMempoolSizeMax - nMempoolUsage, 0 );
19731972 // The cache is large and we're within 10% and 200 MiB or 50% and 50MiB of the limit, but we have time now (not in the middle of a block processing).
19741973 bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::min (std::max (nTotalSpace / 2 , nTotalSpace - MIN_BLOCK_COINSDB_USAGE * 1024 * 1024 ),
@@ -3567,20 +3566,26 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
35673566 pblocktree->ReadFlag (" txindex" , fTxIndex );
35683567 LogPrintf (" %s: transaction index %s\n " , __func__, fTxIndex ? " enabled" : " disabled" );
35693568
3569+ LoadChainTip (chainparams);
3570+ return true ;
3571+ }
3572+
3573+ void LoadChainTip (const CChainParams& chainparams)
3574+ {
3575+ if (chainActive.Tip () && chainActive.Tip ()->GetBlockHash () == pcoinsTip->GetBestBlock ()) return ;
3576+
35703577 // Load pointer to end of best chain
35713578 BlockMap::iterator it = mapBlockIndex.find (pcoinsTip->GetBestBlock ());
35723579 if (it == mapBlockIndex.end ())
3573- return true ;
3580+ return ;
35743581 chainActive.SetTip (it->second );
35753582
35763583 PruneBlockIndexCandidates ();
35773584
3578- LogPrintf (" %s : hashBestChain=%s height=%d date=%s progress=%f\n " , __func__ ,
3585+ LogPrintf (" Loaded best chain : hashBestChain=%s height=%d date=%s progress=%f\n " ,
35793586 chainActive.Tip ()->GetBlockHash ().ToString (), chainActive.Height (),
35803587 DateTimeStrFormat (" %Y-%m-%d %H:%M:%S" , chainActive.Tip ()->GetBlockTime ()),
35813588 GuessVerificationProgress (chainparams.TxData (), chainActive.Tip ()));
3582-
3583- return true ;
35843589}
35853590
35863591CVerifyDB::CVerifyDB ()
@@ -3686,6 +3691,124 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
36863691 return true ;
36873692}
36883693
3694+ /* * Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
3695+ static bool RollforwardBlock (const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
3696+ {
3697+ // TODO: merge with ConnectBlock
3698+ CBlock block;
3699+ if (!ReadBlockFromDisk (block, pindex, params.GetConsensus ())) {
3700+ return error (" ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s" , pindex->nHeight , pindex->GetBlockHash ().ToString ());
3701+ }
3702+
3703+ for (const CTransactionRef& tx : block.vtx ) {
3704+ if (!tx->IsCoinBase ()) {
3705+ for (const CTxIn &txin : tx->vin ) {
3706+ inputs.ModifyCoins (txin.prevout .hash )->Spend (txin.prevout .n );
3707+ }
3708+ }
3709+ // We cannot use ModifyNewCoins here, as the entry may exist in the cache already.
3710+ inputs.ModifyCoins (tx->GetHash ())->FromTx (*tx, pindex->nHeight );
3711+ }
3712+ return true ;
3713+ }
3714+
3715+ /* * Unapply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
3716+ static bool RollbackBlock (const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params)
3717+ {
3718+ // TODO: Merge with DisconnectBlock
3719+ CBlock block;
3720+ if (!ReadBlockFromDisk (block, pindex, params.GetConsensus ())) {
3721+ return error (" RollbackBlock(): ReadBlockFromDisk failed at %d, hash=%s" , pindex->nHeight , pindex->GetBlockHash ().ToString ());
3722+ }
3723+
3724+ CBlockUndo blockUndo;
3725+ CDiskBlockPos pos = pindex->GetUndoPos ();
3726+ if (pos.IsNull ())
3727+ return error (" RollbackBlock(): no undo data available" );
3728+ if (!UndoReadFromDisk (blockUndo, pos, pindex->pprev ->GetBlockHash ()))
3729+ return error (" RollbackBlock(): failure reading undo data" );
3730+
3731+ if (blockUndo.vtxundo .size () + 1 != block.vtx .size ())
3732+ return error (" RollbackBlock(): block and undo data inconsistent" );
3733+
3734+ for (int i = block.vtx .size () - 1 ; i >= 0 ; --i) {
3735+ const CTransaction& tx = *block.vtx [i];
3736+ inputs.ModifyCoins (tx.GetHash ())->Clear ();
3737+ if (i != 0 ) {
3738+ CTxUndo &txundo = blockUndo.vtxundo [i - 1 ];
3739+ if (txundo.vprevout .size () != tx.vin .size ()) {
3740+ return error (" RollbackBlock(): block and undo data inconsistent" );
3741+ }
3742+ for (size_t n = 0 ; n < txundo.vprevout .size (); ++n) {
3743+ ApplyTxInUndo (txundo.vprevout [n], inputs, COutPoint (tx.GetHash (), n)); // Ignore return value
3744+ }
3745+ }
3746+ }
3747+ return true ;
3748+ }
3749+
3750+
3751+ bool ReplayBlocks (const CChainParams& params, CCoinsView* view)
3752+ {
3753+ LOCK (cs_main);
3754+
3755+ CCoinsViewCache cache (view);
3756+
3757+ uint256 hashBest = view->GetBestBlock ();
3758+ if (!hashBest.IsNull ()) return true ;
3759+ std::vector<uint256> hashHeads = view->GetHeadBlocks ();
3760+ if (hashHeads.empty ()) return true ;
3761+
3762+ uiInterface.ShowProgress (_ (" Replaying blocks..." ), 0 );
3763+ LogPrintf (" Replaying blocks\n " );
3764+
3765+ std::vector<const CBlockIndex*> pindexHeads;
3766+ const CBlockIndex* pindexFork = nullptr ;
3767+ bool fGenesis = false ;
3768+
3769+ // Find last common ancestor of all heads.
3770+ for (const uint256& hash : hashHeads) {
3771+ if (hash.IsNull ()) {
3772+ fGenesis = true ;
3773+ continue ;
3774+ }
3775+ auto it = mapBlockIndex.find (hash);
3776+ if (it == mapBlockIndex.end ()) {
3777+ return error (" ReplayBlocks(): chainstate boundaries not in block index" );
3778+ }
3779+ pindexHeads.push_back (it->second );
3780+ pindexFork = pindexFork ? LastCommonAncestor (pindexFork, it->second ) : it->second ;
3781+ }
3782+
3783+ // Build a set of all blocks to rollback (ignoring the branch with the new tip, pindexHeads[0]).
3784+ std::set<const CBlockIndex*, CBlockIndexWorkComparator> vpindexRollback;
3785+ for (size_t i = 1 ; i < pindexHeads.size (); ++i) {
3786+ const CBlockIndex *pindexHead = pindexHeads[i];
3787+ while (fGenesis ? pindexHead != nullptr : pindexHead != pindexFork) {
3788+ vpindexRollback.insert (pindexHead);
3789+ pindexHead = pindexHead->pprev ;
3790+ }
3791+ }
3792+
3793+ // Rollback all of those in order of decreasing work, deduplicated.
3794+ for (auto itRollback = vpindexRollback.rbegin (); itRollback != vpindexRollback.rend (); ++itRollback) {
3795+ LogPrintf (" Rolling backing %s (%i)\n " , (*itRollback)->GetBlockHash ().ToString (), (*itRollback)->nHeight );
3796+ if (!RollbackBlock (*itRollback, cache, params)) return false ;
3797+ }
3798+
3799+ // Roll forward from the forking point to the new tip.
3800+ int nForkHeight = fGenesis ? 1 : pindexFork->nHeight ;
3801+ for (int nHeight = nForkHeight + 1 ; nHeight <= pindexHeads[0 ]->nHeight ; ++nHeight) {
3802+ LogPrintf (" Rolling forward %s (%i)\n " , pindexHeads[0 ]->GetAncestor (nHeight)->nHeight , nHeight);
3803+ if (!RollforwardBlock (pindexHeads[0 ]->GetAncestor (nHeight), cache, params)) return false ;
3804+ }
3805+
3806+ cache.SetBestBlock (pindexHeads[0 ]->GetBlockHash ());
3807+ cache.Flush ();
3808+ uiInterface.ShowProgress (" " , 100 );
3809+ return true ;
3810+ }
3811+
36893812bool RewindBlockIndex (const CChainParams& params)
36903813{
36913814 LOCK (cs_main);
0 commit comments