diff --git a/src/index/base.cpp b/src/index/base.cpp index 7c9b9056b2ce..16f5f1e78873 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -118,8 +118,8 @@ bool BaseIndex::Init() const auto uncommitted_tip{WritesEagerly() ? GetDB().ReadUncommittedTip() : std::nullopt}; if (uncommitted_tip && resume_block == locator_index) { // The entries of the blocks up to it are on disk, but without the blocks the index can't tell whether they - // are still on the active chain, nor rewind them. - if (!m_chainstate->m_blockman.LookupBlockIndex(*uncommitted_tip)) { + // are still on the active chain, nor rewind them. That only matters if readers trust them. + if (!m_chainstate->m_blockman.LookupBlockIndex(*uncommitted_tip) && !IgnoresEntriesOffActiveChain()) { return InitError(strprintf(Untranslated("%s: the block the index was last synced to is not found. Please " "rebuild the index."), GetName())); @@ -401,6 +401,11 @@ void BaseIndex::BlockConnected(const std::shared_ptr& block, const best_block_index->GetBlockHash().ToString()); return; } + // Init() can resume from an uncommitted tip above the active chain. The blocks below it are indexed already, + // and after an unclean stop their data may not be on disk again yet, so they can't be rewound. + if (best_block_index->GetAncestor(pindex->nHeight) == pindex) { + return; + } if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) { FatalError("%s: Failed to rewind index %s to a previous chain tip", __func__, GetName()); diff --git a/src/index/base.h b/src/index/base.h index d59e535f653e..e602924d4df3 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -110,6 +110,11 @@ class BaseIndex : public CValidationInterface /// entries of blocks above it that are no longer on the active chain. virtual bool WritesEagerly() const { return false; } + /// Whether readers ignore entries of blocks that aren't on the active chain, so leftover entries of blocks that + /// left it are harmless. Init() then resumes from the locator when the block index lost the uncommitted tip, + /// instead of failing. + virtual bool IgnoresEntriesOffActiveChain() const { return false; } + protected: std::unique_ptr m_chain; Chainstate* m_chainstate{nullptr}; diff --git a/src/index/govcollateralindex.cpp b/src/index/govcollateralindex.cpp index 068101cc4b5a..f729797b87f6 100644 --- a/src/index/govcollateralindex.cpp +++ b/src/index/govcollateralindex.cpp @@ -107,6 +107,9 @@ bool GovCollateralIndex::CustomRewind(const interfaces::BlockKey& current_tip, c const CBlockIndex* new_tip_index{WITH_LOCK(::cs_main, return m_chainstate->m_blockman.LookupBlockIndex(new_tip.hash))}; for (const CBlockIndex* pindex{WITH_LOCK(::cs_main, return m_chainstate->m_blockman.LookupBlockIndex(current_tip.hash))}; pindex != new_tip_index; pindex = pindex->pprev) { + // A block without data, like one an unclean stop left with only its header, keeps its collaterals: they + // only confirm while it is on the active chain + if (!WITH_LOCK(::cs_main, return pindex->nStatus & BLOCK_HAVE_DATA)) continue; CBlock block; if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) { LogError("%s: Failed to read block %s from disk\n", __func__, pindex->GetBlockHash().ToString()); diff --git a/src/index/govcollateralindex.h b/src/index/govcollateralindex.h index b39b37b56406..c1ff7ce84345 100644 --- a/src/index/govcollateralindex.h +++ b/src/index/govcollateralindex.h @@ -33,6 +33,9 @@ class GovCollateralIndex final : public BaseIndex, public governance::Collateral protected: bool WritesEagerly() const override { return true; } + // A collateral only confirms while its block is on the active chain + bool IgnoresEntriesOffActiveChain() const override { return true; } + bool CustomInit(const std::optional& block) override; bool CustomAppend(const interfaces::BlockInfo& block) override; diff --git a/src/test/baseindex_tests.cpp b/src/test/baseindex_tests.cpp index 68c1d63a7704..599661989c0e 100644 --- a/src/test/baseindex_tests.cpp +++ b/src/test/baseindex_tests.cpp @@ -3,12 +3,15 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include #include #include +#include #include