Skip to content

Commit b8637fc

Browse files
furszyFabcien
authored andcommitted
test: improve robustness of connect_nodes()
Summary: ``` The 'connect_nodes' function in the test framework relies on a stable number of peer connections to verify the new connection between the nodes is successfully established. This approach is fragile, as any of the peers involved in the process can drop, lose, or create a connection at any step, causing subsequent 'wait_until' checks to stall indefinitely even when the peers in question are connected successfully. This commit improves the situation by using the nodes' subversion and the connection direction (inbound/outbound) to identify the exact peer connection and perform the checks exclusively on it. ``` Backport of [[bitcoin/bitcoin#30118 | core#30118]] and [[bitcoin/bitcoin#30252 | core#30252]]. Test Plan: ninja check-functional Reviewers: #bitcoin_abc, PiRK Reviewed By: #bitcoin_abc, PiRK Differential Revision: https://reviews.bitcoinabc.org/D17655
1 parent db526ab commit b8637fc

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

test/functional/test_framework/test_framework.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2014-2019 The Bitcoin Core developers
1+
# Copyright (c) 2014-present The Bitcoin Core developers
22
# Distributed under the MIT software license, see the accompanying
33
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
"""Base class for RPC testing."""
@@ -668,55 +668,58 @@ def connect_nodes(self, a, b):
668668
from_connection = self.nodes[a]
669669
to_connection = self.nodes[b]
670670

671-
from_num_peers = 1 + len(from_connection.getpeerinfo())
672-
to_num_peers = 1 + len(to_connection.getpeerinfo())
673671
host = to_connection.host
674672
if host is None:
675673
host = "127.0.0.1"
676674
ip_port = f"{host}:{str(to_connection.p2p_port)}"
677675
from_connection.addnode(ip_port, "onetry")
678-
# poll until version handshake complete to avoid race conditions
679-
# with transaction relaying
680-
# See comments in net_processing:
681-
# * Must have a version message before anything else
682-
# * Must have a verack message before anything else
683-
self.wait_until(
684-
lambda: sum(peer["version"] != 0 for peer in from_connection.getpeerinfo())
685-
== from_num_peers
686-
)
687-
self.wait_until(
688-
lambda: sum(peer["version"] != 0 for peer in to_connection.getpeerinfo())
689-
== to_num_peers
690-
)
691-
self.wait_until(
692-
lambda: sum(
693-
peer["bytesrecv_per_msg"].pop("verack", 0) == 24
694-
for peer in from_connection.getpeerinfo()
676+
677+
# Use subversion as peer id. Test nodes have their node number appended to the user agent string
678+
from_connection_subver = from_connection.getnetworkinfo()["subversion"]
679+
to_connection_subver = to_connection.getnetworkinfo()["subversion"]
680+
681+
def find_conn(node, peer_subversion, inbound):
682+
return next(
683+
filter(
684+
lambda peer: peer["subver"] == peer_subversion
685+
and peer["inbound"] == inbound,
686+
node.getpeerinfo(),
687+
),
688+
None,
695689
)
696-
== from_num_peers
690+
691+
self.wait_until(
692+
lambda: find_conn(from_connection, to_connection_subver, inbound=False)
693+
is not None
697694
)
698695
self.wait_until(
699-
lambda: sum(
700-
peer["bytesrecv_per_msg"].pop("verack", 0) == 24
701-
for peer in to_connection.getpeerinfo()
702-
)
703-
== to_num_peers
696+
lambda: find_conn(to_connection, from_connection_subver, inbound=True)
697+
is not None
704698
)
705-
# The message bytes are counted before processing the message, so make
706-
# sure it was fully processed by waiting for a ping.
699+
700+
def check_bytesrecv(peer, msg_type, min_bytes_recv):
701+
assert peer is not None, "Error: peer disconnected"
702+
return peer["bytesrecv_per_msg"].pop(msg_type, 0) >= min_bytes_recv
703+
704+
# Poll until version handshake (fSuccessfullyConnected) is complete to
705+
# avoid race conditions, because some message types are blocked from
706+
# being sent or received before fSuccessfullyConnected.
707+
#
708+
# As the flag fSuccessfullyConnected is not exposed, check it by
709+
# waiting for a pong, which can only happen after the flag was set.
707710
self.wait_until(
708-
lambda: sum(
709-
peer["bytesrecv_per_msg"].pop("pong", 0) >= 32
710-
for peer in from_connection.getpeerinfo()
711+
lambda: check_bytesrecv(
712+
find_conn(from_connection, to_connection_subver, inbound=False),
713+
"pong",
714+
32,
711715
)
712-
== from_num_peers
713716
)
714717
self.wait_until(
715-
lambda: sum(
716-
peer["bytesrecv_per_msg"].pop("pong", 0) >= 32
717-
for peer in to_connection.getpeerinfo()
718+
lambda: check_bytesrecv(
719+
find_conn(to_connection, from_connection_subver, inbound=True),
720+
"pong",
721+
32,
718722
)
719-
== to_num_peers
720723
)
721724

722725
def disconnect_nodes(self, a, b):

0 commit comments

Comments
 (0)