Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 2,618 Lines • ▼ Show 20 Lines | do { | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(pindexNew); | const CBlockIndex *pindexFork = chainActive.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->nChainTx || 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("-parkdeepreorg", true)) { | if (fParkedChain && gArgs.GetBoolArg("-parkdeepreorg", true)) { | ||||
const CBlockIndex *pindexTip = chainActive.Tip(); | const CBlockIndex *pindexTip = chainActive.Tip(); | ||||
// During initialization, pindexTip and/or pindexFork may be | // During initialization, pindexTip and/or pindexFork may be | ||||
▲ Show 20 Lines • Show All 413 Lines • ▼ Show 20 Lines | bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | ||||
// 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--; | ||||
} | } | ||||
// In case this was parked, unpark it. | // In case this was parked, unpark it. | ||||
UnparkBlock(pindex); | UnparkBlock(pindex); | ||||
// Make sure it is added to the candidate list if appropriate. | // Make sure it is added to the candidate list if appropriate. | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx) { | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | ||||
pindex->HaveTxsDownloaded()) { | |||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
} | } | ||||
} | } | ||||
return ActivateBestChain(config, state); | return ActivateBestChain(config, state); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | ||||
// DisconnectTip will add transactions to disconnectpool; try to add these | // DisconnectTip will add transactions to disconnectpool; try to add these | ||||
// back to the mempool. | // back to the mempool. | ||||
disconnectpool.updateMempoolForReorg(config, true); | disconnectpool.updateMempoolForReorg(config, true); | ||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, | // The resulting new best tip may not be in setBlockIndexCandidates anymore, | ||||
// so add it again. | // so add it again. | ||||
for (const std::pair<const BlockHash, CBlockIndex *> &it : mapBlockIndex) { | for (const std::pair<const BlockHash, CBlockIndex *> &it : mapBlockIndex) { | ||||
CBlockIndex *i = it.second; | CBlockIndex *i = it.second; | ||||
if (i->IsValid(BlockValidity::TRANSACTIONS) && i->nChainTx && | if (i->IsValid(BlockValidity::TRANSACTIONS) && i->HaveTxsDownloaded() && | ||||
!setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | !setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { | ||||
setBlockIndexCandidates.insert(i); | setBlockIndexCandidates.insert(i); | ||||
} | } | ||||
} | } | ||||
if (invalidate) { | if (invalidate) { | ||||
InvalidChainFound(pindex); | InvalidChainFound(pindex); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | void CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, | ||||
if (pindex->nStatus != newStatus && | if (pindex->nStatus != newStatus && | ||||
pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | ||||
pindex->nStatus = newStatus; | pindex->nStatus = newStatus; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
if (newStatus.isValid()) { | if (newStatus.isValid()) { | ||||
m_failed_blocks.erase(pindex); | m_failed_blocks.erase(pindex); | ||||
} | } | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && | ||||
pindex->HaveTxsDownloaded() && | |||||
setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
template <typename F, typename C> | template <typename F, typename C> | ||||
void CChainState::UpdateFlags(CBlockIndex *pindex, F f, C fchild) { | void CChainState::UpdateFlags(CBlockIndex *pindex, F f, C fchild) { | ||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | bool CChainState::ReceivedBlockTransactions(const CBlock &block, | ||||
pindexNew->nChainTx = 0; | pindexNew->nChainTx = 0; | ||||
pindexNew->nFile = pos.nFile; | pindexNew->nFile = pos.nFile; | ||||
pindexNew->nDataPos = pos.nPos; | pindexNew->nDataPos = pos.nPos; | ||||
pindexNew->nUndoPos = 0; | pindexNew->nUndoPos = 0; | ||||
pindexNew->nStatus = pindexNew->nStatus.withData(); | pindexNew->nStatus = pindexNew->nStatus.withData(); | ||||
pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS); | pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS); | ||||
setDirtyBlockIndex.insert(pindexNew); | setDirtyBlockIndex.insert(pindexNew); | ||||
if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { | if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) { | ||||
// If pindexNew is the genesis block or all parents are | // If pindexNew is the genesis block or all parents are | ||||
// BLOCK_VALID_TRANSACTIONS. | // BLOCK_VALID_TRANSACTIONS. | ||||
std::deque<CBlockIndex *> queue; | std::deque<CBlockIndex *> queue; | ||||
queue.push_back(pindexNew); | queue.push_back(pindexNew); | ||||
// Recursively process any descendant blocks that now may be eligible to | // Recursively process any descendant blocks that now may be eligible to | ||||
// be connected. | // be connected. | ||||
while (!queue.empty()) { | while (!queue.empty()) { | ||||
▲ Show 20 Lines • Show All 1,127 Lines • ▼ Show 20 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->pprev) { | if (pindex->pprev) { | ||||
if (pindex->pprev->nChainTx) { | if (pindex->pprev->HaveTxsDownloaded()) { | ||||
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; | pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; | ||||
} else { | } else { | ||||
pindex->nChainTx = 0; | pindex->nChainTx = 0; | ||||
mapBlocksUnlinked.insert( | mapBlocksUnlinked.insert( | ||||
std::make_pair(pindex->pprev, pindex)); | std::make_pair(pindex->pprev, pindex)); | ||||
} | } | ||||
} else { | } else { | ||||
pindex->nChainTx = pindex->nTx; | pindex->nChainTx = pindex->nTx; | ||||
} | } | ||||
} | } | ||||
if (!pindex->nStatus.hasFailed() && pindex->pprev && | 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->nChainTx || pindex->pprev == nullptr)) { | (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
if (pindex->nStatus.isInvalid() && | if (pindex->nStatus.isInvalid() && | ||||
(!pindexBestInvalid || | (!pindexBestInvalid || | ||||
pindex->nChainWork > pindexBestInvalid->nChainWork)) { | pindex->nChainWork > pindexBestInvalid->nChainWork)) { | ||||
pindexBestInvalid = pindex; | pindexBestInvalid = pindex; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 452 Lines • ▼ Show 20 Lines | bool CChainState::RewindBlockIndex(const Config &config) { | ||||
// Reduce validity flag and have-data flags. | // Reduce validity flag and have-data flags. | ||||
// We do this after actual disconnecting, otherwise we'll end up writing the | // We do this after actual disconnecting, otherwise we'll end up writing the | ||||
// lack of data to disk before writing the chainstate, resulting in a | // lack of data to disk before writing the chainstate, resulting in a | ||||
// failure to continue if interrupted. | // failure to continue if interrupted. | ||||
for (const auto &entry : mapBlockIndex) { | for (const auto &entry : mapBlockIndex) { | ||||
CBlockIndex *pindexIter = entry.second; | CBlockIndex *pindexIter = entry.second; | ||||
if (pindexIter->IsValid(BlockValidity::TRANSACTIONS) && | if (pindexIter->IsValid(BlockValidity::TRANSACTIONS) && | ||||
pindexIter->nChainTx) { | pindexIter->HaveTxsDownloaded()) { | ||||
setBlockIndexCandidates.insert(pindexIter); | setBlockIndexCandidates.insert(pindexIter); | ||||
} | } | ||||
} | } | ||||
if (chainActive.Tip() != nullptr) { | if (chainActive.Tip() != nullptr) { | ||||
// We can't prune block index candidates based on our tip if we have | // We can't prune block index candidates based on our tip if we have | ||||
// no tip due to chainActive being empty! | // no tip due to chainActive being empty! | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
▲ Show 20 Lines • Show All 376 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 == chainActive.Genesis()); | ||||
} | } | ||||
if (pindex->nChainTx == 0) { | 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 | ||||
// (or VALID_TRANSACTIONS) if no pruning has occurred. | // (or VALID_TRANSACTIONS) if no pruning has occurred. | ||||
if (!fHavePruned) { | if (!fHavePruned) { | ||||
// If we've never pruned, then HAVE_DATA should be equivalent to nTx | // If we've never pruned, then HAVE_DATA should be equivalent to nTx | ||||
// > 0 | // > 0 | ||||
assert(pindex->nStatus.hasData() == (pindex->nTx > 0)); | assert(pindex->nStatus.hasData() == (pindex->nTx > 0)); | ||||
assert(pindexFirstMissing == pindexFirstNeverProcessed); | assert(pindexFirstMissing == pindexFirstNeverProcessed); | ||||
} else if (pindex->nStatus.hasData()) { | } else if (pindex->nStatus.hasData()) { | ||||
// If we have pruned, then we can only say that HAVE_DATA implies | // If we have pruned, then we can only say that HAVE_DATA implies | ||||
// nTx > 0 | // nTx > 0 | ||||
assert(pindex->nTx > 0); | assert(pindex->nTx > 0); | ||||
} | } | ||||
if (pindex->nStatus.hasUndo()) { | if (pindex->nStatus.hasUndo()) { | ||||
assert(pindex->nStatus.hasData()); | assert(pindex->nStatus.hasData()); | ||||
} | } | ||||
// This is pruning-independent. | // This is pruning-independent. | ||||
assert((pindex->nStatus.getValidity() >= BlockValidity::TRANSACTIONS) == | assert((pindex->nStatus.getValidity() >= BlockValidity::TRANSACTIONS) == | ||||
(pindex->nTx > 0)); | (pindex->nTx > 0)); | ||||
// All parents having had data (at some point) is equivalent to all | // All parents having had data (at some point) is equivalent to all | ||||
// parents being VALID_TRANSACTIONS, which is equivalent to nChainTx | // parents being VALID_TRANSACTIONS, which is equivalent to | ||||
// being set. | // HaveTxsDownloaded(). All parents having had data (at some point) is | ||||
// nChainTx != 0 is used to signal that all parent blocks have been | // equivalent to all parents being VALID_TRANSACTIONS, which is | ||||
// processed (but may have been pruned). | // equivalent to HaveTxsDownloaded(). | ||||
assert((pindexFirstNeverProcessed != nullptr) == | assert((pindexFirstNeverProcessed == nullptr) == | ||||
(pindex->nChainTx == 0)); | (pindex->HaveTxsDownloaded())); | ||||
assert((pindexFirstNotTransactionsValid != nullptr) == | assert((pindexFirstNotTransactionsValid == nullptr) == | ||||
(pindex->nChainTx == 0)); | (pindex->HaveTxsDownloaded())); | ||||
// nHeight must be consistent. | // nHeight must be consistent. | ||||
assert(pindex->nHeight == nHeight); | assert(pindex->nHeight == nHeight); | ||||
// For every block except the genesis block, the chainwork must be | // For every block except the genesis block, the chainwork must be | ||||
// larger than the parent's. | // larger than the parent's. | ||||
assert(pindex->pprev == nullptr || | assert(pindex->pprev == nullptr || | ||||
pindex->nChainWork >= pindex->pprev->nChainWork); | pindex->nChainWork >= pindex->pprev->nChainWork); | ||||
// The pskip pointer must point back for all but the first 2 blocks. | // The pskip pointer must point back for all but the first 2 blocks. | ||||
assert(nHeight < 2 || | assert(nHeight < 2 || | ||||
▲ Show 20 Lines • Show All 374 Lines • Show Last 20 Lines |