Skip to content

Commit 4572f48

Browse files
committed
Merge bitcoin/bitcoin#29353: test: p2p: adhere to typical VERSION message protocol flow
c340503 test: p2p: adhere to typical VERSION message protocol flow (Sebastian Falbesoner) 7ddfc28 test: p2p: process post-v2-handshake data immediately (Sebastian Falbesoner) b198b9c test: p2p: introduce helper for sending prepared VERSION message (Sebastian Falbesoner) Pull request description: This PR addresses a quirk in the test framework's p2p implementation regarding the version handshake protocol: Currently, the VERSION message is sent immediately after an inbound connection (i.e. TestNode outbound connection) is made. This doesn't follow the usual protocol flow where the initiator sends a version first, the responder processes that and only then responds with its own version message. Change that accordingly by only sending immediate VERSION message for outbound connections (or after v2 handshake for v2 connections, respectively), and sending out VERSION message as response for incoming VERSION messages (i.e. in the function `on_version`) for inbound connections. I first stumbled upon this issue through reading comment https://mirror.b10c.me/bitcoin-bitcoin/24748/#discussion_r1465420112 (see last paragraph) and recently again in the course of working on a v2-followup for #29279, where this causes issues for TestNode outbound connections that disconnect *before* sending out their own version message. Note that these changes lead to slightly more code in some functional tests that override the `on_version` method, as the version reply has to be sent explicitly now, but I think is less confusing and reflects better what is actually happening. ACKs for top commit: epiccurious: utACK c340503 stratospher: tested ACK c340503. very useful to have since we'd want real node behaviour! mzumsande: ACK c340503 sr-gi: tACK bitcoin/bitcoin@c340503 Tree-SHA512: 63eac287d3e1c87a01852bfd9f0530363354bbb642280298673b9c8817056356373adf348955c4e92af95c7c6efa8cc515cee2892e9f077bfbe1bce8e97ad082
2 parents 9eeee7c + c340503 commit 4572f48

File tree

4 files changed

+25
-15
lines changed

4 files changed

+25
-15
lines changed

test/functional/p2p_add_connections.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def on_version(self, message):
1717
# message is received from the test framework. Don't send any responses
1818
# to the node's version message since the connection will already be
1919
# closed.
20-
pass
20+
self.send_version()
2121

2222
class P2PAddConnections(BitcoinTestFramework):
2323
def set_test_params(self):

test/functional/p2p_addr_relay.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def addr_received(self):
7575
return self.num_ipv4_received != 0
7676

7777
def on_version(self, message):
78+
self.send_version()
7879
self.send_message(msg_verack())
7980
if (self.send_getaddr):
8081
self.send_message(msg_getaddr())

test/functional/p2p_sendtxrcncl.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def on_version(self, message):
2929
# Avoid sending verack in response to version.
3030
# When calling add_p2p_connection, wait_for_verack=False must be set (see
3131
# comment in add_p2p_connection).
32+
self.send_version()
3233
if message.nVersion >= 70016 and self.wtxidrelay:
3334
self.send_message(msg_wtxidrelay())
3435

@@ -43,7 +44,8 @@ def on_sendtxrcncl(self, message):
4344

4445
class P2PFeelerReceiver(SendTxrcnclReceiver):
4546
def on_version(self, message):
46-
pass # feeler connections can not send any message other than their own version
47+
# feeler connections can not send any message other than their own version
48+
self.send_version()
4749

4850

4951
class PeerTrackMsgOrder(P2PInterface):

test/functional/test_framework/p2p.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,10 @@ def connection_made(self, transport):
224224
if self.supports_v2_p2p and self.v2_state.initiating and not self.v2_state.tried_v2_handshake:
225225
send_handshake_bytes = self.v2_state.initiate_v2_handshake()
226226
self.send_raw_message(send_handshake_bytes)
227-
# if v2 connection, send `on_connection_send_msg` after initial v2 handshake.
228-
# if reconnection situation, send `on_connection_send_msg` after version message is received in `on_version()`.
229-
if self.on_connection_send_msg and not self.supports_v2_p2p and not self.reconnect:
230-
self.send_message(self.on_connection_send_msg)
231-
self.on_connection_send_msg = None # Never used again
227+
# for v1 outbound connections, send version message immediately after opening
228+
# (for v2 outbound connections, send it after the initial v2 handshake)
229+
if self.p2p_connected_to_node and not self.supports_v2_p2p:
230+
self.send_version()
232231
self.on_open()
233232

234233
def connection_lost(self, exc):
@@ -284,9 +283,13 @@ def v2_handshake(self):
284283
if not is_mac_auth:
285284
raise ValueError("invalid v2 mac tag in handshake authentication")
286285
self.recvbuf = self.recvbuf[length:]
287-
if self.v2_state.tried_v2_handshake and self.on_connection_send_msg:
288-
self.send_message(self.on_connection_send_msg)
289-
self.on_connection_send_msg = None
286+
if self.v2_state.tried_v2_handshake:
287+
# for v2 outbound connections, send version message immediately after v2 handshake
288+
if self.p2p_connected_to_node:
289+
self.send_version()
290+
# process post-v2-handshake data immediately, if available
291+
if len(self.recvbuf) > 0:
292+
self._on_data()
290293

291294
# Socket read methods
292295

@@ -557,11 +560,10 @@ def on_verack(self, message):
557560

558561
def on_version(self, message):
559562
assert message.nVersion >= MIN_P2P_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_P2P_VERSION_SUPPORTED)
560-
# reconnection using v1 P2P has happened since version message can be processed, previously unsent version message is sent using v1 P2P here
561-
if self.reconnect:
562-
if self.on_connection_send_msg:
563-
self.send_message(self.on_connection_send_msg)
564-
self.on_connection_send_msg = None
563+
# for inbound connections, reply to version with own version message
564+
# (could be due to v1 reconnect after a failed v2 handshake)
565+
if not self.p2p_connected_to_node:
566+
self.send_version()
565567
self.reconnect = False
566568
if message.nVersion >= 70016 and self.wtxidrelay:
567569
self.send_message(msg_wtxidrelay())
@@ -676,6 +678,11 @@ def test_function():
676678

677679
# Message sending helper functions
678680

681+
def send_version(self):
682+
if self.on_connection_send_msg:
683+
self.send_message(self.on_connection_send_msg)
684+
self.on_connection_send_msg = None # Never used again
685+
679686
def send_and_ping(self, message, timeout=60):
680687
self.send_message(message)
681688
self.sync_with_ping(timeout=timeout)

0 commit comments

Comments
 (0)