| 4439 | } |
| 4440 | |
| 4441 | bool CChainState::ReplayBlocks() |
| 4442 | { |
| 4443 | LOCK(cs_main); |
| 4444 | |
| 4445 | CCoinsView& db = this->CoinsDB(); |
| 4446 | CCoinsViewCache cache(&db); |
| 4447 | |
| 4448 | std::vector<uint256> hashHeads = db.GetHeadBlocks(); |
| 4449 | if (hashHeads.empty()) return true; // We're already in a consistent state. |
| 4450 | if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); |
| 4451 | |
| 4452 | uiInterface.ShowProgress(_("Replaying blocks…").translated, 0, false); |
| 4453 | LogPrintf("Replaying blocks\n"); |
| 4454 | |
| 4455 | const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. |
| 4456 | const CBlockIndex* pindexNew; // New tip during the interrupted flush. |
| 4457 | const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip. |
| 4458 | |
| 4459 | if (m_blockman.m_block_index.count(hashHeads[0]) == 0) { |
| 4460 | return error("ReplayBlocks(): reorganization to unknown block requested"); |
| 4461 | } |
| 4462 | pindexNew = m_blockman.m_block_index[hashHeads[0]]; |
| 4463 | |
| 4464 | if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. |
| 4465 | if (m_blockman.m_block_index.count(hashHeads[1]) == 0) { |
| 4466 | return error("ReplayBlocks(): reorganization from unknown block requested"); |
| 4467 | } |
| 4468 | pindexOld = m_blockman.m_block_index[hashHeads[1]]; |
| 4469 | pindexFork = LastCommonAncestor(pindexOld, pindexNew); |
| 4470 | assert(pindexFork != nullptr); |
| 4471 | } |
| 4472 | |
| 4473 | // Rollback along the old branch. |
| 4474 | while (pindexOld != pindexFork) { |
| 4475 | if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. |
| 4476 | CBlock block; |
| 4477 | if (!ReadBlockFromDisk(block, pindexOld, m_params.GetConsensus())) { |
| 4478 | return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4479 | } |
| 4480 | LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); |
| 4481 | DisconnectResult res = DisconnectBlock(block, pindexOld, cache); |
| 4482 | if (res == DISCONNECT_FAILED) { |
| 4483 | return error("RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4484 | } |
| 4485 | // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was |
| 4486 | // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations |
| 4487 | // applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations, |
| 4488 | // the result is still a version of the UTXO set with the effects of that block undone. |
| 4489 | } |
| 4490 | pindexOld = pindexOld->pprev; |
| 4491 | } |
| 4492 | |
| 4493 | // Roll forward from the forking point to the new tip. |
| 4494 | int nForkHeight = pindexFork ? pindexFork->nHeight : 0; |
| 4495 | for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) { |
| 4496 | const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); |
| 4497 | LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); |
| 4498 | uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false); |
no test coverage detected