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.
6251if (m_most_recent_block_hash == pBestIndex->GetBlockHash()) {6252cached_cmpctblock_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, *m_most_recent_compact_block);6253}6254}6255if (cached_cmpctblock_msg.has_value()) {6256PushMessage(node, std::move(cached_cmpctblock_msg.value()));6257} else {6258CBlock block;6259const bool ret{m_chainman.m_blockman.ReadBlock(block, *pBestIndex)};6260assert(ret);6261CBlockHeaderAndShortTxIDs cmpctblock{block, m_rng.rand64()};6262MakeAndPushMessage(node, NetMsgType::CMPCTBLOCK, cmpctblock);6263}6264state.pindexBestHeaderSent = pBestIndex;6265} else if (peer.m_prefers_headers) {6266if (vHeaders.size() > 1) {6267LogDebug(BCLog::NET, "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,6268vHeaders.size(),6269vHeaders.front().GetHash().ToString(),6270vHeaders.back().GetHash().ToString(), node.GetId());6271} else {6272LogDebug(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__,6273vHeaders.front().GetHash().ToString(), node.GetId());6274}6275MakeAndPushMessage(node, NetMsgType::HEADERS, TX_WITH_WITNESS(vHeaders));6276state.pindexBestHeaderSent = pBestIndex;6277} else6278fRevertToInv = true;6279}6280if (fRevertToInv) {6281// If falling back to using an inv, just try to inv the tip.6282// The last entry in m_blocks_for_headers_relay was our tip at some point6283// in the past.6284if (!peer.m_blocks_for_headers_relay.empty()) {6285const uint256& hashToAnnounce = peer.m_blocks_for_headers_relay.back();6286const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);6287assert(pindex);6289// Warn if we're announcing a block that is not on the main chain.6290// This should be very rare and could be optimized out.6291// Just log for now.6292if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) {6293LogDebug(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n",6294hashToAnnounce.ToString(), m_chainman.ActiveChain().Tip()->GetBlockHash().ToString());6295}6297// If the peer's chain has this block, don't inv it back.6298if (!PeerHasHeader(&state, pindex)) {6299peer.m_blocks_for_inv_relay.push_back(hashToAnnounce);6300LogDebug(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__,6301node.GetId(), hashToAnnounce.ToString());6302}6303}6304}6305peer.m_blocks_for_headers_relay.clear();6306}6308//6309// Message: inventory6310//6311std::vector<CInv> vInv;6312{6313LOCK(peer.m_block_inv_mutex);6314vInv.reserve(peer.m_blocks_for_inv_relay.size());6316// Add blocks6317for (const uint256& hash : peer.m_blocks_for_inv_relay) {6318vInv.emplace_back(MSG_BLOCK, hash);6319if (vInv.size() == MAX_INV_SZ) {6320MakeAndPushMessage(node, NetMsgType::INV, vInv);6321vInv.clear();6322}6323}6324peer.m_blocks_for_inv_relay.clear();6325}6327if (auto tx_relay = peer.GetTxRelay(); tx_relay != nullptr) {6328LOCK(tx_relay->m_tx_inventory_mutex);6329// Check whether periodic sends should happen6330bool fSendTrickle = node.HasPermission(NetPermissionFlags::NoBan);6331if (tx_relay->m_next_inv_send_time < current_time) {6332fSendTrickle = true;6333if (node.IsInboundConn()) {6334tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL, node.m_network_key);6335} else {6336tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);6337}6338}6340// Time to send but the peer has requested we not relay transactions.6341if (fSendTrickle) {6342LOCK(tx_relay->m_bloom_filter_mutex);6343if (!tx_relay->m_relay_txs) tx_relay->m_tx_inventory_to_send.clear();6344}6346// Respond to BIP35 mempool requests6347if (fSendTrickle && tx_relay->m_send_mempool) {6348auto vtxinfo = m_mempool.infoAll();6350// Ensure we'll respond to GETDATA requests for anything we're about to announce6351tx_relay->m_last_inv_sequence = WITH_LOCK(m_mempool.cs, return m_mempool.GetSequence());6353tx_relay->m_send_mempool = false;6354const CFeeRate filterrate{tx_relay->m_fee_filter_received.load()};6356// we'll send everything in the mempool momentarily, so this is redundant6357tx_relay->m_tx_inventory_to_send.clear();6359LOCK(tx_relay->m_bloom_filter_mutex);6361for (const auto& txinfo : vtxinfo) {6362const Txid& txid{txinfo.tx->GetHash()};6363const Wtxid& wtxid{txinfo.tx->GetWitnessHash()};6364const auto inv = peer.m_wtxid_relay ?6365CInv{MSG_WTX, wtxid.ToUint256()} :6366CInv{MSG_TX, txid.ToUint256()};6368// Don't send transactions that peers will not put into their mempool6369if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {6370continue;6371}6372if (tx_relay->m_bloom_filter) {6373if (!tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;6374}6375tx_relay->m_tx_inventory_known_filter.insert(inv.hash);6376vInv.push_back(inv);6377if (vInv.size() == MAX_INV_SZ) {6378MakeAndPushMessage(node, NetMsgType::INV, vInv);6379vInv.clear();6380}6381}6382}6384// Determine transactions to relay6385if (fSendTrickle) {6386// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.6387// (sorted from higher priority to lowest, skipping low fee)6388const CFeeRate filterrate{tx_relay->m_fee_filter_received.load()};6390auto inv_tx = [&]() EXCLUSIVE_LOCKS_REQUIRED(tx_relay->m_tx_inventory_mutex) {6391auto& invs = tx_relay->m_tx_inventory_to_send;6392std::vector<CTransactionRef> res;6394if (invs.size() == 0) return res;6396// if previous allocations were excessive, shrink to the current size6397if (invs.capacity() > 2 * invs.size()) invs.shrink_to_fit();6399LOCK(m_mempool.cs);6400auto txiters = m_mempool.ExtractBestByMiningScoreWithTopology(invs, invs.size());6401res.reserve(txiters.size());6402for (auto txiter : txiters) {6403if (txiter->GetFee() < filterrate.GetFee(txiter->GetTxSize())) {6404continue; // higher feerate CPFP txs may follow, so just skip, don't stop6405}6406res.push_back(txiter->GetSharedTx());6407}6408// Ensure we'll respond to GETDATA requests for anything we're about to announce6409tx_relay->m_last_inv_sequence = m_mempool.GetSequence();6410return res;6411}();6413LOCK(tx_relay->m_bloom_filter_mutex);6414vInv.reserve(std::min<size_t>(MAX_INV_SZ, vInv.size() + inv_tx.size()));6415for (auto& tx : inv_tx) {6416// `TxRelay::m_tx_inventory_known_filter` contains either txids or wtxids6417// depending on whether our peer supports wtxid-relay. Therefore, first6418// construct the inv and then use its hash for the filter check.6419const auto inv = peer.m_wtxid_relay ?6420CInv{MSG_WTX, tx->GetWitnessHash().ToUint256()} :6421CInv{MSG_TX, tx->GetHash().ToUint256()};6422// Check if not in the filter already6423if (tx_relay->m_tx_inventory_known_filter.contains(inv.hash)) {6424continue;6425}6426if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*tx)) continue;6427// Send6428vInv.push_back(inv);6429if (vInv.size() == MAX_INV_SZ) {6430MakeAndPushMessage(node, NetMsgType::INV, vInv);6431vInv.clear();6432}6433tx_relay->m_tx_inventory_known_filter.insert(inv.hash);6434}6435}6436}6437if (!vInv.empty())6438MakeAndPushMessage(node, NetMsgType::INV, vInv);6440// Detect whether we're stalling6441auto stalling_timeout = m_block_stalling_timeout.load();6442if (state.m_stalling_since.count() && state.m_stalling_since < current_time - stalling_timeout) {6443// Stalling only triggers when the block download window cannot move. During normal steady state,6444// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection6445// should only happen during initial block download.6446LogInfo("Peer is stalling block download, %s", node.DisconnectMsg());6447node.fDisconnect = true;6448// Increase timeout for the next peer so that we don't disconnect multiple peers if our own6449// bandwidth is insufficient.6450const auto new_timeout = std::min(2 * stalling_timeout, BLOCK_STALLING_TIMEOUT_MAX);6451if (stalling_timeout != new_timeout && m_block_stalling_timeout.compare_exchange_strong(stalling_timeout, new_timeout)) {6452LogDebug(BCLog::NET, "Increased stalling timeout temporarily to %d seconds\n", count_seconds(new_timeout));6453}6454return true;6455}6456// In case there is a block that has been in flight from this peer for block_interval * (1 + 0.5 * N)6457// (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.6458// We compensate for other peers to prevent killing off peers due to our own downstream link6459// being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes6460// to unreasonably increase our timeout.6461if (state.vBlocksInFlight.size() > 0) {6462QueuedBlock &queuedBlock = state.vBlocksInFlight.front();6463int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;6464if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {6465LogInfo("Timeout downloading block %s, %s", queuedBlock.pindex->GetBlockHash().ToString(), node.DisconnectMsg());6466node.fDisconnect = true;6467return true;6468}6469}6470// Check for headers sync timeouts6471if (state.fSyncStarted && peer.m_headers_sync_timeout < std::chrono::microseconds::max()) {6472// Detect whether this is a stalling initial-headers-sync peer6473if (m_chainman.m_best_header->Time() <= NodeClock::now() - 24h) {6474if (current_time > peer.m_headers_sync_timeout && nSyncStarted == 1 && (m_num_preferred_download_peers - state.fPreferredDownload >= 1)) {6475// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,6476// and we have others we could be using instead.6477// Note: If all our peers are inbound, then we won't6478// disconnect our sync peer for stalling; we have bigger6479// problems if we can't get any outbound peers.6480if (!node.HasPermission(NetPermissionFlags::NoBan)) {6481LogInfo("Timeout downloading headers, %s", node.DisconnectMsg());6482node.fDisconnect = true;6483return true;6484} else {6485LogInfo("Timeout downloading headers from noban peer, not %s", node.DisconnectMsg());6486// Reset the headers sync state so that we have a6487// chance to try downloading from a different peer.6488// Note: this will also result in at least one more6489// getheaders message to be sent to6490// this peer (eventually).6491state.fSyncStarted = false;6492nSyncStarted--;6493peer.m_headers_sync_timeout = 0us;6494}6495}6496} else {6497// After we've caught up once, reset the timeout so we can't trigger6498// disconnect later.6499peer.m_headers_sync_timeout = std::chrono::microseconds::max();6500}