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.
3001}3003return result.success;3004}3005// Either we didn't have a sync in progress, or something went wrong3006// processing these headers, or we are returning headers to the caller to3007// process.3008return false;3009}3011bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex& chain_start_header, std::vector<CBlockHeader>& headers)3012{3013// Calculate the claimed total work on this chain.3014arith_uint256 total_work = chain_start_header.nChainWork + CalculateClaimedHeadersWork(headers);3016// Our dynamic anti-DoS threshold (minimum work required on a headers chain3017// before we'll store it)3018arith_uint256 minimum_chain_work = GetAntiDoSWorkThreshold();3020// Avoid DoS via low-difficulty-headers by only processing if the headers3021// are part of a chain with sufficient work.3022if (total_work < minimum_chain_work) {3023// Only try to sync with this peer if their headers message was full;3024// otherwise they don't have more headers after this so no point in3025// trying to sync their too-little-work chain.3026if (headers.size() == m_opts.max_headers_result) {3027// Note: we could advance to the last header in this set that is3028// known to us, rather than starting at the first header (which we3029// may already have); however this is unlikely to matter much since3030// ProcessHeadersMessage() already handles the case where all3031// headers in a received message are already known and are3032// ancestors of m_best_header or chainActive.Tip(), by skipping3033// this logic in that case. So even if the first header in this set3034// of headers is known, some header in this set must be new, so3035// advancing to the first unknown header would be a small effect.3036LOCK(peer.m_headers_sync_mutex);3037try {3038peer.m_headers_sync.reset(new HeadersSyncState(peer.m_id, m_chainparams.GetConsensus(),3039m_chainparams.HeadersSync(), chain_start_header, minimum_chain_work));3040} catch (const HeadersSyncState::SystemClockError& e) {3041// The chain state loading logic performs an earlier check to3042// verify that the tip of the locally stored chain is <=3043// system clock + MAX_FUTURE_BLOCK_TIME.3044// But if we have no pre-existing chain state we might get here.3045const auto msg{strprintf("Failure when attempting to initiate headers sync: %s", e.what())};3046std::cerr << msg << std::endl;3047LogError("%s", msg);3048std::abort();3049}3051// Now a HeadersSyncState object for tracking this synchronization3052// is created, process the headers using it as normal. Failures are3053// handled inside of IsContinuationOfLowWorkHeadersSync.3054(void)IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers);3055} else {3056LogDebug(BCLog::NET, "Ignoring low-work chain (height=%u) from peer=%d\n", chain_start_header.nHeight + headers.size(), pfrom.GetId());3057}3059// The peer has not yet given us a chain that meets our work threshold,3060// so we want to prevent further processing of the headers in any case.3061headers = {};3062return true;3063}3065return false;3066}3068bool PeerManagerImpl::IsAncestorOfBestHeaderOrTip(const CBlockIndex* header)3069{3070if (header == nullptr) {3071return false;3072} else if (m_chainman.m_best_header != nullptr && header == m_chainman.m_best_header->GetAncestor(header->nHeight)) {3073return true;3074} else if (m_chainman.ActiveChain().Contains(*header)) {3075return true;3076}3077return false;3078}3080bool PeerManagerImpl::MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer)3081{3082const auto current_time = NodeClock::now();3084// Only allow a new getheaders message to go out if we don't have a recent3085// one already in-flight3086if (current_time - peer.m_last_getheaders_timestamp > HEADERS_RESPONSE_TIME) {3087MakeAndPushMessage(pfrom, NetMsgType::GETHEADERS, locator, uint256());3088peer.m_last_getheaders_timestamp = current_time;3089return true;3090}3091return false;3092}3094/*3095* Given a new headers tip ending in last_header, potentially request blocks towards that tip.3096* We require that the given tip have at least as much work as our tip, and for3097* our current tip to be "close to synced" (see CanDirectFetch()).3098*/3099void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex& last_header)3100{3101LOCK(cs_main);3102CNodeState *nodestate = State(pfrom.GetId());3104if (CanDirectFetch() && last_header.IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= last_header.nChainWork) {3105std::vector<const CBlockIndex*> vToFetch;3106const CBlockIndex* pindexWalk{&last_header};3107// Calculate all the blocks we'd need to switch to last_header, up to a limit.3108while (pindexWalk && !m_chainman.ActiveChain().Contains(*pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {3109if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&3110!IsBlockRequested(pindexWalk->GetBlockHash()) &&3111(!DeploymentActiveAt(*pindexWalk, m_chainman, Consensus::DEPLOYMENT_SEGWIT) || CanServeWitnesses(peer))) {3112// We don't have this block, and it's not yet in flight.3113vToFetch.push_back(pindexWalk);3114}3115pindexWalk = pindexWalk->pprev;3116}3117// If pindexWalk still isn't on our main chain, we're looking at a3118// very large reorg at a time we think we're close to caught up to3119// the main chain -- this shouldn't really happen. Bail out on the3120// direct fetch and rely on parallel download instead.3121// Common ancestor must exist (genesis).3122if (!m_chainman.ActiveChain().Contains(*Assert(pindexWalk))) {3123LogDebug(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",3124last_header.GetBlockHash().ToString(),3125last_header.nHeight);3126} else {3127std::vector<CInv> vGetData;3128// Download as much as possible, from earliest to latest.3129for (const CBlockIndex* pindex : vToFetch | std::views::reverse) {3130if (nodestate->vBlocksInFlight.size() >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {3131// Can't download any more from this peer3132break;3133}3134uint32_t nFetchFlags = GetFetchFlags(peer);3135vGetData.emplace_back(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash());3136BlockRequested(pfrom.GetId(), *pindex);3137LogDebug(BCLog::NET, "Requesting block %s from peer=%d",3138pindex->GetBlockHash().ToString(), pfrom.GetId());3139}3140if (vGetData.size() > 1) {3141LogDebug(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",3142last_header.GetBlockHash().ToString(),3143last_header.nHeight);3144}3145if (vGetData.size() > 0) {3146if (!m_opts.ignore_incoming_txs &&3147nodestate->m_provides_cmpctblocks &&3148vGetData.size() == 1 &&3149mapBlocksInFlight.size() == 1 &&3150last_header.pprev->IsValid(BLOCK_VALID_CHAIN)) {3151// In any case, we want to download using a compact block, not a regular one3152vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);3153}3154MakeAndPushMessage(pfrom, NetMsgType::GETDATA, vGetData);3155}3156}3157}3158}3160/**3161* Given receipt of headers from a peer ending in last_header, along with3162* whether that header was new and whether the headers message was full,3163* update the state we keep for the peer.3164*/3165void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,3166const CBlockIndex& last_header, bool received_new_header, bool may_have_more_headers)3167{3168LOCK(cs_main);3169CNodeState *nodestate = State(pfrom.GetId());3171UpdateBlockAvailability(pfrom.GetId(), last_header.GetBlockHash());3173// From here, pindexBestKnownBlock should be guaranteed to be non-null,3174// because it is set in UpdateBlockAvailability. Some nullptr checks3175// are still present, however, as belt-and-suspenders.3177if (received_new_header && last_header.nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {3178nodestate->m_last_block_announcement = GetTime();3179}3181// If we're in IBD, we want outbound peers that will serve us a useful3182// chain. Disconnect peers that are on chains with insufficient work.3183if (m_chainman.IsInitialBlockDownload() && !may_have_more_headers) {3184// If the peer has no more headers to give us, then we know we have3185// their tip.3186if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < m_chainman.MinimumChainWork()) {3187// This peer has too little work on their headers chain to help3188// us sync -- disconnect if it is an outbound disconnection3189// candidate.3190// Note: We compare their tip to the minimum chain work (rather than3191// m_chainman.ActiveChain().Tip()) because we won't start block download3192// until we have a headers chain that has at least3193// the minimum chain work, even if a peer has a chain past our tip,3194// as an anti-DoS measure.3195if (pfrom.IsOutboundOrBlockRelayConn()) {3196LogInfo("outbound peer headers chain has insufficient work, %s", pfrom.DisconnectMsg());3197pfrom.fDisconnect = true;3198}3199}3200}3202// If this is an outbound full-relay peer, check to see if we should protect3203// it from the bad/lagging chain logic.3204// Note that outbound block-relay peers are excluded from this protection, and3205// thus always subject to eviction under the bad/lagging chain logic.3206// See ChainSyncTimeoutState.3207if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {3208if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {3209LogDebug(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());3210nodestate->m_chain_sync.m_protect = true;3211++m_outbound_peers_with_protect_from_disconnect;3212}3213}3214}3216void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,3217std::vector<CBlockHeader>&& headers,3218bool via_compact_block)3219{3220size_t nCount = headers.size();3222if (nCount == 0) {3223// Nothing interesting. Stop asking this peers for more headers.3224// If we were in the middle of headers sync, receiving an empty headers3225// message suggests that the peer suddenly has nothing to give us3226// (perhaps it reorged to our chain). Clear download state for this peer.3227LOCK(peer.m_headers_sync_mutex);3228if (peer.m_headers_sync) {3229peer.m_headers_sync.reset(nullptr);3230LOCK(m_headers_presync_mutex);3231m_headers_presync_stats.erase(pfrom.GetId());3232}3233// A headers message with no headers cannot be an announcement, so assume3234// it is a response to our last getheaders request, if there is one.3235peer.m_last_getheaders_timestamp = {};3236return;3237}3239// Before we do any processing, make sure these pass basic sanity checks.3240// We'll rely on headers having valid proof-of-work further down, as an3241// anti-DoS criteria (note: this check is required before passing any3242// headers into HeadersSyncState).3243if (!CheckHeadersPoW(headers, peer)) {3244// Misbehaving() calls are handled within CheckHeadersPoW(), so we can3245// just return. (Note that even if a header is announced via compact3246// block, the header itself should be valid, so this type of error can3247// always be punished.)3248return;3249}