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.
2752// Or it might be from the most recent block2753{2754LOCK(m_most_recent_block_mutex);2755if (m_most_recent_block_txs != nullptr) {2756auto it = m_most_recent_block_txs->find(gtxid);2757if (it != m_most_recent_block_txs->end()) return it->second;2758}2759}2761return {};2762}2764void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)2765{2766AssertLockNotHeld(cs_main);2768auto tx_relay = peer.GetTxRelay();2770std::deque<CInv>::iterator it = peer.m_getdata_requests.begin();2771std::vector<CInv> vNotFound;2773// Process as many TX items from the front of the getdata queue as2774// possible, since they're common and it's efficient to batch process2775// them.2776while (it != peer.m_getdata_requests.end() && it->IsGenTxMsg()) {2777if (interruptMsgProc) return;2778// The send buffer provides backpressure. If there's no space in2779// the buffer, pause processing until the next call.2780if (pfrom.fPauseSend) break;2782const CInv &inv = *it++;2784if (tx_relay == nullptr) {2785// Ignore GETDATA requests for transactions from block-relay-only2786// peers and peers that asked us not to announce transactions.2787continue;2788}2790if (auto tx{FindTxForGetData(*tx_relay, ToGenTxid(inv))}) {2791// WTX and WITNESS_TX imply we serialize with witness2792const auto maybe_with_witness = (inv.IsMsgTx() ? TX_NO_WITNESS : TX_WITH_WITNESS);2793MakeAndPushMessage(pfrom, NetMsgType::TX, maybe_with_witness(*tx));2794m_mempool.RemoveUnbroadcastTx(tx->GetHash());2795} else {2796vNotFound.push_back(inv);2797}2798}2800// Only process one BLOCK item per call, since they're uncommon and can be2801// expensive to process.2802if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) {2803const CInv &inv = *it++;2804if (inv.IsGenBlkMsg()) {2805ProcessGetBlockData(pfrom, peer, inv);2806}2807// else: If the first item on the queue is an unknown type, we erase it2808// and continue processing the queue on the next call.2809// NOTE: previously we wouldn't do so and the peer sending us a malformed GETDATA could2810// result in never making progress and this thread using 100% allocated CPU. See2811// https://bitcoincore.org/en/2024/07/03/disclose-getdata-cpu.2812}2814peer.m_getdata_requests.erase(peer.m_getdata_requests.begin(), it);2816if (!vNotFound.empty()) {2817// Let the peer know that we didn't find what it asked for, so it doesn't2818// have to wait around forever.2819// SPV clients care about this message: it's needed when they are2820// recursively walking the dependencies of relevant unconfirmed2821// transactions. SPV clients want to do that because they want to know2822// about (and store and rebroadcast and risk analyze) the dependencies2823// of transactions relevant to them, without having to download the2824// entire memory pool.2825// Also, other nodes can use these messages to automatically request a2826// transaction from some other peer that announced it, and stop2827// waiting for us to respond.2828// In normal operation, we often send NOTFOUND messages for parents of2829// transactions that we relay; if a peer is missing a parent, they may2830// assume we have them and request the parents from us.2831MakeAndPushMessage(pfrom, NetMsgType::NOTFOUND, vNotFound);2832}2833}2835uint32_t PeerManagerImpl::GetFetchFlags(const Peer& peer) const2836{2837uint32_t nFetchFlags = 0;2838if (CanServeWitnesses(peer)) {2839nFetchFlags |= MSG_WITNESS_FLAG;2840}2841return nFetchFlags;2842}2844void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req)2845{2846BlockTransactions resp(req);2847for (size_t i = 0; i < req.indexes.size(); i++) {2848if (req.indexes[i] >= block.vtx.size()) {2849Misbehaving(peer, "getblocktxn with out-of-bounds tx indices");2850return;2851}2852resp.txn[i] = block.vtx[req.indexes[i]];2853}2855if (util::log::ShouldDebugLog(BCLog::CMPCTBLOCK)) {2856uint32_t tx_requested_size{0};2857for (const auto& tx : resp.txn) tx_requested_size += tx->ComputeTotalSize();2858LogDebug(BCLog::CMPCTBLOCK, "%s sent us a GETBLOCKTXN for block %s, sending a BLOCKTXN with %u txns. (%u bytes)", pfrom.LogPeer(), block.GetHash().ToString(), resp.txn.size(), tx_requested_size);2859}2860MakeAndPushMessage(pfrom, NetMsgType::BLOCKTXN, resp);2861}2863bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, Peer& peer)2864{2865// Do these headers have proof-of-work matching what's claimed?2866if (!HasValidProofOfWork(headers, m_chainparams.GetConsensus())) {2867Misbehaving(peer, "header with invalid proof of work");2868return false;2869}2871// Are these headers connected to each other?2872if (!CheckHeadersAreContinuous(headers)) {2873Misbehaving(peer, "non-continuous headers sequence");2874return false;2875}2876return true;2877}2879arith_uint256 PeerManagerImpl::GetAntiDoSWorkThreshold()2880{2881arith_uint256 near_chaintip_work = 0;2882LOCK(cs_main);2883if (m_chainman.ActiveChain().Tip() != nullptr) {2884const CBlockIndex *tip = m_chainman.ActiveChain().Tip();2885// Use a 144 block buffer, so that we'll accept headers that fork from2886// near our tip.2887near_chaintip_work = tip->nChainWork - std::min<arith_uint256>(144*GetBlockProof(*tip), tip->nChainWork);2888}2889return std::max(near_chaintip_work, m_chainman.MinimumChainWork());2890}2892/**2893* Special handling for unconnecting headers that might be part of a block2894* announcement.2895*2896* We'll send a getheaders message in response to try to connect the chain.2897*/2898void PeerManagerImpl::HandleUnconnectingHeaders(CNode& pfrom, Peer& peer,2899const std::vector<CBlockHeader>& headers)2900{2901// Try to fill in the missing headers.2902const CBlockIndex* best_header{WITH_LOCK(cs_main, return m_chainman.m_best_header)};2903if (MaybeSendGetHeaders(pfrom, GetLocator(best_header), peer)) {2904LogDebug(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d)\n",2905headers[0].GetHash().ToString(),2906headers[0].hashPrevBlock.ToString(),2907best_header->nHeight,2908pfrom.GetId());2909}2911// Set hashLastUnknownBlock for this peer, so that if we2912// eventually get the headers - even from a different peer -2913// we can use this peer to download.2914WITH_LOCK(cs_main, UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash()));2915}2917bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const2918{2919uint256 hashLastBlock;2920for (const CBlockHeader& header : headers) {2921if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {2922return false;2923}2924hashLastBlock = header.GetHash();2925}2926return true;2927}2929bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers)2930{2931if (peer.m_headers_sync) {2932auto result = peer.m_headers_sync->ProcessNextHeaders(headers, headers.size() == m_opts.max_headers_result);2933// If it is a valid continuation, we should treat the existing getheaders request as responded to.2934if (result.success) peer.m_last_getheaders_timestamp = {};2935if (result.request_more) {2936auto locator = peer.m_headers_sync->NextHeadersRequestLocator();2937// If we were instructed to ask for a locator, it should not be empty.2938Assume(!locator.vHave.empty());2939// We can only be instructed to request more if processing was successful.2940Assume(result.success);2941if (!locator.vHave.empty()) {2942// It should be impossible for the getheaders request to fail,2943// because we just cleared the last getheaders timestamp.2944bool sent_getheaders = MaybeSendGetHeaders(pfrom, locator, peer);2945Assume(sent_getheaders);2946LogDebug(BCLog::NET, "more getheaders (from %s) to peer=%d\n",2947locator.vHave.front().ToString(), pfrom.GetId());2948}2949}2951if (peer.m_headers_sync->GetState() == HeadersSyncState::State::FINAL) {2952peer.m_headers_sync.reset(nullptr);2954// Delete this peer's entry in m_headers_presync_stats.2955// If this is m_headers_presync_bestpeer, it will be replaced later2956// by the next peer that triggers the else{} branch below.2957LOCK(m_headers_presync_mutex);2958m_headers_presync_stats.erase(pfrom.GetId());2959} else {2960// Build statistics for this peer's sync.2961HeadersPresyncStats stats;2962stats.first = peer.m_headers_sync->GetPresyncWork();2963if (peer.m_headers_sync->GetState() == HeadersSyncState::State::PRESYNC) {2964stats.second = {peer.m_headers_sync->GetPresyncHeight(),2965peer.m_headers_sync->GetPresyncTime()};2966}2968// Update statistics in stats.2969LOCK(m_headers_presync_mutex);2970m_headers_presync_stats[pfrom.GetId()] = stats;2971auto best_it = m_headers_presync_stats.find(m_headers_presync_bestpeer);2972bool best_updated = false;2973if (best_it == m_headers_presync_stats.end()) {2974// If the cached best peer is outdated, iterate over all remaining ones (including2975// newly updated one) to find the best one.2976NodeId peer_best{-1};2977const HeadersPresyncStats* stat_best{nullptr};2978for (const auto& [peer, stat] : m_headers_presync_stats) {2979if (!stat_best || stat > *stat_best) {2980peer_best = peer;2981stat_best = &stat;2982}2983}2984m_headers_presync_bestpeer = peer_best;2985best_updated = (peer_best == pfrom.GetId());2986} else if (best_it->first == pfrom.GetId() || stats > best_it->second) {2987// pfrom was and remains the best peer, or pfrom just became best.2988m_headers_presync_bestpeer = pfrom.GetId();2989best_updated = true;2990}2991if (best_updated && stats.second.has_value()) {2992// If the best peer updated, and it is in its first phase, signal.2993m_headers_presync_should_signal = true;2994}2995}2997if (result.success) {2998// We only overwrite the headers passed in if processing was2999// successful.3000headers.swap(result.pow_validated_headers);