Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | |||||
* Blocks loaded from disk are assigned id 0, so start the counter at 1. | * Blocks loaded from disk are assigned id 0, so start the counter at 1. | ||||
*/ | */ | ||||
std::atomic<int32_t> nBlockSequenceId{1}; | std::atomic<int32_t> nBlockSequenceId{1}; | ||||
/** Decreasing counter (used by subsequent preciousblock calls). */ | /** Decreasing counter (used by subsequent preciousblock calls). */ | ||||
int32_t nBlockReverseSequenceId = -1; | int32_t nBlockReverseSequenceId = -1; | ||||
/** chainwork for the last block that preciousblock has been applied to. */ | /** chainwork for the last block that preciousblock has been applied to. */ | ||||
arith_uint256 nLastPreciousChainwork = 0; | arith_uint256 nLastPreciousChainwork = 0; | ||||
/** | |||||
* In order to efficiently track invalidity of headers, we keep the set of | |||||
* blocks which we tried to connect and found to be invalid here (ie which | |||||
* were set to BLOCK_FAILED_VALID since the last restart). We can then | |||||
* walk this set and check if a new header is a descendant of something in | |||||
* this set, preventing us from having to walk mapBlockIndex when we try | |||||
* to connect a bad block and fail. | |||||
* | |||||
* While this is more complicated than marking everything which descends | |||||
* from an invalid block as invalid at the time we discover it to be | |||||
* invalid, doing so would require walking all of mapBlockIndex to find all | |||||
* descendants. Since this case should be very rare, keeping track of all | |||||
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as | |||||
* well. | |||||
* | |||||
* Because we alreardy walk mapBlockIndex in height-order at startup, we go | |||||
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, | |||||
* instead of putting things in this set. | |||||
*/ | |||||
std::set<CBlockIndex *> g_failed_blocks; | |||||
/** Dirty block index entries. */ | /** Dirty block index entries. */ | ||||
std::set<const CBlockIndex *> setDirtyBlockIndex; | std::set<const CBlockIndex *> setDirtyBlockIndex; | ||||
/** Dirty block file entries. */ | /** Dirty block file entries. */ | ||||
std::set<int> setDirtyFileInfo; | std::set<int> setDirtyFileInfo; | ||||
} // namespace | } // namespace | ||||
CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | ||||
▲ Show 20 Lines • Show All 926 Lines • ▼ Show 20 Lines | LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", | ||||
log(tip->nChainWork.getdouble()) / log(2.0), | log(tip->nChainWork.getdouble()) / log(2.0), | ||||
FormatISO8601DateTime(tip->GetBlockTime())); | FormatISO8601DateTime(tip->GetBlockTime())); | ||||
} | } | ||||
static void InvalidBlockFound(CBlockIndex *pindex, | static void InvalidBlockFound(CBlockIndex *pindex, | ||||
const CValidationState &state) { | const CValidationState &state) { | ||||
if (!state.CorruptionPossible()) { | if (!state.CorruptionPossible()) { | ||||
pindex->nStatus = pindex->nStatus.withFailed(); | pindex->nStatus = pindex->nStatus.withFailed(); | ||||
g_failed_blocks.insert(pindex); | |||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
InvalidChainFound(pindex); | InvalidChainFound(pindex); | ||||
} | } | ||||
} | } | ||||
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | ||||
int nHeight) { | int nHeight) { | ||||
// Mark inputs spent. | // Mark inputs spent. | ||||
▲ Show 20 Lines • Show All 1,888 Lines • ▼ Show 20 Lines | bool PreciousBlock(const Config &config, CValidationState &state, | ||||
return ActivateBestChain(config, state); | return ActivateBestChain(config, state); | ||||
} | } | ||||
static bool UnwindBlock(const Config &config, CValidationState &state, | static bool UnwindBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex, bool invalidate) { | CBlockIndex *pindex, bool invalidate) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Mark the block as either invalid or parked. | // We first disconnect backwards and then mark the blocks as invalid. | ||||
pindex->nStatus = invalidate ? pindex->nStatus.withFailed() | // This prevents a case where pruned nodes may fail to invalidateblock | ||||
: pindex->nStatus.withParked(); | // and be left unable to start as they have no tip candidates (as there | ||||
setDirtyBlockIndex.insert(pindex); | // 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; | |||||
CBlockIndex *invalid_walk_tip = chainActive.Tip(); | |||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
while (chainActive.Contains(pindex)) { | while (chainActive.Contains(pindex)) { | ||||
CBlockIndex *pindexWalk = chainActive.Tip(); | pindex_was_in_chain = true; | ||||
if (pindexWalk != pindex) { | |||||
pindexWalk->nStatus = invalidate | |||||
? pindexWalk->nStatus.withFailedParent() | |||||
: pindexWalk->nStatus.withParkedParent(); | |||||
setDirtyBlockIndex.insert(pindexWalk); | |||||
} | |||||
// 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)) { | if (!DisconnectTip(config, state, &disconnectpool)) { | ||||
// It's probably hopeless to try to make the mempool consistent | // It's probably hopeless to try to make the mempool consistent | ||||
// here if DisconnectTip failed, but we can try. | // here if DisconnectTip failed, but we can try. | ||||
disconnectpool.updateMempoolForReorg(config, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// Now mark the blocks we just disconnected as descendants invalid | |||||
// (note this may not be all descendants). | |||||
while (pindex_was_in_chain && invalid_walk_tip != pindex) { | |||||
invalid_walk_tip->nStatus = | |||||
invalidate ? invalid_walk_tip->nStatus.withFailedParent() | |||||
: invalid_walk_tip->nStatus.withParkedParent(); | |||||
setDirtyBlockIndex.insert(invalid_walk_tip); | |||||
invalid_walk_tip = invalid_walk_tip->pprev; | |||||
} | |||||
// Mark the block as either invalid or parked. | |||||
pindex->nStatus = invalidate ? pindex->nStatus.withFailed() | |||||
: pindex->nStatus.withParked(); | |||||
setDirtyBlockIndex.insert(pindex); | |||||
if (invalidate) { | |||||
g_failed_blocks.insert(pindex); | |||||
} | |||||
// DisconnectTip will add transactions to disconnectpool; try to add these | // DisconnectTip will add transactions to disconnectpool; try to add these | ||||
// back to the mempool. | // back to the mempool. | ||||
disconnectpool.updateMempoolForReorg(config, true); | disconnectpool.updateMempoolForReorg(config, true); | ||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, | // The resulting new best tip may not be in setBlockIndexCandidates anymore, | ||||
// so add it again. | // so add it again. | ||||
for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { | for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { | ||||
CBlockIndex *i = it.second; | CBlockIndex *i = it.second; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
template <typename F> | template <typename F> | ||||
void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) { | void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) { | ||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
if (pindex->nStatus != newStatus && | if (pindex->nStatus != newStatus && | ||||
pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | ||||
pindex->nStatus = newStatus; | pindex->nStatus = newStatus; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
if (newStatus.isValid()) { | |||||
g_failed_blocks.erase(pindex); | |||||
} | |||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && | ||||
setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 12 Lines | void UpdateFlags(CBlockIndex *pindex, F f, C fchild) { | ||||
} | } | ||||
// Update the flags from all ancestors too. | // Update the flags from all ancestors too. | ||||
while (pindex != nullptr) { | while (pindex != nullptr) { | ||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
if (pindex->nStatus != newStatus) { | if (pindex->nStatus != newStatus) { | ||||
pindex->nStatus = newStatus; | pindex->nStatus = newStatus; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
if (newStatus.isValid()) { | |||||
g_failed_blocks.erase(pindex); | |||||
} | |||||
} | } | ||||
pindex = pindex->pprev; | pindex = pindex->pprev; | ||||
} | } | ||||
} | } | ||||
template <typename F> void UpdateFlags(CBlockIndex *pindex, F f) { | template <typename F> void UpdateFlags(CBlockIndex *pindex, F f) { | ||||
// Handy shorthand. | // Handy shorthand. | ||||
UpdateFlags(pindex, f, f); | UpdateFlags(pindex, f, f); | ||||
▲ Show 20 Lines • Show All 651 Lines • ▼ Show 20 Lines | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
state.GetRejectReason().c_str()); | state.GetRejectReason().c_str()); | ||||
} | } | ||||
if (!ContextualCheckBlockHeader(config, block, state, pindexPrev, | if (!ContextualCheckBlockHeader(config, block, state, pindexPrev, | ||||
GetAdjustedTime())) { | GetAdjustedTime())) { | ||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", | return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", | ||||
__func__, hash.ToString(), FormatStateMessage(state)); | __func__, hash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
if (!pindexPrev->IsValid(BlockValidity::SCRIPTS)) { | |||||
for (const CBlockIndex *failedit : g_failed_blocks) { | |||||
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { | |||||
assert(failedit->nStatus.hasFailed()); | |||||
CBlockIndex *invalid_walk = pindexPrev; | |||||
while (invalid_walk != failedit) { | |||||
invalid_walk->nStatus = | |||||
invalid_walk->nStatus.withFailedParent(); | |||||
setDirtyBlockIndex.insert(invalid_walk); | |||||
invalid_walk = invalid_walk->pprev; | |||||
} | |||||
return state.DoS(100, | |||||
error("%s: prev block invalid", __func__), | |||||
REJECT_INVALID, "bad-prevblk"); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
if (pindex == nullptr) { | if (pindex == nullptr) { | ||||
pindex = AddToBlockIndex(block); | pindex = AddToBlockIndex(block); | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
▲ Show 20 Lines • Show All 581 Lines • ▼ Show 20 Lines | for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) { | ||||
mapBlocksUnlinked.insert( | mapBlocksUnlinked.insert( | ||||
std::make_pair(pindex->pprev, pindex)); | std::make_pair(pindex->pprev, pindex)); | ||||
} | } | ||||
} else { | } else { | ||||
pindex->nChainTx = pindex->nTx; | pindex->nChainTx = pindex->nTx; | ||||
} | } | ||||
} | } | ||||
if (!pindex->nStatus.hasFailed() && pindex->pprev && | |||||
pindex->pprev->nStatus.hasFailed()) { | |||||
pindex->nStatus = pindex->nStatus.withFailedParent(); | |||||
setDirtyBlockIndex.insert(pindex); | |||||
} | |||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | ||||
(pindex->nChainTx || pindex->pprev == nullptr)) { | (pindex->nChainTx || pindex->pprev == nullptr)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
if (pindex->nStatus.isInvalid() && | if (pindex->nStatus.isInvalid() && | ||||
(!pindexBestInvalid || | (!pindexBestInvalid || | ||||
pindex->nChainWork > pindexBestInvalid->nChainWork)) { | pindex->nChainWork > pindexBestInvalid->nChainWork)) { | ||||
▲ Show 20 Lines • Show All 475 Lines • ▼ Show 20 Lines | void UnloadBlockIndex() { | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
pindexBestHeader = nullptr; | pindexBestHeader = nullptr; | ||||
g_mempool.clear(); | g_mempool.clear(); | ||||
mapBlocksUnlinked.clear(); | mapBlocksUnlinked.clear(); | ||||
vinfoBlockFile.clear(); | vinfoBlockFile.clear(); | ||||
nLastBlockFile = 0; | nLastBlockFile = 0; | ||||
nBlockSequenceId = 1; | nBlockSequenceId = 1; | ||||
setDirtyBlockIndex.clear(); | setDirtyBlockIndex.clear(); | ||||
g_failed_blocks.clear(); | |||||
setDirtyFileInfo.clear(); | setDirtyFileInfo.clear(); | ||||
versionbitscache.Clear(); | versionbitscache.Clear(); | ||||
for (BlockMap::value_type &entry : mapBlockIndex) { | for (BlockMap::value_type &entry : mapBlockIndex) { | ||||
delete entry.second; | delete entry.second; | ||||
} | } | ||||
mapBlockIndex.clear(); | mapBlockIndex.clear(); | ||||
▲ Show 20 Lines • Show All 718 Lines • Show Last 20 Lines |