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.
1501auto short_message_id = V2_MESSAGE_MAP(msg.m_type);1502if (short_message_id) {1503contents.resize(1 + msg.data.size());1504contents[0] = *short_message_id;1505std::copy(msg.data.begin(), msg.data.end(), contents.begin() + 1);1506} else {1507// Initialize with zeroes, and then write the message type string starting at offset 1.1508// This means contents[0] and the unused positions in contents[1..13] remain 0x00.1509contents.resize(1 + CMessageHeader::MESSAGE_TYPE_SIZE + msg.data.size(), 0);1510std::copy(msg.m_type.begin(), msg.m_type.end(), contents.data() + 1);1511std::copy(msg.data.begin(), msg.data.end(), contents.begin() + 1 + CMessageHeader::MESSAGE_TYPE_SIZE);1512}1513// Construct ciphertext in send buffer.1514m_send_buffer.resize(contents.size() + BIP324Cipher::EXPANSION);1515m_cipher.Encrypt(MakeByteSpan(contents), {}, false, MakeWritableByteSpan(m_send_buffer));1516m_send_type = msg.m_type;1517// Release memory1518ClearShrink(msg.data);1519return true;1520}1522Transport::BytesToSend V2Transport::GetBytesToSend(bool have_next_message) const noexcept1523{1524AssertLockNotHeld(m_send_mutex);1525LOCK(m_send_mutex);1526if (m_send_state == SendState::V1) return m_v1_fallback.GetBytesToSend(have_next_message);1528if (m_send_state == SendState::MAYBE_V1) Assume(m_send_buffer.empty());1529Assume(m_send_pos <= m_send_buffer.size());1530return {1531std::span{m_send_buffer}.subspan(m_send_pos),1532// We only have more to send after the current m_send_buffer if there is a (next)1533// message to be sent, and we're capable of sending packets. */1534have_next_message && m_send_state == SendState::READY,1535m_send_type1536};1537}1539void V2Transport::MarkBytesSent(size_t bytes_sent) noexcept1540{1541AssertLockNotHeld(m_send_mutex);1542LOCK(m_send_mutex);1543if (m_send_state == SendState::V1) return m_v1_fallback.MarkBytesSent(bytes_sent);1545if (m_send_state == SendState::AWAITING_KEY && m_send_pos == 0 && bytes_sent > 0) {1546LogDebug(BCLog::NET, "start sending v2 handshake to peer=%d\n", m_nodeid);1547}1549m_send_pos += bytes_sent;1550Assume(m_send_pos <= m_send_buffer.size());1551if (m_send_pos >= CMessageHeader::HEADER_SIZE) {1552m_sent_v1_header_worth = true;1553}1554// Wipe the buffer when everything is sent.1555if (m_send_pos == m_send_buffer.size()) {1556m_send_pos = 0;1557ClearShrink(m_send_buffer);1558}1559}1561bool V2Transport::ShouldReconnectV1() const noexcept1562{1563AssertLockNotHeld(m_send_mutex);1564AssertLockNotHeld(m_recv_mutex);1565// Only outgoing connections need reconnection.1566if (!m_initiating) return false;1568LOCK(m_recv_mutex);1569// We only reconnect in the very first state and when the receive buffer is empty. Together1570// these conditions imply nothing has been received so far.1571if (m_recv_state != RecvState::KEY) return false;1572if (!m_recv_buffer.empty()) return false;1573// Check if we've sent enough for the other side to disconnect us (if it was V1).1574LOCK(m_send_mutex);1575return m_sent_v1_header_worth;1576}1578size_t V2Transport::GetSendMemoryUsage() const noexcept1579{1580AssertLockNotHeld(m_send_mutex);1581LOCK(m_send_mutex);1582if (m_send_state == SendState::V1) return m_v1_fallback.GetSendMemoryUsage();1584return sizeof(m_send_buffer) + memusage::DynamicUsage(m_send_buffer);1585}1587Transport::Info V2Transport::GetInfo() const noexcept1588{1589AssertLockNotHeld(m_recv_mutex);1590LOCK(m_recv_mutex);1591if (m_recv_state == RecvState::V1) return m_v1_fallback.GetInfo();1593Transport::Info info;1595// Do not report v2 and session ID until the version packet has been received1596// and verified (confirming that the other side very likely has the same keys as us).1597if (m_recv_state != RecvState::KEY_MAYBE_V1 && m_recv_state != RecvState::KEY &&1598m_recv_state != RecvState::GARB_GARBTERM && m_recv_state != RecvState::VERSION) {1599info.transport_type = TransportProtocolType::V2;1600info.session_id = uint256(MakeUCharSpan(m_cipher.GetSessionID()));1601} else {1602info.transport_type = TransportProtocolType::DETECTING;1603}1605return info;1606}1608std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const1609{1610auto it = node.vSendMsg.begin();1611size_t nSentSize = 0;1612bool data_left{false}; //!< second return value (whether unsent data remains)1613std::optional<bool> expected_more;1615while (true) {1616if (it != node.vSendMsg.end()) {1617// If possible, move one message from the send queue to the transport. This fails when1618// there is an existing message still being sent, or (for v2 transports) when the1619// handshake has not yet completed.1620size_t memusage = it->GetMemoryUsage();1621if (node.m_transport->SetMessageToSend(*it)) {1622// Update memory usage of send buffer (as *it will be deleted).1623node.m_send_memusage -= memusage;1624++it;1625}1626}1627const auto& [data, more, msg_type] = node.m_transport->GetBytesToSend(it != node.vSendMsg.end());1628// We rely on the 'more' value returned by GetBytesToSend to correctly predict whether more1629// bytes are still to be sent, to correctly set the MSG_MORE flag. As a sanity check,1630// verify that the previously returned 'more' was correct.1631if (expected_more.has_value()) Assume(!data.empty() == *expected_more);1632expected_more = more;1633data_left = !data.empty(); // will be overwritten on next loop if all of data gets sent1634int nBytes = 0;1635if (!data.empty()) {1636LOCK(node.m_sock_mutex);1637// There is no socket in case we've already disconnected, or in test cases without1638// real connections. In these cases, we bail out immediately and just leave things1639// in the send queue and transport.1640if (!node.m_sock) {1641break;1642}1643int flags = MSG_NOSIGNAL | MSG_DONTWAIT;1644#ifdef MSG_MORE1645if (more) {1646flags |= MSG_MORE;1647}1648#endif1649nBytes = node.m_sock->Send(data.data(), data.size(), flags);1650}1651if (nBytes > 0) {1652node.m_last_send = NodeClock::now();1653node.nSendBytes += nBytes;1654// Notify transport that bytes have been processed.1655node.m_transport->MarkBytesSent(nBytes);1656// Update statistics per message type.1657if (!msg_type.empty()) { // don't report v2 handshake bytes for now1658node.AccountForSentBytes(msg_type, nBytes);1659}1660nSentSize += nBytes;1661if ((size_t)nBytes != data.size()) {1662// could not send full message; stop sending more1663break;1664}1665} else {1666if (nBytes < 0) {1667// error1668int nErr = WSAGetLastError();1669if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) {1670LogDebug(BCLog::NET, "socket send error, %s: %s", node.DisconnectMsg(), NetworkErrorString(nErr));1671node.CloseSocketDisconnect();1672}1673}1674break;1675}1676}1678const bool pause_send{node.m_send_memusage + node.m_transport->GetSendMemoryUsage() > nSendBufferMaxSize};1679if (pause_send && !node.fPauseSend) {1680node.m_pause_send_since = NodeClock::now();1681}1682node.fPauseSend = pause_send;1684if (it == node.vSendMsg.end()) {1685assert(node.m_send_memusage == 0);1686}1687node.vSendMsg.erase(node.vSendMsg.begin(), it);1688return {nSentSize, data_left};1689}1691/** Try to find an inbound connection to evict.1692* Extreme care must be taken to avoid opening the node to attacker1693* triggered network partitioning.1694* The strategy used here is to protect a small number of peers1695* for each of several distinct characteristics which are difficult1696* to forge. In order to partition a node the attacker must be1697* simultaneously better at all of them than honest peers.1698*/1699bool CConnman::AttemptToEvictConnection(bool evict_tx_relay_peer_only, std::optional<NodeId> protect_peer)1700{1701AssertLockNotHeld(m_nodes_mutex);1703std::vector<NodeEvictionCandidate> vEvictionCandidates;1704{1706LOCK(m_nodes_mutex);1707for (const CNode* node : m_nodes) {1708if (node->fDisconnect)1709continue;1710if (protect_peer.has_value() && node->GetId() == protect_peer) {1711continue;1712}1713if (evict_tx_relay_peer_only && !node->m_relays_txs) {1714continue;1715}1716NodeEvictionCandidate candidate{1717.id = node->GetId(),1718.m_connected = node->m_connected,1719.m_min_ping_time = node->m_min_ping_time,1720.m_last_block_time = node->m_last_block_time,1721.m_last_tx_time = node->m_last_tx_time,1722.fRelevantServices = node->m_has_all_wanted_services,1723.m_relay_txs = node->m_relays_txs.load(),1724.fBloomFilter = node->m_bloom_filter_loaded.load(),1725.nKeyedNetGroup = node->nKeyedNetGroup,1726.prefer_evict = node->m_prefer_evict,1727.m_is_local = node->addr.IsLocal(),1728.m_network = node->ConnectedThroughNetwork(),1729.m_noban = node->HasPermission(NetPermissionFlags::NoBan),1730.m_conn_type = node->m_conn_type,1731};1732vEvictionCandidates.push_back(candidate);1733}1734}1735const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));1736if (!node_id_to_evict) {1737return false;1738}1739LOCK(m_nodes_mutex);1740for (CNode* pnode : m_nodes) {1741if (pnode->GetId() == *node_id_to_evict) {1742LogDebug(BCLog::NET, "selected %s connection for eviction, %s", pnode->ConnectionTypeAsString(), pnode->DisconnectMsg());1743TRACEPOINT(net, evicted_inbound_connection,1744pnode->GetId(),1745pnode->m_addr_name.c_str(),1746pnode->ConnectionTypeAsString().c_str(),1747pnode->ConnectedThroughNetwork(),1748TicksSinceEpoch<std::chrono::seconds>(pnode->m_connected));1749pnode->fDisconnect = true;1750return true;