Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
5251if (c->m_chain.Genesis() != nullptr) {5252assert(pindex == c->m_chain.Genesis()); // The chain's genesis block must be this block.5253}5254}5255}5256// nSequenceId can't be set higher than SEQ_ID_INIT_FROM_DISK{1} for blocks that aren't linked5257// (negative is used for preciousblock, SEQ_ID_BEST_CHAIN_FROM_DISK{0} for active chain when loaded from disk)5258if (!pindex->HaveNumChainTxs()) assert(pindex->nSequenceId <= SEQ_ID_INIT_FROM_DISK);5259// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).5260// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.5261if (!m_blockman.m_have_pruned) {5262// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 05263assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));5264assert(pindexFirstMissing == pindexFirstNeverProcessed);5265} else {5266// If we have pruned, then we can only say that HAVE_DATA implies nTx > 05267if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);5268}5269if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);5270if (snap_base && snap_base->GetAncestor(pindex->nHeight) == pindex) {5271// Assumed-valid blocks should connect to the main chain.5272assert((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE);5273}5274// There should only be an nTx value if we have5275// actually seen a block's transactions.5276assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.5277// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveNumChainTxs().5278// HaveNumChainTxs will also be set in the assumeutxo snapshot block from snapshot metadata.5279assert((pindexFirstNeverProcessed == nullptr || pindex == snap_base) == pindex->HaveNumChainTxs());5280assert((pindexFirstNotTransactionsValid == nullptr || pindex == snap_base) == pindex->HaveNumChainTxs());5281assert(pindex->nHeight == nHeight); // nHeight must be consistent.5282assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.5283assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.5284assert(pindexFirstNotTreeValid == nullptr); // All m_blockman.m_block_index entries must at least be TREE valid5285if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == nullptr); // TREE valid implies all parents are TREE valid5286if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == nullptr); // CHAIN valid implies all parents are CHAIN valid5287if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == nullptr); // SCRIPTS valid implies all parents are SCRIPTS valid5288if (pindexFirstInvalid == nullptr) {5289// Checks for not-invalid blocks.5290assert((pindex->nStatus & BLOCK_FAILED_VALID) == 0); // The failed flag cannot be set for blocks without invalid parents.5291} else {5292assert(pindex->nStatus & BLOCK_FAILED_VALID); // Invalid blocks and their descendants must be marked as invalid5293}5294// Make sure m_chain_tx_count sum is correctly computed.5295if (!pindex->pprev) {5296// If no previous block, nTx and m_chain_tx_count must be the same.5297assert(pindex->m_chain_tx_count == pindex->nTx);5298} else if (pindex->pprev->m_chain_tx_count > 0 && pindex->nTx > 0) {5299// If previous m_chain_tx_count is set and number of transactions in block is known, sum must be set.5300assert(pindex->m_chain_tx_count == pindex->nTx + pindex->pprev->m_chain_tx_count);5301} else {5302// Otherwise m_chain_tx_count should only be set if this is a snapshot5303// block, and must be set if it is.5304assert((pindex->m_chain_tx_count != 0) == (pindex == snap_base));5305}5306// There should be no block with more work than m_best_header, unless it's known to be invalid5307assert((pindex->nStatus & BLOCK_FAILED_VALID) || pindex->nChainWork <= m_best_header->nChainWork);5309// Chainstate-specific checks on setBlockIndexCandidates5310for (const auto& c : m_chainstates) {5311if (c->m_chain.Tip() == nullptr) continue;5312// Two main factors determine whether pindex is a candidate in5313// setBlockIndexCandidates:5314//5315// - If pindex has less work than the chain tip, it should not be a5316// candidate, and this will be asserted below. Otherwise it is a5317// potential candidate.5318//5319// - If pindex or one of its parent blocks back to the genesis block5320// or an assumeutxo snapshot never downloaded transactions5321// (pindexFirstNeverProcessed is non-null), it should not be a5322// candidate, and this will be asserted below. The only exception5323// is if pindex itself is an assumeutxo snapshot block. Then it is5324// also a potential candidate.5325if (!CBlockIndexWorkComparator()(pindex, c->m_chain.Tip()) && (pindexFirstNeverProcessed == nullptr || pindex == snap_base)) {5326// If pindex was detected as invalid (pindexFirstInvalid is5327// non-null), it is not required to be in5328// setBlockIndexCandidates.5329if (pindexFirstInvalid == nullptr) {5330// If pindex and all its parents back to the genesis block5331// or an assumeutxo snapshot block downloaded transactions,5332// and the transactions were not pruned (pindexFirstMissing5333// is null), it is a potential candidate. The check5334// excludes pruned blocks, because if any blocks were5335// pruned between pindex and the current chain tip, pindex will5336// only temporarily be added to setBlockIndexCandidates,5337// before being moved to m_blocks_unlinked. This check5338// could be improved to verify that if all blocks between5339// the chain tip and pindex have data, pindex must be a5340// candidate.5341//5342// If pindex is the chain tip, it also is a potential5343// candidate.5344//5345// If the chainstate was loaded from a snapshot and pindex5346// is the base of the snapshot, pindex is also a potential5347// candidate.5348if (pindexFirstMissing == nullptr || pindex == c->m_chain.Tip() || pindex == c->SnapshotBase()) {5349// If this chainstate is not a historical chainstate5350// targeting a specific block, pindex must be in5351// setBlockIndexCandidates. Otherwise, pindex only5352// needs to be added if it is an ancestor of the target5353// block.5354if (!c->TargetBlock() || c->TargetBlock()->GetAncestor(pindex->nHeight) == pindex) {5355assert(c->setBlockIndexCandidates.contains(pindex));5356}5357}5358// If some parent is missing, then it could be that this block was in5359// setBlockIndexCandidates but had to be removed because of the missing data.5360// In this case it must be in m_blocks_unlinked -- see test below.5361}5362} else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates.5363assert(!c->setBlockIndexCandidates.contains(pindex));5364}5365}5366// Check whether this block is in m_blocks_unlinked.5367auto rangeUnlinked{m_blockman.m_blocks_unlinked.equal_range(pindex->pprev)};5368bool foundInUnlinked = false;5369for (auto it = rangeUnlinked.first; it != rangeUnlinked.second; ++it) {5370assert(it->first == pindex->pprev);5371if (it->second == pindex) {5372assert(!foundInUnlinked); // No duplicates in m_blocks_unlinked5373foundInUnlinked = true;5374}5375}5376if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) {5377// If this block has block data available, some parent was never received, and has no invalid parents, it must be in m_blocks_unlinked.5378assert(foundInUnlinked);5379}5380if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in m_blocks_unlinked if we don't HAVE_DATA5381if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.5382if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {5383// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.5384assert(m_blockman.m_have_pruned);5385// This block may have entered m_blocks_unlinked if:5386// - it has a descendant that at some point had more work than the5387// tip, and5388// - we tried switching to that descendant but were missing5389// data for some intermediate block between m_chain and the5390// tip.5391// So if this block is itself better than any m_chain.Tip() and it wasn't in5392// setBlockIndexCandidates, then it must be in m_blocks_unlinked.5393for (const auto& c : m_chainstates) {5394if (!CBlockIndexWorkComparator()(pindex, c->m_chain.Tip()) && !c->setBlockIndexCandidates.contains(pindex)) {5395if (pindexFirstInvalid == nullptr) {5396if (!c->TargetBlock() || c->TargetBlock()->GetAncestor(pindex->nHeight) == pindex) {5397assert(foundInUnlinked);5398}5399}5400}5401}5402}5403// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow5404// End: actual consistency checks.5407// Try descending into the first subnode. Always process forks first and the best header chain after.5408snap_update_firsts();5409auto range{forward.equal_range(pindex)};5410if (range.first != range.second) {5411// A subnode not part of the best header chain was found.5412pindex = range.first->second;5413nHeight++;5414continue;5415} else if (best_hdr_chain.Contains(*pindex)) {5416// Descend further into best header chain.5417nHeight++;5418pindex = best_hdr_chain[nHeight];5419if (!pindex) break; // we are finished, since the best header chain is always processed last5420continue;5421}5422// This is a leaf node.5423// Move upwards until we reach a node of which we have not yet visited the last child.5424while (pindex) {5425// We are going to either move to a parent or a sibling of pindex.5426snap_update_firsts();5427// If pindex was the first with a certain property, unset the corresponding variable.5428if (pindex == pindexFirstInvalid) pindexFirstInvalid = nullptr;5429if (pindex == pindexFirstMissing) pindexFirstMissing = nullptr;5430if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = nullptr;5431if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = nullptr;5432if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = nullptr;5433if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = nullptr;5434if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = nullptr;5435// Find our parent.5436CBlockIndex* pindexPar = pindex->pprev;5437// Find which child we just visited.5438auto rangePar{forward.equal_range(pindexPar)};5439while (rangePar.first->second != pindex) {5440assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child.5441rangePar.first++;5442}5443// Proceed to the next one.5444rangePar.first++;5445if (rangePar.first != rangePar.second) {5446// Move to a sibling not part of the best header chain.5447pindex = rangePar.first->second;5448break;5449} else if (pindexPar == best_hdr_chain[nHeight - 1]) {5450// Move to pindex's sibling on the best-chain, if it has one.5451pindex = best_hdr_chain[nHeight];5452// There will not be a next block if (and only if) parent block is the best header.5453assert((pindex == nullptr) == (pindexPar == best_hdr_chain.Tip()));5454break;5455} else {5456// Move up further.5457pindex = pindexPar;5458nHeight--;5459continue;5460}5461}5462}5464// Check that we actually traversed the entire block index.5465assert(nNodes == forward.size() + best_hdr_chain.Height() + 1);5466}5468std::string Chainstate::ToString()5469{5470AssertLockHeld(::cs_main);5471CBlockIndex* tip = m_chain.Tip();5472return strprintf("Chainstate [%s] @ height %d (%s)",5473m_from_snapshot_blockhash ? "snapshot" : "ibd",5474tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");5475}5477bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)5478{5479AssertLockHeld(::cs_main);5480if (coinstip_size == m_coinstip_cache_size_bytes &&5481coinsdb_size == m_coinsdb_cache_size_bytes) {5482// Cache sizes are unchanged, no need to continue.5483return true;5484}5485size_t old_coinstip_size = m_coinstip_cache_size_bytes;5486m_coinstip_cache_size_bytes = coinstip_size;5487m_coinsdb_cache_size_bytes = coinsdb_size;5488CoinsDB().ResizeCache(coinsdb_size);5490LogInfo("[%s] resized coinsdb cache to %.1f MiB",5491this->ToString(), coinsdb_size / double(1_MiB));5492LogInfo("[%s] resized coinstip cache to %.1f MiB",5493this->ToString(), coinstip_size / double(1_MiB));5495BlockValidationState state;5496bool ret;5498if (coinstip_size > old_coinstip_size) {5499// Likely no need to flush if cache sizes have grown.5500ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED);