Skip to content

Commit 860f916

Browse files
committed
Merge #20524: test: Move MIN_VERSION_SUPPORTED to p2p.py
9f21ed4 [test] Check user agent string from test framework connections (John Newbery) 9ce4c3c [test] Add P2P_SERVICES to p2p.py (John Newbery) 0105426 [test] Move MY_RELAY to p2p.py (John Newbery) 9b4054c [test] Move MY_SUBVERSION to p2p.py (John Newbery) 7e158a6 [test] Move MY_VERSION to p2p.py (John Newbery) 6523111 [test] Move MIN_VERSION_SUPPORTED to p2p.py (John Newbery) Pull request description: The messages.py module should contain code and helpers for [de]serializing p2p messages. Specific usage of those messages should be in p2p.py. This PR moves test framework specific constants to p2p.py. It also changes the SUBVERSION constant to be a string instead of a bytes object. That means that it needs to be explicitly converted to a bytes object to serialize into a version message. Failing to do so would cause an easy-to-spot bug. This should avoid silent failures like the one solved in #20522. ACKs for top commit: laanwj: Code review ACK 9f21ed4 Tree-SHA512: 41d46575ac0ec36ad074d6c6a5b9cef50b05eeb8ddd8ed0a8f0d0c4617cc7b8baa6580af5b83a668230ce1ac27bf0e56914d0361a48b1b05fd75e2e60350eeaf
2 parents db656db + 9f21ed4 commit 860f916

File tree

5 files changed

+61
-31
lines changed

5 files changed

+61
-31
lines changed

test/functional/p2p_filter.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@
1919
msg_mempool,
2020
msg_version,
2121
)
22-
from test_framework.p2p import P2PInterface, p2p_lock
22+
from test_framework.p2p import (
23+
P2PInterface,
24+
P2P_SERVICES,
25+
P2P_SUBVERSION,
26+
P2P_VERSION,
27+
p2p_lock,
28+
)
2329
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
2430
from test_framework.test_framework import BitcoinTestFramework
2531

@@ -216,9 +222,12 @@ def run_test(self):
216222
self.log.info('Test BIP 37 for a node with fRelay = False')
217223
# Add peer but do not send version yet
218224
filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False)
219-
# Send version with fRelay=False
225+
# Send version with relay=False
220226
version_without_fRelay = msg_version()
221-
version_without_fRelay.nRelay = 0
227+
version_without_fRelay.nVersion = P2P_VERSION
228+
version_without_fRelay.strSubVer = P2P_SUBVERSION
229+
version_without_fRelay.nServices = P2P_SERVICES
230+
version_without_fRelay.relay = 0
222231
filter_peer_without_nrelay.send_message(version_without_fRelay)
223232
filter_peer_without_nrelay.wait_for_verack()
224233
assert not self.nodes[0].getpeerinfo()[0]['relaytxes']

test/functional/p2p_leak.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717
msg_ping,
1818
msg_version,
1919
)
20-
from test_framework.p2p import P2PInterface
20+
from test_framework.p2p import (
21+
P2PInterface,
22+
P2P_SUBVERSION,
23+
P2P_SERVICES,
24+
P2P_VERSION_RELAY,
25+
)
2126
from test_framework.test_framework import BitcoinTestFramework
2227
from test_framework.util import (
2328
assert_equal,
@@ -125,12 +130,15 @@ def run_test(self):
125130
assert_equal(ver.addrFrom.port, 0)
126131
assert_equal(ver.addrFrom.ip, '0.0.0.0')
127132
assert_equal(ver.nStartingHeight, 201)
128-
assert_equal(ver.nRelay, 1)
133+
assert_equal(ver.relay, 1)
129134

130135
self.log.info('Check that old peers are disconnected')
131136
p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
132137
old_version_msg = msg_version()
133138
old_version_msg.nVersion = 31799
139+
old_version_msg.strSubVer = P2P_SUBVERSION
140+
old_version_msg.nServices = P2P_SERVICES
141+
old_version_msg.relay = P2P_VERSION_RELAY
134142
with self.nodes[0].assert_debug_log(['peer=3 using obsolete version 31799; disconnecting']):
135143
p2p_old_peer.send_message(old_version_msg)
136144
p2p_old_peer.wait_for_disconnect()

test/functional/test_framework/messages.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@
3131
from test_framework.siphash import siphash256
3232
from test_framework.util import hex_str_to_bytes, assert_equal
3333

34-
MIN_VERSION_SUPPORTED = 60001
35-
MY_VERSION = 70016 # past wtxid relay
36-
MY_SUBVERSION = "/python-p2p-tester:0.0.3/"
37-
MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
38-
3934
MAX_LOCATOR_SZ = 101
4035
MAX_BLOCK_BASE_SIZE = 1000000
4136
MAX_BLOOM_FILTER_SIZE = 36000
@@ -326,22 +321,20 @@ class CBlockLocator:
326321
__slots__ = ("nVersion", "vHave")
327322

328323
def __init__(self):
329-
self.nVersion = MY_VERSION
330324
self.vHave = []
331325

332326
def deserialize(self, f):
333-
self.nVersion = struct.unpack("<i", f.read(4))[0]
327+
struct.unpack("<i", f.read(4))[0] # Ignore version field.
334328
self.vHave = deser_uint256_vector(f)
335329

336330
def serialize(self):
337331
r = b""
338-
r += struct.pack("<i", self.nVersion)
332+
r += struct.pack("<i", 0) # Bitcoin Core ignores version field. Set it to 0.
339333
r += ser_uint256_vector(self.vHave)
340334
return r
341335

342336
def __repr__(self):
343-
return "CBlockLocator(nVersion=%i vHave=%s)" \
344-
% (self.nVersion, repr(self.vHave))
337+
return "CBlockLocator(vHave=%s)" % (repr(self.vHave))
345338

346339

347340
class COutPoint:
@@ -1023,20 +1016,20 @@ def __repr__(self):
10231016

10241017
# Objects that correspond to messages on the wire
10251018
class msg_version:
1026-
__slots__ = ("addrFrom", "addrTo", "nNonce", "nRelay", "nServices",
1019+
__slots__ = ("addrFrom", "addrTo", "nNonce", "relay", "nServices",
10271020
"nStartingHeight", "nTime", "nVersion", "strSubVer")
10281021
msgtype = b"version"
10291022

10301023
def __init__(self):
1031-
self.nVersion = MY_VERSION
1032-
self.nServices = NODE_NETWORK | NODE_WITNESS
1024+
self.nVersion = 0
1025+
self.nServices = 0
10331026
self.nTime = int(time.time())
10341027
self.addrTo = CAddress()
10351028
self.addrFrom = CAddress()
10361029
self.nNonce = random.getrandbits(64)
1037-
self.strSubVer = MY_SUBVERSION
1030+
self.strSubVer = ''
10381031
self.nStartingHeight = -1
1039-
self.nRelay = MY_RELAY
1032+
self.relay = 0
10401033

10411034
def deserialize(self, f):
10421035
self.nVersion = struct.unpack("<i", f.read(4))[0]
@@ -1055,11 +1048,11 @@ def deserialize(self, f):
10551048
if self.nVersion >= 70001:
10561049
# Relay field is optional for version 70001 onwards
10571050
try:
1058-
self.nRelay = struct.unpack("<b", f.read(1))[0]
1051+
self.relay = struct.unpack("<b", f.read(1))[0]
10591052
except:
1060-
self.nRelay = 0
1053+
self.relay = 0
10611054
else:
1062-
self.nRelay = 0
1055+
self.relay = 0
10631056

10641057
def serialize(self):
10651058
r = b""
@@ -1071,14 +1064,14 @@ def serialize(self):
10711064
r += struct.pack("<Q", self.nNonce)
10721065
r += ser_string(self.strSubVer.encode('utf-8'))
10731066
r += struct.pack("<i", self.nStartingHeight)
1074-
r += struct.pack("<b", self.nRelay)
1067+
r += struct.pack("<b", self.relay)
10751068
return r
10761069

10771070
def __repr__(self):
1078-
return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \
1071+
return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i relay=%i)' \
10791072
% (self.nVersion, self.nServices, time.ctime(self.nTime),
10801073
repr(self.addrTo), repr(self.addrFrom), self.nNonce,
1081-
self.strSubVer, self.nStartingHeight, self.nRelay)
1074+
self.strSubVer, self.nStartingHeight, self.relay)
10821075

10831076

10841077
class msg_verack:

test/functional/test_framework/p2p.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from test_framework.messages import (
3232
CBlockHeader,
3333
MAX_HEADERS_RESULTS,
34-
MIN_VERSION_SUPPORTED,
3534
msg_addr,
3635
msg_addrv2,
3736
msg_block,
@@ -79,6 +78,18 @@
7978

8079
logger = logging.getLogger("TestFramework.p2p")
8180

81+
# The minimum P2P version that this test framework supports
82+
MIN_P2P_VERSION_SUPPORTED = 60001
83+
# The P2P version that this test framework implements and sends in its `version` message
84+
# Version 70016 supports wtxid relay
85+
P2P_VERSION = 70016
86+
# The services that this test framework offers in its `version` message
87+
P2P_SERVICES = NODE_NETWORK | NODE_WITNESS
88+
# The P2P user agent string that this test framework sends in its `version` message
89+
P2P_SUBVERSION = "/python-p2p-tester:0.0.3/"
90+
# Value for relay that this test framework sends in its `version` message
91+
P2P_VERSION_RELAY = 1
92+
8293
MESSAGEMAP = {
8394
b"addr": msg_addr,
8495
b"addrv2": msg_addrv2,
@@ -327,14 +338,17 @@ def __init__(self, support_addrv2=False, wtxidrelay=True):
327338
def peer_connect_send_version(self, services):
328339
# Send a version msg
329340
vt = msg_version()
341+
vt.nVersion = P2P_VERSION
342+
vt.strSubVer = P2P_SUBVERSION
343+
vt.relay = P2P_VERSION_RELAY
330344
vt.nServices = services
331345
vt.addrTo.ip = self.dstaddr
332346
vt.addrTo.port = self.dstport
333347
vt.addrFrom.ip = "0.0.0.0"
334348
vt.addrFrom.port = 0
335349
self.on_connection_send_msg = vt # Will be sent in connection_made callback
336350

337-
def peer_connect(self, *args, services=NODE_NETWORK | NODE_WITNESS, send_version=True, **kwargs):
351+
def peer_connect(self, *args, services=P2P_SERVICES, send_version=True, **kwargs):
338352
create_conn = super().peer_connect(*args, **kwargs)
339353

340354
if send_version:
@@ -417,7 +431,7 @@ def on_verack(self, message):
417431
pass
418432

419433
def on_version(self, message):
420-
assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
434+
assert message.nVersion >= MIN_P2P_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_P2P_VERSION_SUPPORTED)
421435
if message.nVersion >= 70016 and self.wtxidrelay:
422436
self.send_message(msg_wtxidrelay())
423437
if self.support_addrv2:

test/functional/test_framework/test_node.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323

2424
from .authproxy import JSONRPCException
2525
from .descriptors import descsum_create
26-
from .messages import MY_SUBVERSION
26+
from .p2p import P2P_SUBVERSION
2727
from .util import (
2828
MAX_NODES,
29+
assert_equal,
2930
append_config,
3031
delete_cookie_file,
3132
get_auth_cookie,
@@ -545,6 +546,11 @@ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs):
545546
# in comparison to the upside of making tests less fragile and unexpected intermittent errors less likely.
546547
p2p_conn.sync_with_ping()
547548

549+
# Consistency check that the Bitcoin Core has received our user agent string. This checks the
550+
# node's newest peer. It could be racy if another Bitcoin Core node has connected since we opened
551+
# our connection, but we don't expect that to happen.
552+
assert_equal(self.getpeerinfo()[-1]['subver'], P2P_SUBVERSION)
553+
548554
return p2p_conn
549555

550556
def add_outbound_p2p_connection(self, p2p_conn, *, p2p_idx, connection_type="outbound-full-relay", **kwargs):
@@ -572,7 +578,7 @@ def addconnection_callback(address, port):
572578

573579
def num_test_p2p_connections(self):
574580
"""Return number of test framework p2p connections to the node."""
575-
return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION])
581+
return len([peer for peer in self.getpeerinfo() if peer['subver'] == P2P_SUBVERSION])
576582

577583
def disconnect_p2ps(self):
578584
"""Close all p2p connections to the node."""

0 commit comments

Comments
 (0)