Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: Merge bitcoin#22423, 22096 #5884

Merged
merged 1 commit into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/net.cpp
Expand Up @@ -1296,16 +1296,29 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,

bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
{
if (conn_type != ConnectionType::OUTBOUND_FULL_RELAY && conn_type != ConnectionType::BLOCK_RELAY) return false;

const int max_connections = conn_type == ConnectionType::OUTBOUND_FULL_RELAY ? m_max_outbound_full_relay : m_max_outbound_block_relay;
std::optional<int> max_connections;
switch (conn_type) {
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
case ConnectionType::FEELER:
return false;
case ConnectionType::OUTBOUND_FULL_RELAY:
max_connections = m_max_outbound_full_relay;
break;
case ConnectionType::BLOCK_RELAY:
max_connections = m_max_outbound_block_relay;
break;
// no limit for ADDR_FETCH because -seednode has no limit either
case ConnectionType::ADDR_FETCH:
break;
} // no default case, so the compiler can warn about missing cases

// Count existing connections
int existing_connections = WITH_LOCK(m_nodes_mutex,
return std::count_if(m_nodes.begin(), m_nodes.end(), [conn_type](CNode* node) { return node->m_conn_type == conn_type; }););

// Max connections of specified type already exist
if (existing_connections >= max_connections) return false;
if (max_connections != std::nullopt && existing_connections >= max_connections) return false;

// Max total outbound connections already exist
CSemaphoreGrant grant(*semOutbound, true);
Expand Down
1 change: 1 addition & 0 deletions src/net.h
Expand Up @@ -1106,6 +1106,7 @@ friend class CNode;
*
* @param[in] address Address of node to try connecting to
* @param[in] conn_type ConnectionType::OUTBOUND or ConnectionType::BLOCK_RELAY
* or ConnectionType::ADDR_FETCH
* @return bool Returns false if there are no available
* slots for this connection:
* - conn_type not a supported ConnectionType
Expand Down
10 changes: 9 additions & 1 deletion src/net_processing.cpp
Expand Up @@ -3657,7 +3657,9 @@ void PeerManagerImpl::ProcessMessage(

m_addrman.Add(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000) peer->m_getaddr_sent = false;
if (pfrom.IsAddrFetchConn()) {

// AddrFetch: Require multiple addresses to avoid disconnecting on self-announcements
if (pfrom.IsAddrFetchConn() && vAddr.size() > 1) {
LogPrint(BCLog::NET_NETCONN, "addrfetch connection completed peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
Expand Down Expand Up @@ -5351,6 +5353,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)

const auto current_time = GetTime<std::chrono::microseconds>();

if (pto->IsAddrFetchConn() && current_time - std::chrono::seconds(pto->nTimeConnected) > 10 * AVG_ADDRESS_BROADCAST_INTERVAL) {
LogPrint(BCLog::NET_NETCONN, "addrfetch connection timeout; disconnecting peer=%d\n", pto->GetId());
pto->fDisconnect = true;
return true;
}

MaybeSendPing(*pto, *peer, current_time);

// MaybeSendPing may have marked peer for disconnection
Expand Down
4 changes: 3 additions & 1 deletion src/rpc/net.cpp
Expand Up @@ -352,7 +352,7 @@ static RPCHelpMan addconnection()
"\nOpen an outbound connection to a specified node. This RPC is for testing only.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."},
{"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open, either \"outbound-full-relay\" or \"block-relay-only\"."},
{"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\" or \"addr-fetch\")."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
Expand All @@ -378,6 +378,8 @@ static RPCHelpMan addconnection()
conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
} else if (conn_type_in == "block-relay-only") {
conn_type = ConnectionType::BLOCK_RELAY;
} else if (conn_type_in == "addr-fetch") {
conn_type = ConnectionType::ADDR_FETCH;
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString());
}
Expand Down
62 changes: 62 additions & 0 deletions test/functional/p2p_addrfetch.py
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to backport bitcoin#22568 in a follow-up PR. There are no any important/critical fix, just tests improvements, so, that's an out-of-scope of current PR

# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
Test p2p addr-fetch connections
"""

import time

from test_framework.messages import msg_addr, CAddress, NODE_NETWORK
from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal

ADDR = CAddress()
ADDR.time = int(time.time())
ADDR.nServices = NODE_NETWORK
ADDR.ip = "192.0.0.8"
ADDR.port = 18444


class P2PAddrFetch(BitcoinTestFramework):

def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1

def run_test(self):
node = self.nodes[0]
self.log.info("Connect to an addr-fetch peer")
peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="addr-fetch")
info = node.getpeerinfo()
assert_equal(len(info), 1)
assert_equal(info[0]['connection_type'], 'addr-fetch')

self.log.info("Check that we send getaddr but don't try to sync headers with the addr-fetch peer")
peer.sync_send_with_ping()
with p2p_lock:
assert peer.message_count['getaddr'] == 1
assert peer.message_count['getheaders'] == 0

self.log.info("Check that answering the getaddr with a single address does not lead to disconnect")
# This prevents disconnecting on self-announcements
msg = msg_addr()
msg.addrs = [ADDR]
peer.send_and_ping(msg)
assert_equal(len(node.getpeerinfo()), 1)

self.log.info("Check that answering with larger addr messages leads to disconnect")
msg.addrs = [ADDR] * 2
peer.send_message(msg)
peer.wait_for_disconnect(timeout=5)

self.log.info("Check timeout for addr-fetch peer that does not send addrs")
peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="addr-fetch")
node.setmocktime(int(time.time()) + 301) # Timeout: 5 minutes
peer.wait_for_disconnect(timeout=5)


if __name__ == '__main__':
P2PAddrFetch().main()
5 changes: 2 additions & 3 deletions test/functional/test_framework/test_node.py
Expand Up @@ -571,9 +571,8 @@ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs):
return p2p_conn

def add_outbound_p2p_connection(self, p2p_conn, *, p2p_idx, connection_type="outbound-full-relay", **kwargs):
"""Add an outbound p2p connection from node. Either
full-relay("outbound-full-relay") or
block-relay-only("block-relay-only") connection.
"""Add an outbound p2p connection from node. Must be an
"outbound-full-relay", "block-relay-only" or "addr-fetch" connection.

This method adds the p2p connection to the self.p2ps list and returns
the connection to the caller.
Expand Down
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Expand Up @@ -207,6 +207,7 @@
'p2p_addr_relay.py',
'p2p_getaddr_caching.py',
'p2p_getdata.py',
'p2p_addrfetch.py',
'rpc_net.py',
'wallet_keypool.py --legacy-wallet',
'wallet_keypool_hd.py --legacy-wallet',
Expand Down