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.
4751if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED;4752}4753}4755LogInfo("Verification: checked last %i blocks at level %i", block_count, nCheckLevel);4756if (nCheckLevel >= 3 && !skipped_l3_checks) {4757LogInfo("Verification: no coin database inconsistencies (%i transactions)", nGoodTransactions);4758}4760if (skipped_l3_checks) {4761return VerifyDBResult::SKIPPED_L3_CHECKS;4762}4763if (skipped_no_block_data) {4764return VerifyDBResult::SKIPPED_MISSING_BLOCKS;4765}4766return VerifyDBResult::SUCCESS;4767}4769/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */4770bool Chainstate::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs)4771{4772AssertLockHeld(cs_main);4773// TODO: merge with ConnectBlock4774CBlock block;4775if (!m_blockman.ReadBlock(block, *pindex)) {4776LogError("ReplayBlock(): ReadBlock failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());4777return false;4778}4780for (const CTransactionRef& tx : block.vtx) {4781if (!tx->IsCoinBase()) {4782for (const CTxIn &txin : tx->vin) {4783inputs.SpendCoin(txin.prevout);4784}4785}4786// Pass check = true as every addition may be an overwrite.4787AddCoins(inputs, *tx, pindex->nHeight, true);4788}4789return true;4790}4792bool Chainstate::ReplayBlocks()4793{4794LOCK(cs_main);4796CCoinsView& db = this->CoinsDB();4797CCoinsViewCache cache(&db);4799std::vector<uint256> hashHeads = db.GetHeadBlocks();4800if (hashHeads.empty()) return true; // We're already in a consistent state.4801if (hashHeads.size() != 2) {4802LogError("ReplayBlocks(): unknown inconsistent state\n");4803return false;4804}4806m_chainman.GetNotifications().progress(_("Replaying blocks…"), 0, false);4807LogInfo("Replaying blocks");4809const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush.4810const CBlockIndex* pindexNew; // New tip during the interrupted flush.4811const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip.4813if (!m_blockman.m_block_index.contains(hashHeads[0])) {4814LogError("ReplayBlocks(): reorganization to unknown block requested\n");4815return false;4816}4817pindexNew = &(m_blockman.m_block_index[hashHeads[0]]);4819if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.4820if (!m_blockman.m_block_index.contains(hashHeads[1])) {4821LogError("ReplayBlocks(): reorganization from unknown block requested\n");4822return false;4823}4824pindexOld = &(m_blockman.m_block_index[hashHeads[1]]);4825pindexFork = LastCommonAncestor(pindexOld, pindexNew);4826assert(pindexFork != nullptr);4827}4829// Rollback along the old branch.4830const int nForkHeight{pindexFork ? pindexFork->nHeight : 0};4831if (pindexOld != pindexFork) {4832LogInfo("Rolling back from %s (%i to %i)", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight, nForkHeight);4833while (pindexOld != pindexFork) {4834if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.4835CBlock block;4836if (!m_blockman.ReadBlock(block, *pindexOld)) {4837LogError("RollbackBlock(): ReadBlock() failed at %d, hash=%s\n", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());4838return false;4839}4840if (pindexOld->nHeight % 10'000 == 0) {4841LogInfo("Rolling back %s (%i)", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);4842}4843DisconnectResult res = DisconnectBlock(block, pindexOld, cache);4844if (res == DISCONNECT_FAILED) {4845LogError("RollbackBlock(): DisconnectBlock failed at %d, hash=%s\n", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());4846return false;4847}4848// If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO was deleted, or an existing UTXO was4849// overwritten. It corresponds to cases where the block-to-be-disconnect never had all its operations4850// applied to the UTXO set. However, as both writing a UTXO and deleting a UTXO are idempotent operations,4851// the result is still a version of the UTXO set with the effects of that block undone.4852}4853pindexOld = pindexOld->pprev;4854}4855LogInfo("Rolled back to %s", pindexFork->GetBlockHash().ToString());4856}4858// Roll forward from the forking point to the new tip.4859if (nForkHeight < pindexNew->nHeight) {4860LogInfo("Rolling forward to %s (%i to %i)", pindexNew->GetBlockHash().ToString(), nForkHeight, pindexNew->nHeight);4861for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {4862const CBlockIndex& pindex{*Assert(pindexNew->GetAncestor(nHeight))};4864if (nHeight % 10'000 == 0) {4865LogInfo("Rolling forward %s (%i)", pindex.GetBlockHash().ToString(), nHeight);4866}4867m_chainman.GetNotifications().progress(_("Replaying blocks…"), (int)((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)), false);4868if (!RollforwardBlock(&pindex, cache)) return false;4869}4870LogInfo("Rolled forward to %s", pindexNew->GetBlockHash().ToString());4871}4873cache.SetBestBlock(pindexNew->GetBlockHash());4874cache.Flush(/*reallocate_cache=*/false); // local CCoinsViewCache goes out of scope4875m_chainman.GetNotifications().progress(bilingual_str{}, 100, false);4876return true;4877}4879bool Chainstate::NeedsRedownload() const4880{4881AssertLockHeld(cs_main);4883// At and above m_params.SegwitHeight, segwit consensus rules must be validated4884CBlockIndex* block{m_chain.Tip()};4886while (block != nullptr && DeploymentActiveAt(*block, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) {4887if (!(block->nStatus & BLOCK_OPT_WITNESS)) {4888// block is insufficiently validated for a segwit client4889return true;4890}4891block = block->pprev;4892}4894return false;4895}4897void Chainstate::ClearBlockIndexCandidates()4898{4899AssertLockHeld(::cs_main);4900setBlockIndexCandidates.clear();4901}4903void Chainstate::PopulateBlockIndexCandidates()4904{4905AssertLockHeld(::cs_main);4907for (CBlockIndex* pindex : m_blockman.GetAllBlockIndices()) {4908// With assumeutxo, the snapshot block is a candidate for the tip, but it4909// may not have BLOCK_VALID_TRANSACTIONS (e.g. if we haven't yet downloaded4910// the block), so we special-case it here.4911if (pindex == SnapshotBase() ||4912(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&4913(pindex->HaveNumChainTxs() || pindex->pprev == nullptr))) {4914TryAddBlockIndexCandidate(pindex);4915}4916}4917}4919bool ChainstateManager::LoadBlockIndex()4920{4921AssertLockHeld(cs_main);4922// Load block index from databases4923if (m_blockman.m_blockfiles_indexed) {4924bool ret{m_blockman.LoadBlockIndexDB(CurrentChainstate().m_from_snapshot_blockhash)};4925if (!ret) return false;4927m_blockman.ScanAndUnlinkAlreadyPrunedFiles();4929std::vector<CBlockIndex*> vSortedByHeight{m_blockman.GetAllBlockIndices()};4930std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),4931CBlockIndexHeightOnlyComparator());4933for (CBlockIndex* pindex : vSortedByHeight) {4934if (m_interrupt) return false;4935if (pindex->nStatus & BLOCK_FAILED_VALID && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {4936m_best_invalid = pindex;4937}4938if (pindex->IsValid(BLOCK_VALID_TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex)))4939m_best_header = pindex;4940}4941}4942return true;4943}4945bool ChainstateManager::LoadGenesisBlock()4946{4947LOCK(cs_main);4949const CBlock& genesis_block{GetParams().GenesisBlock()};4951// Check whether we're already initialized by checking for genesis in4952// m_blockman.m_block_index. Note that we can't use a chainstate's m_chain here, since it is4953// set based on the coins db, not the block index db, which is the only4954// thing loaded at this point.4955if (m_blockman.m_block_index.contains(genesis_block.GetHash())) {4956return true;4957}4959try {4960FlatFilePos blockPos{m_blockman.WriteBlock(genesis_block, 0)};4961if (blockPos.IsNull()) {4962LogError("Writing genesis block to disk failed");4963return false;4964}4965CBlockIndex* pindex{m_blockman.AddToBlockIndex(genesis_block, m_best_header)};4966ReceivedBlockTransactions(genesis_block, pindex, blockPos);4967} catch (const std::runtime_error& e) {4968LogError("Failed to write genesis block: %s", e.what());4969return false;4970}4972return true;4973}4975void ChainstateManager::LoadExternalBlockFile(4976AutoFile& file_in,4977FlatFilePos* dbp,4978std::multimap<uint256, FlatFilePos>* blocks_with_unknown_parent)4979{4980// Either both should be specified (-reindex), or neither (-loadblock).4981assert(!dbp == !blocks_with_unknown_parent);4983const auto start{SteadyClock::now()};4984const CChainParams& params{GetParams()};4986int nLoaded = 0;4987try {4988BufferedFile blkdat{file_in, 2 * MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE + 8};4989// nRewind indicates where to resume scanning in case something goes wrong,4990// such as a block fails to deserialize.4991uint64_t nRewind = blkdat.GetPos();4992while (!blkdat.eof()) {4993if (m_interrupt) return;4995blkdat.SetPos(nRewind);4996nRewind++; // start one byte further next time, in case of failure4997blkdat.SetLimit(); // remove former limit4998unsigned int nSize = 0;4999try {5000// locate a header