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.
5501void PeerManagerImpl::ConsiderEviction(CNode& pto, Peer& peer, std::chrono::seconds time_in_seconds)5502{5503AssertLockHeld(cs_main);5505CNodeState &state = *State(pto.GetId());5507if (!state.m_chain_sync.m_protect && pto.IsOutboundOrBlockRelayConn() && state.fSyncStarted) {5508// This is an outbound peer subject to disconnection if they don't5509// announce a block with as much work as the current tip within5510// CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if5511// their chain has more work than ours, we should sync to it,5512// unless it's invalid, in which case we should find that out and5513// disconnect from them elsewhere).5514if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {5515// The outbound peer has sent us a block with at least as much work as our current tip, so reset the timeout if it was set5516if (state.m_chain_sync.m_timeout != 0s) {5517state.m_chain_sync.m_timeout = 0s;5518state.m_chain_sync.m_work_header = nullptr;5519state.m_chain_sync.m_sent_getheaders = false;5520}5521} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) {5522// At this point we know that the outbound peer has either never sent us a block/header or they have, but its tip is behind ours5523// AND5524// we are noticing this for the first time (m_timeout is 0)5525// OR we noticed this at some point within the last CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds and set a timeout5526// for them, they caught up to our tip at the time of setting the timer but not to our current one (we've also advanced).5527// Either way, set a new timeout based on our current tip.5528state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;5529state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();5530state.m_chain_sync.m_sent_getheaders = false;5531} else if (state.m_chain_sync.m_timeout > 0s && time_in_seconds > state.m_chain_sync.m_timeout) {5532// No evidence yet that our peer has synced to a chain with work equal to that5533// of our tip, when we first detected it was behind. Send a single getheaders5534// message to give the peer a chance to update us.5535if (state.m_chain_sync.m_sent_getheaders) {5536// They've run out of time to catch up!5537LogInfo("Outbound peer has old chain, best known block = %s, %s", state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", pto.DisconnectMsg());5538pto.fDisconnect = true;5539} else {5540assert(state.m_chain_sync.m_work_header);5541// Here, we assume that the getheaders message goes out,5542// because it'll either go out or be skipped because of a5543// getheaders in-flight already, in which case the peer should5544// still respond to us with a sufficiently high work chain tip.5545MaybeSendGetHeaders(pto,5546GetLocator(state.m_chain_sync.m_work_header->pprev),5547peer);5548LogDebug(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());5549state.m_chain_sync.m_sent_getheaders = true;5550// Bump the timeout to allow a response, which could clear the timeout5551// (if the response shows the peer has synced), reset the timeout (if5552// the peer syncs to the required work but not to our tip), or result5553// in disconnect (if we advance to the timeout and pindexBestKnownBlock5554// has not sufficiently progressed)5555state.m_chain_sync.m_timeout = time_in_seconds + HEADERS_RESPONSE_TIME;5556}5557}5558}5559}5561void PeerManagerImpl::EvictExtraOutboundPeers(NodeClock::time_point now)5562{5563// If we have any extra block-relay-only peers, disconnect the youngest unless5564// it's given us a block -- in which case, compare with the second-youngest, and5565// out of those two, disconnect the peer who least recently gave us a block.5566// The youngest block-relay-only peer would be the extra peer we connected5567// to temporarily in order to sync our tip; see net.cpp.5568// Note that we use higher nodeid as a measure for most recent connection.5569if (m_connman.GetExtraBlockRelayCount() > 0) {5570std::pair<NodeId, std::chrono::seconds> youngest_peer{-1, 0}, next_youngest_peer{-1, 0};5572m_connman.ForEachNode([&](CNode* pnode) {5573if (!pnode->IsBlockOnlyConn() || pnode->fDisconnect) return;5574if (pnode->GetId() > youngest_peer.first) {5575next_youngest_peer = youngest_peer;5576youngest_peer.first = pnode->GetId();5577youngest_peer.second = pnode->m_last_block_time;5578}5579});5580NodeId to_disconnect = youngest_peer.first;5581if (youngest_peer.second > next_youngest_peer.second) {5582// Our newest block-relay-only peer gave us a block more recently;5583// disconnect our second youngest.5584to_disconnect = next_youngest_peer.first;5585}5586m_connman.ForNode(to_disconnect, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {5587AssertLockHeld(::cs_main);5588// Make sure we're not getting a block right now, and that5589// we've been connected long enough for this eviction to happen5590// at all.5591// Note that we only request blocks from a peer if we learn of a5592// valid headers chain with at least as much work as our tip.5593CNodeState *node_state = State(pnode->GetId());5594if (node_state == nullptr ||5595(now - pnode->m_connected >= MINIMUM_CONNECT_TIME && node_state->vBlocksInFlight.empty())) {5596pnode->fDisconnect = true;5597LogDebug(BCLog::NET, "disconnecting extra block-relay-only peer=%d (last block received at time %d)\n",5598pnode->GetId(), count_seconds(pnode->m_last_block_time));5599return true;5600} else {5601LogDebug(BCLog::NET, "keeping block-relay-only peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n",5602pnode->GetId(), TicksSinceEpoch<std::chrono::seconds>(pnode->m_connected), node_state->vBlocksInFlight.size());5603}5604return false;5605});5606}5608// Check whether we have too many outbound-full-relay peers5609if (m_connman.GetExtraFullOutboundCount() > 0) {5610// If we have more outbound-full-relay peers than we target, disconnect one.5611// Pick the outbound-full-relay peer that least recently announced5612// us a new block, with ties broken by choosing the more recent5613// connection (higher node id)5614// Protect peers from eviction if we don't have another connection5615// to their network, counting both outbound-full-relay and manual peers.5616NodeId worst_peer = -1;5617int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();5619m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_connman.GetNodesMutex()) {5620AssertLockHeld(::cs_main);5622// Only consider outbound-full-relay peers that are not already5623// marked for disconnection5624if (!pnode->IsFullOutboundConn() || pnode->fDisconnect) return;5625CNodeState *state = State(pnode->GetId());5626if (state == nullptr) return; // shouldn't be possible, but just in case5627// Don't evict our protected peers5628if (state->m_chain_sync.m_protect) return;5629// If this is the only connection on a particular network that is5630// OUTBOUND_FULL_RELAY or MANUAL, protect it.5631if (!m_connman.MultipleManualOrFullOutboundConns(pnode->addr.GetNetwork())) return;5632if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {5633worst_peer = pnode->GetId();5634oldest_block_announcement = state->m_last_block_announcement;5635}5636});5637if (worst_peer != -1) {5638bool disconnected = m_connman.ForNode(worst_peer, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {5639AssertLockHeld(::cs_main);5641// Only disconnect a peer that has been connected to us for5642// some reasonable fraction of our check-frequency, to give5643// it time for new information to have arrived.5644// Also don't disconnect any peer we're trying to download a5645// block from.5646CNodeState &state = *State(pnode->GetId());5647if (now - pnode->m_connected > MINIMUM_CONNECT_TIME && state.vBlocksInFlight.empty()) {5648LogDebug(BCLog::NET, "disconnecting extra outbound peer=%d (last block announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement);5649pnode->fDisconnect = true;5650return true;5651} else {5652LogDebug(BCLog::NET, "keeping outbound peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n",5653pnode->GetId(), TicksSinceEpoch<std::chrono::seconds>(pnode->m_connected), state.vBlocksInFlight.size());5654return false;5655}5656});5657if (disconnected) {5658// If we disconnected an extra peer, that means we successfully5659// connected to at least one peer after the last time we5660// detected a stale tip. Don't try any more extra peers until5661// we next detect a stale tip, to limit the load we put on the5662// network from these extra connections.5663m_connman.SetTryNewOutboundPeer(false);5664}5665}5666}5667}5669void PeerManagerImpl::CheckForStaleTipAndEvictPeers()5670{5671LOCK(cs_main);5673const auto current_time{NodeClock::now()};5674auto now{GetTime<std::chrono::seconds>()};5676EvictExtraOutboundPeers(current_time);5678if (now > m_stale_tip_check_time) {5679// Check whether our tip is stale, and if so, allow using an extra5680// outbound peer5681if (!m_chainman.m_blockman.LoadingBlocks() && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) {5682LogInfo("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n",5683count_seconds(now - m_last_tip_update.load()));5684m_connman.SetTryNewOutboundPeer(true);5685} else if (m_connman.GetTryNewOutboundPeer()) {5686m_connman.SetTryNewOutboundPeer(false);5687}5688m_stale_tip_check_time = now + STALE_CHECK_INTERVAL;5689}5691if (!m_initial_sync_finished && CanDirectFetch()) {5692m_connman.StartExtraBlockRelayPeers();5693m_initial_sync_finished = true;5694}5695}5697void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, NodeClock::time_point now)5698{5699if (m_connman.ShouldRunInactivityChecks(node_to, now) &&5700peer.m_ping_nonce_sent &&5701now > peer.m_ping_start.load() + TIMEOUT_INTERVAL)5702{5703// The ping timeout is using mocktime. To disable the check during5704// testing, increase -peertimeout.5705LogDebug(BCLog::NET, "ping timeout: %fs, %s", Ticks<SecondsDouble>(now - peer.m_ping_start.load()), node_to.DisconnectMsg());5706node_to.fDisconnect = true;5707return;5708}5710bool pingSend = false;5712if (peer.m_ping_queued) {5713// RPC ping request by user5714pingSend = true;5715}5717if (peer.m_ping_nonce_sent == 0 && now > peer.m_ping_start.load() + PING_INTERVAL) {5718// Ping automatically sent as a latency probe & keepalive.5719pingSend = true;5720}5722if (pingSend) {5723uint64_t nonce;5724do {5725nonce = FastRandomContext().rand64();5726} while (nonce == 0);5727peer.m_ping_queued = false;5728peer.m_ping_start = now;5729if (node_to.GetCommonVersion() > BIP0031_VERSION) {5730peer.m_ping_nonce_sent = nonce;5731MakeAndPushMessage(node_to, NetMsgType::PING, nonce);5732} else {5733// Peer is too old to support ping message type with nonce, pong will never arrive.5734peer.m_ping_nonce_sent = 0;5735MakeAndPushMessage(node_to, NetMsgType::PING);5736}5737}5738}5740void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time)5741{5742// Nothing to do for non-address-relay peers5743if (!peer.m_addr_relay_enabled) return;5745LOCK(peer.m_addr_send_times_mutex);5746// Periodically advertise our local address to the peer.5747if (fListen && !m_chainman.IsInitialBlockDownload() &&5748peer.m_next_local_addr_send < current_time) {5749// If we've sent before, clear the bloom filter for the peer, so that our5750// self-announcement will actually go out.