| 4791 | } |
| 4792 | |
| 4793 | bool Chainstate::ReplayBlocks() |
| 4794 | { |
| 4795 | LOCK(cs_main); |
| 4796 | |
| 4797 | CCoinsView& db = this->CoinsDB(); |
| 4798 | CCoinsViewCache cache(&db); |
| 4799 | |
| 4800 | std::vector<uint256> hashHeads = db.GetHeadBlocks(); |
| 4801 | if (hashHeads.empty()) return true; // We're already in a consistent state. |
| 4802 | if (hashHeads.size() != 2) { |
| 4803 | LogError("ReplayBlocks(): unknown inconsistent state\n"); |
| 4804 | return false; |
| 4805 | } |
| 4806 | |
| 4807 | m_chainman.GetNotifications().progress(_("Replaying blocks…"), 0, false); |
| 4808 | LogInfo("Replaying blocks"); |
| 4809 | |
| 4810 | const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. |
| 4811 | const CBlockIndex* pindexNew; // New tip during the interrupted flush. |
| 4812 | const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip. |
| 4813 | |
| 4814 | if (!m_blockman.m_block_index.contains(hashHeads[0])) { |
| 4815 | LogError("ReplayBlocks(): reorganization to unknown block requested\n"); |
| 4816 | return false; |
| 4817 | } |
| 4818 | pindexNew = &(m_blockman.m_block_index[hashHeads[0]]); |
| 4819 | |
| 4820 | if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. |
| 4821 | if (!m_blockman.m_block_index.contains(hashHeads[1])) { |
| 4822 | LogError("ReplayBlocks(): reorganization from unknown block requested\n"); |
| 4823 | return false; |
| 4824 | } |
| 4825 | pindexOld = &(m_blockman.m_block_index[hashHeads[1]]); |
| 4826 | pindexFork = LastCommonAncestor(pindexOld, pindexNew); |
| 4827 | assert(pindexFork != nullptr); |
| 4828 | } |
| 4829 | |
| 4830 | // Rollback along the old branch. |
| 4831 | const int nForkHeight{pindexFork ? pindexFork->nHeight : 0}; |
| 4832 | if (pindexOld != pindexFork) { |
| 4833 | LogInfo("Rolling back from %s (%i to %i)", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight, nForkHeight); |
| 4834 | while (pindexOld != pindexFork) { |
| 4835 | if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. |
| 4836 | CBlock block; |
| 4837 | if (!m_blockman.ReadBlock(block, *pindexOld)) { |
| 4838 | LogError("RollbackBlock(): ReadBlock() failed at %d, hash=%s\n", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4839 | return false; |
| 4840 | } |
| 4841 | if (pindexOld->nHeight % 10'000 == 0) { |
| 4842 | LogInfo("Rolling back %s (%i)", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); |
| 4843 | } |
| 4844 | DisconnectResult res = DisconnectBlock(block, pindexOld, cache); |
| 4845 | if (res == DISCONNECT_FAILED) { |
| 4846 | LogError("RollbackBlock(): DisconnectBlock failed at %d, hash=%s\n", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); |
| 4847 | return false; |
| 4848 | } |
| 4849 | // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was |
| 4850 | // overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations |
no test coverage detected