Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 3,019 Lines • ▼ Show 20 Lines | |||||
bool PreciousBlock(const Config &config, CValidationState &state, | bool PreciousBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return g_chainstate.PreciousBlock(config, state, pindex); | return g_chainstate.PreciousBlock(config, state, pindex); | ||||
} | } | ||||
bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex, bool invalidate) { | CBlockIndex *pindex, bool invalidate) { | ||||
CBlockIndex *to_mark_failed_or_parked = pindex; | |||||
bool pindex_was_in_chain = false; | |||||
// Disconnect (descendants of) pindex, and mark them invalid. | |||||
while (true) { | |||||
if (ShutdownRequested()) { | |||||
break; | |||||
} | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// We first disconnect backwards and then mark the blocks as invalid. | if (!chainActive.Contains(pindex)) { | ||||
// This prevents a case where pruned nodes may fail to invalidateblock | break; | ||||
// and be left unable to start as they have no tip candidates (as there | } | ||||
// are no blocks that meet the "have data and are not invalid per | |||||
// nStatus" criteria for inclusion in setBlockIndexCandidates). | |||||
bool pindex_was_in_chain = false; | pindex_was_in_chain = true; | ||||
CBlockIndex *invalid_walk_tip = chainActive.Tip(); | CBlockIndex *invalid_walk_tip = chainActive.Tip(); | ||||
DisconnectedBlockTransactions disconnectpool; | |||||
while (chainActive.Contains(pindex)) { | |||||
pindex_was_in_chain = true; | |||||
// ActivateBestChain considers blocks already in chainActive | // ActivateBestChain considers blocks already in chainActive | ||||
// unconditionally valid already, so force disconnect away from it. | // unconditionally valid already, so force disconnect away from it. | ||||
if (!DisconnectTip(config, state, &disconnectpool)) { | |||||
// It's probably hopeless to try to make the mempool consistent | DisconnectedBlockTransactions disconnectpool; | ||||
// here if DisconnectTip failed, but we can try. | |||||
disconnectpool.updateMempoolForReorg(config, false); | bool ret = DisconnectTip(config, state, &disconnectpool); | ||||
// DisconnectTip will add transactions to disconnectpool. | |||||
// Adjust the mempool to be consistent with the new tip, adding | |||||
// transactions back to the mempool if disconnecting was successful. | |||||
disconnectpool.updateMempoolForReorg(config, /* fAddToMempool = */ ret); | |||||
if (!ret) { | |||||
return false; | return false; | ||||
} | } | ||||
} | |||||
// Now mark the blocks we just disconnected as descendants invalid | assert(invalid_walk_tip->pprev == chainActive.Tip()); | ||||
// (note this may not be all descendants). | |||||
while (pindex_was_in_chain && invalid_walk_tip != pindex) { | // We immediately mark the disconnected blocks as invalid. | ||||
// This prevents a case where pruned nodes may fail to invalidateblock | |||||
// and be left unable to start as they have no tip candidates (as there | |||||
// are no blocks that meet the "have data and are not invalid per | |||||
// nStatus" criteria for inclusion in setBlockIndexCandidates). | |||||
invalid_walk_tip->nStatus = | invalid_walk_tip->nStatus = | ||||
invalidate ? invalid_walk_tip->nStatus.withFailedParent() | invalidate ? invalid_walk_tip->nStatus.withFailedParent() | ||||
: invalid_walk_tip->nStatus.withParkedParent(); | : invalid_walk_tip->nStatus.withParkedParent(); | ||||
setDirtyBlockIndex.insert(invalid_walk_tip); | setDirtyBlockIndex.insert(invalid_walk_tip); | ||||
invalid_walk_tip = invalid_walk_tip->pprev; | setBlockIndexCandidates.insert(invalid_walk_tip->pprev); | ||||
// If we abort invalidation after this iteration, make sure | |||||
// the last disconnected block gets marked failed (rather than | |||||
// just child of failed) | |||||
to_mark_failed_or_parked = invalid_walk_tip; | |||||
} | } | ||||
// Mark the block as either invalid or parked. | { | ||||
pindex->nStatus = invalidate ? pindex->nStatus.withFailed() | // Mark pindex (or the last disconnected block) as invalid or parked, | ||||
: pindex->nStatus.withParked(); | // regardless of whether it was in the main chain or not. | ||||
setDirtyBlockIndex.insert(pindex); | LOCK(cs_main); | ||||
if (invalidate) { | if (chainActive.Contains(to_mark_failed_or_parked)) { | ||||
m_failed_blocks.insert(pindex); | // If the to-be-marked invalid block is in the active chain, | ||||
// something is interfering and we can't proceed. | |||||
return false; | |||||
} | } | ||||
// DisconnectTip will add transactions to disconnectpool; try to add these | to_mark_failed_or_parked->nStatus = | ||||
// back to the mempool. | invalidate ? to_mark_failed_or_parked->nStatus.withFailed() | ||||
disconnectpool.updateMempoolForReorg(config, true); | : to_mark_failed_or_parked->nStatus.withParked(); | ||||
setDirtyBlockIndex.insert(to_mark_failed_or_parked); | |||||
if (invalidate) { | |||||
m_failed_blocks.insert(to_mark_failed_or_parked); | |||||
} | |||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, | // The resulting new best tip may not be in setBlockIndexCandidates | ||||
// so add it again. | // anymore, so add it again. | ||||
for (const std::pair<const BlockHash, CBlockIndex *> &it : mapBlockIndex) { | for (const std::pair<const BlockHash, CBlockIndex *> &it : | ||||
mapBlockIndex) { | |||||
CBlockIndex *i = it.second; | CBlockIndex *i = it.second; | ||||
if (i->IsValid(BlockValidity::TRANSACTIONS) && i->HaveTxsDownloaded() && | if (i->IsValid(BlockValidity::TRANSACTIONS) && | ||||
i->HaveTxsDownloaded() && | |||||
!setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | !setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | ||||
setBlockIndexCandidates.insert(i); | setBlockIndexCandidates.insert(i); | ||||
} | } | ||||
} | } | ||||
if (invalidate) { | if (invalidate) { | ||||
InvalidChainFound(pindex); | InvalidChainFound(to_mark_failed_or_parked); | ||||
} | |||||
} | } | ||||
// Only notify about a new block tip if the active chain was modified. | // Only notify about a new block tip if the active chain was modified. | ||||
if (pindex_was_in_chain) { | if (pindex_was_in_chain) { | ||||
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); | uiInterface.NotifyBlockTip(IsInitialBlockDownload(), | ||||
to_mark_failed_or_parked->pprev); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, | bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (!FinalizeBlockInternal(config, state, pindex)) { | if (!FinalizeBlockInternal(config, state, pindex)) { | ||||
▲ Show 20 Lines • Show All 2,558 Lines • Show Last 20 Lines |