Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 2,477 Lines • ▼ Show 20 Lines | static bool ConnectTip(const Config &config, CValidationState &state, | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Return the tip of the chain with the most work in it, that isn't known to be | * Return the tip of the chain with the most work in it, that isn't known to be | ||||
* invalid (it's however far from certain to be valid). | * invalid (it's however far from certain to be valid). | ||||
*/ | */ | ||||
static CBlockIndex *FindMostWorkChain() { | static CBlockIndex *FindMostWorkChain() { | ||||
int iterator_offset = 0; | |||||
do { | do { | ||||
CBlockIndex *pindexNew = nullptr; | CBlockIndex *pindexNew = nullptr; | ||||
// Find the best candidate header. | // Find the best candidate header. | ||||
{ | { | ||||
std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator | std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator | ||||
it = setBlockIndexCandidates.rbegin(); | it = setBlockIndexCandidates.rbegin(); | ||||
if (iterator_offset > 0) { | |||||
// Avoid using EC chains but do not remove them from the index | |||||
// map to not break the CheckBlockIndex | |||||
// This code will be upgraded to allow the node to select an EC | |||||
// chain as the MostWorkChain | |||||
int i = 0; | |||||
while (i < iterator_offset) { | |||||
it++; | |||||
i++; | |||||
// If the others chains have missing data or have bad blocks | |||||
// their blocks are removed | |||||
if (it == setBlockIndexCandidates.rend()) { | |||||
return nullptr; | |||||
} | |||||
} | |||||
} else { | |||||
if (it == setBlockIndexCandidates.rend()) return nullptr; | if (it == setBlockIndexCandidates.rend()) return nullptr; | ||||
} | |||||
pindexNew = *it; | pindexNew = *it; | ||||
} | } | ||||
// 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 fInvalidAncestor = false; | bool fInvalidAncestor = false; | ||||
bool fCurrentChainIsEC = false; | |||||
while (pindexTest && !chainActive.Contains(pindexTest)) { | while (pindexTest && !chainActive.Contains(pindexTest)) { | ||||
assert(pindexTest->nChainTx || pindexTest->nHeight == 0); | assert(pindexTest->nChainTx || pindexTest->nHeight == 0); | ||||
// Pruned nodes may have entries in setBlockIndexCandidates for | // Pruned nodes may have entries in setBlockIndexCandidates for | ||||
// which block files have been deleted. Remove those as candidates | // which block files have been deleted. Remove those as candidates | ||||
// for the most work chain if we come across them; we can't switch | // for the most work chain if we come across them; we can't switch | ||||
// to a chain unless we have all the non-active-chain parent blocks. | // to a chain unless we have all the non-active-chain parent blocks. | ||||
bool fFailedChain = pindexTest->nStatus & BLOCK_NOT_VALID_MASK; | bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; | ||||
bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); | bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); | ||||
if (pindexTest->nStatus & BLOCK_EXCESSIVE) { | |||||
// Ignore this chain and continue | |||||
++iterator_offset; | |||||
fCurrentChainIsEC = true; | |||||
break; | |||||
} | |||||
if (fFailedChain || fMissingData) { | if (fFailedChain || fMissingData) { | ||||
// Candidate chain is not usable (either invalid or missing | // Candidate chain is not usable (either invalid or missing | ||||
// data) | // data) | ||||
if (fFailedChain && | if (fFailedChain && | ||||
(pindexBestInvalid == nullptr || | (pindexBestInvalid == nullptr || | ||||
pindexNew->nChainWork > pindexBestInvalid->nChainWork)) | pindexNew->nChainWork > pindexBestInvalid->nChainWork)) | ||||
pindexBestInvalid = pindexNew; | pindexBestInvalid = pindexNew; | ||||
CBlockIndex *pindexFailed = pindexNew; | CBlockIndex *pindexFailed = pindexNew; | ||||
Show All 13 Lines | do { | ||||
pindexFailed = pindexFailed->pprev; | pindexFailed = pindexFailed->pprev; | ||||
} | } | ||||
setBlockIndexCandidates.erase(pindexTest); | setBlockIndexCandidates.erase(pindexTest); | ||||
fInvalidAncestor = true; | fInvalidAncestor = true; | ||||
break; | break; | ||||
} | } | ||||
pindexTest = pindexTest->pprev; | pindexTest = pindexTest->pprev; | ||||
} | } | ||||
if (!fInvalidAncestor) return pindexNew; | if (!fCurrentChainIsEC && !fInvalidAncestor) return pindexNew; | ||||
} while (true); | } while (true); | ||||
} | } | ||||
/** Delete all entries in setBlockIndexCandidates that are worse than the | /** Delete all entries in setBlockIndexCandidates that are worse than the | ||||
* current tip. */ | * current tip. */ | ||||
static void PruneBlockIndexCandidates() { | static void 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. | ||||
▲ Show 20 Lines • Show All 386 Lines • ▼ Show 20 Lines | bool ReceivedBlockTransactions(const CBlock &block, CValidationState &state, | ||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const CDiskBlockPos &pos) { | const CDiskBlockPos &pos) { | ||||
pindexNew->nTx = block.vtx.size(); | pindexNew->nTx = block.vtx.size(); | ||||
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 |= BLOCK_HAVE_DATA; | pindexNew->nStatus |= BLOCK_HAVE_DATA; | ||||
if (!(pindexNew->nStatus & BLOCK_EXCESSIVE)) { | |||||
// The validations are only executed when the blocks are not excessive | |||||
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); | pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); | ||||
} | |||||
setDirtyBlockIndex.insert(pindexNew); | setDirtyBlockIndex.insert(pindexNew); | ||||
if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { | if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { | ||||
// 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); | ||||
Show All 20 Lines | if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { | ||||
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); | mapBlocksUnlinked.erase(it); | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { | if (pindexNew->pprev && (pindexNew->pprev->IsValid(BLOCK_VALID_TREE) || | ||||
pindexNew->pprev->IsExcessive())) { | |||||
// Allow parent blocks to be excessive | |||||
mapBlocksUnlinked.insert( | mapBlocksUnlinked.insert( | ||||
std::make_pair(pindexNew->pprev, pindexNew)); | std::make_pair(pindexNew->pprev, pindexNew)); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 504 Lines • ▼ Show 20 Lines | static bool AcceptBlock(const Config &config, | ||||
if (!AcceptBlockHeader(config, block, state, &pindex)) { | if (!AcceptBlockHeader(config, block, state, &pindex)) { | ||||
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 & BLOCK_HAVE_DATA; | bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; | ||||
bool fIsExcessive = pindex->nStatus & BLOCK_EXCESSIVE; | |||||
bool fHasMoreWork = | bool fHasMoreWork = | ||||
(chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork | (chainActive.Tip() ? pindex->nChainWork > chainActive.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(chainActive.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 datastructure to efficiently look up if a | // This requires some new chain datastructure 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. | ||||
// TODO: deal better with return value and error conditions for duplicate | // TODO: deal better with return value and error conditions for duplicate | ||||
// and unrequested blocks. | // and unrequested blocks. | ||||
if (fAlreadyHave) { | if (fAlreadyHave || fIsExcessive) { | ||||
return true; | return true; | ||||
} | } | ||||
// If we didn't ask for it: | // If we didn't ask for it: | ||||
if (!fRequested) { | if (!fRequested) { | ||||
// This is a previously-processed block that was pruned. | // This is a previously-processed block that was pruned. | ||||
if (pindex->nTx != 0) { | if (pindex->nTx != 0) { | ||||
return true; | return true; | ||||
Show All 9 Lines | if (!fRequested) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = true; | *fNewBlock = true; | ||||
} | } | ||||
bool fIsBlockExcessive = false; | |||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
if (!CheckBlock(config, block, state, chainparams.GetConsensus()) || | if (!CheckBlock(config, block, state, chainparams.GetConsensus()) || | ||||
!ContextualCheckBlock(config, block, state, chainparams.GetConsensus(), | !ContextualCheckBlock(config, block, state, chainparams.GetConsensus(), | ||||
pindex->pprev)) { | pindex->pprev)) { | ||||
if (state.IsInvalid() && !state.CorruptionPossible()) { | if (state.IsInvalid() && !state.CorruptionPossible()) { | ||||
pindex->nStatus |= BLOCK_FAILED_VALID; | pindex->nStatus |= BLOCK_FAILED_VALID; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} else if (state.IsExcessive() && !state.CorruptionPossible()) { | } else if (state.IsExcessive() && !state.CorruptionPossible()) { | ||||
pindex->nStatus |= BLOCK_EXCESSIVE; | pindex->nStatus |= BLOCK_EXCESSIVE; | ||||
setDirtyBlockIndex.insert(pindex); | fIsBlockExcessive = true; | ||||
} | |||||
if (!fIsBlockExcessive) { | |||||
// If the block is excessive delay the return until the block is | |||||
// stored | |||||
return error("%s: %s (block %s)", __func__, | |||||
FormatStateMessage(state), block.GetHash().ToString()); | |||||
} | } | ||||
return error("%s: %s (block %s)", __func__, FormatStateMessage(state), | |||||
block.GetHash().ToString()); | |||||
} | } | ||||
// Header is valid/has work, merkle tree and segwit merkle tree are | // Header is valid/has work, merkle tree and segwit merkle tree are | ||||
// good...RELAY NOW (but if it does not build on our best tip, let the | // good...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) { | |||||
// Do not relay excessive blocks, they may be invalid | |||||
if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev && | |||||
!fIsBlockExcessive) { | |||||
GetMainSignals().NewPoWValidBlock(pindex, pblock); | GetMainSignals().NewPoWValidBlock(pindex, pblock); | ||||
} | } | ||||
int nHeight = pindex->nHeight; | int nHeight = pindex->nHeight; | ||||
// Write block to history file | // Write block to history file | ||||
try { | try { | ||||
unsigned int nBlockSize = | unsigned int nBlockSize = | ||||
Show All 19 Lines | try { | ||||
return AbortNode(state, std::string("System error: ") + e.what()); | return AbortNode(state, std::string("System error: ") + e.what()); | ||||
} | } | ||||
if (fCheckForPruning) { | if (fCheckForPruning) { | ||||
// we just allocated more disk space for block files. | // we just allocated more disk space for block files. | ||||
FlushStateToDisk(state, FLUSH_STATE_NONE); | FlushStateToDisk(state, FLUSH_STATE_NONE); | ||||
} | } | ||||
if (fIsBlockExcessive) { | |||||
// After saving the excessive block to disk, inform that the block is | |||||
// bigger than the defined max size | |||||
return error("%s: %s (block %s)", __func__, FormatStateMessage(state), | |||||
block.GetHash().ToString()); | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
bool ProcessNewBlock(const Config &config, | bool ProcessNewBlock(const Config &config, | ||||
const std::shared_ptr<const CBlock> pblock, | const std::shared_ptr<const CBlock> pblock, | ||||
bool fForceProcessing, bool *fNewBlock) { | bool fForceProcessing, bool *fNewBlock) { | ||||
{ | { | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (fNewBlock) *fNewBlock = false; | if (fNewBlock) *fNewBlock = false; | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
CValidationState state; | CValidationState state; | ||||
// Ensure that CheckBlock() passes before calling AcceptBlock, as | // Ensure that CheckBlock() passes before calling AcceptBlock, as | ||||
// belt-and-suspenders. | // belt-and-suspenders. | ||||
bool ret = | bool ret = | ||||
CheckBlock(config, *pblock, state, chainparams.GetConsensus()); | CheckBlock(config, *pblock, state, chainparams.GetConsensus()); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (ret) { | if (ret || state.IsExcessive()) { | ||||
// Store to disk | // Store to disk if there are not errors or the block is excessive | ||||
ret = AcceptBlock(config, pblock, state, &pindex, fForceProcessing, | ret = AcceptBlock(config, pblock, state, &pindex, fForceProcessing, | ||||
nullptr, fNewBlock); | nullptr, fNewBlock); | ||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | CheckBlockIndex(chainparams.GetConsensus()); | ||||
if (!ret) { | if (!ret) { | ||||
GetMainSignals().BlockChecked(*pblock, state); | GetMainSignals().BlockChecked(*pblock, state); | ||||
return error("%s: AcceptBlock FAILED", __func__); | return error("%s: AcceptBlock FAILED", __func__); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 907 Lines • ▼ Show 20 Lines | static void CheckBlockIndex(const Consensus::Params &consensusParams) { | ||||
// (regardless of being valid or not). | // (regardless of being valid or not). | ||||
CBlockIndex *pindexFirstNotScriptsValid = nullptr; | CBlockIndex *pindexFirstNotScriptsValid = nullptr; | ||||
while (pindex != nullptr) { | while (pindex != nullptr) { | ||||
nNodes++; | nNodes++; | ||||
if (pindexFirstInvalid == nullptr && | if (pindexFirstInvalid == nullptr && | ||||
pindex->nStatus & BLOCK_FAILED_VALID) { | pindex->nStatus & BLOCK_FAILED_VALID) { | ||||
pindexFirstInvalid = pindex; | pindexFirstInvalid = pindex; | ||||
} | } | ||||
if (!(pindex->nStatus & BLOCK_EXCESSIVE)) { | |||||
// If the block is excessive this validations were not executed | |||||
// This need to be validated before making the Excessive chain an | |||||
// active chain | |||||
if (pindexFirstMissing == nullptr && | if (pindexFirstMissing == nullptr && | ||||
!(pindex->nStatus & BLOCK_HAVE_DATA)) { | !(pindex->nStatus & BLOCK_HAVE_DATA)) { | ||||
pindexFirstMissing = pindex; | pindexFirstMissing = pindex; | ||||
} | } | ||||
if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) { | if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) { | ||||
pindexFirstNeverProcessed = pindex; | pindexFirstNeverProcessed = pindex; | ||||
} | } | ||||
if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && | if (pindex->pprev != nullptr && | ||||
pindexFirstNotTreeValid == nullptr && | |||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) { | (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) { | ||||
pindexFirstNotTreeValid = pindex; | pindexFirstNotTreeValid = pindex; | ||||
} | } | ||||
if (pindex->pprev != nullptr && | if (pindex->pprev != nullptr && | ||||
pindexFirstNotTransactionsValid == nullptr && | pindexFirstNotTransactionsValid == nullptr && | ||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) { | (pindex->nStatus & BLOCK_VALID_MASK) < | ||||
BLOCK_VALID_TRANSACTIONS) { | |||||
pindexFirstNotTransactionsValid = pindex; | pindexFirstNotTransactionsValid = pindex; | ||||
} | } | ||||
if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && | if (pindex->pprev != nullptr && | ||||
pindexFirstNotChainValid == nullptr && | |||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) { | (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) { | ||||
pindexFirstNotChainValid = pindex; | pindexFirstNotChainValid = pindex; | ||||
} | } | ||||
if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && | if (pindex->pprev != nullptr && | ||||
pindexFirstNotScriptsValid == nullptr && | |||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) { | (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) { | ||||
pindexFirstNotScriptsValid = pindex; | pindexFirstNotScriptsValid = pindex; | ||||
} | } | ||||
} | |||||
// 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->nChainTx == 0) { | ||||
// 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 & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); | assert(!(pindex->nStatus & BLOCK_EXCESSIVE_OR_STORED_DATA) == | ||||
(pindex->nTx == 0)); | |||||
assert(pindexFirstMissing == pindexFirstNeverProcessed); | assert(pindexFirstMissing == pindexFirstNeverProcessed); | ||||
} else { | } else { | ||||
// 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 | ||||
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); | if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); | ||||
} | } | ||||
if (pindex->nStatus & BLOCK_HAVE_UNDO) { | if (pindex->nStatus & BLOCK_HAVE_UNDO) { | ||||
assert(pindex->nStatus & BLOCK_HAVE_DATA); | assert(pindex->nStatus & BLOCK_HAVE_DATA); | ||||
} | } | ||||
// This is pruning-independent. | // This is pruning-independent. | ||||
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= | assert((((pindex->nStatus & BLOCK_VALID_MASK) >= | ||||
BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); | BLOCK_VALID_TRANSACTIONS || | ||||
pindex->nStatus & BLOCK_EXCESSIVE)) == (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 nChainTx | ||||
// being set. | // being set. | ||||
// nChainTx != 0 is used to signal that all parent blocks have been | // nChainTx != 0 is used to signal that all parent blocks have been | ||||
// processed (but may have been pruned). | // processed (but may have been pruned). | ||||
assert((pindexFirstNeverProcessed != nullptr) == | assert((pindexFirstNeverProcessed != nullptr) == | ||||
(pindex->nChainTx == 0)); | (pindex->nChainTx == 0)); | ||||
assert((pindexFirstNotTransactionsValid != nullptr) == | assert((pindexFirstNotTransactionsValid != nullptr) == | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | while (pindex != nullptr) { | ||||
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 & BLOCK_HAVE_DATA) && | if (pindex->pprev && | ||||
(pindex->nStatus & BLOCK_EXCESSIVE_OR_STORED_DATA) && | |||||
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. | // mapBlocksUnlinked. | ||||
assert(foundInUnlinked); | assert(foundInUnlinked); | ||||
} | } | ||||
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) { | if (!(pindex->nStatus & BLOCK_EXCESSIVE_OR_STORED_DATA)) { | ||||
// Can't be in mapBlocksUnlinked if we don't HAVE_DATA | // Can't be in mapBlocksUnlinked 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. | // mapBlocksUnlinked. | ||||
assert(!foundInUnlinked); | assert(!foundInUnlinked); | ||||
} | } | ||||
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && | if (pindex->pprev && | ||||
(pindex->nStatus & BLOCK_EXCESSIVE_OR_STORED_DATA) && | |||||
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 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 | ||||
▲ Show 20 Lines • Show All 266 Lines • Show Last 20 Lines |