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.
3001struct ConnectedBlock {3002const CBlockIndex* pindex;3003std::shared_ptr<const CBlock> pblock;3004};3006/**3007* Connect a new block to m_chain. block_to_connect is either nullptr or a pointer to a CBlock3008* corresponding to pindexNew, to bypass loading it again from disk.3009*3010* The block is added to connected_blocks if connection succeeds.3011*/3012bool Chainstate::ConnectTip(3013BlockValidationState& state,3014CBlockIndex* pindexNew,3015std::shared_ptr<const CBlock> block_to_connect,3016std::vector<ConnectedBlock>& connected_blocks,3017DisconnectedBlockTransactions& disconnectpool)3018{3019AssertLockHeld(cs_main);3020if (m_mempool) AssertLockHeld(m_mempool->cs);3022assert(pindexNew->pprev == m_chain.Tip());3023// Read block from disk.3024const auto time_1{SteadyClock::now()};3025if (!block_to_connect) {3026std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();3027if (!m_blockman.ReadBlock(*pblockNew, *pindexNew)) {3028return FatalError(m_chainman.GetNotifications(), state, _("Failed to read block."));3029}3030block_to_connect = std::move(pblockNew);3031} else {3032LogDebug(BCLog::BENCH, " - Using cached block\n");3033}3034// Apply the block atomically to the chain state.3035const auto time_2{SteadyClock::now()};3036SteadyClock::time_point time_3;3037// When adding aggregate statistics in the future, keep in mind that3038// num_blocks_total may be zero until the ConnectBlock() call below.3039LogDebug(BCLog::BENCH, " - Load block from disk: %.2fms\n",3040Ticks<MillisecondsDouble>(time_2 - time_1));3041{3042CoinsViewOverlay& view{*m_coins_views->m_connect_block_view};3043const auto reset_guard{view.StartFetching(*block_to_connect)};3044bool rv = ConnectBlock(*block_to_connect, state, pindexNew, view);3045if (m_chainman.m_options.signals) {3046m_chainman.m_options.signals->BlockChecked(block_to_connect, state);3047}3048if (!rv) {3049if (state.IsInvalid())3050InvalidBlockFound(pindexNew, state);3051LogError("%s: ConnectBlock %s failed, %s\n", __func__, pindexNew->GetBlockHash().ToString(), state.ToString());3052return false;3053}3054time_3 = SteadyClock::now();3055m_chainman.time_connect_total += time_3 - time_2;3056assert(m_chainman.num_blocks_total > 0);3057LogDebug(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",3058Ticks<MillisecondsDouble>(time_3 - time_2),3059Ticks<SecondsDouble>(m_chainman.time_connect_total),3060Ticks<MillisecondsDouble>(m_chainman.time_connect_total) / m_chainman.num_blocks_total);3061view.Flush(/*reallocate_cache=*/false); // No need to reallocate since it only has capacity for 1 block3062}3063const auto time_4{SteadyClock::now()};3064m_chainman.time_flush += time_4 - time_3;3065LogDebug(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",3066Ticks<MillisecondsDouble>(time_4 - time_3),3067Ticks<SecondsDouble>(m_chainman.time_flush),3068Ticks<MillisecondsDouble>(m_chainman.time_flush) / m_chainman.num_blocks_total);3069// Write the chain state to disk, if necessary.3070if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {3071return false;3072}3073const auto time_5{SteadyClock::now()};3074m_chainman.time_chainstate += time_5 - time_4;3075LogDebug(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",3076Ticks<MillisecondsDouble>(time_5 - time_4),3077Ticks<SecondsDouble>(m_chainman.time_chainstate),3078Ticks<MillisecondsDouble>(m_chainman.time_chainstate) / m_chainman.num_blocks_total);3079// Remove conflicting transactions from the mempool.3080std::vector<RemovedMempoolTransactionInfo> txs_removed_for_block;3081if (m_mempool) {3082txs_removed_for_block = m_mempool->removeForBlock(block_to_connect->vtx);3083disconnectpool.removeForBlock(block_to_connect->vtx);3084}3085// Update m_chain & related variables.3086m_chain.SetTip(*pindexNew);3087m_chainman.UpdateIBDStatus();3088// Not fired while IBD is active. removeForBlock() above still runs.3089if (m_mempool && m_chainman.m_options.signals && !m_chainman.IsInitialBlockDownload()) {3090m_chainman.m_options.signals->MempoolTransactionsRemovedForBlock(block_to_connect, std::move(txs_removed_for_block), pindexNew->nHeight);3091}3092UpdateTip(pindexNew);3094const auto time_6{SteadyClock::now()};3095m_chainman.time_post_connect += time_6 - time_5;3096m_chainman.time_total += time_6 - time_1;3097LogDebug(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",3098Ticks<MillisecondsDouble>(time_6 - time_5),3099Ticks<SecondsDouble>(m_chainman.time_post_connect),3100Ticks<MillisecondsDouble>(m_chainman.time_post_connect) / m_chainman.num_blocks_total);3101LogDebug(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",3102Ticks<MillisecondsDouble>(time_6 - time_1),3103Ticks<SecondsDouble>(m_chainman.time_total),3104Ticks<MillisecondsDouble>(m_chainman.time_total) / m_chainman.num_blocks_total);3106// See if this chainstate has reached a target block and can be used to3107// validate an assumeutxo snapshot. If it can, hashing the UTXO database3108// will be slow, and cs_main could remain locked here for several minutes.3109// If the snapshot is validated, the UTXO hash will be saved to3110// this->m_target_utxohash, causing HistoricalChainstate() to return null3111// and this chainstate to no longer be used. ActivateBestChain() will also3112// stop connecting blocks to this chainstate because this->ReachedTarget()3113// will be true and this->setBlockIndexCandidates will not have additional3114// blocks.3115Chainstate& current_cs{m_chainman.CurrentChainstate()};3116m_chainman.MaybeValidateSnapshot(*this, current_cs);3118connected_blocks.emplace_back(pindexNew, std::move(block_to_connect));3119return true;3120}3122/**3123* Return the tip of the chain with the most work in it, that isn't3124* known to be invalid (it's however far from certain to be valid).3125*/3126CBlockIndex* Chainstate::FindMostWorkChain()3127{3128AssertLockHeld(::cs_main);3129do {3130CBlockIndex *pindexNew = nullptr;3132// Find the best candidate header.3133{3134std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin();3135if (it == setBlockIndexCandidates.rend())3136return nullptr;3137pindexNew = *it;3138}3140// Check whether all blocks on the path between the currently active chain and the candidate are valid.3141// Just going until the active chain is an optimization, as we know all blocks in it are valid already.3142bool fInvalidAncestor = false;3143for (CBlockIndex *pindexTest = pindexNew; pindexTest && !m_chain.Contains(*pindexTest); pindexTest = pindexTest->pprev) {3144assert(pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0);3146// Pruned nodes may have entries in setBlockIndexCandidates for3147// which block files have been deleted. Remove those as candidates3148// for the most work chain if we come across them; we can't switch3149// to a chain unless we have all the non-active-chain parent blocks.3150bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_VALID;3151bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA);3152if (fFailedChain || fMissingData) {3153// Candidate chain is not usable (either invalid or missing data)3154if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork)) {3155m_chainman.m_best_invalid = pindexNew;3156}3157// Remove the entire chain from the set.3158for (CBlockIndex *pindexFailed = pindexNew; pindexFailed != pindexTest; pindexFailed = pindexFailed->pprev) {3159// If we're missing data and not a descendant of an invalid block,3160// then add back to m_blocks_unlinked, so that if the block arrives in the future3161// we can try adding to setBlockIndexCandidates again.3162if (fMissingData && !fFailedChain) {3163// Avoid duplicate entries in m_blocks_unlinked. If the same entry is3164// processed twice in ReceivedBlockTransactions(), it may be re-added to3165// setBlockIndexCandidates with a modified nSequenceId, breaking ordering3166// guarantees and leading to undefined behavior.3167m_blockman.AddUnlinkedBlock(pindexFailed);3168}3169setBlockIndexCandidates.erase(pindexFailed);3170}3171setBlockIndexCandidates.erase(pindexTest);3172fInvalidAncestor = true;3173break;3174}3175}3176if (!fInvalidAncestor)3177return pindexNew;3178} while(true);3179}3181/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */3182void Chainstate::PruneBlockIndexCandidates() {3183// Note that we can't delete the current block itself, as we may need to return to it later in case a3184// reorganization to a better block fails.3185std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();3186while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, m_chain.Tip())) {3187setBlockIndexCandidates.erase(it++);3188}3189// Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.3190assert(!setBlockIndexCandidates.empty());3191}3193/**3194* Try to make some progress towards making index_most_work the active block.3195* pblock is either nullptr or a pointer to a CBlock corresponding to index_most_work.3196*3197* @returns true unless a system error occurred3198*/3199bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex& index_most_work, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, std::vector<ConnectedBlock>& connected_blocks)3200{3201AssertLockHeld(cs_main);3202if (m_mempool) AssertLockHeld(m_mempool->cs);3204const CBlockIndex* pindexOldTip = m_chain.Tip();3205const CBlockIndex* pindexFork = m_chain.FindFork(index_most_work);3207// Disconnect active blocks which are no longer in the best chain.3208bool fBlocksDisconnected = false;3209DisconnectedBlockTransactions disconnectpool{MAX_DISCONNECTED_TX_POOL_BYTES};3210while (m_chain.Tip() && m_chain.Tip() != pindexFork) {3211if (!DisconnectTip(state, &disconnectpool)) {3212// This is likely a fatal error, but keep the mempool consistent,3213// just in case. Only remove from the mempool in this case.3214MaybeUpdateMempoolForReorg(disconnectpool, false);3216// If we're unable to disconnect a block during normal operation,3217// then that is a failure of our local system -- we should abort3218// rather than stay on a less work chain.3219FatalError(m_chainman.GetNotifications(), state, _("Failed to disconnect block."));3220return false;3221}3222fBlocksDisconnected = true;3223}3225// Build list of new blocks to connect (in descending height order).3226std::vector<CBlockIndex*> vpindexToConnect;3227bool fContinue = true;3228int nHeight = pindexFork ? pindexFork->nHeight : -1;3229while (fContinue && nHeight != index_most_work.nHeight) {3230// Don't iterate the entire list of potential improvements toward the best tip, as we likely only need3231// a few blocks along the way.3232int nTargetHeight = std::min(nHeight + 32, index_most_work.nHeight);3233vpindexToConnect.clear();3234vpindexToConnect.reserve(nTargetHeight - nHeight);3235CBlockIndex* pindexIter = index_most_work.GetAncestor(nTargetHeight);3236while (pindexIter && pindexIter->nHeight != nHeight) {3237vpindexToConnect.push_back(pindexIter);3238pindexIter = pindexIter->pprev;3239}3240nHeight = nTargetHeight;3242// Connect new blocks.3243for (CBlockIndex* pindexConnect : vpindexToConnect | std::views::reverse) {3244if (!ConnectTip(state, pindexConnect, pindexConnect == &index_most_work ? pblock : std::shared_ptr<const CBlock>(), connected_blocks, disconnectpool)) {3245if (state.IsInvalid()) {3246// The block violates a consensus rule.3247if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {3248InvalidChainFound(vpindexToConnect.front());3249}3250state = BlockValidationState();