Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2021-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""6Test v2 transport7"""8import socket10from test_framework.messages import MAGIC_BYTES, NODE_P2P_V211from test_framework.test_framework import BitcoinTestFramework12from test_framework.util import (13assert_not_equal,14assert_equal,15p2p_port,16assert_raises_rpc_error17)20class V2TransportTest(BitcoinTestFramework):21def set_test_params(self):22self.setup_clean_chain = True23self.num_nodes = 524self.extra_args = [["-v2transport=1"], ["-v2transport=1"], ["-v2transport=0"], ["-v2transport=0"], ["-v2transport=0"]]26def run_test(self):27sending_handshake = "start sending v2 handshake to peer"28downgrading_to_v1 = "retrying with v1 transport protocol for peer"29self.disconnect_nodes(0, 1)30self.disconnect_nodes(1, 2)31self.disconnect_nodes(2, 3)32self.disconnect_nodes(3, 4)34# verify local services35network_info = self.nodes[2].getnetworkinfo()36assert_equal(int(network_info["localservices"], 16) & NODE_P2P_V2, 0)37assert "P2P_V2" not in network_info["localservicesnames"]38network_info = self.nodes[1].getnetworkinfo()39assert_equal(int(network_info["localservices"], 16) & NODE_P2P_V2, NODE_P2P_V2)40assert "P2P_V2" in network_info["localservicesnames"]42# V2 nodes can sync with V2 nodes43assert_equal(self.nodes[0].getblockcount(), 0)44assert_equal(self.nodes[1].getblockcount(), 0)45with self.nodes[0].assert_debug_log(expected_msgs=[sending_handshake],46unexpected_msgs=[downgrading_to_v1]):47self.connect_nodes(0, 1, peer_advertises_v2=True)48self.generate(self.nodes[0], 5, sync_fun=lambda: self.sync_all(self.nodes[0:2]))49assert_equal(self.nodes[1].getblockcount(), 5)50# verify there is a v2 connection between node 0 and 151node_0_info = self.nodes[0].getpeerinfo()52node_1_info = self.nodes[1].getpeerinfo()53assert_equal(len(node_0_info), 1)54assert_equal(len(node_1_info), 1)55assert_equal(node_0_info[0]["transport_protocol_type"], "v2")56assert_equal(node_1_info[0]["transport_protocol_type"], "v2")57assert_equal(len(node_0_info[0]["session_id"]), 64)58assert_equal(len(node_1_info[0]["session_id"]), 64)59assert_equal(node_0_info[0]["session_id"], node_1_info[0]["session_id"])61# V1 nodes can sync with each other62assert_equal(self.nodes[2].getblockcount(), 0)63assert_equal(self.nodes[3].getblockcount(), 0)65# addnode rpc error when v2transport requested but not enabled66ip_port = "127.0.0.1:{}".format(p2p_port(3))67assert_raises_rpc_error(-8, "Error: v2transport requested but not enabled (see -v2transport)", self.nodes[2].addnode, node=ip_port, command='add', v2transport=True)69with self.nodes[2].assert_debug_log(expected_msgs=[],70unexpected_msgs=[sending_handshake, downgrading_to_v1]):71self.connect_nodes(2, 3, peer_advertises_v2=False)72self.generate(self.nodes[2], 8, sync_fun=lambda: self.sync_all(self.nodes[2:4]))73assert_equal(self.nodes[3].getblockcount(), 8)74assert_not_equal(self.nodes[0].getbestblockhash(), self.nodes[2].getbestblockhash())75# verify there is a v1 connection between node 2 and 376node_2_info = self.nodes[2].getpeerinfo()77node_3_info = self.nodes[3].getpeerinfo()78assert_equal(len(node_2_info), 1)79assert_equal(len(node_3_info), 1)80assert_equal(node_2_info[0]["transport_protocol_type"], "v1")81assert_equal(node_3_info[0]["transport_protocol_type"], "v1")82assert_equal(len(node_2_info[0]["session_id"]), 0)83assert_equal(len(node_3_info[0]["session_id"]), 0)85# V1 nodes can sync with V2 nodes86self.disconnect_nodes(0, 1)87self.disconnect_nodes(2, 3)88with self.nodes[2].assert_debug_log(expected_msgs=[],89unexpected_msgs=[sending_handshake, downgrading_to_v1]):90self.connect_nodes(2, 1, peer_advertises_v2=False) # cannot enable v2 on v1 node91self.sync_all(self.nodes[1:3])92assert_equal(self.nodes[1].getblockcount(), 8)93assert_not_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())94# verify there is a v1 connection between node 1 and 295node_1_info = self.nodes[1].getpeerinfo()96node_2_info = self.nodes[2].getpeerinfo()97assert_equal(len(node_1_info), 1)98assert_equal(len(node_2_info), 1)99assert_equal(node_1_info[0]["transport_protocol_type"], "v1")100assert_equal(node_2_info[0]["transport_protocol_type"], "v1")101assert_equal(len(node_1_info[0]["session_id"]), 0)102assert_equal(len(node_2_info[0]["session_id"]), 0)104# V2 nodes can sync with V1 nodes105self.disconnect_nodes(1, 2)106with self.nodes[0].assert_debug_log(expected_msgs=[],107unexpected_msgs=[sending_handshake, downgrading_to_v1]):108self.connect_nodes(0, 3, peer_advertises_v2=False)109self.sync_all([self.nodes[0], self.nodes[3]])110assert_equal(self.nodes[0].getblockcount(), 8)111# verify there is a v1 connection between node 0 and 3112node_0_info = self.nodes[0].getpeerinfo()113node_3_info = self.nodes[3].getpeerinfo()114assert_equal(len(node_0_info), 1)115assert_equal(len(node_3_info), 1)116assert_equal(node_0_info[0]["transport_protocol_type"], "v1")117assert_equal(node_3_info[0]["transport_protocol_type"], "v1")118assert_equal(len(node_0_info[0]["session_id"]), 0)119assert_equal(len(node_3_info[0]["session_id"]), 0)121# V2 node mines another block and everyone gets it122self.connect_nodes(0, 1, peer_advertises_v2=True)123self.connect_nodes(1, 2, peer_advertises_v2=False)124self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:4]))125assert_equal(self.nodes[0].getblockcount(), 9) # sync_all() verifies tip hashes match127# V1 node mines another block and everyone gets it128self.generate(self.nodes[3], 2, sync_fun=lambda: self.sync_all(self.nodes[0:4]))129assert_equal(self.nodes[2].getblockcount(), 11) # sync_all() verifies tip hashes match131assert_equal(self.nodes[4].getblockcount(), 0)132# Peer 4 is v1 p2p, but is falsely advertised as v2.133with self.nodes[1].assert_debug_log(expected_msgs=[sending_handshake, downgrading_to_v1]):134self.connect_nodes(1, 4, peer_advertises_v2=True)135self.sync_all()136assert_equal(self.nodes[4].getblockcount(), 11)138# Check v1 prefix detection139V1_PREFIX = MAGIC_BYTES["regtest"] + b"version\x00\x00\x00\x00\x00"140assert_equal(len(V1_PREFIX), 16)141with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:142with self.nodes[0].wait_for_new_peer():143s.connect(("127.0.0.1", p2p_port(0)))144s.sendall(V1_PREFIX[:-1])145assert_equal(self.nodes[0].getpeerinfo()[-1]["transport_protocol_type"], "detecting")146s.sendall(bytes([V1_PREFIX[-1]])) # send out last prefix byte147self.wait_until(lambda: self.nodes[0].getpeerinfo()[-1]["transport_protocol_type"] == "v1")149# Check wrong network prefix detection (hits if the next 12 bytes correspond to a v1 version message)150wrong_network_magic_prefix = MAGIC_BYTES["signet"] + V1_PREFIX[4:]151with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:152with self.nodes[0].wait_for_new_peer():153s.connect(("127.0.0.1", p2p_port(0)))154with self.nodes[0].assert_debug_log(["V2 transport error: V1 peer with wrong MessageStart"], timeout=2):155s.sendall(wrong_network_magic_prefix + b"somepayload")157# Check detection of missing garbage terminator (hits after fixed amount of data if terminator never matches garbage)158MAX_KEY_GARB_AND_GARBTERM_LEN = 64 + 4095 + 16159with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:160with self.nodes[0].wait_for_new_peer():161s.connect(("127.0.0.1", p2p_port(0)))162s.sendall(b'\x00' * (MAX_KEY_GARB_AND_GARBTERM_LEN - 1))163self.wait_until(lambda: self.nodes[0].getpeerinfo()[-1]["bytesrecv"] == MAX_KEY_GARB_AND_GARBTERM_LEN - 1)164with self.nodes[0].assert_debug_log(["V2 transport error: missing garbage terminator"]):165peer_id = self.nodes[0].getpeerinfo()[-1]["id"]166s.sendall(b'\x00') # send out last byte167# should disconnect immediately168self.wait_until(lambda: peer_id not in [p["id"] for p in self.nodes[0].getpeerinfo()])171if __name__ == '__main__':172V2TransportTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.