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) 2017-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"""Test message sending before handshake completion.7Before receiving a VERACK, a node should not send anything but VERSION/VERACK8and feature negotiation messages (WTXIDRELAY, SENDADDRV2, FEATURE).10This test connects to a node and sends it a few messages, trying to entice it11into sending us something it shouldn't."""13import time15from test_framework.messages import (16msg_getaddr,17msg_ping,18msg_version,19)20from test_framework.p2p import (21P2PInterface,22P2P_SUBVERSION,23P2P_SERVICES,24P2P_VERSION_RELAY,25)26from test_framework.test_framework import BitcoinTestFramework27from test_framework.util import (28assert_equal,29assert_greater_than_or_equal,30)32PEER_TIMEOUT = 335class LazyPeer(P2PInterface):36def __init__(self):37super().__init__()38self.unexpected_msg = False39self.ever_connected = False40self.got_wtxidrelay = False41self.got_sendaddrv2 = False42self.got_feature = False44def bad_message(self, message):45self.unexpected_msg = True46print("should not have received message: %s" % message.msgtype)48def on_open(self):49self.ever_connected = True51# Does not respond to "version" with "verack"52def on_version(self, message): self.bad_message(message)53def on_verack(self, message): self.bad_message(message)54def on_inv(self, message): self.bad_message(message)55def on_addr(self, message): self.bad_message(message)56def on_getdata(self, message): self.bad_message(message)57def on_getblocks(self, message): self.bad_message(message)58def on_tx(self, message): self.bad_message(message)59def on_block(self, message): self.bad_message(message)60def on_getaddr(self, message): self.bad_message(message)61def on_headers(self, message): self.bad_message(message)62def on_getheaders(self, message): self.bad_message(message)63def on_ping(self, message): self.bad_message(message)64def on_mempool(self, message): self.bad_message(message)65def on_pong(self, message): self.bad_message(message)66def on_feefilter(self, message): self.bad_message(message)67def on_sendheaders(self, message): self.bad_message(message)68def on_sendcmpct(self, message): self.bad_message(message)69def on_cmpctblock(self, message): self.bad_message(message)70def on_getblocktxn(self, message): self.bad_message(message)71def on_blocktxn(self, message): self.bad_message(message)72def on_wtxidrelay(self, message): self.got_wtxidrelay = True73def on_sendaddrv2(self, message): self.got_sendaddrv2 = True74def on_feature(self, message): self.got_feature = True77# Peer that sends a version but not a verack.78class NoVerackIdlePeer(LazyPeer):79def __init__(self):80self.version_received = False81super().__init__()83def on_verack(self, message): pass84# When version is received, don't reply with a verack. Instead, see if the85# node will give us a message that it shouldn't. This is not an exhaustive86# list!87def on_version(self, message):88self.version_received = True89self.send_without_ping(msg_ping())90self.send_without_ping(msg_getaddr())93class P2PVersionStore(P2PInterface):94version_received = None96def on_version(self, msg):97# Responds with an appropriate verack98super().on_version(msg)99self.version_received = msg102class P2PLeakTest(BitcoinTestFramework):103def set_test_params(self):104self.num_nodes = 1105self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]107def create_old_version(self, nversion):108old_version_msg = msg_version()109old_version_msg.nVersion = nversion110old_version_msg.strSubVer = P2P_SUBVERSION111old_version_msg.nServices = P2P_SERVICES112old_version_msg.relay = P2P_VERSION_RELAY113return old_version_msg115def run_test(self):116self.log.info('Check that the node doesn\'t send unexpected messages before handshake completion')117# Peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.118no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)120# Peer that sends a version but not a verack.121no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)123# Pre-wtxidRelay peer that sends a version but not a verack and does not support feature negotiation124# messages which start at nVersion >= 70016125pre_wtxidrelay_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), send_version=False, wait_for_verack=False)126pre_wtxidrelay_peer.send_without_ping(self.create_old_version(70015))128# Wait until the peer gets the verack in response to the version. Though, don't wait for the node to receive the129# verack, since the peer never sent one130no_verack_idle_peer.wait_for_verack()131pre_wtxidrelay_peer.wait_for_verack()133no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)134no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)135pre_wtxidrelay_peer.wait_until(lambda: pre_wtxidrelay_peer.version_received)137# Mine a block and make sure that it's not sent to the connected peers138self.generate(self.nodes[0], nblocks=1)140# Give the node enough time to possibly leak out a message141time.sleep(PEER_TIMEOUT + 2)143self.log.info("Connect peer to ensure the net thread runs the disconnect logic at least once")144self.nodes[0].add_p2p_connection(P2PInterface())146# Make sure only expected messages came in147assert not no_version_idle_peer.unexpected_msg148assert not no_version_idle_peer.got_wtxidrelay149assert not no_version_idle_peer.got_sendaddrv2150assert not no_version_idle_peer.got_feature152assert not no_verack_idle_peer.unexpected_msg153assert no_verack_idle_peer.got_wtxidrelay154assert no_verack_idle_peer.got_sendaddrv2155#assert no_verack_idle_peer.got_feature ## uncomment once a feature message is supported157assert not pre_wtxidrelay_peer.unexpected_msg158assert not pre_wtxidrelay_peer.got_wtxidrelay159assert not pre_wtxidrelay_peer.got_sendaddrv2160assert not pre_wtxidrelay_peer.got_feature162# Expect peers to be disconnected due to timeout163assert not no_version_idle_peer.is_connected164assert not no_verack_idle_peer.is_connected165assert not pre_wtxidrelay_peer.is_connected167self.log.info('Check that the version message does not leak the local address of the node')168p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())169ver = p2p_version_store.version_received170# Check that received time is within one hour of now171assert_greater_than_or_equal(ver.nTime, time.time() - 3600)172assert_greater_than_or_equal(time.time() + 3600, ver.nTime)173assert_equal(ver.addrFrom.port, 0)174assert_equal(ver.addrFrom.ip, '0.0.0.0')175assert_equal(ver.nStartingHeight, 201)176assert_equal(ver.relay, 1)178self.log.info('Check that old peers are disconnected')179p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)180with self.nodes[0].assert_debug_log(["using obsolete version 31799, disconnecting peer=5"]):181p2p_old_peer.send_without_ping(self.create_old_version(31799))182p2p_old_peer.wait_for_disconnect()185if __name__ == '__main__':186P2PLeakTest(__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.