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.
2251{2252auto most_recent_block_txs = std::make_unique<std::map<GenTxid, CTransactionRef>>();2253for (const auto& tx : pblock->vtx) {2254most_recent_block_txs->emplace(tx->GetHash(), tx);2255most_recent_block_txs->emplace(tx->GetWitnessHash(), tx);2256}2258LOCK(m_most_recent_block_mutex);2259m_most_recent_block_hash = hashBlock;2260m_most_recent_block = pblock;2261m_most_recent_compact_block = pcmpctblock;2262m_most_recent_block_txs = std::move(most_recent_block_txs);2263}2265m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {2266AssertLockHeld(::cs_main);2268if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)2269return;2270ProcessBlockAvailability(pnode->GetId());2271CNodeState &state = *State(pnode->GetId());2272// If the peer has, or we announced to them the previous block already,2273// but we don't think they have this one, go ahead and announce it2274if (state.m_requested_hb_cmpctblocks && !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {2276LogDebug(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock",2277hashBlock.ToString(), pnode->GetId());2279const CSerializedNetMsg& ser_cmpctblock{lazy_ser.get()};2280PushMessage(*pnode, ser_cmpctblock.Copy());2281state.pindexBestHeaderSent = pindex;2282}2283});2284}2286/**2287* Update our best height and announce any block hashes which weren't previously2288* in m_chainman.ActiveChain() to our peers.2289*/2290void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)2291{2292SetBestBlock(pindexNew->nHeight, std::chrono::seconds{pindexNew->GetBlockTime()});2294// Don't relay inventory during initial block download.2295if (fInitialDownload) return;2297// Find the hashes of all blocks that weren't previously in the best chain.2298std::vector<uint256> vHashes;2299const CBlockIndex *pindexToAnnounce = pindexNew;2300while (pindexToAnnounce != pindexFork) {2301vHashes.push_back(pindexToAnnounce->GetBlockHash());2302pindexToAnnounce = pindexToAnnounce->pprev;2303if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {2304// Limit announcements in case of a huge reorganization.2305// Rely on the peer's synchronization mechanism in that case.2306break;2307}2308}2310{2311LOCK(m_peer_mutex);2312for (auto& it : m_peer_map) {2313Peer& peer = *it.second;2314LOCK(peer.m_block_inv_mutex);2315for (const uint256& hash : vHashes | std::views::reverse) {2316peer.m_blocks_for_headers_relay.push_back(hash);2317}2318}2319}2321m_connman.WakeMessageHandler();2322}2324/**2325* Handle invalid block rejection and consequent peer discouragement, maintain which2326* peers announce compact blocks.2327*/2328void PeerManagerImpl::BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state)2329{2330LOCK(cs_main);2332const uint256 hash(block->GetHash());2333std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);2335// If the block failed validation, we know where it came from and we're still connected2336// to that peer, maybe punish.2337if (state.IsInvalid() &&2338it != mapBlockSource.end() &&2339State(it->second.first)) {2340MaybePunishNodeForBlock(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);2341}2342// Check that:2343// 1. The block is valid2344// 2. We're not in initial block download2345// 3. This is currently the best block we're aware of. We haven't updated2346// the tip yet so we have no way to check this directly here. Instead we2347// just check that there are currently no other blocks in flight.2348else if (state.IsValid() &&2349!m_chainman.IsInitialBlockDownload() &&2350mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {2351if (it != mapBlockSource.end()) {2352MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first);2353}2354}2355if (it != mapBlockSource.end())2356mapBlockSource.erase(it);2357}2359//////////////////////////////////////////////////////////////////////////////2360//2361// Messages2362//2364bool PeerManagerImpl::AlreadyHaveBlock(const uint256& block_hash)2365{2366return m_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;2367}2369void PeerManagerImpl::SendPings()2370{2371LOCK(m_peer_mutex);2372for(auto& it : m_peer_map) it.second->m_ping_queued = true;2373}2375std::vector<Wtxid> InvToSendBucket::TakeForProcessing(CTxMemPool& mempool)2376{2377AssertLockHeld(mempool.cs);2379size_t n_to_take = static_cast<size_t>(std::max<double>(count_bucket.value() - count_floor, 0));2381std::vector<Wtxid> best;2383auto itervec = mempool.ExtractBestByMiningScoreWithTopology(backlog, n_to_take);2384bool tokens_left = true;2385for (auto txiter : itervec) {2386auto& wtxid = txiter->GetTx().GetWitnessHash();2387if (tokens_left) {2388best.push_back(wtxid);2389if (!decrement(txiter->GetTx().ComputeTotalSize())) {2390tokens_left = false;2391}2392} else {2393backlog.push_back(wtxid);2394}2395}2397// if the backlog is now empty, consider shrinking it if it's oversized2398if (backlog.empty() && backlog.capacity() > INVENTORY_BUCKET_BACKLOG_CAPACITY) {2399std::vector<Wtxid> dummy;2400dummy.reserve(INVENTORY_BUCKET_BACKLOG_CAPACITY);2401dummy.swap(backlog);2402}2404return best;2405}2407void PeerManagerImpl::ProcessInvBacklog(NodeClock::time_point now, bool backlog_bumped)2408{2409// Don't run the body of this function unless it's been a little2410// while since the last run, or we just added a new tx to the backlog.2411if (!backlog_bumped && now <= m_next_inv_bucket_check.load()) return;2412m_next_inv_bucket_check = now + INVENTORY_BUCKET_CHECK_DELAY;2414LOCK(m_inv_to_send_mutex);2415m_inbound_inv_bucket.increment(now);2416m_outbound_inv_bucket.increment(now);2418// Regular heartbeat logging when there's a backlog2419if (!m_next_inv_bucket_heartbeat.has_value()) {2420if (m_inbound_inv_bucket.backlog.size() >= INVENTORY_BUCKET_BACKLOG_HEARTBEAT_MIN || m_outbound_inv_bucket.backlog.size() >= INVENTORY_BUCKET_BACKLOG_HEARTBEAT_MIN) {2421m_next_inv_bucket_heartbeat = now;2422}2423}2424if (m_next_inv_bucket_heartbeat.has_value() && now >= *m_next_inv_bucket_heartbeat) {2425LogDebug(BCLog::NET, "Transaction rate-limiting backlog inbound=%d itok=%.1f isz=%.1f outbound=%d otok=%.1f osz=%.1f",2426m_inbound_inv_bucket.backlog.size(),2427m_inbound_inv_bucket.count_bucket.value(),2428m_inbound_inv_bucket.size_bucket.value(),2429m_outbound_inv_bucket.backlog.size(),2430m_outbound_inv_bucket.count_bucket.value(),2431m_outbound_inv_bucket.size_bucket.value());2432if (m_inbound_inv_bucket.backlog.empty() && m_outbound_inv_bucket.backlog.empty()) {2433m_next_inv_bucket_heartbeat = std::nullopt;2434} else {2435m_next_inv_bucket_heartbeat = now + INVENTORY_BUCKET_BACKLOG_HEARTBEAT;2436}2437}2439// Early exit to skip pointlessly touching mempool lock2440bool in_avail = m_inbound_inv_bucket.avail();2441bool out_avail = m_outbound_inv_bucket.avail();2442if (!in_avail && !out_avail) return;2444std::vector<Wtxid> for_inbound;2445std::vector<Wtxid> for_outbound;2447{2448LOCK(m_mempool.cs);2449if (in_avail) for_inbound = m_inbound_inv_bucket.TakeForProcessing(m_mempool);2450if (out_avail) for_outbound = m_outbound_inv_bucket.TakeForProcessing(m_mempool);2451}2453if (!for_inbound.empty() || !for_outbound.empty()) {2454bool any_inbound_connected = false;2455bool any_outbound_connected = false;2456for (const PeerRef& peer_ref : GetAllPeers()) {2457if (!peer_ref) continue;2458Peer& peer{*peer_ref};2459auto tx_relay = peer.GetTxRelay();2460if (!tx_relay) continue;2462LOCK(tx_relay->m_tx_inventory_mutex);2463// Only queue transactions for announcement once the version handshake2464// is completed. The time of arrival for these transactions is2465// otherwise at risk of leaking to a spy, if the spy is able to2466// distinguish transactions received during the handshake from the rest2467// in the announcement.2468if (tx_relay->m_next_inv_send_time == 0s) continue;2469if (peer.m_is_inbound) {2470any_inbound_connected = true;2471} else {2472any_outbound_connected = true;2473}2474for (auto& i : (peer.m_is_inbound ? for_inbound : for_outbound)) {2475tx_relay->m_tx_inventory_to_send.push_back(i);2476}2477}2479// if the node has no in/outbound connections, clear the corresponding backlog entirely2480// this reduces wasted memory, and avoids having the bucket artificially empty for when2481// future peers do connect.2482if (!any_inbound_connected) m_inbound_inv_bucket.backlog.clear();2483if (!any_outbound_connected) m_outbound_inv_bucket.backlog.clear();2484}2485}2487void PeerManagerImpl::InitiateTxBroadcastToAll(const Wtxid& wtxid)2488{2489{2490LOCK(m_inv_to_send_mutex);2491m_inbound_inv_bucket.backlog.push_back(wtxid);2492m_outbound_inv_bucket.backlog.push_back(wtxid);2493}2494ProcessInvBacklog(NodeClock::now(), /*backlog_bumped=*/true);2495}2497node::TransactionError PeerManagerImpl::InitiateTxBroadcastPrivate(const CTransactionRef& tx)2498{2499const auto txstr{strprintf("txid=%s, wtxid=%s", tx->GetHash().ToString(), tx->GetWitnessHash().ToString())};2500switch (m_tx_for_private_broadcast.Add(tx)) {