merge bitcoin#29358: use v2 everywhere for P2PConnection if --v2transport is enabled

This commit is contained in:
Kittywhiskers Van Gogh 2024-10-14 21:48:13 +00:00
parent 4cce72fc3e
commit 54972e8fa0
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
8 changed files with 80 additions and 35 deletions

View File

@ -1277,6 +1277,10 @@ class FullBlockTest(BitcoinTestFramework):
b89a = self.update_block("89a", [tx]) b89a = self.update_block("89a", [tx])
self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
# Don't use v2transport for the large reorg, which is too slow with the unoptimized python ChaCha20 implementation
if self.options.v2transport:
self.nodes[0].disconnect_p2ps()
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
self.log.info("Test a re-org of ~2 days' worth of blocks (1088 blocks)") self.log.info("Test a re-org of ~2 days' worth of blocks (1088 blocks)")
self.move_tip(88) self.move_tip(88)

View File

@ -68,7 +68,8 @@ class MaxUploadTest(BitcoinTestFramework):
p2p_conns = [] p2p_conns = []
for _ in range(3): for _ in range(3):
p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn())) # Don't use v2transport in this test (too slow with the unoptimized python ChaCha20 implementation)
p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False))
# Now mine a big block # Now mine a big block
mine_large_block(self, self.nodes[0], self.utxo_cache) mine_large_block(self, self.nodes[0], self.utxo_cache)
@ -150,7 +151,7 @@ class MaxUploadTest(BitcoinTestFramework):
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-mocktime="+str(current_mocktime)]) self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000", "-mocktime="+str(current_mocktime)])
# Reconnect to self.nodes[0] # Reconnect to self.nodes[0]
peer = self.nodes[0].add_p2p_connection(TestP2PConn()) peer = self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False)
#retrieve 20 blocks which should be enough to break the 1MB limit #retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)] getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)]

View File

@ -83,7 +83,8 @@ class P2PIBDStallingTest(BitcoinTestFramework):
# Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc # Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc
# returning the number of downloaded (but not connected) blocks. # returning the number of downloaded (but not connected) blocks.
self.wait_until(lambda: self.total_bytes_recv_for_blocks() == 172761) bytes_recv = 172761 if not self.options.v2transport else 169692
self.wait_until(lambda: self.total_bytes_recv_for_blocks() == bytes_recv)
self.all_sync_send_with_ping(peers) self.all_sync_send_with_ping(peers)
# If there was a peer marked for stalling, it would get disconnected # If there was a peer marked for stalling, it would get disconnected

View File

@ -90,6 +90,9 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps() self.nodes[0].disconnect_p2ps()
def test_magic_bytes(self): def test_magic_bytes(self):
# Skip with v2, magic bytes are v1-specific
if self.options.v2transport:
return
self.log.info("Test message with invalid magic bytes disconnects peer") self.log.info("Test message with invalid magic bytes disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore()) conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']): with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']):
@ -101,6 +104,9 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps() self.nodes[0].disconnect_p2ps()
def test_checksum(self): def test_checksum(self):
# Skip with v2, the checksum is v1-specific
if self.options.v2transport:
return
self.log.info("Test message with invalid checksum logs an error") self.log.info("Test message with invalid checksum logs an error")
conn = self.nodes[0].add_p2p_connection(P2PDataStore()) conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']): with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
@ -118,7 +124,11 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_size(self): def test_size(self):
self.log.info("Test message with oversized payload disconnects peer") self.log.info("Test message with oversized payload disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore()) conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['Header error: Size too large (badmsg, 3145729 bytes)']): error_msg = (
['V2 transport error: packet too large (3145742 bytes)'] if self.options.v2transport
else ['Header error: Size too large (badmsg, 3145729 bytes)']
)
with self.nodes[0].assert_debug_log(error_msg):
msg = msg_unrecognized(str_data="d"*(VALID_DATA_LIMIT + 1)) msg = msg_unrecognized(str_data="d"*(VALID_DATA_LIMIT + 1))
msg = conn.build_message(msg) msg = conn.build_message(msg)
conn.send_raw_message(msg) conn.send_raw_message(msg)
@ -128,15 +138,26 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_msgtype(self): def test_msgtype(self):
self.log.info("Test message with invalid message type logs an error") self.log.info("Test message with invalid message type logs an error")
conn = self.nodes[0].add_p2p_connection(P2PDataStore()) conn = self.nodes[0].add_p2p_connection(P2PDataStore())
with self.nodes[0].assert_debug_log(['Header error: Invalid message type']): if self.options.v2transport:
msgtype = 99 # not defined
msg = msg_unrecognized(str_data="d") msg = msg_unrecognized(str_data="d")
msg = conn.build_message(msg) contents = msgtype.to_bytes(1, 'big') + msg.serialize()
# Modify msgtype tmsg = conn.v2_state.v2_enc_packet(contents, ignore=False)
msg = msg[:7] + b'\x00' + msg[7 + 1:] with self.nodes[0].assert_debug_log(['V2 transport error: invalid message type']):
conn.send_raw_message(msg) conn.send_raw_message(tmsg)
conn.sync_with_ping(timeout=1) conn.sync_with_ping(timeout=1)
# Check that traffic is accounted for (24 bytes header + 2 bytes payload) # Check that traffic is accounted for (20 bytes plus 3 bytes contents)
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26) assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 23)
else:
with self.nodes[0].assert_debug_log(['Header error: Invalid message type']):
msg = msg_unrecognized(str_data="d")
msg = conn.build_message(msg)
# Modify msgtype
msg = msg[:7] + b'\x00' + msg[7 + 1:]
conn.send_raw_message(msg)
conn.sync_with_ping(timeout=1)
# Check that traffic is accounted for (24 bytes header + 2 bytes payload)
assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
self.nodes[0].disconnect_p2ps() self.nodes[0].disconnect_p2ps()
def test_oversized_msg(self, msg, size): def test_oversized_msg(self, msg, size):
@ -164,8 +185,10 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_resource_exhaustion(self): def test_resource_exhaustion(self):
self.log.info("Test node stays up despite many large junk messages") self.log.info("Test node stays up despite many large junk messages")
conn = self.nodes[0].add_p2p_connection(P2PDataStore()) # Don't use v2 here - the non-optimised encryption would take too long to encrypt
conn2 = self.nodes[0].add_p2p_connection(P2PDataStore()) # the large messages
conn = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
conn2 = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)
msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT) msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT)
assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH

View File

@ -69,11 +69,8 @@ class TimeoutsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']): with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']):
no_verack_node.send_message(msg_ping()) no_verack_node.send_message(msg_ping())
# With v2, non-version messages before the handshake would be interpreted as part of the key exchange. with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
# Therefore, don't execute this part of the test if v2transport is chosen. no_version_node.send_message(msg_ping())
if not self.options.v2transport:
with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
no_version_node.send_message(msg_ping())
self.mock_forward(1) self.mock_forward(1)
assert "version" in no_verack_node.last_message assert "version" in no_verack_node.last_message
@ -83,14 +80,20 @@ class TimeoutsTest(BitcoinTestFramework):
assert no_send_node.is_connected assert no_send_node.is_connected
no_verack_node.send_message(msg_ping()) no_verack_node.send_message(msg_ping())
if not self.options.v2transport: no_version_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
expected_timeout_logs = [ if self.options.v2transport:
"version handshake timeout peer=0", expected_timeout_logs = [
f"socket no message in first 3 seconds, {'0' if self.options.v2transport else '1'} 0 peer=1", "version handshake timeout peer=0",
"socket no message in first 3 seconds, 0 0 peer=2", "version handshake timeout peer=1",
] "version handshake timeout peer=2",
]
else:
expected_timeout_logs = [
"version handshake timeout peer=0",
"socket no message in first 3 seconds, 1 0 peer=1",
"socket no message in first 3 seconds, 0 0 peer=2",
]
with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs): with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs):
self.mock_forward(3) self.mock_forward(3)

View File

@ -77,7 +77,7 @@ class P2PEarlyKey(BitcoinTestFramework):
self.log.info('Sending first 4 bytes of ellswift which match network magic') self.log.info('Sending first 4 bytes of ellswift which match network magic')
self.log.info('If a response is received, assertion failure would happen in our custom data_received() function') self.log.info('If a response is received, assertion failure would happen in our custom data_received() function')
# send happens in `initiate_v2_handshake()` in `connection_made()` # send happens in `initiate_v2_handshake()` in `connection_made()`
peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True) peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True, wait_for_v2_handshake=False)
self.wait_until(lambda: peer1.connection_opened) self.wait_until(lambda: peer1.connection_opened)
self.log.info('Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is') self.log.info('Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is')
self.log.info('expected now, our custom data_received() function wouldn\'t result in assertion failure') self.log.info('expected now, our custom data_received() function wouldn\'t result in assertion failure')

View File

@ -118,6 +118,9 @@ class NetTest(DashTestFramework):
peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id] peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id]
peer_info.pop("addr") peer_info.pop("addr")
peer_info.pop("addrbind") peer_info.pop("addrbind")
# The next two fields will vary for v2 connections because we send a rng-based number of decoy messages
peer_info.pop("bytesrecv")
peer_info.pop("bytessent")
assert_equal( assert_equal(
peer_info, peer_info,
{ {
@ -126,9 +129,7 @@ class NetTest(DashTestFramework):
"addr_relay_enabled": False, "addr_relay_enabled": False,
"bip152_hb_from": False, "bip152_hb_from": False,
"bip152_hb_to": False, "bip152_hb_to": False,
"bytesrecv": 0,
"bytesrecv_per_msg": {}, "bytesrecv_per_msg": {},
"bytessent": 0,
"bytessent_per_msg": {}, "bytessent_per_msg": {},
"connection_type": "inbound", "connection_type": "inbound",
"conntime": no_version_peer_conntime, "conntime": no_version_peer_conntime,
@ -137,21 +138,21 @@ class NetTest(DashTestFramework):
"inflight": [], "inflight": [],
"last_block": 0, "last_block": 0,
"last_transaction": 0, "last_transaction": 0,
"lastrecv": 0, "lastrecv": 0 if not self.options.v2transport else no_version_peer_conntime,
"lastsend": 0, "lastsend": 0 if not self.options.v2transport else no_version_peer_conntime,
"masternode": False, "masternode": False,
"network": "not_publicly_routable", "network": "not_publicly_routable",
"permissions": [], "permissions": [],
"relaytxes": False, "relaytxes": False,
"services": "0000000000000000", "services": "0000000000000000",
"servicesnames": [], "servicesnames": [],
"session_id": "", "session_id": "" if not self.options.v2transport else no_version_peer.v2_state.peer['session_id'].hex(),
"startingheight": -1, "startingheight": -1,
"subver": "", "subver": "",
"synced_blocks": -1, "synced_blocks": -1,
"synced_headers": -1, "synced_headers": -1,
"timeoffset": 0, "timeoffset": 0,
"transport_protocol_type": "v1" if not self.options.v2transport else "detecting", "transport_protocol_type": "v1" if not self.options.v2transport else "v2",
"version": 0, "version": 0,
}, },
) )

View File

@ -632,7 +632,7 @@ class TestNode():
assert_msg += "with expected error " + expected_msg assert_msg += "with expected error " + expected_msg
self._raise_assertion_error(assert_msg) self._raise_assertion_error(assert_msg)
def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=True, supports_v2_p2p=False, **kwargs): def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=True, supports_v2_p2p=None, wait_for_v2_handshake=True, **kwargs):
"""Add an inbound p2p connection to the node. """Add an inbound p2p connection to the node.
This method adds the p2p connection to the self.p2ps list and also This method adds the p2p connection to the self.p2ps list and also
@ -649,6 +649,9 @@ class TestNode():
kwargs['dstport'] = p2p_port(self.index) kwargs['dstport'] = p2p_port(self.index)
if 'dstaddr' not in kwargs: if 'dstaddr' not in kwargs:
kwargs['dstaddr'] = '127.0.0.1' kwargs['dstaddr'] = '127.0.0.1'
if supports_v2_p2p is None:
supports_v2_p2p = self.use_v2transport
p2p_conn.p2p_connected_to_node = True p2p_conn.p2p_connected_to_node = True
if self.use_v2transport: if self.use_v2transport:
@ -658,6 +661,8 @@ class TestNode():
self.p2ps.append(p2p_conn) self.p2ps.append(p2p_conn)
p2p_conn.wait_until(lambda: p2p_conn.is_connected, check_connected=False) p2p_conn.wait_until(lambda: p2p_conn.is_connected, check_connected=False)
if supports_v2_p2p and wait_for_v2_handshake:
p2p_conn.wait_until(lambda: p2p_conn.v2_state.tried_v2_handshake)
if send_version: if send_version:
p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg) p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
if wait_for_verack: if wait_for_verack:
@ -685,7 +690,7 @@ class TestNode():
return p2p_conn return p2p_conn
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): 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):
"""Add an outbound p2p connection from node. Must be an """Add an outbound p2p connection from node. Must be an
"outbound-full-relay", "block-relay-only", "addr-fetch" or "feeler" connection. "outbound-full-relay", "block-relay-only", "addr-fetch" or "feeler" connection.
@ -713,6 +718,11 @@ class TestNode():
self.addconnection('%s:%d' % (address, port), connection_type, advertise_v2_p2p) self.addconnection('%s:%d' % (address, port), connection_type, advertise_v2_p2p)
p2p_conn.p2p_connected_to_node = False p2p_conn.p2p_connected_to_node = False
if supports_v2_p2p is None:
supports_v2_p2p = self.use_v2transport
if advertise_v2_p2p is None:
advertise_v2_p2p = self.use_v2transport
if advertise_v2_p2p: if advertise_v2_p2p:
kwargs['services'] = kwargs.get('services', P2P_SERVICES) | NODE_P2P_V2 kwargs['services'] = kwargs.get('services', P2P_SERVICES) | NODE_P2P_V2
assert self.use_v2transport # only a v2 TestNode could make a v2 outbound connection assert self.use_v2transport # only a v2 TestNode could make a v2 outbound connection
@ -735,6 +745,8 @@ class TestNode():
p2p_conn.wait_for_connect() p2p_conn.wait_for_connect()
self.p2ps.append(p2p_conn) self.p2ps.append(p2p_conn)
if supports_v2_p2p:
p2p_conn.wait_until(lambda: p2p_conn.v2_state.tried_v2_handshake)
p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg) p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
if wait_for_verack: if wait_for_verack:
p2p_conn.wait_for_verack() p2p_conn.wait_for_verack()