| 4346 | } |
| 4347 | |
| 4348 | bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view) |
| 4349 | { |
| 4350 | LOCK(cs_main); |
| 4351 | |
| 4352 | CCoinsViewCache cache(view); |
| 4353 | |
| 4354 | std::vector<uint256> hashHeads = view->GetHeadBlocks(); |
| 4355 | if (hashHeads.empty()) return true; // We're already in a consistent state. |
| 4356 | if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); |
| 4357 | |
| 4358 | uiInterface.ShowProgress(_("Replaying blocks..."), 0, false); |
| 4359 | LogPrintf("Replaying blocks\n"); |
| 4360 | |
| 4361 | const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. |
| 4362 | const CBlockIndex* pindexNew; // New tip during the interrupted flush. |
| 4363 | const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip. |
| 4364 | |
| 4365 | if (mapBlockIndex.count(hashHeads[0]) == 0) { |
| 4366 | return error("ReplayBlocks(): reorganization to unknown block requested"); |
| 4367 | } |
| 4368 | pindexNew = mapBlockIndex[hashHeads[0]]; |
| 4369 | |
| 4370 | if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. |
| 4371 | if (mapBlockIndex.count(hashHeads[1]) == 0) { |
| 4372 | return error("ReplayBlocks(): reorganization from unknown block requested"); |
| 4373 | } |
| 4374 | pindexOld = mapBlockIndex[hashHeads[1]]; |
| 4375 | pindexFork = LastCommonAncestor(pindexOld, pindexNew); |
| 4376 | assert(pindexFork != nullptr); |
| 4377 | } |
| 4378 | |
| 4379 | // Rollback along the old branch. |
| 4380 | while (pindexOld != pindexFork) { |
| 4381 | if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. |
| 4382 | CBlock block; |
| 4383 | if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) { |
| 4384 | return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4385 | } |
| 4386 | LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); |
| 4387 | DisconnectResult res = DisconnectBlock(block, pindexOld, cache); |
| 4388 | if (res == DISCONNECT_FAILED) { |
| 4389 | return error("RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4390 | } |
| 4391 | // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was |
| 4392 | // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations |
| 4393 | // applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations, |
| 4394 | // the result is still a version of the UTXO set with the effects of that block undone. |
| 4395 | } |
| 4396 | pindexOld = pindexOld->pprev; |
| 4397 | } |
| 4398 | |
| 4399 | // Roll forward from the forking point to the new tip. |
| 4400 | int nForkHeight = pindexFork ? pindexFork->nHeight : 0; |
| 4401 | for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) { |
| 4402 | const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); |
| 4403 | LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); |
| 4404 | if (!RollforwardBlock(pindex, cache, params)) return false; |
| 4405 | } |
no test coverage detected