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.
2501case PrivateBroadcast::AddResult::Added:2502LogDebug(BCLog::PRIVBROADCAST, "Requesting %d new connections due to %s", PrivateBroadcast::INITIAL_CONNECTION_COUNT, txstr);2503m_connman.m_private_broadcast.NumToOpenAdd(PrivateBroadcast::INITIAL_CONNECTION_COUNT);2504return node::TransactionError::OK;2505case PrivateBroadcast::AddResult::AlreadyPresent:2506LogDebug(BCLog::PRIVBROADCAST, "Ignoring unnecessary request to schedule an already scheduled transaction: %s", txstr);2507return node::TransactionError::OK;2508case PrivateBroadcast::AddResult::QueueFull:2509LogDebug(BCLog::PRIVBROADCAST, "Rejecting private broadcast, queue full (cap=%u): %s", PrivateBroadcast::MAX_TRANSACTIONS, txstr);2510return node::TransactionError::PRIVATE_BROADCAST_FULL;2511} // no default case, so the compiler can warn about missing cases2512assert(false);2513}2515void PeerManagerImpl::RelayAddress(NodeId originator,2516const CAddress& addr,2517bool fReachable)2518{2519// We choose the same nodes within a given 24h window (if the list of connected2520// nodes does not change) and we don't relay to nodes that already know an2521// address. So within 24h we will likely relay a given address once. This is to2522// prevent a peer from unjustly giving their address better propagation by sending2523// it to us repeatedly.2525if (!fReachable && !addr.IsRelayable()) return;2527// Relay to a limited number of other nodes2528// Use deterministic randomness to send to the same nodes for 24 hours2529// at a time so the m_addr_knowns of the chosen nodes prevent repeats2530const uint64_t hash_addr{CServiceHash(0, 0)(addr)};2531const auto current_time{GetTime<std::chrono::seconds>()};2532// Adding address hash makes exact rotation time different per address, while preserving periodicity.2533const uint64_t time_addr{(static_cast<uint64_t>(count_seconds(current_time)) + hash_addr) / count_seconds(ROTATE_ADDR_RELAY_DEST_INTERVAL)};2534const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY)2535.Write(hash_addr)2536.Write(time_addr)};2538// Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers.2539unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1;2541std::array<std::pair<uint64_t, Peer*>, 2> best{{{0, nullptr}, {0, nullptr}}};2542assert(nRelayNodes <= best.size());2544LOCK(m_peer_mutex);2546for (auto& [id, peer] : m_peer_map) {2547if (peer->m_addr_relay_enabled && id != originator && IsAddrCompatible(*peer, addr)) {2548uint64_t hashKey = CSipHasher(hasher).Write(id).Finalize();2549for (unsigned int i = 0; i < nRelayNodes; i++) {2550if (hashKey > best[i].first) {2551std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);2552best[i] = std::make_pair(hashKey, peer.get());2553break;2554}2555}2556}2557};2559for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {2560PushAddress(*best[i].second, addr);2561}2562}2564void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv)2565{2566// First perform the stateless checks:2567// A filtered-block can only ever be requested if we offer NODE_BLOOM2568if (inv.IsMsgFilteredBlk() && !(peer.m_our_services & NODE_BLOOM)) {2569LogDebug(BCLog::NET, "filtered block request received when NODE_BLOOM service disabled, %s", pfrom.DisconnectMsg());2570pfrom.fDisconnect = true;2571return;2572}2574std::shared_ptr<const CBlock> a_recent_block;2575std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;2576{2577LOCK(m_most_recent_block_mutex);2578a_recent_block = m_most_recent_block;2579a_recent_compact_block = m_most_recent_compact_block;2580}2582bool need_activate_chain = false;2583{2584LOCK(cs_main);2585const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);2586if (pindex) {2587if (pindex->HaveNumChainTxs() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&2588pindex->IsValid(BLOCK_VALID_TREE)) {2589// If we have the block and all of its parents, but have not yet validated it,2590// we might be in the middle of connecting it (ie in the unlock of cs_main2591// before ActivateBestChain but after AcceptBlock).2592// In this case, we need to run ActivateBestChain prior to checking the relay2593// conditions below.2594need_activate_chain = true;2595}2596}2597} // release cs_main before calling ActivateBestChain2598if (need_activate_chain) {2599BlockValidationState state;2600if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) {2601LogDebug(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());2602}2603}2605const CBlockIndex* pindex{nullptr};2606const CBlockIndex* tip{nullptr};2607bool can_direct_fetch{false};2608FlatFilePos block_pos{};2609{2610LOCK(cs_main);2611pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);2612if (!pindex) {2613return;2614}2615if (!BlockRequestAllowed(*pindex)) {2616LogDebug(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());2617return;2618}2619// disconnect node in case we have reached the outbound limit for serving historical blocks2620if (m_connman.OutboundTargetReached(true) &&2621(((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&2622!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target2623) {2624LogDebug(BCLog::NET, "historical block serving limit reached, %s", pfrom.DisconnectMsg());2625pfrom.fDisconnect = true;2626return;2627}2628tip = m_chainman.ActiveChain().Tip();2629// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold2630if (!pfrom.HasPermission(NetPermissionFlags::NoBan) && (2631(((peer.m_our_services & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((peer.m_our_services & NODE_NETWORK) != NODE_NETWORK) && (tip->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )2632)) {2633LogDebug(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, %s", pfrom.DisconnectMsg());2634//disconnect node and prevent it from stalling (would otherwise wait for the missing block)2635pfrom.fDisconnect = true;2636return;2637}2638// Pruned nodes may have deleted the block, so check whether2639// it's available before trying to send.2640if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {2641return;2642}2643can_direct_fetch = CanDirectFetch();2644block_pos = pindex->GetBlockPos();2645}2647std::shared_ptr<const CBlock> pblock;2648if (a_recent_block && a_recent_block->GetHash() == inv.hash) {2649pblock = a_recent_block;2650} else if (inv.IsMsgWitnessBlk()) {2651// Fast-path: in this case it is possible to serve the block directly from disk,2652// as the network format matches the format on disk2653if (const auto block_data{m_chainman.m_blockman.ReadRawBlock(block_pos)}) {2654MakeAndPushMessage(pfrom, NetMsgType::BLOCK, std::span{*block_data});2655} else {2656if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {2657LogDebug(BCLog::NET, "Block was pruned before it could be read, %s", pfrom.DisconnectMsg());2658} else {2659LogError("Cannot load block from disk, %s", pfrom.DisconnectMsg());2660}2661pfrom.fDisconnect = true;2662return;2663}2664// Don't set pblock as we've sent the block2665} else {2666// Send block from disk2667std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();2668if (!m_chainman.m_blockman.ReadBlock(*pblockRead, block_pos, inv.hash)) {2669if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {2670LogDebug(BCLog::NET, "Block was pruned before it could be read, %s", pfrom.DisconnectMsg());2671} else {2672LogError("Cannot load block from disk, %s", pfrom.DisconnectMsg());2673}2674pfrom.fDisconnect = true;2675return;2676}2677pblock = pblockRead;2678}2679if (pblock) {2680if (inv.IsMsgBlk()) {2681MakeAndPushMessage(pfrom, NetMsgType::BLOCK, TX_NO_WITNESS(*pblock));2682} else if (inv.IsMsgWitnessBlk()) {2683MakeAndPushMessage(pfrom, NetMsgType::BLOCK, TX_WITH_WITNESS(*pblock));2684} else if (inv.IsMsgFilteredBlk()) {2685bool sendMerkleBlock = false;2686CMerkleBlock merkleBlock;2687if (auto tx_relay = peer.GetTxRelay(); tx_relay != nullptr) {2688LOCK(tx_relay->m_bloom_filter_mutex);2689if (tx_relay->m_bloom_filter) {2690sendMerkleBlock = true;2691merkleBlock = CMerkleBlock(*pblock, *tx_relay->m_bloom_filter);2692}2693}2694if (sendMerkleBlock) {2695MakeAndPushMessage(pfrom, NetMsgType::MERKLEBLOCK, merkleBlock);2696// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see2697// This avoids hurting performance by pointlessly requiring a round-trip2698// Note that there is currently no way for a node to request any single transactions we didn't send here -2699// they must either disconnect and retry or request the full block.2700// Thus, the protocol spec specified allows for us to provide duplicate txn here,2701// however we MUST always provide at least what the remote peer needs2702for (const auto& [tx_idx, _] : merkleBlock.vMatchedTxn)2703MakeAndPushMessage(pfrom, NetMsgType::TX, TX_NO_WITNESS(*pblock->vtx[tx_idx]));2704}2705// else2706// no response2707} else if (inv.IsMsgCmpctBlk()) {2708// If a peer is asking for old blocks, we're almost guaranteed2709// they won't have a useful mempool to match against a compact block,2710// and we don't feel like constructing the object for them, so2711// instead we respond with the full, non-compact block.2712if (can_direct_fetch && pindex->nHeight >= tip->nHeight - MAX_CMPCTBLOCK_DEPTH) {2713if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == inv.hash) {2714MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);2715} else {2716CBlockHeaderAndShortTxIDs cmpctblock{*pblock, m_rng.rand64()};2717MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, cmpctblock);2718}2719} else {2720MakeAndPushMessage(pfrom, NetMsgType::BLOCK, TX_WITH_WITNESS(*pblock));2721}2722}2723}2725{2726LOCK(peer.m_block_inv_mutex);2727// Trigger the peer node to send a getblocks request for the next batch of inventory2728if (inv.hash == peer.m_continuation_block) {2729// Send immediately. This must send even if redundant,2730// and we want it right after the last block so they don't2731// wait for other stuff first.2732std::vector<CInv> vInv;2733vInv.emplace_back(MSG_BLOCK, tip->GetBlockHash());2734MakeAndPushMessage(pfrom, NetMsgType::INV, vInv);2735peer.m_continuation_block.SetNull();2736}2737}2738}2740CTransactionRef PeerManagerImpl::FindTxForGetData(const Peer::TxRelay& tx_relay, const GenTxid& gtxid)2741{2742// If a tx was in the mempool prior to the last INV for this peer, permit the request.2743auto txinfo{std::visit(2744[&](const auto& id) {2745return m_mempool.info_for_relay(id, WITH_LOCK(tx_relay.m_tx_inventory_mutex, return tx_relay.m_last_inv_sequence));2746},2747gtxid)};2748if (txinfo.tx) {2749return std::move(txinfo.tx);2750}