Skip to content

Commit 4598291

Browse files
committed
test: add addnode connection limit test to rpc_net.py
Add test_addnode_connection_limit to verify that MAX_ADDNODE_CONNECTIONS (8) is enforced when using the addnode RPC. The test adds 9 targets, confirms exactly 8 connect and identifies the blocked peer, then frees a slot and verifies the blocked peer connects. Closes bitcoin#28635
1 parent ca85b8c commit 4598291

File tree

1 file changed

+63
-9
lines changed

1 file changed

+63
-9
lines changed

test/functional/rpc_net.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,24 @@ def seed_addrman(node):
6161

6262
class NetTest(BitcoinTestFramework):
6363
def set_test_params(self):
64-
self.num_nodes = 2
64+
self.num_nodes = 11
6565
self.extra_args = [
6666
["-minrelaytxfee=0.00001000"],
6767
["-minrelaytxfee=0.00000500"],
68-
]
69-
# Specify a non-working proxy to make sure no actual connections to public IPs are attempted
70-
for args in self.extra_args:
71-
args.append("-proxy=127.0.0.1:1")
68+
] + [[]] * 9
69+
# Specify a non-working proxy on nodes 0-1 to make sure no actual
70+
# connections to public IPs are attempted; nodes 2-10 need to accept
71+
# real connections for the addnode limit test.
72+
for i in range(2):
73+
self.extra_args[i].append("-proxy=127.0.0.1:1")
7274
self.supports_cli = False
7375

76+
def setup_network(self):
77+
self.setup_nodes()
78+
# Only connect nodes 0 and 1; nodes 2-10 are addnode targets used
79+
# exclusively by test_addnode_connection_limit.
80+
self.connect_nodes(1, 0)
81+
7482
def run_test(self):
7583
# We need miniwallet to make a transaction
7684
self.wallet = MiniWallet(self.nodes[0])
@@ -80,7 +88,7 @@ def run_test(self):
8088
# the two nodes being connected both ways.
8189
# Topology will look like: node0 <--> node1
8290
self.connect_nodes(0, 1)
83-
self.sync_all()
91+
self.sync_blocks(self.nodes[:2])
8492

8593
self.test_connection_count()
8694
self.test_getpeerinfo()
@@ -93,6 +101,7 @@ def run_test(self):
93101
self.test_sendmsgtopeer()
94102
self.test_getaddrmaninfo()
95103
self.test_getrawaddrman()
104+
self.test_addnode_connection_limit()
96105

97106
def test_connection_count(self):
98107
self.log.info("Test getconnectioncount")
@@ -103,11 +112,11 @@ def test_getpeerinfo(self):
103112
self.log.info("Test getpeerinfo")
104113
# Create a few getpeerinfo last_block/last_transaction values.
105114
self.wallet.send_self_transfer(from_node=self.nodes[0]) # Make a transaction so we can see it in the getpeerinfo results
106-
self.generate(self.nodes[1], 1)
115+
self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_blocks(self.nodes[:2]))
107116
time_now = int(time.time())
108-
peer_info = [x.getpeerinfo() for x in self.nodes]
117+
peer_info = [x.getpeerinfo() for x in self.nodes[:2]]
109118
# Verify last_block and last_transaction keys/values.
110-
for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']):
119+
for node, peer, field in product(range(2), range(2), ['last_block', 'last_transaction']):
111120
assert field in peer_info[node][peer].keys()
112121
if peer_info[node][peer][field] != 0:
113122
assert_approx(peer_info[node][peer][field], time_now, vspan=60)
@@ -587,5 +596,50 @@ def check_getrawaddrman_entries(expected):
587596
check_getrawaddrman_entries(expected)
588597

589598

599+
def test_addnode_connection_limit(self):
600+
self.log.info("Test addnode connection limit (MAX_ADDNODE_CONNECTIONS=8)")
601+
MAX_ADDNODE_CONNECTIONS = 8
602+
node = self.nodes[0]
603+
604+
def count_manual():
605+
return sum(1 for p in node.getpeerinfo() if p['connection_type'] == 'manual')
606+
607+
# Restart node 0 without proxy, with -maxconnections=0 to prove
608+
# addnode connections bypass the general connection limit.
609+
self.restart_node(0, extra_args=["-maxconnections=0"])
610+
611+
targets = MAX_ADDNODE_CONNECTIONS + 1 # 9 targets (nodes 2-10), only 8 can connect
612+
613+
self.log.info("Verify clean initial state")
614+
assert_equal(node.getaddednodeinfo(), [])
615+
assert_equal(count_manual(), 0)
616+
617+
self.log.info(f"Add {targets} nodes via addnode RPC")
618+
for i in range(2, 2 + targets):
619+
node.addnode(node=f"127.0.0.1:{p2p_port(i)}", command='add')
620+
assert_equal(len(node.getaddednodeinfo()), targets)
621+
622+
self.log.info(f"Wait for {MAX_ADDNODE_CONNECTIONS} manual connections")
623+
self.wait_until(lambda: count_manual() >= MAX_ADDNODE_CONNECTIONS, timeout=120)
624+
assert_equal(count_manual(), MAX_ADDNODE_CONNECTIONS)
625+
626+
self.log.info("Verify the 9th (last-added) target was not connected")
627+
last_target_port = p2p_port(2 + targets - 1)
628+
connected_ports = {int(p['addr'].rsplit(':', 1)[1]) for p in node.getpeerinfo()
629+
if p['connection_type'] == 'manual'}
630+
assert last_target_port not in connected_ports
631+
632+
self.log.info("Remove and disconnect one connected peer to free a slot")
633+
connected = [p for p in node.getpeerinfo() if p['connection_type'] == 'manual']
634+
assert_greater_than(len(connected), 0)
635+
victim_addr = connected[0]['addr']
636+
node.addnode(node=victim_addr, command='remove')
637+
node.disconnectnode(victim_addr)
638+
639+
self.log.info("Wait for the previously-blocked node to connect")
640+
self.wait_until(lambda: count_manual() >= MAX_ADDNODE_CONNECTIONS, timeout=120)
641+
assert_equal(count_manual(), MAX_ADDNODE_CONNECTIONS)
642+
assert_equal(len(node.getaddednodeinfo()), MAX_ADDNODE_CONNECTIONS)
643+
590644
if __name__ == '__main__':
591645
NetTest(__file__).main()

0 commit comments

Comments
 (0)