Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
#include <boost/thread.hpp> // boost::this_thread::interruption_point() (mingw) | #include <boost/thread.hpp> // boost::this_thread::interruption_point() (mingw) | ||||
#include <string> | #include <string> | ||||
#include <thread> | #include <thread> | ||||
#define MICRO 0.000001 | #define MICRO 0.000001 | ||||
#define MILLI 0.001 | #define MILLI 0.001 | ||||
static CChainState g_chainstate; | namespace { | ||||
BlockManager g_blockman; | |||||
} // namespace | |||||
static CChainState g_chainstate(g_blockman); | |||||
CChainState &ChainstateActive() { | CChainState &ChainstateActive() { | ||||
return g_chainstate; | return g_chainstate; | ||||
} | } | ||||
CChain &ChainActive() { | CChain &ChainActive() { | ||||
return g_chainstate.m_chain; | return g_chainstate.m_chain; | ||||
} | } | ||||
/** | /** | ||||
* Global state | * Global state | ||||
* | * | ||||
* Mutex to guard access to validation specific variables, such as reading | * Mutex to guard access to validation specific variables, such as reading | ||||
* or changing the chainstate. | * or changing the chainstate. | ||||
* | * | ||||
* This may also need to be locked when updating the transaction pool, e.g. on | * This may also need to be locked when updating the transaction pool, e.g. on | ||||
* AcceptToMemoryPool. See CTxMemPool::cs comment for details. | * AcceptToMemoryPool. See CTxMemPool::cs comment for details. | ||||
* | * | ||||
* The transaction pool has a separate lock to allow reading from it and the | * The transaction pool has a separate lock to allow reading from it and the | ||||
* chainstate at the same time. | * chainstate at the same time. | ||||
*/ | */ | ||||
RecursiveMutex cs_main; | RecursiveMutex cs_main; | ||||
BlockMap &mapBlockIndex = ::ChainstateActive().mapBlockIndex; | BlockMap &mapBlockIndex = g_blockman.m_block_index; | ||||
CBlockIndex *pindexBestHeader = nullptr; | CBlockIndex *pindexBestHeader = nullptr; | ||||
Mutex g_best_block_mutex; | Mutex g_best_block_mutex; | ||||
std::condition_variable g_best_block_cv; | std::condition_variable g_best_block_cv; | ||||
uint256 g_best_block; | uint256 g_best_block; | ||||
std::atomic_bool fImporting(false); | std::atomic_bool fImporting(false); | ||||
std::atomic_bool fReindex(false); | std::atomic_bool fReindex(false); | ||||
bool fHavePruned = false; | bool fHavePruned = false; | ||||
bool fPruneMode = false; | bool fPruneMode = false; | ||||
Show All 20 Lines | |||||
CBlockIndex *&pindexBestParked = ::ChainstateActive().pindexBestParked; | CBlockIndex *&pindexBestParked = ::ChainstateActive().pindexBestParked; | ||||
/** | /** | ||||
* The best finalized block. | * The best finalized block. | ||||
* This block cannot be reorged in any way except by explicit user action. | * This block cannot be reorged in any way except by explicit user action. | ||||
*/ | */ | ||||
CBlockIndex const *&pindexFinalized = ::ChainstateActive().pindexFinalized; | CBlockIndex const *&pindexFinalized = ::ChainstateActive().pindexFinalized; | ||||
/** | |||||
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B | |||||
* has transactions. Pruned nodes may have entries where B is missing data. | |||||
*/ | |||||
std::multimap<CBlockIndex *, CBlockIndex *> &mapBlocksUnlinked = | |||||
::ChainstateActive().mapBlocksUnlinked; | |||||
RecursiveMutex cs_LastBlockFile; | RecursiveMutex cs_LastBlockFile; | ||||
std::vector<CBlockFileInfo> vinfoBlockFile; | std::vector<CBlockFileInfo> vinfoBlockFile; | ||||
int nLastBlockFile = 0; | int nLastBlockFile = 0; | ||||
/** | /** | ||||
* Global flag to indicate we should check to see if there are block/undo files | * Global flag to indicate we should check to see if there are block/undo files | ||||
* that should be deleted. Set on startup or if we allocate more file space when | * that should be deleted. Set on startup or if we allocate more file space when | ||||
* we're in prune mode. | * we're in prune mode. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 814 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())); | ||||
} | } | ||||
void CChainState::InvalidBlockFound(CBlockIndex *pindex, | void CChainState::InvalidBlockFound(CBlockIndex *pindex, | ||||
const BlockValidationState &state) { | const BlockValidationState &state) { | ||||
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { | if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { | ||||
pindex->nStatus = pindex->nStatus.withFailed(); | pindex->nStatus = pindex->nStatus.withFailed(); | ||||
m_failed_blocks.insert(pindex); | m_blockman.m_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 587 Lines • ▼ Show 20 Lines | bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, | ||||
if (!hashAssumeValid.IsNull()) { | if (!hashAssumeValid.IsNull()) { | ||||
// We've been configured with the hash of a block which has been | // We've been configured with the hash of a block which has been | ||||
// externally verified to have a valid history. A suitable default value | // externally verified to have a valid history. A suitable default value | ||||
// is included with the software and updated from time to time. Because | // is included with the software and updated from time to time. Because | ||||
// validity relative to a piece of software is an objective fact these | // validity relative to a piece of software is an objective fact these | ||||
// defaults can be easily reviewed. This setting doesn't force the | // defaults can be easily reviewed. This setting doesn't force the | ||||
// selection of any particular chain but makes validating some faster by | // selection of any particular chain but makes validating some faster by | ||||
// effectively caching the result of part of the verification. | // effectively caching the result of part of the verification. | ||||
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); | BlockMap::const_iterator it = | ||||
if (it != mapBlockIndex.end()) { | m_blockman.m_block_index.find(hashAssumeValid); | ||||
if (it != m_blockman.m_block_index.end()) { | |||||
if (it->second->GetAncestor(pindex->nHeight) == pindex && | if (it->second->GetAncestor(pindex->nHeight) == pindex && | ||||
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && | pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && | ||||
pindexBestHeader->nChainWork >= nMinimumChainWork) { | pindexBestHeader->nChainWork >= nMinimumChainWork) { | ||||
// This block is a member of the assumed verified chain and an | // This block is a member of the assumed verified chain and an | ||||
// ancestor of the best header. The equivalent time check | // ancestor of the best header. The equivalent time check | ||||
// discourages hash power from extorting the network via DOS | // discourages hash power from extorting the network via DOS | ||||
// attack into accepting an invalid block through telling users | // attack into accepting an invalid block through telling users | ||||
// they must manually set assumevalid. Requiring a software | // they must manually set assumevalid. Requiring a software | ||||
▲ Show 20 Lines • Show All 1,007 Lines • ▼ Show 20 Lines | do { | ||||
// Remove the entire chain from the set. | // Remove the entire chain from the set. | ||||
while (pindexTest != pindexFailed) { | while (pindexTest != pindexFailed) { | ||||
if (fInvalidChain || fParkedChain) { | if (fInvalidChain || fParkedChain) { | ||||
pindexFailed->nStatus = | pindexFailed->nStatus = | ||||
pindexFailed->nStatus.withFailedParent(fInvalidChain) | pindexFailed->nStatus.withFailedParent(fInvalidChain) | ||||
.withParkedParent(fParkedChain); | .withParkedParent(fParkedChain); | ||||
} else if (fMissingData) { | } else if (fMissingData) { | ||||
// If we're missing data, then add back to | // If we're missing data, then add back to | ||||
// mapBlocksUnlinked, so that if the block arrives in the | // m_blocks_unlinked, so that if the block arrives in the | ||||
// future we can try adding to setBlockIndexCandidates | // future we can try adding to setBlockIndexCandidates | ||||
// again. | // again. | ||||
mapBlocksUnlinked.insert( | m_blockman.m_blocks_unlinked.insert( | ||||
std::make_pair(pindexFailed->pprev, pindexFailed)); | std::make_pair(pindexFailed->pprev, pindexFailed)); | ||||
} | } | ||||
setBlockIndexCandidates.erase(pindexFailed); | setBlockIndexCandidates.erase(pindexFailed); | ||||
pindexFailed = pindexFailed->pprev; | pindexFailed = pindexFailed->pprev; | ||||
} | } | ||||
if (fInvalidChain || fParkedChain) { | if (fInvalidChain || fParkedChain) { | ||||
// We discovered a new chain tip that is either parked or | // We discovered a new chain tip that is either parked or | ||||
▲ Show 20 Lines • Show All 388 Lines • ▼ Show 20 Lines | bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, | ||||
// add equal-work blocks to setBlockIndexCandidates as we disconnect. | // add equal-work blocks to setBlockIndexCandidates as we disconnect. | ||||
// To avoid walking the block index repeatedly in search of candidates, | // 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 | // build a map once so that we can look up candidate blocks by chain | ||||
// work as we go. | // work as we go. | ||||
std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work; | std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const auto &entry : mapBlockIndex) { | for (const auto &entry : m_blockman.m_block_index) { | ||||
CBlockIndex *candidate = entry.second; | CBlockIndex *candidate = entry.second; | ||||
// We don't need to put anything in our active chain into the | // We don't need to put anything in our active chain into the | ||||
// multimap, because those candidates will be found and considered | // multimap, because those candidates will be found and considered | ||||
// as we disconnect. | // as we disconnect. | ||||
// Instead, consider only non-active-chain blocks that have at | // Instead, consider only non-active-chain blocks that have at | ||||
// least as much work as where we expect the new tip to end up. | // least as much work as where we expect the new tip to end up. | ||||
if (!m_chain.Contains(candidate) && | if (!m_chain.Contains(candidate) && | ||||
!CBlockIndexWorkComparator()(candidate, pindex->pprev) && | !CBlockIndexWorkComparator()(candidate, pindex->pprev) && | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | CheckBlockIndex(chainparams.GetConsensus()); | ||||
// 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_blockman.m_failed_blocks.insert(to_mark_failed_or_parked); | ||||
} | } | ||||
// If any new blocks somehow arrived while we were disconnecting | // If any new blocks somehow arrived while we were disconnecting | ||||
// (above), then the pre-calculation of what should go into | // (above), then the pre-calculation of what should go into | ||||
// setBlockIndexCandidates may have missed entries. This would | // setBlockIndexCandidates may have missed entries. This would | ||||
// technically be an inconsistency in the block index, but if we clean | // technically be an inconsistency in the block index, but if we clean | ||||
// it up here, this should be an essentially unobservable error. | // it up here, this should be an essentially unobservable error. | ||||
// Loop back over all block index entries and add any missing entries | // Loop back over all block index entries and add any missing entries | ||||
// to setBlockIndexCandidates. | // to setBlockIndexCandidates. | ||||
for (const std::pair<const BlockHash, CBlockIndex *> &it : | for (const std::pair<const BlockHash, CBlockIndex *> &it : | ||||
mapBlockIndex) { | m_blockman.m_block_index) { | ||||
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 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | bool CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, | ||||
CBlockIndex *pindex, F f) { | CBlockIndex *pindex, F f) { | ||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
if (pindex->nStatus != newStatus && | if (pindex->nStatus != newStatus && | ||||
(!pindexBase || | (!pindexBase || | ||||
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()) { | if (newStatus.isValid()) { | ||||
m_failed_blocks.erase(pindex); | m_blockman.m_failed_blocks.erase(pindex); | ||||
} | } | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | ||||
pindex->HaveTxsDownloaded() && | pindex->HaveTxsDownloaded() && | ||||
setBlockIndexCandidates.value_comp()(::ChainActive().Tip(), | setBlockIndexCandidates.value_comp()(::ChainActive().Tip(), | ||||
pindex)) { | pindex)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
Show All 20 Lines | void CChainState::UpdateFlags(CBlockIndex *pindex, CBlockIndex *&pindexReset, | ||||
if (pindexReset && | if (pindexReset && | ||||
pindexReset->GetAncestor(pindexDeepestChanged->nHeight) == | pindexReset->GetAncestor(pindexDeepestChanged->nHeight) == | ||||
pindexDeepestChanged) { | pindexDeepestChanged) { | ||||
// reset pindexReset if it had a modified ancestor. | // reset pindexReset if it had a modified ancestor. | ||||
pindexReset = nullptr; | pindexReset = nullptr; | ||||
} | } | ||||
// Update all blocks under modified blocks. | // Update all blocks under modified blocks. | ||||
BlockMap::iterator it = mapBlockIndex.begin(); | BlockMap::iterator it = m_blockman.m_block_index.begin(); | ||||
while (it != mapBlockIndex.end()) { | while (it != m_blockman.m_block_index.end()) { | ||||
UpdateFlagsForBlock(pindex, it->second, fChild); | UpdateFlagsForBlock(pindex, it->second, fChild); | ||||
UpdateFlagsForBlock(pindexDeepestChanged, it->second, | UpdateFlagsForBlock(pindexDeepestChanged, it->second, | ||||
fAncestorWasChanged); | fAncestorWasChanged); | ||||
it++; | it++; | ||||
} | } | ||||
} | } | ||||
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { | void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
bool IsBlockFinalized(const CBlockIndex *pindex) { | bool IsBlockFinalized(const CBlockIndex *pindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
return pindexFinalized && | return pindexFinalized && | ||||
pindexFinalized->GetAncestor(pindex->nHeight) == pindex; | pindexFinalized->GetAncestor(pindex->nHeight) == pindex; | ||||
} | } | ||||
CBlockIndex *CChainState::AddToBlockIndex(const CBlockHeader &block) { | CBlockIndex *BlockManager::AddToBlockIndex(const CBlockHeader &block) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Check for duplicate | // Check for duplicate | ||||
BlockHash hash = block.GetHash(); | BlockHash hash = block.GetHash(); | ||||
BlockMap::iterator it = mapBlockIndex.find(hash); | BlockMap::iterator it = m_block_index.find(hash); | ||||
if (it != mapBlockIndex.end()) { | if (it != m_block_index.end()) { | ||||
return it->second; | return it->second; | ||||
} | } | ||||
// Construct new block index object | // Construct new block index object | ||||
CBlockIndex *pindexNew = new CBlockIndex(block); | CBlockIndex *pindexNew = new CBlockIndex(block); | ||||
// We assign the sequence id to blocks only when the full data is available, | // We assign the sequence id to blocks only when the full data is available, | ||||
// to avoid miners withholding blocks but broadcasting headers, to get a | // to avoid miners withholding blocks but broadcasting headers, to get a | ||||
// competitive advantage. | // competitive advantage. | ||||
pindexNew->nSequenceId = 0; | pindexNew->nSequenceId = 0; | ||||
BlockMap::iterator mi = | BlockMap::iterator mi = | ||||
mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; | m_block_index.insert(std::make_pair(hash, pindexNew)).first; | ||||
pindexNew->phashBlock = &((*mi).first); | pindexNew->phashBlock = &((*mi).first); | ||||
BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); | BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock); | ||||
if (miPrev != mapBlockIndex.end()) { | if (miPrev != m_block_index.end()) { | ||||
pindexNew->pprev = (*miPrev).second; | pindexNew->pprev = (*miPrev).second; | ||||
pindexNew->nHeight = pindexNew->pprev->nHeight + 1; | pindexNew->nHeight = pindexNew->pprev->nHeight + 1; | ||||
pindexNew->BuildSkip(); | pindexNew->BuildSkip(); | ||||
} | } | ||||
pindexNew->nTimeReceived = GetTime(); | pindexNew->nTimeReceived = GetTime(); | ||||
pindexNew->nTimeMax = | pindexNew->nTimeMax = | ||||
(pindexNew->pprev | (pindexNew->pprev | ||||
? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) | ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (pindexNew->UpdateChainStats()) { | ||||
if (m_chain.Tip() == nullptr || | if (m_chain.Tip() == nullptr || | ||||
!setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) { | !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, | std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator> | std::multimap<CBlockIndex *, CBlockIndex *>::iterator> | ||||
range = mapBlocksUnlinked.equal_range(pindex); | range = m_blockman.m_blocks_unlinked.equal_range(pindex); | ||||
while (range.first != range.second) { | while (range.first != range.second) { | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = | std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = | ||||
range.first; | range.first; | ||||
queue.push_back(it->second); | queue.push_back(it->second); | ||||
range.first++; | range.first++; | ||||
mapBlocksUnlinked.erase(it); | m_blockman.m_blocks_unlinked.erase(it); | ||||
} | } | ||||
} | } | ||||
} else if (pindexNew->pprev && | } else if (pindexNew->pprev && | ||||
pindexNew->pprev->IsValid(BlockValidity::TREE)) { | pindexNew->pprev->IsValid(BlockValidity::TREE)) { | ||||
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); | m_blockman.m_blocks_unlinked.insert( | ||||
std::make_pair(pindexNew->pprev, pindexNew)); | |||||
} | } | ||||
} | } | ||||
static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, | static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, | ||||
unsigned int nHeight, uint64_t nTime, | unsigned int nHeight, uint64_t nTime, | ||||
bool fKnown = false) { | bool fKnown = false) { | ||||
LOCK(cs_LastBlockFile); | LOCK(cs_LastBlockFile); | ||||
▲ Show 20 Lines • Show All 398 Lines • ▼ Show 20 Lines | static bool ContextualCheckBlock(const CBlock &block, | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* If the provided block header is valid, add it to the block index. | * If the provided block header is valid, add it to the block index. | ||||
* | * | ||||
* Returns true if the block is successfully added to the block index. | * Returns true if the block is successfully added to the block index. | ||||
*/ | */ | ||||
bool CChainState::AcceptBlockHeader(const Config &config, | bool BlockManager::AcceptBlockHeader(const Config &config, | ||||
const CBlockHeader &block, | const CBlockHeader &block, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
CBlockIndex **ppindex) { | CBlockIndex **ppindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
// Check for duplicate | // Check for duplicate | ||||
BlockHash hash = block.GetHash(); | BlockHash hash = block.GetHash(); | ||||
BlockMap::iterator miSelf = mapBlockIndex.find(hash); | BlockMap::iterator miSelf = m_block_index.find(hash); | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (hash != chainparams.GetConsensus().hashGenesisBlock) { | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
if (miSelf != mapBlockIndex.end()) { | if (miSelf != m_block_index.end()) { | ||||
// Block header is already known. | // Block header is already known. | ||||
pindex = miSelf->second; | pindex = miSelf->second; | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, | LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, | ||||
hash.ToString()); | hash.ToString()); | ||||
return state.Invalid( | return state.Invalid( | ||||
BlockValidationResult::BLOCK_CACHED_INVALID, 0, | BlockValidationResult::BLOCK_CACHED_INVALID, 0, | ||||
"duplicate"); | "duplicate"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), | if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), | ||||
BlockValidationOptions(config))) { | BlockValidationOptions(config))) { | ||||
return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, | return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, | ||||
hash.ToString(), FormatStateMessage(state)); | hash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
// Get prev block index | // Get prev block index | ||||
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); | BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock); | ||||
if (mi == mapBlockIndex.end()) { | if (mi == m_block_index.end()) { | ||||
LogPrintf("ERROR: %s: prev block not found\n", __func__); | LogPrintf("ERROR: %s: prev block not found\n", __func__); | ||||
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, 0, | return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, 0, | ||||
"prev-blk-not-found"); | "prev-blk-not-found"); | ||||
} | } | ||||
CBlockIndex *pindexPrev = (*mi).second; | CBlockIndex *pindexPrev = (*mi).second; | ||||
assert(pindexPrev); | assert(pindexPrev); | ||||
if (pindexPrev->nStatus.isInvalid()) { | if (pindexPrev->nStatus.isInvalid()) { | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | bool BlockManager::AcceptBlockHeader(const Config &config, | ||||
if (pindex == nullptr) { | if (pindex == nullptr) { | ||||
pindex = AddToBlockIndex(block); | pindex = AddToBlockIndex(block); | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | |||||
return true; | return true; | ||||
} | } | ||||
// Exposed wrapper for AcceptBlockHeader | // Exposed wrapper for AcceptBlockHeader | ||||
bool ProcessNewBlockHeaders(const Config &config, | bool ProcessNewBlockHeaders(const Config &config, | ||||
const std::vector<CBlockHeader> &headers, | const std::vector<CBlockHeader> &headers, | ||||
BlockValidationState &state, | BlockValidationState &state, | ||||
const CBlockIndex **ppindex) { | const CBlockIndex **ppindex) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
// Use a temp pindex instead of ppindex to avoid a const_cast | // Use a temp pindex instead of ppindex to avoid a const_cast | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (!::ChainstateActive().AcceptBlockHeader(config, header, state, | bool accepted = | ||||
&pindex)) { | g_blockman.AcceptBlockHeader(config, header, state, &pindex); | ||||
::ChainstateActive().CheckBlockIndex( | |||||
config.GetChainParams().GetConsensus()); | |||||
if (!accepted) { | |||||
return false; | return false; | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | bool CChainState::AcceptBlock(const Config &config, | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlock &block = *pblock; | const CBlock &block = *pblock; | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = false; | *fNewBlock = false; | ||||
} | } | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (!AcceptBlockHeader(config, block, state, &pindex)) { | |||||
bool accepted_header = | |||||
m_blockman.AcceptBlockHeader(config, block, state, &pindex); | |||||
CheckBlockIndex(config.GetChainParams().GetConsensus()); | |||||
if (!accepted_header) { | |||||
return false; | return false; | ||||
} | } | ||||
// Try to process all requested blocks that we don't have, but only | // Try to process all requested blocks that we don't have, but only | ||||
// process an unrequested block if it's new and has enough work to | // process an unrequested block if it's new and has enough work to | ||||
// advance our tip, and isn't too many blocks ahead. | // advance our tip, and isn't too many blocks ahead. | ||||
bool fAlreadyHave = pindex->nStatus.hasData(); | bool fAlreadyHave = pindex->nStatus.hasData(); | ||||
▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* Prune a block file (modify associated database entries) | * Prune a block file (modify associated database entries) | ||||
*/ | */ | ||||
void PruneOneBlockFile(const int fileNumber) { | void PruneOneBlockFile(const int fileNumber) { | ||||
LOCK(cs_LastBlockFile); | LOCK(cs_LastBlockFile); | ||||
for (const auto &entry : mapBlockIndex) { | for (const auto &entry : g_blockman.m_block_index) { | ||||
CBlockIndex *pindex = entry.second; | CBlockIndex *pindex = entry.second; | ||||
if (pindex->nFile == fileNumber) { | if (pindex->nFile == fileNumber) { | ||||
pindex->nStatus = pindex->nStatus.withData(false).withUndo(false); | pindex->nStatus = pindex->nStatus.withData(false).withUndo(false); | ||||
pindex->nFile = 0; | pindex->nFile = 0; | ||||
pindex->nDataPos = 0; | pindex->nDataPos = 0; | ||||
pindex->nUndoPos = 0; | pindex->nUndoPos = 0; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
// Prune from mapBlocksUnlinked -- any block we prune would have | // Prune from m_blocks_unlinked -- any block we prune would have | ||||
// to be downloaded again in order to consider its chain, at which | // to be downloaded again in order to consider its chain, at which | ||||
// point it would be considered as a candidate for | // point it would be considered as a candidate for | ||||
// mapBlocksUnlinked or setBlockIndexCandidates. | // m_blocks_unlinked or setBlockIndexCandidates. | ||||
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, | auto range = | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator> | g_blockman.m_blocks_unlinked.equal_range(pindex->pprev); | ||||
range = mapBlocksUnlinked.equal_range(pindex->pprev); | |||||
while (range.first != range.second) { | while (range.first != range.second) { | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = | std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = | ||||
range.first; | range.first; | ||||
range.first++; | range.first++; | ||||
if (_it->second == pindex) { | if (_it->second == pindex) { | ||||
mapBlocksUnlinked.erase(_it); | g_blockman.m_blocks_unlinked.erase(_it); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
vinfoBlockFile[fileNumber].SetNull(); | vinfoBlockFile[fileNumber].SetNull(); | ||||
setDirtyFileInfo.insert(fileNumber); | setDirtyFileInfo.insert(fileNumber); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | |||||
static FILE *OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) { | static FILE *OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) { | ||||
return UndoFileSeq().Open(pos, fReadOnly); | return UndoFileSeq().Open(pos, fReadOnly); | ||||
} | } | ||||
fs::path GetBlockPosFilename(const FlatFilePos &pos) { | fs::path GetBlockPosFilename(const FlatFilePos &pos) { | ||||
return BlockFileSeq().FileName(pos); | return BlockFileSeq().FileName(pos); | ||||
} | } | ||||
CBlockIndex *CChainState::InsertBlockIndex(const BlockHash &hash) { | CBlockIndex *BlockManager::InsertBlockIndex(const BlockHash &hash) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (hash.IsNull()) { | if (hash.IsNull()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Return existing | // Return existing | ||||
BlockMap::iterator mi = mapBlockIndex.find(hash); | BlockMap::iterator mi = m_block_index.find(hash); | ||||
if (mi != mapBlockIndex.end()) { | if (mi != m_block_index.end()) { | ||||
return (*mi).second; | return (*mi).second; | ||||
} | } | ||||
// Create new | // Create new | ||||
CBlockIndex *pindexNew = new CBlockIndex(); | CBlockIndex *pindexNew = new CBlockIndex(); | ||||
mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; | mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first; | ||||
pindexNew->phashBlock = &((*mi).first); | pindexNew->phashBlock = &((*mi).first); | ||||
return pindexNew; | return pindexNew; | ||||
} | } | ||||
bool CChainState::LoadBlockIndex(const Consensus::Params ¶ms, | bool BlockManager::LoadBlockIndex(const Consensus::Params ¶ms, | ||||
CBlockTreeDB &blocktree) { | CBlockTreeDB &blocktree) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (!blocktree.LoadBlockIndexGuts( | if (!blocktree.LoadBlockIndexGuts( | ||||
params, [this](const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED( | params, [this](const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED( | ||||
cs_main) { return this->InsertBlockIndex(hash); })) { | cs_main) { return this->InsertBlockIndex(hash); })) { | ||||
return false; | return false; | ||||
} | } | ||||
// Calculate nChainWork | // Calculate nChainWork | ||||
Show All 15 Lines | for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) { | ||||
GetBlockProof(*pindex); | GetBlockProof(*pindex); | ||||
pindex->nTimeMax = | pindex->nTimeMax = | ||||
(pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) | (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) | ||||
: pindex->nTime); | : pindex->nTime); | ||||
// We can link the chain of blocks for which we've received transactions | // We can link the chain of blocks for which we've received transactions | ||||
// at some point. Pruned nodes may have deleted the block. | // at some point. Pruned nodes may have deleted the block. | ||||
if (pindex->nTx > 0) { | if (pindex->nTx > 0) { | ||||
if (!pindex->UpdateChainStats() && pindex->pprev) { | if (!pindex->UpdateChainStats() && pindex->pprev) { | ||||
mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); | m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex)); | ||||
} | } | ||||
} | } | ||||
if (!pindex->nStatus.hasFailed() && pindex->pprev && | if (!pindex->nStatus.hasFailed() && pindex->pprev && | ||||
pindex->pprev->nStatus.hasFailed()) { | pindex->pprev->nStatus.hasFailed()) { | ||||
pindex->nStatus = pindex->nStatus.withFailedParent(); | pindex->nStatus = pindex->nStatus.withFailedParent(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} | } | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | ||||
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) { | (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) { | ||||
setBlockIndexCandidates.insert(pindex); | ::ChainstateActive().setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
if (pindex->nStatus.isInvalid() && | if (pindex->nStatus.isInvalid() && | ||||
(!pindexBestInvalid || | (!pindexBestInvalid || | ||||
pindex->nChainWork > pindexBestInvalid->nChainWork)) { | pindex->nChainWork > pindexBestInvalid->nChainWork)) { | ||||
pindexBestInvalid = pindex; | pindexBestInvalid = pindex; | ||||
} | } | ||||
Show All 12 Lines | for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) { | ||||
CBlockIndexWorkComparator()(pindexBestHeader, pindex))) { | CBlockIndexWorkComparator()(pindexBestHeader, pindex))) { | ||||
pindexBestHeader = pindex; | pindexBestHeader = pindex; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void BlockManager::Unload() { | |||||
m_failed_blocks.clear(); | |||||
m_blocks_unlinked.clear(); | |||||
for (const BlockMap::value_type &entry : m_block_index) { | |||||
delete entry.second; | |||||
} | |||||
m_block_index.clear(); | |||||
} | |||||
static bool LoadBlockIndexDB(const Consensus::Params ¶ms) | static bool LoadBlockIndexDB(const Consensus::Params ¶ms) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
if (!::ChainstateActive().LoadBlockIndex(params, *pblocktree)) { | if (!g_blockman.LoadBlockIndex(params, *pblocktree)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Load block file info | // Load block file info | ||||
pblocktree->ReadLastBlockFile(nLastBlockFile); | pblocktree->ReadLastBlockFile(nLastBlockFile); | ||||
vinfoBlockFile.resize(nLastBlockFile + 1); | vinfoBlockFile.resize(nLastBlockFile + 1); | ||||
LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); | LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); | ||||
for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { | for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { | ||||
▲ Show 20 Lines • Show All 382 Lines • ▼ Show 20 Lines | |||||
bool ReplayBlocks(const Consensus::Params ¶ms, CCoinsView *view) { | bool ReplayBlocks(const Consensus::Params ¶ms, CCoinsView *view) { | ||||
return ::ChainstateActive().ReplayBlocks(params, view); | return ::ChainstateActive().ReplayBlocks(params, view); | ||||
} | } | ||||
// May NOT be used after any connections are up as much of the peer-processing | // May NOT be used after any connections are up as much of the peer-processing | ||||
// logic assumes a consistent block index state | // logic assumes a consistent block index state | ||||
void CChainState::UnloadBlockIndex() { | void CChainState::UnloadBlockIndex() { | ||||
nBlockSequenceId = 1; | nBlockSequenceId = 1; | ||||
m_failed_blocks.clear(); | |||||
setBlockIndexCandidates.clear(); | setBlockIndexCandidates.clear(); | ||||
} | } | ||||
// May NOT be used after any connections are up as much | // May NOT be used after any connections are up as much | ||||
// of the peer-processing logic assumes a consistent | // of the peer-processing logic assumes a consistent | ||||
// block index state | // block index state | ||||
void UnloadBlockIndex() { | void UnloadBlockIndex() { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
::ChainActive().SetTip(nullptr); | ::ChainActive().SetTip(nullptr); | ||||
g_blockman.Unload(); | |||||
pindexFinalized = nullptr; | pindexFinalized = nullptr; | ||||
pindexBestInvalid = nullptr; | pindexBestInvalid = nullptr; | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
pindexBestHeader = nullptr; | pindexBestHeader = nullptr; | ||||
pindexBestForkTip = nullptr; | pindexBestForkTip = nullptr; | ||||
pindexBestForkBase = nullptr; | pindexBestForkBase = nullptr; | ||||
ResetASERTAnchorBlockCache(); | ResetASERTAnchorBlockCache(); | ||||
g_mempool.clear(); | g_mempool.clear(); | ||||
mapBlocksUnlinked.clear(); | |||||
vinfoBlockFile.clear(); | vinfoBlockFile.clear(); | ||||
nLastBlockFile = 0; | nLastBlockFile = 0; | ||||
setDirtyBlockIndex.clear(); | setDirtyBlockIndex.clear(); | ||||
setDirtyFileInfo.clear(); | setDirtyFileInfo.clear(); | ||||
for (const BlockMap::value_type &entry : mapBlockIndex) { | |||||
delete entry.second; | |||||
} | |||||
mapBlockIndex.clear(); | |||||
fHavePruned = false; | fHavePruned = false; | ||||
::ChainstateActive().UnloadBlockIndex(); | ::ChainstateActive().UnloadBlockIndex(); | ||||
} | } | ||||
bool LoadBlockIndex(const Consensus::Params ¶ms) { | bool LoadBlockIndex(const Consensus::Params ¶ms) { | ||||
// Load block index from databases | // Load block index from databases | ||||
bool needs_init = fReindex; | bool needs_init = fReindex; | ||||
Show All 30 Lines | bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { | ||||
} | } | ||||
try { | try { | ||||
const CBlock &block = chainparams.GenesisBlock(); | const CBlock &block = chainparams.GenesisBlock(); | ||||
FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); | FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); | ||||
if (blockPos.IsNull()) { | if (blockPos.IsNull()) { | ||||
return error("%s: writing genesis block to disk failed", __func__); | return error("%s: writing genesis block to disk failed", __func__); | ||||
} | } | ||||
CBlockIndex *pindex = AddToBlockIndex(block); | CBlockIndex *pindex = m_blockman.AddToBlockIndex(block); | ||||
ReceivedBlockTransactions(block, pindex, blockPos); | ReceivedBlockTransactions(block, pindex, blockPos); | ||||
} catch (const std::runtime_error &e) { | } catch (const std::runtime_error &e) { | ||||
return error("%s: failed to write genesis block: %s", __func__, | return error("%s: failed to write genesis block: %s", __func__, | ||||
e.what()); | e.what()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 342 Lines • ▼ Show 20 Lines | while (pindex != nullptr) { | ||||
// m_chain.Tip() must also be there even if some data has | // m_chain.Tip() must also be there even if some data has | ||||
// been pruned. | // been pruned. | ||||
if (pindex == m_chain.Tip()) { | if (pindex == m_chain.Tip()) { | ||||
assert(setBlockIndexCandidates.count(pindex)); | assert(setBlockIndexCandidates.count(pindex)); | ||||
} | } | ||||
// If some parent is missing, then it could be that this block | // If some parent is missing, then it could be that this block | ||||
// was in setBlockIndexCandidates but had to be removed because | // was in setBlockIndexCandidates but had to be removed because | ||||
// of the missing data. In this case it must be in | // of the missing data. In this case it must be in | ||||
// mapBlocksUnlinked -- see test below. | // m_blocks_unlinked -- see test below. | ||||
} | } | ||||
} else { | } else { | ||||
// If this block sorts worse than the current tip or some ancestor's | // If this block sorts worse than the current tip or some ancestor's | ||||
// block has never been seen, it cannot be in | // block has never been seen, it cannot be in | ||||
// setBlockIndexCandidates. | // setBlockIndexCandidates. | ||||
assert(setBlockIndexCandidates.count(pindex) == 0); | assert(setBlockIndexCandidates.count(pindex) == 0); | ||||
} | } | ||||
// Check whether this block is in mapBlocksUnlinked. | // Check whether this block is in m_blocks_unlinked. | ||||
std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, | std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, | ||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator> | std::multimap<CBlockIndex *, CBlockIndex *>::iterator> | ||||
rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); | rangeUnlinked = | ||||
m_blockman.m_blocks_unlinked.equal_range(pindex->pprev); | |||||
bool foundInUnlinked = false; | bool foundInUnlinked = false; | ||||
while (rangeUnlinked.first != rangeUnlinked.second) { | while (rangeUnlinked.first != rangeUnlinked.second) { | ||||
assert(rangeUnlinked.first->first == pindex->pprev); | assert(rangeUnlinked.first->first == pindex->pprev); | ||||
if (rangeUnlinked.first->second == pindex) { | if (rangeUnlinked.first->second == pindex) { | ||||
foundInUnlinked = true; | foundInUnlinked = true; | ||||
break; | break; | ||||
} | } | ||||
rangeUnlinked.first++; | rangeUnlinked.first++; | ||||
} | } | ||||
if (pindex->pprev && pindex->nStatus.hasData() && | if (pindex->pprev && pindex->nStatus.hasData() && | ||||
pindexFirstNeverProcessed != nullptr && | pindexFirstNeverProcessed != nullptr && | ||||
pindexFirstInvalid == nullptr) { | pindexFirstInvalid == nullptr) { | ||||
// If this block has block data available, some parent was never | // If this block has block data available, some parent was never | ||||
// received, and has no invalid parents, it must be in | // received, and has no invalid parents, it must be in | ||||
// mapBlocksUnlinked. | // m_blocks_unlinked. | ||||
assert(foundInUnlinked); | assert(foundInUnlinked); | ||||
} | } | ||||
if (!pindex->nStatus.hasData()) { | if (!pindex->nStatus.hasData()) { | ||||
// Can't be in mapBlocksUnlinked if we don't HAVE_DATA | // Can't be in m_blocks_unlinked if we don't HAVE_DATA | ||||
assert(!foundInUnlinked); | assert(!foundInUnlinked); | ||||
} | } | ||||
if (pindexFirstMissing == nullptr) { | if (pindexFirstMissing == nullptr) { | ||||
// We aren't missing data for any parent -- cannot be in | // We aren't missing data for any parent -- cannot be in | ||||
// mapBlocksUnlinked. | // m_blocks_unlinked. | ||||
assert(!foundInUnlinked); | assert(!foundInUnlinked); | ||||
} | } | ||||
if (pindex->pprev && pindex->nStatus.hasData() && | if (pindex->pprev && pindex->nStatus.hasData() && | ||||
pindexFirstNeverProcessed == nullptr && | pindexFirstNeverProcessed == nullptr && | ||||
pindexFirstMissing != nullptr) { | pindexFirstMissing != nullptr) { | ||||
// We HAVE_DATA for this block, have received data for all parents | // We HAVE_DATA for this block, have received data for all parents | ||||
// at some point, but we're currently missing data for some parent. | // at some point, but we're currently missing data for some parent. | ||||
// We must have pruned. | // We must have pruned. | ||||
assert(fHavePruned); | assert(fHavePruned); | ||||
// This block may have entered mapBlocksUnlinked if: | // This block may have entered m_blocks_unlinked if: | ||||
// - it has a descendant that at some point had more work than the | // - it has a descendant that at some point had more work than the | ||||
// tip, and | // tip, and | ||||
// - we tried switching to that descendant but were missing | // - we tried switching to that descendant but were missing | ||||
// data for some intermediate block between m_chain and the | // data for some intermediate block between m_chain and the | ||||
// tip. | // tip. | ||||
// So if this block is itself better than m_chain.Tip() and it | // So if this block is itself better than m_chain.Tip() and it | ||||
// wasn't in | // wasn't in | ||||
// setBlockIndexCandidates, then it must be in mapBlocksUnlinked. | // setBlockIndexCandidates, then it must be in m_blocks_unlinked. | ||||
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && | if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && | ||||
setBlockIndexCandidates.count(pindex) == 0) { | setBlockIndexCandidates.count(pindex) == 0) { | ||||
if (pindexFirstInvalid == nullptr) { | if (pindexFirstInvalid == nullptr) { | ||||
assert(foundInUnlinked); | assert(foundInUnlinked); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Perhaps too slow | // Perhaps too slow | ||||
▲ Show 20 Lines • Show All 303 Lines • Show Last 20 Lines |