Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | private: | ||||
* | * | ||||
* Because we already walk mapBlockIndex in height-order at startup, we go | * Because we already walk mapBlockIndex in height-order at startup, we go | ||||
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that | * ahead and mark descendants of invalid blocks as FAILED_CHILD at that | ||||
* time, instead of putting things in this set. | * time, instead of putting things in this set. | ||||
*/ | */ | ||||
std::set<CBlockIndex *> m_failed_blocks; | std::set<CBlockIndex *> m_failed_blocks; | ||||
public: | public: | ||||
CChain chainActive; | CChain m_chain; | ||||
BlockMap mapBlockIndex GUARDED_BY(cs_main); | BlockMap mapBlockIndex GUARDED_BY(cs_main); | ||||
std::multimap<CBlockIndex *, CBlockIndex *> mapBlocksUnlinked; | std::multimap<CBlockIndex *, CBlockIndex *> mapBlocksUnlinked; | ||||
CBlockIndex *pindexBestInvalid = nullptr; | CBlockIndex *pindexBestInvalid = nullptr; | ||||
CBlockIndex *pindexBestParked = nullptr; | CBlockIndex *pindexBestParked = nullptr; | ||||
CBlockIndex const *pindexFinalized = nullptr; | CBlockIndex const *pindexFinalized = nullptr; | ||||
bool LoadBlockIndex(const Config &config, CBlockTreeDB &blocktree) | bool LoadBlockIndex(const Config &config, CBlockTreeDB &blocktree) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | |||||
* 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 = g_chainstate.mapBlockIndex; | BlockMap &mapBlockIndex = g_chainstate.mapBlockIndex; | ||||
CChain &chainActive = g_chainstate.chainActive; | CChain &chainActive = g_chainstate.m_chain; | ||||
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; | ||||
int nScriptCheckThreads = 0; | int nScriptCheckThreads = 0; | ||||
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; | ||||
▲ Show 20 Lines • Show All 2,053 Lines • ▼ Show 20 Lines | LogPrintf( | ||||
(unsigned long)pindexNew->nChainTx, | (unsigned long)pindexNew->nChainTx, | ||||
FormatISO8601DateTime(pindexNew->GetBlockTime()), | FormatISO8601DateTime(pindexNew->GetBlockTime()), | ||||
GuessVerificationProgress(config.GetChainParams().TxData(), pindexNew), | GuessVerificationProgress(config.GetChainParams().TxData(), pindexNew), | ||||
pcoinsTip->DynamicMemoryUsage() * (1.0 / (1 << 20)), | pcoinsTip->DynamicMemoryUsage() * (1.0 / (1 << 20)), | ||||
pcoinsTip->GetCacheSize()); | pcoinsTip->GetCacheSize()); | ||||
} | } | ||||
/** | /** | ||||
* Disconnect chainActive's tip. | * Disconnect m_chain's tip. | ||||
* After calling, the mempool will be in an inconsistent state, with | * After calling, the mempool will be in an inconsistent state, with | ||||
* transactions from disconnected blocks being added to disconnectpool. You | * transactions from disconnected blocks being added to disconnectpool. You | ||||
* should make the mempool consistent again by calling updateMempoolForReorg. | * should make the mempool consistent again by calling updateMempoolForReorg. | ||||
* with cs_main held. | * with cs_main held. | ||||
* | * | ||||
* If disconnectpool is nullptr, then no disconnected transactions are added to | * If disconnectpool is nullptr, then no disconnected transactions are added to | ||||
* disconnectpool (note that the caller is responsible for mempool consistency | * disconnectpool (note that the caller is responsible for mempool consistency | ||||
* in any case). | * in any case). | ||||
*/ | */ | ||||
bool CChainState::DisconnectTip(const Config &config, CValidationState &state, | bool CChainState::DisconnectTip(const Config &config, CValidationState &state, | ||||
DisconnectedBlockTransactions *disconnectpool) { | DisconnectedBlockTransactions *disconnectpool) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
CBlockIndex *pindexDelete = chainActive.Tip(); | CBlockIndex *pindexDelete = m_chain.Tip(); | ||||
const Consensus::Params &consensusParams = | const Consensus::Params &consensusParams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
assert(pindexDelete); | assert(pindexDelete); | ||||
// Read block from disk. | // Read block from disk. | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
CBlock &block = *pblock; | CBlock &block = *pblock; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (disconnectpool) { | ||||
disconnectpool->addForBlock(block.vtx); | disconnectpool->addForBlock(block.vtx); | ||||
} | } | ||||
// If the tip is finalized, then undo it. | // If the tip is finalized, then undo it. | ||||
if (pindexFinalized == pindexDelete) { | if (pindexFinalized == pindexDelete) { | ||||
pindexFinalized = pindexDelete->pprev; | pindexFinalized = pindexDelete->pprev; | ||||
} | } | ||||
chainActive.SetTip(pindexDelete->pprev); | m_chain.SetTip(pindexDelete->pprev); | ||||
// Update chainActive and related variables. | // Update chainActive and related variables. | ||||
UpdateTip(config, pindexDelete->pprev); | UpdateTip(config, pindexDelete->pprev); | ||||
// Let wallets know transactions went from 1-confirmed to | // Let wallets know transactions went from 1-confirmed to | ||||
// 0-confirmed or conflicted: | // 0-confirmed or conflicted: | ||||
GetMainSignals().BlockDisconnected(pblock); | GetMainSignals().BlockDisconnected(pblock); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | while (pindex && (pindex != pindexFinalized)) { | ||||
pindex = pindex->pprev; | pindex = pindex->pprev; | ||||
} | } | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
/** | /** | ||||
* Connect a new block to chainActive. pblock is either nullptr or a pointer to | * Connect a new block to m_chain. pblock is either nullptr or a pointer to | ||||
* a CBlock corresponding to pindexNew, to bypass loading it again from disk. | * a CBlock corresponding to pindexNew, to bypass loading it again from disk. | ||||
* | * | ||||
* The block is always added to connectTrace (either after loading from disk or | * The block is always added to connectTrace (either after loading from disk or | ||||
* by copying pblock) - if that is not intended, care must be taken to remove | * by copying pblock) - if that is not intended, care must be taken to remove | ||||
* the last entry in blocksConnected in case of failure. | * the last entry in blocksConnected in case of failure. | ||||
*/ | */ | ||||
bool CChainState::ConnectTip(const Config &config, CValidationState &state, | bool CChainState::ConnectTip(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
ConnectTrace &connectTrace, | ConnectTrace &connectTrace, | ||||
DisconnectedBlockTransactions &disconnectpool) { | DisconnectedBlockTransactions &disconnectpool) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CChainParams ¶ms = config.GetChainParams(); | const CChainParams ¶ms = config.GetChainParams(); | ||||
const Consensus::Params &consensusParams = params.GetConsensus(); | const Consensus::Params &consensusParams = params.GetConsensus(); | ||||
assert(pindexNew->pprev == chainActive.Tip()); | assert(pindexNew->pprev == m_chain.Tip()); | ||||
// Read block from disk. | // Read block from disk. | ||||
int64_t nTime1 = GetTimeMicros(); | int64_t nTime1 = GetTimeMicros(); | ||||
std::shared_ptr<const CBlock> pthisBlock; | std::shared_ptr<const CBlock> pthisBlock; | ||||
if (!pblock) { | if (!pblock) { | ||||
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); | ||||
if (!ReadBlockFromDisk(*pblockNew, pindexNew, consensusParams)) { | if (!ReadBlockFromDisk(*pblockNew, pindexNew, consensusParams)) { | ||||
return AbortNode(state, "Failed to read block"); | return AbortNode(state, "Failed to read block"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | bool CChainState::ConnectTip(const Config &config, CValidationState &state, | ||||
if (pindexNew->pprev != nullptr && | if (pindexNew->pprev != nullptr && | ||||
GetNextBlockScriptFlags(consensusParams, pindexNew) != | GetNextBlockScriptFlags(consensusParams, pindexNew) != | ||||
GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) { | GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) { | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"Disconnecting mempool due to acceptance of upgrade block\n"); | "Disconnecting mempool due to acceptance of upgrade block\n"); | ||||
disconnectpool.importMempool(g_mempool); | disconnectpool.importMempool(g_mempool); | ||||
} | } | ||||
// Update chainActive & related variables. | // Update m_chain & related variables. | ||||
chainActive.SetTip(pindexNew); | m_chain.SetTip(pindexNew); | ||||
UpdateTip(config, pindexNew); | UpdateTip(config, pindexNew); | ||||
int64_t nTime6 = GetTimeMicros(); | int64_t nTime6 = GetTimeMicros(); | ||||
nTimePostConnect += nTime6 - nTime5; | nTimePostConnect += nTime6 - nTime5; | ||||
nTimeTotal += nTime6 - nTime1; | nTimeTotal += nTime6 - nTime1; | ||||
LogPrint(BCLog::BENCH, | LogPrint(BCLog::BENCH, | ||||
" - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", | " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", | ||||
(nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, | (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, | ||||
Show All 31 Lines | do { | ||||
LogPrintf("Mark block %s invalid because it forks prior to the " | LogPrintf("Mark block %s invalid because it forks prior to the " | ||||
"finalization point %d.\n", | "finalization point %d.\n", | ||||
pindexNew->GetBlockHash().ToString(), | pindexNew->GetBlockHash().ToString(), | ||||
pindexFinalized->nHeight); | pindexFinalized->nHeight); | ||||
pindexNew->nStatus = pindexNew->nStatus.withFailed(); | pindexNew->nStatus = pindexNew->nStatus.withFailed(); | ||||
InvalidChainFound(pindexNew); | InvalidChainFound(pindexNew); | ||||
} | } | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(pindexNew); | const CBlockIndex *pindexFork = m_chain.FindFork(pindexNew); | ||||
// Check whether all blocks on the path between the currently active | // Check whether all blocks on the path between the currently active | ||||
// chain and the candidate are valid. Just going until the active chain | // chain and the candidate are valid. Just going until the active chain | ||||
// is an optimization, as we know all blocks in it are valid already. | // is an optimization, as we know all blocks in it are valid already. | ||||
CBlockIndex *pindexTest = pindexNew; | CBlockIndex *pindexTest = pindexNew; | ||||
bool hasValidAncestor = true; | bool hasValidAncestor = true; | ||||
while (hasValidAncestor && pindexTest && pindexTest != pindexFork) { | while (hasValidAncestor && pindexTest && pindexTest != pindexFork) { | ||||
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0); | assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0); | ||||
// If this is a parked chain, but it has enough PoW, clear the park | // If this is a parked chain, but it has enough PoW, clear the park | ||||
// state. | // state. | ||||
bool fParkedChain = pindexTest->nStatus.isOnParkedChain(); | bool fParkedChain = pindexTest->nStatus.isOnParkedChain(); | ||||
if (fParkedChain && gArgs.GetBoolArg("-automaticunparking", true)) { | if (fParkedChain && gArgs.GetBoolArg("-automaticunparking", true)) { | ||||
const CBlockIndex *pindexTip = chainActive.Tip(); | const CBlockIndex *pindexTip = m_chain.Tip(); | ||||
// During initialization, pindexTip and/or pindexFork may be | // During initialization, pindexTip and/or pindexFork may be | ||||
// null. In this case, we just ignore the fact that the chain is | // null. In this case, we just ignore the fact that the chain is | ||||
// parked. | // parked. | ||||
if (!pindexTip || !pindexFork) { | if (!pindexTip || !pindexFork) { | ||||
UnparkBlock(pindexTest); | UnparkBlock(pindexTest); | ||||
continue; | continue; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | |||||
* Delete all entries in setBlockIndexCandidates that are worse than the current | * Delete all entries in setBlockIndexCandidates that are worse than the current | ||||
* tip. | * tip. | ||||
*/ | */ | ||||
void CChainState::PruneBlockIndexCandidates() { | void CChainState::PruneBlockIndexCandidates() { | ||||
// Note that we can't delete the current block itself, as we may need to | // Note that we can't delete the current block itself, as we may need to | ||||
// return to it later in case a reorganization to a better block fails. | // return to it later in case a reorganization to a better block fails. | ||||
auto it = setBlockIndexCandidates.begin(); | auto it = setBlockIndexCandidates.begin(); | ||||
while (it != setBlockIndexCandidates.end() && | while (it != setBlockIndexCandidates.end() && | ||||
setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { | setBlockIndexCandidates.value_comp()(*it, m_chain.Tip())) { | ||||
setBlockIndexCandidates.erase(it++); | setBlockIndexCandidates.erase(it++); | ||||
} | } | ||||
// Either the current tip or a successor of it we're working towards is left | // Either the current tip or a successor of it we're working towards is left | ||||
// in setBlockIndexCandidates. | // in setBlockIndexCandidates. | ||||
assert(!setBlockIndexCandidates.empty()); | assert(!setBlockIndexCandidates.empty()); | ||||
} | } | ||||
/** | /** | ||||
* Try to make some progress towards making pindexMostWork the active block. | * Try to make some progress towards making pindexMostWork the active block. | ||||
* pblock is either nullptr or a pointer to a CBlock corresponding to | * pblock is either nullptr or a pointer to a CBlock corresponding to | ||||
* pindexMostWork. | * pindexMostWork. | ||||
*/ | */ | ||||
bool CChainState::ActivateBestChainStep( | bool CChainState::ActivateBestChainStep( | ||||
const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, | const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, | ||||
const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, | const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, | ||||
ConnectTrace &connectTrace) { | ConnectTrace &connectTrace) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlockIndex *pindexOldTip = chainActive.Tip(); | const CBlockIndex *pindexOldTip = m_chain.Tip(); | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); | const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); | ||||
// Disconnect active blocks which are no longer in the best chain. | // Disconnect active blocks which are no longer in the best chain. | ||||
bool fBlocksDisconnected = false; | bool fBlocksDisconnected = false; | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
while (chainActive.Tip() && chainActive.Tip() != pindexFork) { | while (m_chain.Tip() && m_chain.Tip() != pindexFork) { | ||||
if (!DisconnectTip(config, state, &disconnectpool)) { | if (!DisconnectTip(config, state, &disconnectpool)) { | ||||
// This is likely a fatal error, but keep the mempool consistent, | // This is likely a fatal error, but keep the mempool consistent, | ||||
// just in case. Only remove from the mempool in this case. | // just in case. Only remove from the mempool in this case. | ||||
disconnectpool.updateMempoolForReorg(config, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} | } | ||||
fBlocksDisconnected = true; | fBlocksDisconnected = true; | ||||
Show All 39 Lines | while (fContinue && nHeight != pindexMostWork->nHeight) { | ||||
// A system error occurred (disk space, database error, ...). | // A system error occurred (disk space, database error, ...). | ||||
// Make the mempool consistent with the current tip, just in | // Make the mempool consistent with the current tip, just in | ||||
// case any observers try to use it before shutdown. | // case any observers try to use it before shutdown. | ||||
disconnectpool.updateMempoolForReorg(config, false); | disconnectpool.updateMempoolForReorg(config, false); | ||||
return false; | return false; | ||||
} else { | } else { | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
if (!pindexOldTip || | if (!pindexOldTip || | ||||
chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { | m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) { | ||||
// We're in a better position than we were. Return | // We're in a better position than we were. Return | ||||
// temporarily to release the lock. | // temporarily to release the lock. | ||||
fContinue = false; | fContinue = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | do { | ||||
// Note that if a validationinterface callback ends up calling | // Note that if a validationinterface callback ends up calling | ||||
// ActivateBestChain this may lead to a deadlock! We should | // ActivateBestChain this may lead to a deadlock! We should | ||||
// probably have a DEBUG_LOCKORDER test for this in the future. | // probably have a DEBUG_LOCKORDER test for this in the future. | ||||
SyncWithValidationInterfaceQueue(); | SyncWithValidationInterfaceQueue(); | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CBlockIndex *starting_tip = chainActive.Tip(); | CBlockIndex *starting_tip = m_chain.Tip(); | ||||
bool blocks_connected = false; | bool blocks_connected = false; | ||||
do { | do { | ||||
// We absolutely may not unlock cs_main until we've made forward | // We absolutely may not unlock cs_main until we've made forward | ||||
// progress (with the exception of shutdown due to hardware | // progress (with the exception of shutdown due to hardware | ||||
// issues, low disk space, etc). | // issues, low disk space, etc). | ||||
// Destructed before cs_main is unlocked | // Destructed before cs_main is unlocked | ||||
ConnectTrace connectTrace(g_mempool); | ConnectTrace connectTrace(g_mempool); | ||||
if (pindexMostWork == nullptr) { | if (pindexMostWork == nullptr) { | ||||
pindexMostWork = FindMostWorkChain(); | pindexMostWork = FindMostWorkChain(); | ||||
} | } | ||||
// Whether we have anything to do at all. | // Whether we have anything to do at all. | ||||
if (pindexMostWork == nullptr || | if (pindexMostWork == nullptr || | ||||
pindexMostWork == chainActive.Tip()) { | pindexMostWork == m_chain.Tip()) { | ||||
break; | break; | ||||
} | } | ||||
bool fInvalidFound = false; | bool fInvalidFound = false; | ||||
std::shared_ptr<const CBlock> nullBlockPtr; | std::shared_ptr<const CBlock> nullBlockPtr; | ||||
if (!ActivateBestChainStep( | if (!ActivateBestChainStep( | ||||
config, state, pindexMostWork, | config, state, pindexMostWork, | ||||
pblock && pblock->GetHash() == | pblock && pblock->GetHash() == | ||||
pindexMostWork->GetBlockHash() | pindexMostWork->GetBlockHash() | ||||
? pblock | ? pblock | ||||
: nullBlockPtr, | : nullBlockPtr, | ||||
fInvalidFound, connectTrace)) { | fInvalidFound, connectTrace)) { | ||||
return false; | return false; | ||||
} | } | ||||
blocks_connected = true; | blocks_connected = true; | ||||
if (fInvalidFound) { | if (fInvalidFound) { | ||||
// Wipe cache, we may need another branch now. | // Wipe cache, we may need another branch now. | ||||
pindexMostWork = nullptr; | pindexMostWork = nullptr; | ||||
} | } | ||||
pindexNewTip = chainActive.Tip(); | pindexNewTip = m_chain.Tip(); | ||||
for (const PerBlockConnectTrace &trace : | for (const PerBlockConnectTrace &trace : | ||||
connectTrace.GetBlocksConnected()) { | connectTrace.GetBlocksConnected()) { | ||||
assert(trace.pblock && trace.pindex); | assert(trace.pblock && trace.pindex); | ||||
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, | GetMainSignals().BlockConnected(trace.pblock, trace.pindex, | ||||
trace.conflictedTxs); | trace.conflictedTxs); | ||||
} | } | ||||
} while (!chainActive.Tip() || | } while (!m_chain.Tip() || | ||||
(starting_tip && CBlockIndexWorkComparator()( | (starting_tip && CBlockIndexWorkComparator()( | ||||
chainActive.Tip(), starting_tip))); | m_chain.Tip(), starting_tip))); | ||||
// Check the index once we're done with the above loop, since | // Check the index once we're done with the above loop, since | ||||
// we're going to release cs_main soon. If the index is in a bad | // we're going to release cs_main soon. If the index is in a bad | ||||
// state now, then it's better to know immediately rather than | // state now, then it's better to know immediately rather than | ||||
// randomly have it cause a problem in a race. | // randomly have it cause a problem in a race. | ||||
CheckBlockIndex(params.GetConsensus()); | CheckBlockIndex(params.GetConsensus()); | ||||
if (!blocks_connected) { | if (!blocks_connected) { | ||||
return true; | return true; | ||||
} | } | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(starting_tip); | const CBlockIndex *pindexFork = m_chain.FindFork(starting_tip); | ||||
bool fInitialDownload = IsInitialBlockDownload(); | bool fInitialDownload = IsInitialBlockDownload(); | ||||
// Notify external listeners about the new tip. | // Notify external listeners about the new tip. | ||||
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is | // Enqueue while holding cs_main to ensure that UpdatedBlockTip is | ||||
// called in the order in which blocks are connected | // called in the order in which blocks are connected | ||||
if (pindexFork != pindexNewTip) { | if (pindexFork != pindexNewTip) { | ||||
// Notify ValidationInterface subscribers | // Notify ValidationInterface subscribers | ||||
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, | GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, | ||||
Show All 33 Lines | bool ActivateBestChain(const Config &config, CValidationState &state, | ||||
std::shared_ptr<const CBlock> pblock) { | std::shared_ptr<const CBlock> pblock) { | ||||
return g_chainstate.ActivateBestChain(config, state, std::move(pblock)); | return g_chainstate.ActivateBestChain(config, state, std::move(pblock)); | ||||
} | } | ||||
bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (pindex->nChainWork < chainActive.Tip()->nChainWork) { | if (pindex->nChainWork < m_chain.Tip()->nChainWork) { | ||||
// Nothing to do, this block is not at the tip. | // Nothing to do, this block is not at the tip. | ||||
return true; | return true; | ||||
} | } | ||||
if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { | if (m_chain.Tip()->nChainWork > nLastPreciousChainwork) { | ||||
// The chain has been extended since the last call, reset the | // The chain has been extended since the last call, reset the | ||||
// counter. | // counter. | ||||
nBlockReverseSequenceId = -1; | nBlockReverseSequenceId = -1; | ||||
} | } | ||||
nLastPreciousChainwork = chainActive.Tip()->nChainWork; | nLastPreciousChainwork = m_chain.Tip()->nChainWork; | ||||
setBlockIndexCandidates.erase(pindex); | setBlockIndexCandidates.erase(pindex); | ||||
pindex->nSequenceId = nBlockReverseSequenceId; | pindex->nSequenceId = nBlockReverseSequenceId; | ||||
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) { | if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) { | ||||
// We can't keep reducing the counter if somebody really wants to | // We can't keep reducing the counter if somebody really wants to | ||||
// call preciousblock 2**31-1 times on the same set of tips... | // call preciousblock 2**31-1 times on the same set of tips... | ||||
nBlockReverseSequenceId--; | nBlockReverseSequenceId--; | ||||
} | } | ||||
Show All 25 Lines | bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | ||||
// 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 (!chainActive.Contains(pindex)) { | if (!m_chain.Contains(pindex)) { | ||||
break; | break; | ||||
} | } | ||||
pindex_was_in_chain = true; | pindex_was_in_chain = true; | ||||
CBlockIndex *invalid_walk_tip = chainActive.Tip(); | CBlockIndex *invalid_walk_tip = m_chain.Tip(); | ||||
// ActivateBestChain considers blocks already in chainActive | // 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 = DisconnectTip(config, state, &disconnectpool); | bool ret = DisconnectTip(config, 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); | ||||
if (!ret) { | if (!ret) { | ||||
return false; | return false; | ||||
} | } | ||||
assert(invalid_walk_tip->pprev == chainActive.Tip()); | assert(invalid_walk_tip->pprev == m_chain.Tip()); | ||||
// We immediately mark the disconnected blocks as invalid. | // We immediately mark the disconnected blocks as invalid. | ||||
// This prevents a case where pruned nodes may fail to invalidateblock | // 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 | // 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 | // are no blocks that meet the "have data and are not invalid per | ||||
// nStatus" criteria for inclusion in setBlockIndexCandidates). | // nStatus" criteria for inclusion in setBlockIndexCandidates). | ||||
invalid_walk_tip->nStatus = | invalid_walk_tip->nStatus = | ||||
Show All 22 Lines | while (true) { | ||||
// 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; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (chainActive.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 | // The resulting new best tip may not be in setBlockIndexCandidates | ||||
// anymore, so add it again. | // anymore, so add it again. | ||||
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, chainActive.Tip())) { | !setBlockIndexCandidates.value_comp()(i, m_chain.Tip())) { | ||||
setBlockIndexCandidates.insert(i); | setBlockIndexCandidates.insert(i); | ||||
} | } | ||||
} | } | ||||
if (invalidate) { | if (invalidate) { | ||||
InvalidChainFound(to_mark_failed_or_parked); | InvalidChainFound(to_mark_failed_or_parked); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) { | ||||
// We assign a sequence is when transaction are received to | // We assign a sequence is when transaction are received to | ||||
// prevent a miner from being able to broadcast a block but not | // prevent a miner from being able to broadcast a block but not | ||||
// its content. However, a sequence id may have been set | // its content. However, a sequence id may have been set | ||||
// manually, for instance via PreciousBlock, in which case, we | // manually, for instance via PreciousBlock, in which case, we | ||||
// don't need to assign one. | // don't need to assign one. | ||||
pindex->nSequenceId = nBlockSequenceId++; | pindex->nSequenceId = nBlockSequenceId++; | ||||
} | } | ||||
if (chainActive.Tip() == nullptr || | if (m_chain.Tip() == nullptr || | ||||
!setBlockIndexCandidates.value_comp()(pindex, | !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) { | ||||
chainActive.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 = mapBlocksUnlinked.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 = | ||||
▲ Show 20 Lines • Show All 626 Lines • ▼ Show 20 Lines | if (fAlreadyHave) { | ||||
return true; | return true; | ||||
} | } | ||||
// Compare block header timestamps and received times of the block and the | // Compare block header timestamps and received times of the block and the | ||||
// chaintip. If they have the same chain height, use these diffs as a | // chaintip. If they have the same chain height, use these diffs as a | ||||
// tie-breaker, attempting to pick the more honestly-mined block. | // tie-breaker, attempting to pick the more honestly-mined block. | ||||
int64_t newBlockTimeDiff = std::llabs(pindex->GetReceivedTimeDiff()); | int64_t newBlockTimeDiff = std::llabs(pindex->GetReceivedTimeDiff()); | ||||
int64_t chainTipTimeDiff = | int64_t chainTipTimeDiff = | ||||
chainActive.Tip() ? std::llabs(chainActive.Tip()->GetReceivedTimeDiff()) | m_chain.Tip() ? std::llabs(m_chain.Tip()->GetReceivedTimeDiff()) : 0; | ||||
: 0; | |||||
bool isSameHeight = chainActive.Tip() && | bool isSameHeight = | ||||
(pindex->nChainWork == chainActive.Tip()->nChainWork); | m_chain.Tip() && (pindex->nChainWork == m_chain.Tip()->nChainWork); | ||||
if (isSameHeight) { | if (isSameHeight) { | ||||
LogPrintf("Chain tip timestamp-to-received-time difference: hash=%s, " | LogPrintf("Chain tip timestamp-to-received-time difference: hash=%s, " | ||||
"diff=%d\n", | "diff=%d\n", | ||||
chainActive.Tip()->GetBlockHash().ToString(), | m_chain.Tip()->GetBlockHash().ToString(), chainTipTimeDiff); | ||||
chainTipTimeDiff); | |||||
LogPrintf("New block timestamp-to-received-time difference: hash=%s, " | LogPrintf("New block timestamp-to-received-time difference: hash=%s, " | ||||
"diff=%d\n", | "diff=%d\n", | ||||
pindex->GetBlockHash().ToString(), newBlockTimeDiff); | pindex->GetBlockHash().ToString(), newBlockTimeDiff); | ||||
} | } | ||||
bool fHasMoreOrSameWork = | bool fHasMoreOrSameWork = | ||||
(chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork | (m_chain.Tip() ? pindex->nChainWork >= m_chain.Tip()->nChainWork | ||||
: true); | : true); | ||||
// Blocks that are too out-of-order needlessly limit the effectiveness of | // Blocks that are too out-of-order needlessly limit the effectiveness of | ||||
// pruning, because pruning will not delete block files that contain any | // pruning, because pruning will not delete block files that contain any | ||||
// blocks which are too close in height to the tip. Apply this test | // blocks which are too close in height to the tip. Apply this test | ||||
// regardless of whether pruning is enabled; it should generally be safe to | // regardless of whether pruning is enabled; it should generally be safe to | ||||
// not process unrequested blocks. | // not process unrequested blocks. | ||||
bool fTooFarAhead = | bool fTooFarAhead = | ||||
(pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); | (pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP)); | ||||
// TODO: Decouple this function from the block download logic by removing | // TODO: Decouple this function from the block download logic by removing | ||||
// fRequested | // fRequested | ||||
// This requires some new chain data structure to efficiently look up if a | // This requires some new chain data structure to efficiently look up if a | ||||
// block is in a chain leading to a candidate for best tip, despite not | // block is in a chain leading to a candidate for best tip, despite not | ||||
// being such a candidate itself. | // being such a candidate itself. | ||||
// If we didn't ask for it: | // If we didn't ask for it: | ||||
Show All 39 Lines | bool CChainState::AcceptBlock(const Config &config, | ||||
// If connecting the new block would require rewinding more than one block | // If connecting the new block would require rewinding more than one block | ||||
// from the active chain (i.e., a "deep reorg"), then mark the new block as | // from the active chain (i.e., a "deep reorg"), then mark the new block as | ||||
// parked. If it has enough work then it will be automatically unparked | // parked. If it has enough work then it will be automatically unparked | ||||
// later, during FindMostWorkChain. We mark the block as parked at the very | // later, during FindMostWorkChain. We mark the block as parked at the very | ||||
// last minute so we can make sure everything is ready to be reorged if | // last minute so we can make sure everything is ready to be reorged if | ||||
// needed. | // needed. | ||||
if (gArgs.GetBoolArg("-parkdeepreorg", true)) { | if (gArgs.GetBoolArg("-parkdeepreorg", true)) { | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(pindex); | const CBlockIndex *pindexFork = m_chain.FindFork(pindex); | ||||
if (pindexFork && pindexFork->nHeight + 1 < chainActive.Height()) { | if (pindexFork && pindexFork->nHeight + 1 < m_chain.Height()) { | ||||
LogPrintf("Park block %s as it would cause a deep reorg.\n", | LogPrintf("Park block %s as it would cause a deep reorg.\n", | ||||
pindex->GetBlockHash().ToString()); | pindex->GetBlockHash().ToString()); | ||||
pindex->nStatus = pindex->nStatus.withParked(); | pindex->nStatus = pindex->nStatus.withParked(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} | } | ||||
} | } | ||||
// Header is valid/has work and the merkle tree is good. | // Header is valid/has work and the merkle tree is good. | ||||
// Relay now, but if it does not build on our best tip, let the | // Relay now, but if it does not build on our best tip, let the | ||||
// SendMessages loop relay it. | // SendMessages loop relay it. | ||||
if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) { | if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev) { | ||||
GetMainSignals().NewPoWValidBlock(pindex, pblock); | GetMainSignals().NewPoWValidBlock(pindex, pblock); | ||||
} | } | ||||
// Write block to history file | // Write block to history file | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = true; | *fNewBlock = true; | ||||
} | } | ||||
try { | try { | ||||
▲ Show 20 Lines • Show All 879 Lines • ▼ Show 20 Lines | bool LoadBlockIndex(const Config &config) { | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { | bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Check whether we're already initialized by checking for genesis in | // Check whether we're already initialized by checking for genesis in | ||||
// mapBlockIndex. Note that we can't use chainActive here, since it is | // mapBlockIndex. Note that we can't use m_chain here, since it is | ||||
// set based on the coins db, not the block index db, which is the only | // set based on the coins db, not the block index db, which is the only | ||||
// thing loaded at this point. | // thing loaded at this point. | ||||
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash())) { | if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash())) { | ||||
return true; | return true; | ||||
} | } | ||||
try { | try { | ||||
const CBlock &block = chainparams.GenesisBlock(); | const CBlock &block = chainparams.GenesisBlock(); | ||||
▲ Show 20 Lines • Show All 183 Lines • ▼ Show 20 Lines | if (!fCheckBlockIndex) { | ||||
return; | return; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// During a reindex, we read the genesis block and call CheckBlockIndex | // During a reindex, we read the genesis block and call CheckBlockIndex | ||||
// before ActivateBestChain, so we have the genesis block in mapBlockIndex | // before ActivateBestChain, so we have the genesis block in mapBlockIndex | ||||
// but no active chain. (A few of the tests when iterating the block tree | // but no active chain. (A few of the tests when iterating the block tree | ||||
// require that chainActive has been initialized.) | // require that m_chain has been initialized.) | ||||
if (chainActive.Height() < 0) { | if (m_chain.Height() < 0) { | ||||
assert(mapBlockIndex.size() <= 1); | assert(mapBlockIndex.size() <= 1); | ||||
return; | return; | ||||
} | } | ||||
// Build forward-pointing map of the entire block tree. | // Build forward-pointing map of the entire block tree. | ||||
std::multimap<CBlockIndex *, CBlockIndex *> forward; | std::multimap<CBlockIndex *, CBlockIndex *> forward; | ||||
for (const auto &entry : mapBlockIndex) { | for (const auto &entry : mapBlockIndex) { | ||||
forward.emplace(entry.second->pprev, entry.second); | forward.emplace(entry.second->pprev, entry.second); | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | while (pindex != nullptr) { | ||||
} | } | ||||
// Begin: actual consistency checks. | // Begin: actual consistency checks. | ||||
if (pindex->pprev == nullptr) { | if (pindex->pprev == nullptr) { | ||||
// Genesis block checks. | // Genesis block checks. | ||||
// Genesis block's hash must match. | // Genesis block's hash must match. | ||||
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); | assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); | ||||
// The current active chain's genesis block must be this block. | // The current active chain's genesis block must be this block. | ||||
assert(pindex == chainActive.Genesis()); | assert(pindex == m_chain.Genesis()); | ||||
} | } | ||||
if (!pindex->HaveTxsDownloaded()) { | if (!pindex->HaveTxsDownloaded()) { | ||||
// nSequenceId can't be set positive for blocks that aren't linked | // nSequenceId can't be set positive for blocks that aren't linked | ||||
// (negative is used for preciousblock) | // (negative is used for preciousblock) | ||||
assert(pindex->nSequenceId <= 0); | assert(pindex->nSequenceId <= 0); | ||||
} | } | ||||
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or | // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or | ||||
// not pruning has occurred). HAVE_DATA is only equivalent to nTx > 0 | // not pruning has occurred). HAVE_DATA is only equivalent to nTx > 0 | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | while (pindex != nullptr) { | ||||
assert(!pindex->nStatus.isInvalid()); | assert(!pindex->nStatus.isInvalid()); | ||||
} | } | ||||
if (pindexFirstParked == nullptr) { | if (pindexFirstParked == nullptr) { | ||||
// Checks for not-parked blocks. | // Checks for not-parked blocks. | ||||
// The parked mask cannot be set for blocks without parked parents. | // The parked mask cannot be set for blocks without parked parents. | ||||
// (i.e., hasParkedParent only if an ancestor is properly parked). | // (i.e., hasParkedParent only if an ancestor is properly parked). | ||||
assert(!pindex->nStatus.isOnParkedChain()); | assert(!pindex->nStatus.isOnParkedChain()); | ||||
} | } | ||||
if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && | if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && | ||||
pindexFirstNeverProcessed == nullptr) { | pindexFirstNeverProcessed == nullptr) { | ||||
if (pindexFirstInvalid == nullptr) { | if (pindexFirstInvalid == nullptr) { | ||||
// If this block sorts at least as good as the current tip and | // If this block sorts at least as good as the current tip and | ||||
// is valid and we have all data for its parents, it must be in | // is valid and we have all data for its parents, it must be in | ||||
// setBlockIndexCandidates or be parked. | // setBlockIndexCandidates or be parked. | ||||
if (pindexFirstMissing == nullptr) { | if (pindexFirstMissing == nullptr) { | ||||
assert(pindex->nStatus.isOnParkedChain() || | assert(pindex->nStatus.isOnParkedChain() || | ||||
setBlockIndexCandidates.count(pindex)); | setBlockIndexCandidates.count(pindex)); | ||||
} | } | ||||
// chainActive.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 == chainActive.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. | // mapBlocksUnlinked -- see test below. | ||||
} | } | ||||
} else { | } else { | ||||
Show All 38 Lines | while (pindex != 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 mapBlocksUnlinked 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 chainActive and the | // data for some intermediate block between m_chain and the | ||||
// tip. | // tip. | ||||
// So if this block is itself better than chainActive.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 mapBlocksUnlinked. | ||||
if (!CBlockIndexWorkComparator()(pindex, chainActive.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 | ||||
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); | // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); | ||||
▲ Show 20 Lines • Show All 303 Lines • Show Last 20 Lines |