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.
3251fInvalidFound = true;3252fContinue = false;3253break;3254} else {3255// A system error occurred (disk space, database error, ...).3256// Make the mempool consistent with the current tip, just in case3257// any observers try to use it before shutdown.3258MaybeUpdateMempoolForReorg(disconnectpool, false);3259return false;3260}3261} else {3262PruneBlockIndexCandidates();3263if (!pindexOldTip || m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {3264// We're in a better position than we were. Return temporarily to release the lock.3265fContinue = false;3266break;3267}3268}3269}3270}3272if (fBlocksDisconnected) {3273// If any blocks were disconnected, disconnectpool may be non empty. Add3274// any disconnected transactions back to the mempool.3275MaybeUpdateMempoolForReorg(disconnectpool, true);3276}3277if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);3279CheckForkWarningConditions();3281return true;3282}3284static SynchronizationState GetSynchronizationState(bool init, bool blockfiles_indexed)3285{3286if (!init) return SynchronizationState::POST_INIT;3287if (!blockfiles_indexed) return SynchronizationState::INIT_REINDEX;3288return SynchronizationState::INIT_DOWNLOAD;3289}3291void ChainstateManager::UpdateIBDStatus()3292{3293AssertLockHeld(cs_main);3294if (!m_cached_is_ibd.load(std::memory_order_relaxed)) return;3295if (m_blockman.LoadingBlocks()) return;3296if (!CurrentChainstate().m_chain.IsTipRecent(MinimumChainWork(), m_options.max_tip_age)) return;3297LogInfo("Leaving InitialBlockDownload (latching to false)");3298m_cached_is_ibd.store(false, std::memory_order_relaxed);3299}3301bool ChainstateManager::NotifyHeaderTip()3302{3303bool fNotify = false;3304bool fInitialBlockDownload = false;3305CBlockIndex* pindexHeader = nullptr;3306{3307LOCK(GetMutex());3308pindexHeader = m_best_header;3310if (pindexHeader != m_last_notified_header) {3311fNotify = true;3312fInitialBlockDownload = IsInitialBlockDownload();3313m_last_notified_header = pindexHeader;3314}3315}3316// Send block tip changed notifications without the lock held3317if (fNotify) {3318GetNotifications().headerTip(GetSynchronizationState(fInitialBlockDownload, m_blockman.m_blockfiles_indexed), pindexHeader->nHeight, pindexHeader->nTime, false);3319}3320return fNotify;3321}3323static void LimitValidationInterfaceQueue(ValidationSignals& signals) LOCKS_EXCLUDED(cs_main) {3324AssertLockNotHeld(cs_main);3326if (signals.CallbacksPending() > 10) {3327signals.SyncWithValidationInterfaceQueue();3328}3329}3331bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)3332{3333AssertLockNotHeld(m_chainstate_mutex);3335// Note that while we're often called here from ProcessNewBlock, this is3336// far from a guarantee. Things in the P2P/RPC will often end up calling3337// us in the middle of ProcessNewBlock - do not assume pblock is set3338// sanely for performance or correctness!3339AssertLockNotHeld(::cs_main);3341// ABC maintains a fair degree of expensive-to-calculate internal state3342// because this function periodically releases cs_main so that it does not lock up other threads for too long3343// during large connects - and to allow for e.g. the callback queue to drain3344// we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time3345LOCK(m_chainstate_mutex);3347// Belt-and-suspenders check that we aren't attempting to advance the3348// chainstate past the target block.3349if (WITH_LOCK(::cs_main, return m_target_utxohash)) {3350LogError("%s", STR_INTERNAL_BUG("m_target_utxohash is set - this chainstate should not be in operation."));3351return Assume(false);3352}3354CBlockIndex *pindexMostWork = nullptr;3355CBlockIndex *pindexNewTip = nullptr;3356bool exited_ibd{false};3357do {3358// Block until the validation queue drains. This should largely3359// never happen in normal operation, however may happen during3360// reindex, causing memory blowup if we run too far ahead.3361// Note that if a validationinterface callback ends up calling3362// ActivateBestChain this may lead to a deadlock! We should3363// probably have a DEBUG_LOCKORDER test for this in the future.3364if (m_chainman.m_options.signals) LimitValidationInterfaceQueue(*m_chainman.m_options.signals);3366{3367LOCK(cs_main);3368{3369// Lock transaction pool for at least as long as it takes for connected_blocks to be consumed3370LOCK(MempoolMutex());3371const bool was_in_ibd = m_chainman.IsInitialBlockDownload();3372CBlockIndex* starting_tip = m_chain.Tip();3373bool blocks_connected = false;3374do {3375// We absolutely may not unlock cs_main until we've made forward progress3376// (with the exception of shutdown due to hardware issues, low disk space, etc).3377std::vector<ConnectedBlock> connected_blocks; // Destructed before cs_main is unlocked3379if (pindexMostWork == nullptr) {3380pindexMostWork = FindMostWorkChain();3381}3383// Whether we have anything to do at all.3384if (pindexMostWork == nullptr || pindexMostWork == m_chain.Tip()) {3385break;3386}3388bool fInvalidFound = false;3389std::shared_ptr<const CBlock> nullBlockPtr;3390// BlockConnected signals must be sent for the original role;3391// in case snapshot validation is completed during ActivateBestChainStep, the3392// result of GetRole() changes from BACKGROUND to NORMAL.3393const ChainstateRole chainstate_role{this->GetRole()};3394if (!ActivateBestChainStep(state, *pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connected_blocks)) {3395// A system error occurred3396return false;3397}3398blocks_connected = true;3400if (fInvalidFound) {3401// Wipe cache, we may need another branch now.3402pindexMostWork = nullptr;3403}3404pindexNewTip = m_chain.Tip();3406for (auto& [index, block] : std::move(connected_blocks)) {3407if (m_chainman.m_options.signals) {3408m_chainman.m_options.signals->BlockConnected(chainstate_role, std::move(Assert(block)), Assert(index));3409}3410}3412// Break this do-while to ensure we don't advance past the target block.3413if (ReachedTarget()) {3414break;3415}3416} while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));3417if (!blocks_connected) return true;3419const CBlockIndex* pindexFork = starting_tip ? m_chain.FindFork(*starting_tip) : nullptr;3420bool still_in_ibd = m_chainman.IsInitialBlockDownload();3422if (was_in_ibd && !still_in_ibd) {3423// Active chainstate has exited IBD.3424exited_ibd = true;3425}3427// Notify external listeners about the new tip.3428// Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected3429if (this == &m_chainman.ActiveChainstate() && pindexFork != pindexNewTip) {3430// Notify ValidationInterface subscribers3431if (m_chainman.m_options.signals) {3432m_chainman.m_options.signals->UpdatedBlockTip(pindexNewTip, pindexFork, still_in_ibd);3433}3435if (kernel::IsInterrupted(m_chainman.GetNotifications().blockTip(3436/*state=*/GetSynchronizationState(still_in_ibd, m_chainman.m_blockman.m_blockfiles_indexed),3437/*index=*/*pindexNewTip,3438/*verification_progress=*/m_chainman.GuessVerificationProgress(pindexNewTip))))3439{3440// Just breaking and returning success for now. This could3441// be changed to bubble up the kernel::Interrupted value to3442// the caller so the caller could distinguish between3443// completed and interrupted operations.3444break;3445}3446}3447} // release MempoolMutex3448// Notify external listeners about the new tip, even if pindexFork == pindexNewTip.3449if (m_chainman.m_options.signals && this == &m_chainman.ActiveChainstate()) {3450m_chainman.m_options.signals->ActiveTipChange(*Assert(pindexNewTip), m_chainman.IsInitialBlockDownload());3451}3452} // release cs_main3453// When we reach this point, we switched to a new tip (stored in pindexNewTip).3455bool reached_target;3456{3457LOCK(m_chainman.GetMutex());3458if (exited_ibd) {3459// If a background chainstate is in use, we may need to rebalance our3460// allocation of caches once a chainstate exits initial block download.3461m_chainman.MaybeRebalanceCaches();3462}3464// Write changes periodically to disk, after relay.3465if (!FlushStateToDisk(state, FlushStateMode::PERIODIC)) {3466return false;3467}3469reached_target = ReachedTarget();3470}3472if (reached_target) {3473// Chainstate has reached the target block, so exit.3474//3475// Restart indexes so indexes can resync and index new blocks after3476// the target block.3477//3478// This cannot be done while holding cs_main (within3479// MaybeValidateSnapshot) or a cs_main deadlock will occur.3480if (m_chainman.snapshot_download_completed) {3481m_chainman.snapshot_download_completed();3482}3483break;3484}3486// We check interrupt only after giving ActivateBestChainStep a chance to run once so that we3487// never interrupt before connecting the genesis block during LoadChainTip(). Previously this3488// caused an assert() failure during interrupt in such cases as the UTXO DB flushing checks3489// that the best block hash is non-null.3490if (m_chainman.m_interrupt) break;3491} while (pindexNewTip != pindexMostWork);3493m_chainman.CheckBlockIndex();3495return true;3496}3498bool Chainstate::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)3499{3500AssertLockNotHeld(m_chainstate_mutex);