Skip to content

Commit 5c6d900

Browse files
committed
Merge bitcoin/bitcoin#29358: test: use v2 everywhere for P2PConnection if --v2transport is enabled
bf5662c test: enable v2 for python p2p depending on global --v2transport flag (Martin Zumsande) 6e9e39d test: Don't use v2transport when it's too slow. (Martin Zumsande) 87549c8 test: enable p2p_invalid_messages.py with v2transport (Martin Zumsande) 5fc9db5 test: enable p2p_sendtxrcncl.py with v2transport (Martin Zumsande) Pull request description: #24748 added v2 transport to the python `P2PConnection`, but so far each test that wants to make use of it needs to enable it on an individual basis. This PR changes it so that if the test suite is run with `--v2transport` option, v2 is used in each test by default, not only for connections between two bitcoind instances as before, but also wherever `P2PConnection` is used. Individual tests can override this global option. To do that, a few tests need to be adjusted. In addition, I added a commit to always use v1 in a few select subtests that send a large number of large messages (e.g. large reorgs). These tests don't have a fundamental problem with v2 but become very slow due to the unoptimised python ChaCha20 implementation (~30 minutes on my computer, so probably not suitable to be run in the CI). As a result, `python3 test_runner.py --v2transport` should succeed and use `v2` everywhere (unless v1 is chosen explicitly). [Edit]: To make the "test each commit" CI pass, several test fixes were squashed into the last commit, which actually enables v2 p2p for `P2PConnection`. I have an unsquashed version at https://github.com/mzumsande/bitcoin/tree/202401_bip324_alltests_unsquashed, in case that helps with review. ACKs for top commit: fjahr: tACK bf5662c vasild: ACK bf5662c stratospher: reACK bf5662c. theStack: Tested ACK bf5662c Tree-SHA512: 4f5a08248ba8a755f7d0f48deb2b79bef03292345cacb7deef01be955481093800e4e56ff218ea56734eef5de1fb3ab0f04657447ea27d393bb536539d7b289d
2 parents ee7e4b0 + bf5662c commit 5c6d900

File tree

8 files changed

+81
-36
lines changed

8 files changed

+81
-36
lines changed

test/functional/feature_block.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,10 @@ def run_test(self):
12631263
b89a = self.update_block("89a", [tx])
12641264
self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
12651265

1266+
# Don't use v2transport for the large reorg, which is too slow with the unoptimized python ChaCha20 implementation
1267+
if self.options.v2transport:
1268+
self.nodes[0].disconnect_p2ps()
1269+
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
12661270
self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)")
12671271

12681272
self.move_tip(88)

test/functional/feature_maxuploadtarget.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ def run_test(self):
8181
p2p_conns = []
8282

8383
for _ in range(3):
84-
p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn()))
84+
# Don't use v2transport in this test (too slow with the unoptimized python ChaCha20 implementation)
85+
p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False))
8586

8687
# Now mine a big block
8788
mine_large_block(self, self.wallet, self.nodes[0])
@@ -173,7 +174,7 @@ def run_test(self):
173174
self.assert_uploadtarget_state(target_reached=False, serve_historical_blocks=False)
174175

175176
# Reconnect to self.nodes[0]
176-
peer = self.nodes[0].add_p2p_connection(TestP2PConn())
177+
peer = self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False)
177178

178179
# Sending mempool message shouldn't disconnect peer, as total limit isn't reached yet
179180
peer.send_and_ping(msg_mempool())

test/functional/p2p_ibd_stalling.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ def run_test(self):
8080

8181
# Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc
8282
# returning the number of downloaded (but not connected) blocks.
83-
self.wait_until(lambda: self.total_bytes_recv_for_blocks() == 172761)
83+
bytes_recv = 172761 if not self.options.v2transport else 169692
84+
self.wait_until(lambda: self.total_bytes_recv_for_blocks() == bytes_recv)
8485

8586
self.all_sync_send_with_ping(peers)
8687
# If there was a peer marked for stalling, it would get disconnected

test/functional/p2p_invalid_messages.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ def test_duplicate_version_msg(self):
109109
self.nodes[0].disconnect_p2ps()
110110

111111
def test_magic_bytes(self):
112+
# Skip with v2, magic bytes are v1-specific
113+
if self.options.v2transport:
114+
return
112115
self.log.info("Test message with invalid magic bytes disconnects peer")
113116
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
114117
with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']):
@@ -120,6 +123,9 @@ def test_magic_bytes(self):
120123
self.nodes[0].disconnect_p2ps()
121124

122125
def test_checksum(self):
126+
# Skip with v2, the checksum is v1-specific
127+
if self.options.v2transport:
128+
return
123129
self.log.info("Test message with invalid checksum logs an error")
124130
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
125131
with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
@@ -137,7 +143,11 @@ def test_checksum(self):
137143
def test_size(self):
138144
self.log.info("Test message with oversized payload disconnects peer")
139145
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
140-
with self.nodes[0].assert_debug_log(['Header error: Size too large (badmsg, 4000001 bytes)']):
146+
error_msg = (
147+
['V2 transport error: packet too large (4000014 bytes)'] if self.options.v2transport
148+
else ['Header error: Size too large (badmsg, 4000001 bytes)']
149+
)
150+
with self.nodes[0].assert_debug_log(error_msg):
141151
msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
142152
msg = conn.build_message(msg)
143153
conn.send_raw_message(msg)
@@ -147,15 +157,26 @@ def test_size(self):
147157
def test_msgtype(self):
148158
self.log.info("Test message with invalid message type logs an error")
149159
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
150-
with self.nodes[0].assert_debug_log(['Header error: Invalid message type']):
160+
if self.options.v2transport:
161+
msgtype = 99 # not defined
151162
msg = msg_unrecognized(str_data="d")
152-
msg = conn.build_message(msg)
153-
# Modify msgtype
154-
msg = msg[:7] + b'\x00' + msg[7 + 1:]
155-
conn.send_raw_message(msg)
156-
conn.sync_with_ping(timeout=1)
157-
# Check that traffic is accounted for (24 bytes header + 2 bytes payload)
158-
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
163+
contents = msgtype.to_bytes(1, 'big') + msg.serialize()
164+
tmsg = conn.v2_state.v2_enc_packet(contents, ignore=False)
165+
with self.nodes[0].assert_debug_log(['V2 transport error: invalid message type']):
166+
conn.send_raw_message(tmsg)
167+
conn.sync_with_ping(timeout=1)
168+
# Check that traffic is accounted for (20 bytes plus 3 bytes contents)
169+
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 23)
170+
else:
171+
with self.nodes[0].assert_debug_log(['Header error: Invalid message type']):
172+
msg = msg_unrecognized(str_data="d")
173+
msg = conn.build_message(msg)
174+
# Modify msgtype
175+
msg = msg[:7] + b'\x00' + msg[7 + 1:]
176+
conn.send_raw_message(msg)
177+
conn.sync_with_ping(timeout=1)
178+
# Check that traffic is accounted for (24 bytes header + 2 bytes payload)
179+
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
159180
self.nodes[0].disconnect_p2ps()
160181

161182
def test_addrv2(self, label, required_log_messages, raw_addrv2):
@@ -306,8 +327,10 @@ def test_noncontinuous_headers_msg(self):
306327

307328
def test_resource_exhaustion(self):
308329
self.log.info("Test node stays up despite many large junk messages")
309-
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
310-
conn2 = self.nodes[0].add_p2p_connection(P2PDataStore())
330+
# Don't use v2 here - the non-optimised encryption would take too long to encrypt
331+
# the large messages
332+
conn = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
333+
conn2 = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
311334
msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT)
312335
assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH
313336

test/functional/p2p_timeouts.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,8 @@ def run_test(self):
6969
with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']):
7070
no_verack_node.send_message(msg_ping())
7171

72-
# With v2, non-version messages before the handshake would be interpreted as part of the key exchange.
73-
# Therefore, don't execute this part of the test if v2transport is chosen.
74-
if not self.options.v2transport:
75-
with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
76-
no_version_node.send_message(msg_ping())
72+
with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
73+
no_version_node.send_message(msg_ping())
7774

7875
self.mock_forward(1)
7976
assert "version" in no_verack_node.last_message
@@ -83,14 +80,20 @@ def run_test(self):
8380
assert no_send_node.is_connected
8481

8582
no_verack_node.send_message(msg_ping())
86-
if not self.options.v2transport:
87-
no_version_node.send_message(msg_ping())
88-
89-
expected_timeout_logs = [
90-
"version handshake timeout peer=0",
91-
f"socket no message in first 3 seconds, {'0' if self.options.v2transport else '1'} 0 peer=1",
92-
"socket no message in first 3 seconds, 0 0 peer=2",
93-
]
83+
no_version_node.send_message(msg_ping())
84+
85+
if self.options.v2transport:
86+
expected_timeout_logs = [
87+
"version handshake timeout peer=0",
88+
"version handshake timeout peer=1",
89+
"version handshake timeout peer=2",
90+
]
91+
else:
92+
expected_timeout_logs = [
93+
"version handshake timeout peer=0",
94+
"socket no message in first 3 seconds, 1 0 peer=1",
95+
"socket no message in first 3 seconds, 0 0 peer=2",
96+
]
9497

9598
with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs):
9699
self.mock_forward(2)

test/functional/p2p_v2_earlykeyresponse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def run_test(self):
7575
self.log.info('Sending first 4 bytes of ellswift which match network magic')
7676
self.log.info('If a response is received, assertion failure would happen in our custom data_received() function')
7777
# send happens in `initiate_v2_handshake()` in `connection_made()`
78-
peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True)
78+
peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True, wait_for_v2_handshake=False)
7979
self.wait_until(lambda: peer1.connection_opened)
8080
self.log.info('Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is')
8181
self.log.info('expected now, our custom data_received() function wouldn\'t result in assertion failure')

test/functional/rpc_net.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ def test_getpeerinfo(self):
117117
peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id]
118118
peer_info.pop("addr")
119119
peer_info.pop("addrbind")
120+
# The next two fields will vary for v2 connections because we send a rng-based number of decoy messages
121+
peer_info.pop("bytesrecv")
122+
peer_info.pop("bytessent")
120123
assert_equal(
121124
peer_info,
122125
{
@@ -125,9 +128,7 @@ def test_getpeerinfo(self):
125128
"addr_relay_enabled": False,
126129
"bip152_hb_from": False,
127130
"bip152_hb_to": False,
128-
"bytesrecv": 0,
129131
"bytesrecv_per_msg": {},
130-
"bytessent": 0,
131132
"bytessent_per_msg": {},
132133
"connection_type": "inbound",
133134
"conntime": no_version_peer_conntime,
@@ -136,22 +137,22 @@ def test_getpeerinfo(self):
136137
"inflight": [],
137138
"last_block": 0,
138139
"last_transaction": 0,
139-
"lastrecv": 0,
140-
"lastsend": 0,
140+
"lastrecv": 0 if not self.options.v2transport else no_version_peer_conntime,
141+
"lastsend": 0 if not self.options.v2transport else no_version_peer_conntime,
141142
"minfeefilter": Decimal("0E-8"),
142143
"network": "not_publicly_routable",
143144
"permissions": [],
144145
"presynced_headers": -1,
145146
"relaytxes": False,
146147
"services": "0000000000000000",
147148
"servicesnames": [],
148-
"session_id": "",
149+
"session_id": "" if not self.options.v2transport else no_version_peer.v2_state.peer['session_id'].hex(),
149150
"startingheight": -1,
150151
"subver": "",
151152
"synced_blocks": -1,
152153
"synced_headers": -1,
153154
"timeoffset": 0,
154-
"transport_protocol_type": "v1" if not self.options.v2transport else "detecting",
155+
"transport_protocol_type": "v1" if not self.options.v2transport else "v2",
155156
"version": 0,
156157
},
157158
)

test/functional/test_framework/test_node.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, mat
667667
assert_msg += "with expected error " + expected_msg
668668
self._raise_assertion_error(assert_msg)
669669

670-
def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=True, supports_v2_p2p=False, **kwargs):
670+
def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=True, supports_v2_p2p=None, wait_for_v2_handshake=True, **kwargs):
671671
"""Add an inbound p2p connection to the node.
672672
673673
This method adds the p2p connection to the self.p2ps list and also
@@ -684,6 +684,9 @@ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=Tru
684684
kwargs['dstport'] = p2p_port(self.index)
685685
if 'dstaddr' not in kwargs:
686686
kwargs['dstaddr'] = '127.0.0.1'
687+
if supports_v2_p2p is None:
688+
supports_v2_p2p = self.use_v2transport
689+
687690

688691
p2p_conn.p2p_connected_to_node = True
689692
if self.use_v2transport:
@@ -693,6 +696,8 @@ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=Tru
693696

694697
self.p2ps.append(p2p_conn)
695698
p2p_conn.wait_until(lambda: p2p_conn.is_connected, check_connected=False)
699+
if supports_v2_p2p and wait_for_v2_handshake:
700+
p2p_conn.wait_until(lambda: p2p_conn.v2_state.tried_v2_handshake)
696701
if send_version:
697702
p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
698703
if wait_for_verack:
@@ -721,7 +726,7 @@ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=Tru
721726

722727
return p2p_conn
723728

724-
def add_outbound_p2p_connection(self, p2p_conn, *, wait_for_verack=True, p2p_idx, connection_type="outbound-full-relay", supports_v2_p2p=False, advertise_v2_p2p=False, **kwargs):
729+
def add_outbound_p2p_connection(self, p2p_conn, *, wait_for_verack=True, p2p_idx, connection_type="outbound-full-relay", supports_v2_p2p=None, advertise_v2_p2p=None, **kwargs):
725730
"""Add an outbound p2p connection from node. Must be an
726731
"outbound-full-relay", "block-relay-only", "addr-fetch" or "feeler" connection.
727732
@@ -749,6 +754,11 @@ def addconnection_callback(address, port):
749754
self.addconnection('%s:%d' % (address, port), connection_type, advertise_v2_p2p)
750755

751756
p2p_conn.p2p_connected_to_node = False
757+
if supports_v2_p2p is None:
758+
supports_v2_p2p = self.use_v2transport
759+
if advertise_v2_p2p is None:
760+
advertise_v2_p2p = self.use_v2transport
761+
752762
if advertise_v2_p2p:
753763
kwargs['services'] = kwargs.get('services', P2P_SERVICES) | NODE_P2P_V2
754764
assert self.use_v2transport # only a v2 TestNode could make a v2 outbound connection
@@ -771,6 +781,8 @@ def addconnection_callback(address, port):
771781
p2p_conn.wait_for_connect()
772782
self.p2ps.append(p2p_conn)
773783

784+
if supports_v2_p2p:
785+
p2p_conn.wait_until(lambda: p2p_conn.v2_state.tried_v2_handshake)
774786
p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
775787
if wait_for_verack:
776788
p2p_conn.wait_for_verack()

0 commit comments

Comments
 (0)