Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | public: | ||||
bool DisconnectTip(const Config &config, CValidationState &state, | bool DisconnectTip(const Config &config, CValidationState &state, | ||||
DisconnectedBlockTransactions *disconnectpool) | DisconnectedBlockTransactions *disconnectpool) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
// Manual block validity manipulation: | // Manual block validity manipulation: | ||||
bool PreciousBlock(const Config &config, CValidationState &state, | bool PreciousBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main); | CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main); | ||||
bool UnwindBlock(const Config &config, CValidationState &state, | bool UnwindBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex, bool invalidate) | CBlockIndex *pindex, bool invalidate); | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | |||||
void ResetBlockFailureFlags(CBlockIndex *pindex) | void ResetBlockFailureFlags(CBlockIndex *pindex) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
template <typename F> | template <typename F> | ||||
void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) | void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
template <typename F, typename C> | template <typename F, typename C> | ||||
void UpdateFlags(CBlockIndex *pindex, F f, C fchild) | void UpdateFlags(CBlockIndex *pindex, F f, C fchild) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
▲ Show 20 Lines • Show All 2,881 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) { | ||||
AssertLockHeld(cs_main); | |||||
// We first disconnect backwards and then mark the 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). | |||||
CBlockIndex *to_mark_failed = pindex; | |||||
bool pindex_was_in_chain = false; | bool pindex_was_in_chain = false; | ||||
CBlockIndex *invalid_walk_tip = chainActive.Tip(); | |||||
DisconnectedBlockTransactions disconnectpool; | // Disconnect (descendants of) pindex, and mark them invalid. | ||||
while (chainActive.Contains(pindex)) { | while (true) { | ||||
if (ShutdownRequested()) break; | |||||
LOCK(cs_main); | |||||
if (!chainActive.Contains(pindex)) break; | |||||
pindex_was_in_chain = true; | pindex_was_in_chain = true; | ||||
CBlockIndex *invalid_walk_tip = chainActive.Tip(); | |||||
// 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 | |||||
// here if DisconnectTip failed, but we can try. | |||||
disconnectpool.updateMempoolForReorg(config, false); | |||||
return false; | |||||
} | |||||
} | |||||
// Now mark the blocks we just disconnected as descendants invalid | DisconnectedBlockTransactions disconnectpool; | ||||
// (note this may not be all descendants). | bool ret = DisconnectTip(config, state, &disconnectpool); | ||||
while (pindex_was_in_chain && invalid_walk_tip != pindex) { | // 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; | |||||
assert(invalid_walk_tip->pprev == chainActive.Tip()); | |||||
// 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(); | ||||
setBlockIndexCandidates.erase(invalid_walk_tip); | |||||
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 = 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, regardless | ||||
// of whether it was in the main chain or not. | |||||
LOCK(cs_main); | |||||
if (chainActive.Contains(to_mark_failed)) { | |||||
// If the to-be-marked invalid block is in the active chain, | |||||
// something is interfering and we can't proceed. | |||||
return false; | |||||
} | |||||
to_mark_failed->nStatus = invalidate ? pindex->nStatus.withFailed() | |||||
: pindex->nStatus.withParked(); | : pindex->nStatus.withParked(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(to_mark_failed); | ||||
setBlockIndexCandidates.erase(to_mark_failed); | |||||
if (invalidate) { | if (invalidate) { | ||||
m_failed_blocks.insert(pindex); | m_failed_blocks.insert(to_mark_failed); | ||||
} | } | ||||
// DisconnectTip will add transactions to disconnectpool; try to add these | // The resulting new best tip may not be in setBlockIndexCandidates | ||||
// back to the mempool. | // anymore, so add it again. | ||||
disconnectpool.updateMempoolForReorg(config, true); | for (const std::pair<const BlockHash, CBlockIndex *> &it : | ||||
mapBlockIndex) { | |||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, | |||||
// so add it again. | |||||
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); | ||||
} | |||||
} | } | ||||
// 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->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 All 14 Lines | if (!AreOnTheSameFork(pindex, chainActive.Tip())) { | ||||
return InvalidateBlock(config, state, pindexToInvalidate); | return InvalidateBlock(config, state, pindexToInvalidate); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool InvalidateBlock(const Config &config, CValidationState &state, | bool InvalidateBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
LOCK(cs_main); | |||||
return g_chainstate.UnwindBlock(config, state, pindex, true); | return g_chainstate.UnwindBlock(config, state, pindex, true); | ||||
} | } | ||||
bool ParkBlock(const Config &config, CValidationState &state, | bool ParkBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return g_chainstate.UnwindBlock(config, state, pindex, false); | return g_chainstate.UnwindBlock(config, state, pindex, false); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,540 Lines • Show Last 20 Lines |