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.
4001return nMaxOutboundLimit;4002}4004std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const4005{4006return MAX_UPLOAD_TIMEFRAME;4007}4009std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const4010{4011AssertLockNotHeld(m_total_bytes_sent_mutex);4012LOCK(m_total_bytes_sent_mutex);4013return GetMaxOutboundTimeLeftInCycle_();4014}4016std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle_() const4017{4018AssertLockHeld(m_total_bytes_sent_mutex);4020if (nMaxOutboundLimit == 0)4021return 0s;4023if (nMaxOutboundCycleStartTime.count() == 0)4024return MAX_UPLOAD_TIMEFRAME;4026const std::chrono::seconds cycleEndTime = nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME;4027const auto now = GetTime<std::chrono::seconds>();4028return (cycleEndTime < now) ? 0s : cycleEndTime - now;4029}4031bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const4032{4033AssertLockNotHeld(m_total_bytes_sent_mutex);4034LOCK(m_total_bytes_sent_mutex);4035if (nMaxOutboundLimit == 0)4036return false;4038if (historicalBlockServingLimit)4039{4040// keep a large enough buffer to at least relay each block once4041const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle_();4042const uint64_t buffer = timeLeftInCycle / std::chrono::minutes{10} * MAX_BLOCK_SERIALIZED_SIZE;4043if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer)4044return true;4045}4046else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit)4047return true;4049return false;4050}4052uint64_t CConnman::GetOutboundTargetBytesLeft() const4053{4054AssertLockNotHeld(m_total_bytes_sent_mutex);4055LOCK(m_total_bytes_sent_mutex);4056if (nMaxOutboundLimit == 0)4057return 0;4059return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle;4060}4062uint64_t CConnman::GetTotalBytesRecv() const4063{4064return nTotalBytesRecv;4065}4067uint64_t CConnman::GetTotalBytesSent() const4068{4069AssertLockNotHeld(m_total_bytes_sent_mutex);4070LOCK(m_total_bytes_sent_mutex);4071return nTotalBytesSent;4072}4074ServiceFlags CConnman::GetLocalServices() const4075{4076return m_local_services;4077}4079static std::unique_ptr<Transport> MakeTransport(NodeId id, bool use_v2transport, bool inbound) noexcept4080{4081if (use_v2transport) {4082return std::make_unique<V2Transport>(id, /*initiating=*/!inbound);4083} else {4084return std::make_unique<V1Transport>(id);4085}4086}4088CNode::CNode(NodeId idIn,4089std::shared_ptr<Sock> sock,4090const CAddress& addrIn,4091uint64_t nKeyedNetGroupIn,4092uint64_t nLocalHostNonceIn,4093const CService& addrBindIn,4094const std::string& addrNameIn,4095ConnectionType conn_type_in,4096bool inbound_onion,4097uint64_t network_key,4098CNodeOptions&& node_opts)4099: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},4100m_permission_flags{node_opts.permission_flags},4101m_sock{sock},4102m_connected{NodeClock::now()},4103m_proxy_override{std::move(node_opts.proxy_override)},4104addr{addrIn},4105addrBind{addrBindIn},4106m_addr_name{addrNameIn.empty() ? addr.ToStringAddrPort() : addrNameIn},4107m_dest(addrNameIn),4108m_inbound_onion{inbound_onion},4109m_prefer_evict{node_opts.prefer_evict},4110nKeyedNetGroup{nKeyedNetGroupIn},4111m_network_key{network_key},4112m_conn_type{conn_type_in},4113id{idIn},4114nLocalHostNonce{nLocalHostNonceIn},4115m_recv_flood_size{node_opts.recv_flood_size},4116m_i2p_sam_session{std::move(node_opts.i2p_sam_session)}4117{4118if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);4120for (const auto& msg : ALL_NET_MESSAGE_TYPES) {4121mapRecvBytesPerMsgType[msg] = 0;4122}4123mapRecvBytesPerMsgType[NET_MESSAGE_TYPE_OTHER] = 0;4125if (fLogIPs) {4126LogDebug(BCLog::NET, "Added connection to %s peer=%d\n", m_addr_name, id);4127} else {4128LogDebug(BCLog::NET, "Added connection peer=%d\n", id);4129}4130}4132void CNode::MarkReceivedMsgsForProcessing()4133{4134AssertLockNotHeld(m_msg_process_queue_mutex);4136size_t nSizeAdded = 0;4137for (const auto& msg : vRecvMsg) {4138// vRecvMsg contains only completed CNetMessage4139// the single possible partially deserialized message are held by TransportDeserializer4140nSizeAdded += msg.GetMemoryUsage();4141}4143LOCK(m_msg_process_queue_mutex);4144m_msg_process_queue.splice(m_msg_process_queue.end(), vRecvMsg);4145m_msg_process_queue_size += nSizeAdded;4146fPauseRecv = m_msg_process_queue_size > m_recv_flood_size;4147}4149std::optional<std::pair<CNetMessage, bool>> CNode::PollMessage()4150{4151LOCK(m_msg_process_queue_mutex);4152if (m_msg_process_queue.empty()) return std::nullopt;4154std::list<CNetMessage> msgs;4155// Just take one message4156msgs.splice(msgs.begin(), m_msg_process_queue, m_msg_process_queue.begin());4157m_msg_process_queue_size -= msgs.front().GetMemoryUsage();4158fPauseRecv = m_msg_process_queue_size > m_recv_flood_size;4160return std::make_pair(std::move(msgs.front()), !m_msg_process_queue.empty());4161}4163bool CConnman::NodeFullyConnected(const CNode* pnode)4164{4165return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect;4166}4168/// Private broadcast connections only need to send certain message types.4169/// Other messages are not needed and may degrade privacy.4170static bool IsOutboundMessageAllowedInPrivateBroadcast(std::string_view type) noexcept4171{4172return type == NetMsgType::VERSION ||4173type == NetMsgType::VERACK ||4174type == NetMsgType::INV ||4175type == NetMsgType::TX ||4176type == NetMsgType::PING;4177}4179void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)4180{4181AssertLockNotHeld(m_total_bytes_sent_mutex);4183if (pnode->IsPrivateBroadcastConn() && !IsOutboundMessageAllowedInPrivateBroadcast(msg.m_type)) {4184LogDebug(BCLog::PRIVBROADCAST, "Omitting send of message '%s', %s", msg.m_type, pnode->LogPeer());4185return;4186}4188if (!m_private_broadcast.m_outbound_tor_ok_at_least_once.load() && !pnode->IsInboundConn() &&4189pnode->addr.IsTor() && msg.m_type == NetMsgType::VERACK) {4190// If we are sending the peer VERACK that means we successfully sent4191// and received another message to/from that peer (VERSION).4192m_private_broadcast.m_outbound_tor_ok_at_least_once.store(true);4193}4195size_t nMessageSize = msg.data.size();4196LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());4197if (m_capture_messages) {4198CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);4199}4201TRACEPOINT(net, outbound_message,4202pnode->GetId(),4203pnode->m_addr_name.c_str(),4204pnode->ConnectionTypeAsString().c_str(),4205msg.m_type.c_str(),4206msg.data.size(),4207msg.data.data()4208);4210size_t nBytesSent = 0;4211{4212LOCK(pnode->cs_vSend);4213// Check if the transport still has unsent bytes, and indicate to it that we're about to4214// give it a message to send.4215const auto& [to_send, more, _msg_type] =4216pnode->m_transport->GetBytesToSend(/*have_next_message=*/true);4217const bool queue_was_empty{to_send.empty() && pnode->vSendMsg.empty()};4219// Update memory usage of send buffer.4220pnode->m_send_memusage += msg.GetMemoryUsage();4221if (pnode->m_send_memusage + pnode->m_transport->GetSendMemoryUsage() > nSendBufferMaxSize && !pnode->fPauseSend) {4222pnode->m_pause_send_since = NodeClock::now();4223pnode->fPauseSend = true;4224}4225// Move message to vSendMsg queue.4226pnode->vSendMsg.push_back(std::move(msg));4228// If there was nothing to send before, and there is now (predicted by the "more" value4229// returned by the GetBytesToSend call above), attempt "optimistic write":4230// because the poll/select loop may pause for SELECT_TIMEOUT_MILLISECONDS before actually4231// doing a send, try sending from the calling thread if the queue was empty before.4232// With a V1Transport, more will always be true here, because adding a message always4233// results in sendable bytes there, but with V2Transport this is not the case (it may4234// still be in the handshake).4235if (queue_was_empty && more) {4236std::tie(nBytesSent, std::ignore) = SocketSendData(*pnode);4237}4238}4239if (nBytesSent) RecordBytesSent(nBytesSent);4240}4242bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)4243{4244AssertLockNotHeld(m_nodes_mutex);4246CNode* found = nullptr;4247LOCK(m_nodes_mutex);4248for (auto&& pnode : m_nodes) {4249if(pnode->GetId() == id) {4250found = pnode;