Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 2,962 Lines • ▼ Show 20 Lines | bool PreciousBlock(const Config &config, BlockValidationState &state, | ||||
return ::ChainstateActive().PreciousBlock(config, state, pindex); | return ::ChainstateActive().PreciousBlock(config, state, pindex); | ||||
} | } | ||||
bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, | bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex, bool invalidate) { | CBlockIndex *pindex, bool invalidate) { | ||||
CBlockIndex *to_mark_failed_or_parked = pindex; | CBlockIndex *to_mark_failed_or_parked = pindex; | ||||
bool pindex_was_in_chain = false; | bool pindex_was_in_chain = false; | ||||
int disconnected = 0; | int disconnected = 0; | ||||
const CChainParams &chainparams = config.GetChainParams(); | |||||
// We do not allow ActivateBestChain() to run while UnwindBlock() is | |||||
// running, as that could cause the tip to change while we disconnect | |||||
// blocks. (Note for backport of Core PR16849: we acquire | |||||
// LOCK(m_cs_chainstate) in the Park, Invalidate and FinalizeBlock functions | |||||
// due to differences in our code) | |||||
AssertLockHeld(m_cs_chainstate); | |||||
// We'll be acquiring and releasing cs_main below, to allow the validation | |||||
// callbacks to run. However, we should keep the block index in a | |||||
// consistent state as we disconnect blocks -- in particular we need to | |||||
// add equal-work blocks to setBlockIndexCandidates as we disconnect. | |||||
// To avoid walking the block index repeatedly in search of candidates, | |||||
// build a map once so that we can look up candidate blocks by chain | |||||
// work as we go. | |||||
std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work; | |||||
{ | |||||
LOCK(cs_main); | |||||
for (const auto &entry : mapBlockIndex) { | |||||
CBlockIndex *candidate = entry.second; | |||||
// We don't need to put anything in our active chain into the | |||||
// multimap, because those candidates will be found and considered | |||||
// as we disconnect. | |||||
// Instead, consider only non-active-chain blocks that have at | |||||
// least as much work as where we expect the new tip to end up. | |||||
if (!m_chain.Contains(candidate) && | |||||
!CBlockIndexWorkComparator()(candidate, pindex->pprev) && | |||||
candidate->IsValid(BlockValidity::TRANSACTIONS) && | |||||
candidate->HaveTxsDownloaded()) { | |||||
candidate_blocks_by_work.insert( | |||||
std::make_pair(candidate->nChainWork, candidate)); | |||||
} | |||||
} | |||||
} | |||||
// Disconnect (descendants of) pindex, and mark them invalid. | // Disconnect (descendants of) pindex, and mark them invalid. | ||||
while (true) { | while (true) { | ||||
if (ShutdownRequested()) { | if (ShutdownRequested()) { | ||||
break; | break; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (!m_chain.Contains(pindex)) { | if (!m_chain.Contains(pindex)) { | ||||
break; | break; | ||||
} | } | ||||
pindex_was_in_chain = true; | pindex_was_in_chain = true; | ||||
CBlockIndex *invalid_walk_tip = m_chain.Tip(); | CBlockIndex *invalid_walk_tip = m_chain.Tip(); | ||||
// ActivateBestChain considers blocks already in m_chain | // ActivateBestChain considers blocks already in m_chain | ||||
// unconditionally valid already, so force disconnect away from it. | // unconditionally valid already, so force disconnect away from it. | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
bool ret = | bool ret = DisconnectTip(chainparams, state, &disconnectpool); | ||||
DisconnectTip(config.GetChainParams(), state, &disconnectpool); | |||||
// DisconnectTip will add transactions to disconnectpool. | // DisconnectTip will add transactions to disconnectpool. | ||||
// Adjust the mempool to be consistent with the new tip, adding | // Adjust the mempool to be consistent with the new tip, adding | ||||
// transactions back to the mempool if disconnecting was successful, | // transactions back to the mempool if disconnecting was successful, | ||||
// and we're not doing a very deep invalidation (in which case | // and we're not doing a very deep invalidation (in which case | ||||
// keeping the mempool up to date is probably futile anyway). | // keeping the mempool up to date is probably futile anyway). | ||||
disconnectpool.updateMempoolForReorg( | disconnectpool.updateMempoolForReorg( | ||||
config, /* fAddToMempool = */ (++disconnected <= 10) && ret); | config, /* fAddToMempool = */ (++disconnected <= 10) && ret); | ||||
Show All 28 Lines | while (true) { | ||||
? to_mark_failed_or_parked->nStatus.withFailed(false) | ? to_mark_failed_or_parked->nStatus.withFailed(false) | ||||
.withFailedParent() | .withFailedParent() | ||||
: to_mark_failed_or_parked->nStatus.withParked(false) | : to_mark_failed_or_parked->nStatus.withParked(false) | ||||
.withParkedParent()); | .withParkedParent()); | ||||
setDirtyBlockIndex.insert(to_mark_failed_or_parked); | setDirtyBlockIndex.insert(to_mark_failed_or_parked); | ||||
} | } | ||||
// Add any equal or more work headers to setBlockIndexCandidates | |||||
auto candidate_it = candidate_blocks_by_work.lower_bound( | |||||
invalid_walk_tip->pprev->nChainWork); | |||||
while (candidate_it != candidate_blocks_by_work.end()) { | |||||
if (!CBlockIndexWorkComparator()(candidate_it->second, | |||||
invalid_walk_tip->pprev)) { | |||||
setBlockIndexCandidates.insert(candidate_it->second); | |||||
candidate_it = candidate_blocks_by_work.erase(candidate_it); | |||||
} else { | |||||
++candidate_it; | |||||
} | |||||
} | |||||
// Track the last disconnected block, so we can correct its | // Track the last disconnected block, so we can correct its | ||||
// FailedParent (or ParkedParent) status in future iterations, or, if | // FailedParent (or ParkedParent) status in future iterations, or, if | ||||
// it's the last one, call InvalidChainFound on it. | // it's the last one, call InvalidChainFound on it. | ||||
to_mark_failed_or_parked = invalid_walk_tip; | to_mark_failed_or_parked = invalid_walk_tip; | ||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (m_chain.Contains(to_mark_failed_or_parked)) { | if (m_chain.Contains(to_mark_failed_or_parked)) { | ||||
// If the to-be-marked invalid block is in the active chain, | // If the to-be-marked invalid block is in the active chain, | ||||
// something is interfering and we can't proceed. | // something is interfering and we can't proceed. | ||||
return false; | return false; | ||||
} | } | ||||
// Mark pindex (or the last disconnected block) as invalid (or parked), | // Mark pindex (or the last disconnected block) as invalid (or parked), | ||||
// even when it never was in the main chain. | // even when it never was in the main chain. | ||||
to_mark_failed_or_parked->nStatus = | to_mark_failed_or_parked->nStatus = | ||||
invalidate ? to_mark_failed_or_parked->nStatus.withFailed() | invalidate ? to_mark_failed_or_parked->nStatus.withFailed() | ||||
: to_mark_failed_or_parked->nStatus.withParked(); | : to_mark_failed_or_parked->nStatus.withParked(); | ||||
setDirtyBlockIndex.insert(to_mark_failed_or_parked); | setDirtyBlockIndex.insert(to_mark_failed_or_parked); | ||||
if (invalidate) { | if (invalidate) { | ||||
m_failed_blocks.insert(to_mark_failed_or_parked); | m_failed_blocks.insert(to_mark_failed_or_parked); | ||||
} | } | ||||
// The resulting new best tip may not be in setBlockIndexCandidates | // If any new blocks somehow arrived while we were disconnecting | ||||
// anymore, so add it again. | // (above), then the pre-calculation of what should go into | ||||
// setBlockIndexCandidates may have missed entries. This would | |||||
// technically be an inconsistency in the block index, but if we clean | |||||
// it up here, this should be an essentially unobservable error. | |||||
// Loop back over all block index entries and add any missing entries | |||||
// to setBlockIndexCandidates. | |||||
for (const std::pair<const BlockHash, CBlockIndex *> &it : | for (const std::pair<const BlockHash, CBlockIndex *> &it : | ||||
mapBlockIndex) { | mapBlockIndex) { | ||||
CBlockIndex *i = it.second; | CBlockIndex *i = it.second; | ||||
if (i->IsValid(BlockValidity::TRANSACTIONS) && | if (i->IsValid(BlockValidity::TRANSACTIONS) && | ||||
i->HaveTxsDownloaded() && | i->HaveTxsDownloaded() && | ||||
!setBlockIndexCandidates.value_comp()(i, m_chain.Tip())) { | !setBlockIndexCandidates.value_comp()(i, m_chain.Tip())) { | ||||
setBlockIndexCandidates.insert(i); | setBlockIndexCandidates.insert(i); | ||||
} | } | ||||
Show All 10 Lines | if (pindex_was_in_chain) { | ||||
to_mark_failed_or_parked->pprev); | to_mark_failed_or_parked->pprev); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CChainState::InvalidateBlock(const Config &config, | bool CChainState::InvalidateBlock(const Config &config, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
AssertLockNotHeld(m_cs_chainstate); | |||||
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock | |||||
LOCK(m_cs_chainstate); | |||||
return UnwindBlock(config, state, pindex, true); | return UnwindBlock(config, state, pindex, true); | ||||
} | } | ||||
bool CChainState::ParkBlock(const Config &config, BlockValidationState &state, | bool CChainState::ParkBlock(const Config &config, BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
AssertLockNotHeld(m_cs_chainstate); | |||||
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock | |||||
LOCK(m_cs_chainstate); | |||||
return UnwindBlock(config, state, pindex, false); | return UnwindBlock(config, state, pindex, false); | ||||
} | } | ||||
bool CChainState::FinalizeBlock(const Config &config, | bool CChainState::FinalizeBlock(const Config &config, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
AssertLockNotHeld(m_cs_chainstate); | |||||
// See 'Note for backport of Core PR16849' in CChainState::UnwindBlock | |||||
LOCK(m_cs_chainstate); | |||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
CBlockIndex *pindexToInvalidate = nullptr; | CBlockIndex *pindexToInvalidate = nullptr; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (!MarkBlockAsFinal(config, state, pindex)) { | if (!MarkBlockAsFinal(config, state, pindex)) { | ||||
// state is set by MarkBlockAsFinal. | // state is set by MarkBlockAsFinal. | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,583 Lines • Show Last 20 Lines |