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.
4501if (state.IsValid()) NONFATAL_UNREACHABLE();4502return state;4503}4505/**4506* At this point ProcessNewBlock would call AcceptBlock(), but we4507* don't want to store the block or its header. Run individual checks4508* instead:4509* - skip AcceptBlockHeader() because:4510* - we don't want to update the block index4511* - we do not care about duplicates4512* - we already ran CheckBlockHeader() via CheckBlock()4513* - we already checked for prev-blk-not-found4514* - we know the tip is valid, so no need to check bad-prevblk4515* - we already ran CheckBlock()4516* - do run ContextualCheckBlockHeader()4517* - do run ContextualCheckBlock()4518*/4520if (!ContextualCheckBlockHeader(block, state, chainstate.m_chainman, tip)) {4521if (state.IsValid()) NONFATAL_UNREACHABLE();4522return state;4523}4525if (!ContextualCheckBlock(block, state, chainstate.m_chainman, tip)) {4526if (state.IsValid()) NONFATAL_UNREACHABLE();4527return state;4528}4530// We don't want ConnectBlock to update the actual chainstate, so create4531// a cache on top of it, along with a dummy block index.4532CBlockIndex index_dummy{block};4533uint256 block_hash(block.GetHash());4534index_dummy.pprev = tip;4535index_dummy.nHeight = tip->nHeight + 1;4536index_dummy.phashBlock = &block_hash;4537CCoinsViewCache view_dummy(&chainstate.CoinsTip());4539// Set fJustCheck to true in order to update, and not clear, validation caches.4540if(!chainstate.ConnectBlock(block, state, &index_dummy, view_dummy, /*fJustCheck=*/true)) {4541if (state.IsValid()) NONFATAL_UNREACHABLE();4542return state;4543}4545// Ensure no check returned successfully while also setting an invalid state.4546if (!state.IsValid()) NONFATAL_UNREACHABLE();4548return state;4549}4551/* This function is called from the RPC code for pruneblockchain */4552void PruneBlockFilesManual(Chainstate& active_chainstate, int nManualPruneHeight)4553{4554BlockValidationState state;4555if (!active_chainstate.FlushStateToDisk(4556state, FlushStateMode::NONE, nManualPruneHeight)) {4557LogWarning("Failed to flush state after manual prune (%s)", state.ToString());4558}4559}4561bool Chainstate::LoadChainTip()4562{4563AssertLockHeld(cs_main);4564const CCoinsViewCache& coins_cache = CoinsTip();4565assert(!coins_cache.GetBestBlock().IsNull()); // Never called when the coins view is empty4566CBlockIndex* tip = m_chain.Tip();4568if (tip && tip->GetBlockHash() == coins_cache.GetBestBlock()) {4569return true;4570}4572// Load pointer to end of best chain4573CBlockIndex* pindex = m_blockman.LookupBlockIndex(coins_cache.GetBestBlock());4574if (!pindex) {4575return false;4576}4577m_chain.SetTip(*pindex);4578m_chainman.UpdateIBDStatus();4579m_last_flushed_block = pindex;4580tip = m_chain.Tip();4582// nSequenceId is one of the keys used to sort setBlockIndexCandidates. Ensure all4583// candidate sets are empty to avoid UB, as nSequenceId is about to be modified.4584for (const auto& cs : m_chainman.m_chainstates) {4585assert(cs->setBlockIndexCandidates.empty());4586}4588// Make sure our chain tip before shutting down scores better than any other candidate4589// to maintain a consistent best tip over reboots in case of a tie.4590auto target = tip;4591while (target) {4592target->nSequenceId = SEQ_ID_BEST_CHAIN_FROM_DISK;4593target = target->pprev;4594}4596LogInfo("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f",4597tip->GetBlockHash().ToString(),4598m_chain.Height(),4599FormatISO8601DateTime(tip->GetBlockTime()),4600m_chainman.GuessVerificationProgress(tip));4602// Ensure KernelNotifications m_tip_block is set even if no new block arrives.4603if (!this->GetRole().historical) {4604// Ignoring return value for now.4605(void)m_chainman.GetNotifications().blockTip(4606/*state=*/GetSynchronizationState(/*init=*/true, m_chainman.m_blockman.m_blockfiles_indexed),4607/*index=*/*pindex,4608/*verification_progress=*/m_chainman.GuessVerificationProgress(tip));4609}4611CheckForkWarningConditions();4613return true;4614}4616CVerifyDB::CVerifyDB(Notifications& notifications)4617: m_notifications{notifications}4618{4619m_notifications.progress(_("Verifying blocks…"), 0, false);4620}4622CVerifyDB::~CVerifyDB()4623{4624m_notifications.progress(bilingual_str{}, 100, false);4625}4627VerifyDBResult CVerifyDB::VerifyDB(4628Chainstate& chainstate,4629const Consensus::Params& consensus_params,4630CCoinsView& coinsview,4631int nCheckLevel, int nCheckDepth)4632{4633AssertLockHeld(cs_main);4635if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr) {4636return VerifyDBResult::SUCCESS;4637}4639// Verify blocks in the best chain4640if (nCheckDepth <= 0 || nCheckDepth > chainstate.m_chain.Height()) {4641nCheckDepth = chainstate.m_chain.Height();4642}4643nCheckLevel = std::max(0, std::min(4, nCheckLevel));4644LogInfo("Verifying last %i blocks at level %i", nCheckDepth, nCheckLevel);4645CCoinsViewCache coins(&coinsview);4646CBlockIndex* pindex;4647CBlockIndex* pindexFailure = nullptr;4648int nGoodTransactions = 0;4649BlockValidationState state;4650int reportDone = 0;4651bool skipped_no_block_data{false};4652bool skipped_l3_checks{false};4653LogInfo("Verification progress: 0%%");4655const bool is_snapshot_cs{chainstate.m_from_snapshot_blockhash};4657for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {4658const int percentageDone = std::max(1, std::min(99, (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));4659if (reportDone < percentageDone / 10) {4660// report every 10% step4661LogInfo("Verification progress: %d%%", percentageDone);4662reportDone = percentageDone / 10;4663}4664m_notifications.progress(_("Verifying blocks…"), percentageDone, false);4665if (pindex->nHeight <= chainstate.m_chain.Height() - nCheckDepth) {4666break;4667}4668if ((chainstate.m_blockman.IsPruneMode() || is_snapshot_cs) && !(pindex->nStatus & BLOCK_HAVE_DATA)) {4669// If pruning or running under an assumeutxo snapshot, only go4670// back as far as we have data.4671LogInfo("Block verification stopping at height %d (no data). This could be due to pruning or use of an assumeutxo snapshot.", pindex->nHeight);4672skipped_no_block_data = true;4673break;4674}4675CBlock block;4676// check level 0: read from disk4677if (!chainstate.m_blockman.ReadBlock(block, *pindex)) {4678LogError("Verification error: ReadBlock failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());4679return VerifyDBResult::CORRUPTED_BLOCK_DB;4680}4681// check level 1: verify block validity4682if (nCheckLevel >= 1 && !CheckBlock(block, state, consensus_params)) {4683LogError("Verification error: found bad block at %d, hash=%s (%s)",4684pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());4685return VerifyDBResult::CORRUPTED_BLOCK_DB;4686}4687// check level 2: verify undo validity4688if (nCheckLevel >= 2 && pindex) {4689CBlockUndo undo;4690if (!pindex->GetUndoPos().IsNull()) {4691if (!chainstate.m_blockman.ReadBlockUndo(undo, *pindex)) {4692LogError("Verification error: found bad undo data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());4693return VerifyDBResult::CORRUPTED_BLOCK_DB;4694}4695}4696}4697// check level 3: check for inconsistencies during memory-only disconnect of tip blocks4698size_t curr_coins_usage = coins.DynamicMemoryUsage() + chainstate.CoinsTip().DynamicMemoryUsage();4700if (nCheckLevel >= 3) {4701if (curr_coins_usage <= chainstate.m_coinstip_cache_size_bytes) {4702assert(coins.GetBestBlock() == pindex->GetBlockHash());4703DisconnectResult res = chainstate.DisconnectBlock(block, pindex, coins);4704if (res == DISCONNECT_FAILED) {4705LogError("Verification error: irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());4706return VerifyDBResult::CORRUPTED_BLOCK_DB;4707}4708if (res == DISCONNECT_UNCLEAN) {4709nGoodTransactions = 0;4710pindexFailure = pindex;4711} else {4712nGoodTransactions += block.vtx.size();4713}4714} else {4715skipped_l3_checks = true;4716}4717}4718if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;4719}4720if (pindexFailure) {4721LogError("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);4722return VerifyDBResult::CORRUPTED_BLOCK_DB;4723}4724if (skipped_l3_checks) {4725LogWarning("Skipped verification of level >=3 (insufficient database cache size). Consider increasing -dbcache.");4726}4728// store block count as we move pindex at check level >= 44729int block_count = chainstate.m_chain.Height() - pindex->nHeight;4731// check level 4: try reconnecting blocks4732if (nCheckLevel >= 4 && !skipped_l3_checks) {4733while (pindex != chainstate.m_chain.Tip()) {4734const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));4735if (reportDone < percentageDone / 10) {4736// report every 10% step4737LogInfo("Verification progress: %d%%", percentageDone);4738reportDone = percentageDone / 10;4739}4740m_notifications.progress(_("Verifying blocks…"), percentageDone, false);4741pindex = chainstate.m_chain.Next(*pindex);4742CBlock block;4743if (!chainstate.m_blockman.ReadBlock(block, *pindex)) {4744LogError("Verification error: ReadBlock failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());4745return VerifyDBResult::CORRUPTED_BLOCK_DB;4746}4747if (!chainstate.ConnectBlock(block, state, pindex, coins)) {4748LogError("Verification error: found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());4749return VerifyDBResult::CORRUPTED_BLOCK_DB;4750}