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.
3501AssertLockNotHeld(::cs_main);3502{3503LOCK(cs_main);3504if (pindex->nChainWork < m_chain.Tip()->nChainWork) {3505// Nothing to do, this block is not at the tip.3506return true;3507}3508if (m_chain.Tip()->nChainWork > m_chainman.nLastPreciousChainwork) {3509// The chain has been extended since the last call, reset the counter.3510m_chainman.nBlockReverseSequenceId = -1;3511}3512m_chainman.nLastPreciousChainwork = m_chain.Tip()->nChainWork;3513setBlockIndexCandidates.erase(pindex);3514pindex->nSequenceId = m_chainman.nBlockReverseSequenceId;3515if (m_chainman.nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {3516// We can't keep reducing the counter if somebody really wants to3517// call preciousblock 2**31-1 times on the same set of tips...3518m_chainman.nBlockReverseSequenceId--;3519}3520if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveNumChainTxs()) {3521setBlockIndexCandidates.insert(pindex);3522PruneBlockIndexCandidates();3523}3524}3526return ActivateBestChain(state, std::shared_ptr<const CBlock>());3527}3529bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* const pindex)3530{3531AssertLockNotHeld(m_chainstate_mutex);3532AssertLockNotHeld(::cs_main);3534// Genesis block can't be invalidated3535assert(pindex);3536if (pindex->nHeight == 0) return false;3538// We do not allow ActivateBestChain() to run while InvalidateBlock() is3539// running, as that could cause the tip to change while we disconnect3540// blocks.3541LOCK(m_chainstate_mutex);3543// We'll be acquiring and releasing cs_main below, to allow the validation3544// callbacks to run. However, we should keep the block index in a3545// consistent state as we disconnect blocks -- in particular we need to3546// add equal-work blocks to setBlockIndexCandidates as we disconnect.3547// To avoid walking the block index repeatedly in search of candidates,3548// build a map once so that we can look up candidate blocks by chain3549// work as we go.3550std::multimap<const arith_uint256, CBlockIndex*> highpow_outofchain_headers;3552{3553LOCK(cs_main);3554for (auto& entry : m_blockman.m_block_index) {3555CBlockIndex& candidate = entry.second;3556// We don't need to put anything in our active chain into the3557// multimap, because those candidates will be found and considered3558// as we disconnect.3559// Instead, consider only non-active-chain blocks that score3560// at least as good with CBlockIndexWorkComparator as the new tip.3561if (!m_chain.Contains(candidate) &&3562!CBlockIndexWorkComparator()(&candidate, pindex->pprev) &&3563!(candidate.nStatus & BLOCK_FAILED_VALID)) {3564highpow_outofchain_headers.insert({candidate.nChainWork, &candidate});3565}3566}3567}3569CBlockIndex* to_mark_failed = pindex;3570bool pindex_was_in_chain = false;3571int disconnected = 0;3573// Disconnect (descendants of) pindex, and mark them invalid.3574while (true) {3575if (m_chainman.m_interrupt) break;3577// Make sure the queue of validation callbacks doesn't grow unboundedly.3578if (m_chainman.m_options.signals) LimitValidationInterfaceQueue(*m_chainman.m_options.signals);3580LOCK(cs_main);3581// Lock for as long as disconnectpool is in scope to make sure MaybeUpdateMempoolForReorg is3582// called after DisconnectTip without unlocking in between3583LOCK(MempoolMutex());3584if (!m_chain.Contains(*pindex)) break;3585pindex_was_in_chain = true;3586CBlockIndex* const disconnected_tip{m_chain.Tip()};3588// ActivateBestChain considers blocks already in m_chain3589// unconditionally valid already, so force disconnect away from it.3590DisconnectedBlockTransactions disconnectpool{MAX_DISCONNECTED_TX_POOL_BYTES};3591bool ret = DisconnectTip(state, &disconnectpool);3592// DisconnectTip will add transactions to disconnectpool.3593// Adjust the mempool to be consistent with the new tip, adding3594// transactions back to the mempool if disconnecting was successful,3595// and we're not doing a very deep invalidation (in which case3596// keeping the mempool up to date is probably futile anyway).3597MaybeUpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);3598if (!ret) return false;3599CBlockIndex* new_tip{m_chain.Tip()};3600assert(disconnected_tip->pprev == new_tip);3602// We immediately mark the disconnected blocks as invalid.3603// This prevents a case where pruned nodes may fail to invalidateblock3604// and be left unable to start as they have no tip candidates (as there3605// are no blocks that meet the "have data and are not invalid per3606// nStatus" criteria for inclusion in setBlockIndexCandidates).3607disconnected_tip->nStatus |= BLOCK_FAILED_VALID;3608m_blockman.m_dirty_blockindex.insert(disconnected_tip);3609setBlockIndexCandidates.erase(disconnected_tip);3610setBlockIndexCandidates.insert(new_tip);3612// Mark out-of-chain descendants of the invalidated block as invalid3613// Add any equal or more work headers that are not invalidated to setBlockIndexCandidates3614// Recalculate m_best_header if it became invalid.3615auto candidate_it = highpow_outofchain_headers.lower_bound(new_tip->nChainWork);3617const bool best_header_needs_update{m_chainman.m_best_header->GetAncestor(disconnected_tip->nHeight) == disconnected_tip};3618if (best_header_needs_update) {3619// new_tip is definitely still valid at this point, but there may be better ones3620m_chainman.m_best_header = new_tip;3621}3623while (candidate_it != highpow_outofchain_headers.end()) {3624CBlockIndex* candidate{candidate_it->second};3625if (candidate->GetAncestor(disconnected_tip->nHeight) == disconnected_tip) {3626// Children of failed blocks are marked as BLOCK_FAILED_VALID.3627candidate->nStatus |= BLOCK_FAILED_VALID;3628m_blockman.m_dirty_blockindex.insert(candidate);3629// If invalidated, the block is irrelevant for setBlockIndexCandidates3630// and for m_best_header and can be removed from the cache.3631candidate_it = highpow_outofchain_headers.erase(candidate_it);3632continue;3633}3634if (!CBlockIndexWorkComparator()(candidate, new_tip) &&3635candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&3636candidate->HaveNumChainTxs()) {3637setBlockIndexCandidates.insert(candidate);3638// Do not remove candidate from the highpow_outofchain_headers cache, because it might be a descendant of the block being invalidated3639// which needs to be marked failed later.3640}3641if (best_header_needs_update &&3642m_chainman.m_best_header->nChainWork < candidate->nChainWork) {3643m_chainman.m_best_header = candidate;3644}3645++candidate_it;3646}3648// Track the last disconnected block to call InvalidChainFound on it.3649to_mark_failed = disconnected_tip;3650}3652m_chainman.CheckBlockIndex();3654{3655LOCK(cs_main);3656if (m_chain.Contains(*to_mark_failed)) {3657// If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.3658return false;3659}3661// Mark pindex as invalid if it never was in the main chain3662if (!pindex_was_in_chain && !(pindex->nStatus & BLOCK_FAILED_VALID)) {3663pindex->nStatus |= BLOCK_FAILED_VALID;3664m_blockman.m_dirty_blockindex.insert(pindex);3665setBlockIndexCandidates.erase(pindex);3666}3668// If any new blocks somehow arrived while we were disconnecting3669// (above), then the pre-calculation of what should go into3670// setBlockIndexCandidates may have missed entries. This would3671// technically be an inconsistency in the block index, but if we clean3672// it up here, this should be an essentially unobservable error.3673// Loop back over all block index entries and add any missing entries3674// to setBlockIndexCandidates.3675for (auto& [_, block_index] : m_blockman.m_block_index) {3676if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {3677setBlockIndexCandidates.insert(&block_index);3678}3679}3681InvalidChainFound(to_mark_failed);3682}3684// Only notify about a new block tip if the active chain was modified.3685if (pindex_was_in_chain) {3686// Ignoring return value for now, this could be changed to bubble up3687// kernel::Interrupted value to the caller so the caller could3688// distinguish between completed and interrupted operations. It might3689// also make sense for the blockTip notification to have an enum3690// parameter indicating the source of the tip change so hooks can3691// distinguish user-initiated invalidateblock changes from other3692// changes.3693(void)m_chainman.GetNotifications().blockTip(3694/*state=*/GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_blockfiles_indexed),3695/*index=*/*to_mark_failed->pprev,3696/*verification_progress=*/WITH_LOCK(m_chainman.GetMutex(), return m_chainman.GuessVerificationProgress(to_mark_failed->pprev)));3698// Fire ActiveTipChange now for the current chain tip to make sure clients are notified.3699// ActivateBestChain may call this as well, but not necessarily.3700if (m_chainman.m_options.signals) {3701m_chainman.m_options.signals->ActiveTipChange(*Assert(m_chain.Tip()), m_chainman.IsInitialBlockDownload());3702}3703}3704return true;3705}3707void Chainstate::SetBlockFailureFlags(CBlockIndex* invalid_block)3708{3709AssertLockHeld(cs_main);3711for (auto& [_, block_index] : m_blockman.m_block_index) {3712if (invalid_block != &block_index && block_index.GetAncestor(invalid_block->nHeight) == invalid_block) {3713block_index.nStatus |= BLOCK_FAILED_VALID;3714m_blockman.m_dirty_blockindex.insert(&block_index);3715}3716}3717}3719void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {3720AssertLockHeld(cs_main);3722int nHeight = pindex->nHeight;3724// Remove the invalidity flag from this block and all its descendants and ancestors.3725for (auto& [_, block_index] : m_blockman.m_block_index) {3726if ((block_index.nStatus & BLOCK_FAILED_VALID) && (block_index.GetAncestor(nHeight) == pindex || pindex->GetAncestor(block_index.nHeight) == &block_index)) {3727block_index.nStatus &= ~BLOCK_FAILED_VALID;3728m_blockman.m_dirty_blockindex.insert(&block_index);3729if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {3730setBlockIndexCandidates.insert(&block_index);3731}3732if (&block_index == m_chainman.m_best_invalid) {3733// Reset invalid block marker if it was pointing to one of those.3734m_chainman.m_best_invalid = nullptr;3735}3736}3737}3738}3740void Chainstate::TryAddBlockIndexCandidate(CBlockIndex* pindex)3741{3742AssertLockHeld(cs_main);3744// Do not continue building a chainstate that is based on an invalid3745// snapshot. This is a belt-and-suspenders type of check because if an3746// invalid snapshot is loaded, the node will shut down to force a manual3747// intervention. But it is good to handle this case correctly regardless.3748if (m_assumeutxo == Assumeutxo::INVALID) {3749return;3750}