Skip to content

Commit 411ba32

Browse files
committed
Merge bitcoin#24748: test/BIP324: functional tests for v2 P2P encryption
bc9283c [test] Add functional test to test early key response behaviour in BIP 324 (stratospher) ffe6a56 [test] Check whether v2 TestNode performs downgrading (stratospher) ba73735 [test] Add functional tests to test v2 P2P behaviour (stratospher) 4115cf9 [test] Ignore BIP324 decoy messages (stratospher) 8c054aa [test] Allow inbound and outbound connections supporting v2 P2P protocol (stratospher) 382894c [test] Reconnect using v1 P2P when v2 P2P terminates due to magic byte mismatch (stratospher) a94e350 [test] Build v2 P2P messages (stratospher) bb7bffe [test] Use lock for sending P2P messages in test framework (stratospher) 5b91fb1 [test] Read v2 P2P messages (stratospher) 05bddb2 [test] Perform initial v2 handshake (stratospher) a049d1b [test] Introduce EncryptedP2PState object in P2PConnection (stratospher) b89fa59 [test] Construct class to handle v2 P2P protocol functions (stratospher) 8d6c848 [test] Move MAGIC_BYTES to messages.py (stratospher) 595ad4b [test/crypto] Add ECDH (stratospher) 4487b80 [rpc/net] Allow v2 p2p support in addconnection (stratospher) Pull request description: This PR introduces support for v2 P2P encryption(BIP 324) in the existing functional test framework and adds functional tests for the same. ### commits overview 1. introduces a new class `EncryptedP2PState` to store the keys, functions for performing the initial v2 handshake and encryption/decryption. 3. this class is used by `P2PConnection` in inbound/outbound connections to perform the initial v2 handshake before the v1 version handshake. Only after the initial v2 handshake is performed do application layer P2P messages(version, verack etc..) get exchanged. (in a v2 connection) - `v2_state` is the object of class `EncryptedP2PState` in `P2PConnection` used to store its keys, session-id etc. - a node [advertising](https://github.com/stratospher/blogosphere/blob/main/integration_test_bip324.md#advertising-to-support-v2-p2p) support for v2 P2P is different from a node actually [supporting v2 P2P](https://github.com/stratospher/blogosphere/blob/main/integration_test_bip324.md#supporting-v2-p2p) (differ when false advertisement of services occur) - introduce a boolean variable `supports_v2_p2p` in `P2PConnection` to denote if it supports v2 P2P. - introduce a boolean variable `advertises_v2_p2p` to denote whether `P2PConnection` which mimics peer behaviour advertises V2 P2P support. Default option is `False`. - In the test framework, you can create Inbound and Outbound connections to `TestNode` 1. During **Inbound Connections**, `P2PConnection` is the initiator [`TestNode` <--------- `P2PConnection`] - Case 1: - if the `TestNode` advertises/signals v2 P2P support (means `self.nodes[i]` set up with `"-v2transport=1"`), different behaviour will be exhibited based on whether: 1. `P2PConnection` supports v2 P2P 2. `P2PConnection` does not support v2 P2P - In a real world scenario, the initiator node would intrinsically know if they support v2 P2P based on whatever code they choose to run. However, in the test scenario where we mimic peer behaviour, we have no way of knowing if `P2PConnection` should support v2 P2P or not. So `supports_v2_p2p` boolean variable is used as an option to enable support for v2 P2P in `P2PConnection`. - Since the `TestNode` advertises v2 P2P support (using "-v2transport=1"), our initiator `P2PConnection` would send: 1. (if the `P2PConnection` supports v2 P2P) ellswift + garbage bytes to initiate the connection 2. (if the `P2PConnection` does not support v2 P2P) version message to initiate the connection - Case 2: - if the `TestNode` doesn't signal v2 P2P support; `P2PConnection` being the initiator would send version message to initiate a connection. 2. During **Outbound Connections** [TestNode --------> P2PConnection] - initiator `TestNode` would send: - (if the `P2PConnection` advertises v2 P2P) ellswift + garbage bytes to initiate the connection - (if the `P2PConnection` advertises v2 P2P) version message to initiate the connection - Suppose `P2PConnection` advertises v2 P2P support when it actually doesn't support v2 P2P (false advertisement scenario) - `TestNode` sends ellswift + garbage bytes - `P2PConnection` receives but can't process it and disconnects. - `TestNode` then tries using v1 P2P and sends version message - `P2PConnection` receives/processes this successfully and they communicate on v1 P2P 4. the encrypted P2P messages follow a different format - 3 byte length + 1-13 byte message_type + payload + 16 byte MAC 5. includes support for testing decoy messages and v2 connection downgrade(using false advertisement - when a v2 node makes an outbound connection to a node which doesn't support v2 but is advertised as v2 by some malicious intermediary) ### run the tests * functional test - `test/functional/p2p_v2_encrypted.py` `test/functional/p2p_v2_earlykeyresponse.py` I'm also super grateful to @ dhruv for his really valuable feedback on this branch. Also written a more elaborate explanation here - https://github.com/stratospher/blogosphere/blob/main/integration_test_bip324.md ACKs for top commit: naumenkogs: ACK bc9283c mzumsande: Code Review ACK bc9283c theStack: Code-review ACK bc9283c glozow: ACK bc9283c Tree-SHA512: 9b54ed27e925e1775e0e0d35e959cdbf2a9a1aab7bcf5d027e66f8b59780bdd0458a7a4311ddc7dd67657a4a2a2cd5034ead75524420d58a83f642a8304c9811
2 parents 87fcc93 + bc9283c commit 411ba32

15 files changed

+752
-66
lines changed

src/net.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
18271827
RandAddEvent((uint32_t)id);
18281828
}
18291829

1830-
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
1830+
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type, bool use_v2transport = false)
18311831
{
18321832
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
18331833
std::optional<int> max_connections;
@@ -1860,7 +1860,7 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ
18601860
CSemaphoreGrant grant(*semOutbound, true);
18611861
if (!grant) return false;
18621862

1863-
OpenNetworkConnection(CAddress(), false, std::move(grant), address.c_str(), conn_type, /*use_v2transport=*/false);
1863+
OpenNetworkConnection(CAddress(), false, std::move(grant), address.c_str(), conn_type, /*use_v2transport=*/use_v2transport);
18641864
return true;
18651865
}
18661866

src/net.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,13 +1189,14 @@ class CConnman
11891189
* @param[in] address Address of node to try connecting to
11901190
* @param[in] conn_type ConnectionType::OUTBOUND, ConnectionType::BLOCK_RELAY,
11911191
* ConnectionType::ADDR_FETCH or ConnectionType::FEELER
1192+
* @param[in] use_v2transport Set to true if node attempts to connect using BIP 324 v2 transport protocol.
11921193
* @return bool Returns false if there are no available
11931194
* slots for this connection:
11941195
* - conn_type not a supported ConnectionType
11951196
* - Max total outbound connection capacity filled
11961197
* - Max connection capacity for type is filled
11971198
*/
1198-
bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
1199+
bool AddConnection(const std::string& address, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
11991200

12001201
size_t GetNodeCount(ConnectionDirection) const;
12011202
uint32_t GetMappedAS(const CNetAddr& addr) const;

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
303303
{ "sendmsgtopeer", 0, "peer_id" },
304304
{ "stop", 0, "wait" },
305305
{ "addnode", 2, "v2transport" },
306+
{ "addconnection", 2, "v2transport" },
306307
};
307308
// clang-format on
308309

src/rpc/net.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ static RPCHelpMan addconnection()
371371
{
372372
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."},
373373
{"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\", \"addr-fetch\" or \"feeler\")."},
374+
{"v2transport", RPCArg::Type::BOOL, RPCArg::Default{false}, "Attempt to connect using BIP324 v2 transport protocol"},
374375
},
375376
RPCResult{
376377
RPCResult::Type::OBJ, "", "",
@@ -379,8 +380,8 @@ static RPCHelpMan addconnection()
379380
{ RPCResult::Type::STR, "connection_type", "Type of connection opened." },
380381
}},
381382
RPCExamples{
382-
HelpExampleCli("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\"")
383-
+ HelpExampleRpc("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\"")
383+
HelpExampleCli("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true")
384+
+ HelpExampleRpc("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true")
384385
},
385386
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
386387
{
@@ -402,11 +403,16 @@ static RPCHelpMan addconnection()
402403
} else {
403404
throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString());
404405
}
406+
bool use_v2transport = !request.params[2].isNull() && request.params[2].get_bool();
405407

406408
NodeContext& node = EnsureAnyNodeContext(request.context);
407409
CConnman& connman = EnsureConnman(node);
408410

409-
const bool success = connman.AddConnection(address, conn_type);
411+
if (use_v2transport && !(connman.GetLocalServices() & NODE_P2P_V2)) {
412+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Adding v2transport connections requires -v2transport init flag to be set.");
413+
}
414+
415+
const bool success = connman.AddConnection(address, conn_type, use_v2transport);
410416
if (!success) {
411417
throw JSONRPCError(RPC_CLIENT_NODE_CAPACITY_REACHED, "Error: Already at capacity for specified connection type.");
412418
}

test/functional/feature_addrman.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88
import re
99
import struct
1010

11-
from test_framework.messages import ser_uint256, hash256
11+
from test_framework.messages import ser_uint256, hash256, MAGIC_BYTES
1212
from test_framework.netutil import ADDRMAN_NEW_BUCKET_COUNT, ADDRMAN_TRIED_BUCKET_COUNT, ADDRMAN_BUCKET_SIZE
13-
from test_framework.p2p import MAGIC_BYTES
1413
from test_framework.test_framework import BitcoinTestFramework
1514
from test_framework.test_node import ErrorMatch
1615
from test_framework.util import assert_equal

test/functional/feature_reindex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"""
1212

1313
from test_framework.test_framework import BitcoinTestFramework
14-
from test_framework.p2p import MAGIC_BYTES
14+
from test_framework.messages import MAGIC_BYTES
1515
from test_framework.util import assert_equal
1616

1717

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
import random
7+
8+
from test_framework.test_framework import BitcoinTestFramework
9+
from test_framework.crypto.ellswift import ellswift_create
10+
from test_framework.p2p import P2PInterface
11+
from test_framework.v2_p2p import EncryptedP2PState
12+
13+
14+
class TestEncryptedP2PState(EncryptedP2PState):
15+
""" Modify v2 P2P protocol functions for testing that "The responder waits until one byte is received which does
16+
not match the 16 bytes consisting of the network magic followed by "version\x00\x00\x00\x00\x00"." (see BIP 324)
17+
18+
- if `send_net_magic` is True, send first 4 bytes of ellswift (match network magic) else send remaining 60 bytes
19+
- `can_data_be_received` is a variable used to assert if data is received on recvbuf.
20+
- v2 TestNode shouldn't respond back if we send V1_PREFIX and data shouldn't be received on recvbuf.
21+
This state is represented using `can_data_be_received` = False.
22+
- v2 TestNode responds back when mismatch from V1_PREFIX happens and data can be received on recvbuf.
23+
This state is represented using `can_data_be_received` = True.
24+
"""
25+
26+
def __init__(self):
27+
super().__init__(initiating=True, net='regtest')
28+
self.send_net_magic = True
29+
self.can_data_be_received = False
30+
31+
def initiate_v2_handshake(self, garbage_len=random.randrange(4096)):
32+
"""Initiator begins the v2 handshake by sending its ellswift bytes and garbage.
33+
Here, the 64 bytes ellswift is assumed to have it's 4 bytes match network magic bytes. It is sent in 2 phases:
34+
1. when `send_network_magic` = True, send first 4 bytes of ellswift (matches network magic bytes)
35+
2. when `send_network_magic` = False, send remaining 60 bytes of ellswift
36+
"""
37+
if self.send_net_magic:
38+
self.privkey_ours, self.ellswift_ours = ellswift_create()
39+
self.sent_garbage = random.randbytes(garbage_len)
40+
self.send_net_magic = False
41+
return b"\xfa\xbf\xb5\xda"
42+
else:
43+
self.can_data_be_received = True
44+
return self.ellswift_ours[4:] + self.sent_garbage
45+
46+
47+
class PeerEarlyKey(P2PInterface):
48+
"""Custom implementation of P2PInterface which uses modified v2 P2P protocol functions for testing purposes."""
49+
def __init__(self):
50+
super().__init__()
51+
self.v2_state = None
52+
53+
def connection_made(self, transport):
54+
"""64 bytes ellswift is sent in 2 parts during `initial_v2_handshake()`"""
55+
self.v2_state = TestEncryptedP2PState()
56+
super().connection_made(transport)
57+
58+
def data_received(self, t):
59+
# check that data can be received on recvbuf only when mismatch from V1_PREFIX happens (send_net_magic = False)
60+
assert self.v2_state.can_data_be_received and not self.v2_state.send_net_magic
61+
62+
63+
class P2PEarlyKey(BitcoinTestFramework):
64+
def set_test_params(self):
65+
self.num_nodes = 1
66+
self.extra_args = [["-v2transport=1", "-peertimeout=3"]]
67+
68+
def run_test(self):
69+
self.log.info('Sending ellswift bytes in parts to ensure that response from responder is received only when')
70+
self.log.info('ellswift bytes have a mismatch from the 16 bytes(network magic followed by "version\\x00\\x00\\x00\\x00\\x00")')
71+
node0 = self.nodes[0]
72+
self.log.info('Sending first 4 bytes of ellswift which match network magic')
73+
self.log.info('If a response is received, assertion failure would happen in our custom data_received() function')
74+
# send happens in `initiate_v2_handshake()` in `connection_made()`
75+
peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True)
76+
self.log.info('Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is')
77+
self.log.info('expected now, our custom data_received() function wouldn\'t result in assertion failure')
78+
ellswift_and_garbage_data = peer1.v2_state.initiate_v2_handshake()
79+
peer1.send_raw_message(ellswift_and_garbage_data)
80+
peer1.wait_for_disconnect(timeout=5)
81+
self.log.info('successful disconnection when MITM happens in the key exchange phase')
82+
83+
84+
if __name__ == '__main__':
85+
P2PEarlyKey().main()

test/functional/p2p_v2_encrypted.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""
6+
Test encrypted v2 p2p proposed in BIP 324
7+
"""
8+
from test_framework.blocktools import (
9+
create_block,
10+
create_coinbase,
11+
)
12+
from test_framework.p2p import (
13+
P2PDataStore,
14+
P2PInterface,
15+
)
16+
from test_framework.test_framework import BitcoinTestFramework
17+
from test_framework.util import (
18+
assert_equal,
19+
assert_greater_than,
20+
check_node_connections,
21+
)
22+
from test_framework.crypto.chacha20 import REKEY_INTERVAL
23+
24+
25+
class P2PEncrypted(BitcoinTestFramework):
26+
def set_test_params(self):
27+
self.num_nodes = 2
28+
self.extra_args = [["-v2transport=1"], ["-v2transport=1"]]
29+
30+
def setup_network(self):
31+
self.setup_nodes()
32+
33+
def generate_blocks(self, node, number):
34+
test_blocks = []
35+
last_block = node.getbestblockhash()
36+
tip = int(last_block, 16)
37+
tipheight = node.getblockcount()
38+
last_block_time = node.getblock(last_block)['time']
39+
for _ in range(number):
40+
# Create some blocks
41+
block = create_block(tip, create_coinbase(tipheight + 1), last_block_time + 1)
42+
block.solve()
43+
test_blocks.append(block)
44+
tip = block.sha256
45+
tipheight += 1
46+
last_block_time += 1
47+
return test_blocks
48+
49+
def create_test_block(self, txs):
50+
block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600, txlist=txs)
51+
block.solve()
52+
return block
53+
54+
def run_test(self):
55+
node0, node1 = self.nodes[0], self.nodes[1]
56+
self.log.info("Check inbound connection to v2 TestNode from v2 P2PConnection is v2")
57+
peer1 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=True)
58+
assert peer1.supports_v2_p2p
59+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
60+
61+
self.log.info("Check inbound connection to v2 TestNode from v1 P2PConnection is v1")
62+
peer2 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=False)
63+
assert not peer2.supports_v2_p2p
64+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
65+
66+
self.log.info("Check outbound connection from v2 TestNode to v1 P2PConnection advertised as v1 is v1")
67+
peer3 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, supports_v2_p2p=False, advertise_v2_p2p=False)
68+
assert not peer3.supports_v2_p2p
69+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
70+
71+
# v2 TestNode performs downgrading here
72+
self.log.info("Check outbound connection from v2 TestNode to v1 P2PConnection advertised as v2 is v1")
73+
peer4 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, supports_v2_p2p=False, advertise_v2_p2p=True)
74+
assert not peer4.supports_v2_p2p
75+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
76+
77+
self.log.info("Check outbound connection from v2 TestNode to v2 P2PConnection advertised as v2 is v2")
78+
peer5 = node0.add_outbound_p2p_connection(P2PInterface(), p2p_idx=2, supports_v2_p2p=True, advertise_v2_p2p=True)
79+
assert peer5.supports_v2_p2p
80+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
81+
82+
self.log.info("Check if version is sent and verack is received in inbound/outbound connections")
83+
assert_equal(len(node0.getpeerinfo()), 5) # check if above 5 connections are present in node0's getpeerinfo()
84+
for peer in node0.getpeerinfo():
85+
assert_greater_than(peer['bytessent_per_msg']['version'], 0)
86+
assert_greater_than(peer['bytesrecv_per_msg']['verack'], 0)
87+
88+
self.log.info("Testing whether blocks propagate - check if tips sync when number of blocks >= REKEY_INTERVAL")
89+
# tests whether rekeying (which happens every REKEY_INTERVAL packets) works correctly
90+
test_blocks = self.generate_blocks(node0, REKEY_INTERVAL+1)
91+
92+
for i in range(2):
93+
peer6 = node0.add_p2p_connection(P2PDataStore(), supports_v2_p2p=True)
94+
assert peer6.supports_v2_p2p
95+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v2")
96+
97+
# Consider: node0 <-- peer6. node0 and node1 aren't connected here.
98+
# Construct the following topology: node1 <--> node0 <-- peer6
99+
# and test that blocks produced by peer6 will be received by node1 if sent normally
100+
# and won't be received by node1 if sent as decoy messages
101+
102+
# First, check whether blocks produced be peer6 are received by node0 if sent normally
103+
# and not received by node0 if sent as decoy messages.
104+
if i:
105+
# check that node0 receives blocks produced by peer6
106+
self.log.info("Check if blocks produced by node0's p2p connection is received by node0")
107+
peer6.send_blocks_and_test(test_blocks, node0, success=True) # node0's tip advances
108+
else:
109+
# check that node0 doesn't receive blocks produced by peer6 since they are sent as decoy messages
110+
self.log.info("Check if blocks produced by node0's p2p connection sent as decoys aren't received by node0")
111+
peer6.send_blocks_and_test(test_blocks, node0, success=False, is_decoy=True) # node0's tip doesn't advance
112+
113+
# Then, connect node0 and node1 using v2 and check whether the blocks are received by node1
114+
self.connect_nodes(0, 1, peer_advertises_v2=True)
115+
self.log.info("Wait for node1 to receive all the blocks from node0")
116+
self.sync_all()
117+
self.log.info("Make sure node0 and node1 have same block tips")
118+
assert_equal(node0.getbestblockhash(), node1.getbestblockhash())
119+
120+
self.disconnect_nodes(0, 1)
121+
122+
self.log.info("Check the connections opened as expected")
123+
check_node_connections(node=node0, num_in=4, num_out=3)
124+
125+
self.log.info("Check inbound connection to v1 TestNode from v2 P2PConnection is v1")
126+
self.restart_node(0, ["-v2transport=0"])
127+
peer1 = node0.add_p2p_connection(P2PInterface(), wait_for_verack=True, supports_v2_p2p=True)
128+
assert not peer1.supports_v2_p2p
129+
assert_equal(node0.getpeerinfo()[-1]["transport_protocol_type"], "v1")
130+
check_node_connections(node=node0, num_in=1, num_out=0)
131+
132+
133+
if __name__ == '__main__':
134+
P2PEncrypted().main()

test/functional/p2p_v2_transport.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
"""
88
import socket
99

10-
from test_framework.messages import NODE_P2P_V2
11-
from test_framework.p2p import MAGIC_BYTES
10+
from test_framework.messages import MAGIC_BYTES, NODE_P2P_V2
1211
from test_framework.test_framework import BitcoinTestFramework
1312
from test_framework.util import (
1413
assert_equal,

test/functional/rpc_net.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,10 @@ def test_addnode_getaddednodeinfo(self):
244244
def test_service_flags(self):
245245
self.log.info("Test service flags")
246246
self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63))
247-
assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
247+
if self.options.v2transport:
248+
assert_equal(['UNKNOWN[2^4]', 'P2P_V2', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
249+
else:
250+
assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
248251
self.nodes[0].disconnect_p2ps()
249252

250253
def test_getnodeaddresses(self):

test/functional/test_framework/messages.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@
7575

7676
DEFAULT_MEMPOOL_EXPIRY_HOURS = 336 # hours
7777

78+
MAGIC_BYTES = {
79+
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
80+
"testnet3": b"\x0b\x11\x09\x07", # testnet3
81+
"regtest": b"\xfa\xbf\xb5\xda", # regtest
82+
"signet": b"\x0a\x03\xcf\x40", # signet
83+
}
84+
7885
def sha256(s):
7986
return hashlib.sha256(s).digest()
8087

0 commit comments

Comments
 (0)