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.
3751AssertLockNotHeld(m_reconnections_mutex);3753if (fAddressesInitialized) {3754DumpAddresses();3755fAddressesInitialized = false;3757if (m_use_addrman_outgoing) {3758// Anchor connections are only dumped during clean shutdown.3759std::vector<CAddress> anchors_to_dump = GetCurrentBlockRelayOnlyConns();3760if (anchors_to_dump.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {3761anchors_to_dump.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);3762}3763DumpAnchors(gArgs.GetDataDirNet() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);3764}3765}3767// Delete peer connections.3768std::vector<CNode*> nodes;3769WITH_LOCK(m_nodes_mutex, nodes.swap(m_nodes));3770for (CNode* pnode : nodes) {3771LogDebug(BCLog::NET, "Stopping node, %s", pnode->DisconnectMsg());3772pnode->CloseSocketDisconnect();3773DeleteNode(pnode);3774}3776for (CNode* pnode : m_nodes_disconnected) {3777DeleteNode(pnode);3778}3779m_nodes_disconnected.clear();3780WITH_LOCK(m_reconnections_mutex, m_reconnections.clear());3781vhListenSocket.clear();3782semOutbound.reset();3783semAddnode.reset();3784}3786void CConnman::DeleteNode(CNode* pnode)3787{3788assert(pnode);3789m_msgproc->FinalizeNode(*pnode);3790delete pnode;3791}3793CConnman::~CConnman()3794{3795Interrupt();3796Stop();3797}3799std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const3800{3801std::vector<CAddress> addresses = addrman.get().GetAddr(max_addresses, max_pct, network, filtered);3802if (m_banman) {3803addresses.erase(std::remove_if(addresses.begin(), addresses.end(),3804[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),3805addresses.end());3806}3807return addresses;3808}3810std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)3811{3812uint64_t network_id = requestor.m_network_key;3813const auto current_time = GetTime<std::chrono::microseconds>();3814auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});3815CachedAddrResponse& cache_entry = r.first->second;3816if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.3817cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);3818// Choosing a proper cache lifetime is a trade-off between the privacy leak minimization3819// and the usefulness of ADDR responses to honest users.3820//3821// Longer cache lifetime makes it more difficult for an attacker to scrape3822// enough AddrMan data to maliciously infer something useful.3823// By the time an attacker scraped enough AddrMan records, most of3824// the records should be old enough to not leak topology info by3825// e.g. analyzing real-time changes in timestamps.3826//3827// It takes only several hundred requests to scrape everything from an AddrMan containing 100,000 nodes,3828// so ~24 hours of cache lifetime indeed makes the data less inferable by the time3829// most of it could be scraped (considering that timestamps are updated via3830// ADDR self-announcements and when nodes communicate).3831// We also should be robust to those attacks which may not require scraping *full* victim's AddrMan3832// (because even several timestamps of the same handful of nodes may leak privacy).3833//3834// On the other hand, longer cache lifetime makes ADDR responses3835// outdated and less useful for an honest requestor, e.g. if most nodes3836// in the ADDR response are no longer active.3837//3838// However, the churn in the network is known to be rather low. Since we consider3839// nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days,3840// max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference3841// in terms of the freshness of the response.3842cache_entry.m_cache_entry_expiration = current_time +384321h + FastRandomContext().randrange<std::chrono::microseconds>(6h);3844}3845return cache_entry.m_addrs_response_cache;3846}3848bool CConnman::AddNode(const AddedNodeParams& add)3849{3850const CService resolved(LookupNumeric(add.m_added_node, GetDefaultPort(add.m_added_node)));3851const bool resolved_is_valid{resolved.IsValid()};3853LOCK(m_added_nodes_mutex);3854for (const auto& it : m_added_node_params) {3855if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))) return false;3856}3858m_added_node_params.push_back(add);3859return true;3860}3862bool CConnman::RemoveAddedNode(std::string_view node)3863{3864LOCK(m_added_nodes_mutex);3865for (auto it = m_added_node_params.begin(); it != m_added_node_params.end(); ++it) {3866if (node == it->m_added_node) {3867m_added_node_params.erase(it);3868return true;3869}3870}3871return false;3872}3874bool CConnman::AddedNodesContain(const CAddress& addr) const3875{3876AssertLockNotHeld(m_added_nodes_mutex);3877const std::string addr_str{addr.ToStringAddr()};3878const std::string addr_port_str{addr.ToStringAddrPort()};3879LOCK(m_added_nodes_mutex);3880return (m_added_node_params.size() < 24 // bound the query to a reasonable limit3881&& std::any_of(m_added_node_params.cbegin(), m_added_node_params.cend(),3882[&](const auto& p) { return p.m_added_node == addr_str || p.m_added_node == addr_port_str; }));3883}3885size_t CConnman::GetNodeCount(ConnectionDirection flags) const3886{3887LOCK(m_nodes_mutex);3888if (flags == ConnectionDirection::Both) // Shortcut if we want total3889return m_nodes.size();3891int nNum = 0;3892for (const auto& pnode : m_nodes) {3893if (flags & (pnode->IsInboundConn() ? ConnectionDirection::In : ConnectionDirection::Out)) {3894nNum++;3895}3896}3898return nNum;3899}3902std::map<CNetAddr, LocalServiceInfo> CConnman::getNetLocalAddresses() const3903{3904LOCK(g_maplocalhost_mutex);3905return mapLocalHost;3906}3908uint32_t CConnman::GetMappedAS(const CNetAddr& addr) const3909{3910return m_netgroupman.GetMappedAS(addr);3911}3913void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const3914{3915AssertLockNotHeld(m_nodes_mutex);3917vstats.clear();3918LOCK(m_nodes_mutex);3919vstats.reserve(m_nodes.size());3920for (CNode* pnode : m_nodes) {3921vstats.emplace_back();3922pnode->CopyStats(vstats.back());3923vstats.back().m_mapped_as = GetMappedAS(pnode->addr);3924}3925}3927bool CConnman::DisconnectNode(std::string_view strNode)3928{3929LOCK(m_nodes_mutex);3930auto it = std::ranges::find_if(m_nodes, [&strNode](CNode* node) { return node->m_addr_name == strNode; });3931if (it != m_nodes.end()) {3932CNode* node{*it};3933LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), node->DisconnectMsg());3934node->fDisconnect = true;3935return true;3936}3937return false;3938}3940bool CConnman::DisconnectNode(const CSubNet& subnet, bool disconnect_private_broadcast)3941{3942AssertLockNotHeld(m_nodes_mutex);3943bool disconnected = false;3944LOCK(m_nodes_mutex);3945for (CNode* pnode : m_nodes) {3946if (subnet.Match(pnode->addr) && (disconnect_private_broadcast || !pnode->IsPrivateBroadcastConn())) {3947LogDebug(BCLog::NET, "disconnect by subnet%s match, %s", (fLogIPs ? strprintf("=%s", subnet.ToString()) : ""), pnode->DisconnectMsg());3948pnode->fDisconnect = true;3949disconnected = true;3950}3951}3952return disconnected;3953}3955bool CConnman::DisconnectNode(const CNetAddr& addr)3956{3957AssertLockNotHeld(m_nodes_mutex);3958return DisconnectNode(CSubNet(addr));3959}3961bool CConnman::DisconnectNode(NodeId id)3962{3963LOCK(m_nodes_mutex);3964for(CNode* pnode : m_nodes) {3965if (id == pnode->GetId()) {3966LogDebug(BCLog::NET, "disconnect by id, %s", pnode->DisconnectMsg());3967pnode->fDisconnect = true;3968return true;3969}3970}3971return false;3972}3974void CConnman::RecordBytesRecv(uint64_t bytes)3975{3976nTotalBytesRecv += bytes;3977}3979void CConnman::RecordBytesSent(uint64_t bytes)3980{3981AssertLockNotHeld(m_total_bytes_sent_mutex);3982LOCK(m_total_bytes_sent_mutex);3984nTotalBytesSent += bytes;3986const auto now = GetTime<std::chrono::seconds>();3987if (nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME < now)3988{3989// timeframe expired, reset cycle3990nMaxOutboundCycleStartTime = now;3991nMaxOutboundTotalBytesSentInCycle = 0;3992}3994nMaxOutboundTotalBytesSentInCycle += bytes;3995}3997uint64_t CConnman::GetMaxOutboundTarget() const3998{3999AssertLockNotHeld(m_total_bytes_sent_mutex);4000LOCK(m_total_bytes_sent_mutex);