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) 2022-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 SENDTXRCNCL message6"""8from test_framework.messages import (9msg_sendtxrcncl,10msg_verack,11msg_version,12msg_wtxidrelay,13NODE_BLOOM,14)15from test_framework.p2p import (16P2PInterface,17P2P_SERVICES,18P2P_SUBVERSION,19P2P_VERSION,20)21from test_framework.test_framework import BitcoinTestFramework22from test_framework.util import (23assert_equal,24assert_not_equal,25)27class PeerNoVerack(P2PInterface):28def __init__(self, wtxidrelay=True):29super().__init__(wtxidrelay=wtxidrelay)31def on_version(self, message):32# Avoid sending verack in response to version.33# When calling add_p2p_connection, wait_for_verack=False must be set (see34# comment in add_p2p_connection).35self.send_version()36if message.nVersion >= 70016 and self.wtxidrelay:37self.send_without_ping(msg_wtxidrelay())39class SendTxrcnclReceiver(P2PInterface):40def __init__(self):41super().__init__()42self.sendtxrcncl_msg_received = None44def on_sendtxrcncl(self, message):45self.sendtxrcncl_msg_received = message48class P2PFeelerReceiver(SendTxrcnclReceiver):49def on_version(self, message):50# feeler connections can not send any message other than their own version51self.send_version()54class PeerTrackMsgOrder(P2PInterface):55def __init__(self):56super().__init__()57self.messages = []59def on_message(self, message):60super().on_message(message)61self.messages.append(message)63def create_sendtxrcncl_msg():64sendtxrcncl_msg = msg_sendtxrcncl()65sendtxrcncl_msg.version = 166sendtxrcncl_msg.salt = 267return sendtxrcncl_msg69class SendTxRcnclTest(BitcoinTestFramework):70def set_test_params(self):71self.num_nodes = 172self.extra_args = [['-txreconciliation']]74def run_test(self):75# Check everything concerning *sending* SENDTXRCNCL76# First, *sending* to *inbound*.77self.log.info('SENDTXRCNCL sent to an inbound')78peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)79assert peer.sendtxrcncl_msg_received80assert_equal(peer.sendtxrcncl_msg_received.version, 1)81self.nodes[0].disconnect_p2ps()83self.log.info('SENDTXRCNCL should be sent before VERACK')84peer = self.nodes[0].add_p2p_connection(PeerTrackMsgOrder(), send_version=True, wait_for_verack=True)85peer.wait_for_verack()86verack_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'verack'][0]87sendtxrcncl_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'sendtxrcncl'][0]88assert sendtxrcncl_index < verack_index89self.nodes[0].disconnect_p2ps()91self.log.info('SENDTXRCNCL on pre-WTXID version should not be sent')92peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)93pre_wtxid_version_msg = msg_version()94pre_wtxid_version_msg.nVersion = 7001595pre_wtxid_version_msg.strSubVer = P2P_SUBVERSION96pre_wtxid_version_msg.nServices = P2P_SERVICES97pre_wtxid_version_msg.relay = 198peer.send_without_ping(pre_wtxid_version_msg)99peer.wait_for_verack()100assert not peer.sendtxrcncl_msg_received101self.nodes[0].disconnect_p2ps()103self.log.info('SENDTXRCNCL for fRelay=false should not be sent')104peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)105no_txrelay_version_msg = msg_version()106no_txrelay_version_msg.nVersion = P2P_VERSION107no_txrelay_version_msg.strSubVer = P2P_SUBVERSION108no_txrelay_version_msg.nServices = P2P_SERVICES109no_txrelay_version_msg.relay = 0110peer.send_without_ping(no_txrelay_version_msg)111peer.wait_for_verack()112assert not peer.sendtxrcncl_msg_received113self.nodes[0].disconnect_p2ps()115self.log.info('SENDTXRCNCL for fRelay=false should not be sent (with NODE_BLOOM offered)')116self.restart_node(0, ["-peerbloomfilters", "-txreconciliation"])117peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)118no_txrelay_version_msg = msg_version()119no_txrelay_version_msg.nVersion = P2P_VERSION120no_txrelay_version_msg.strSubVer = P2P_SUBVERSION121no_txrelay_version_msg.nServices = P2P_SERVICES122no_txrelay_version_msg.relay = 0123peer.send_without_ping(no_txrelay_version_msg)124peer.wait_for_verack()125assert_not_equal(peer.nServices & NODE_BLOOM, 0)126assert not peer.sendtxrcncl_msg_received127self.nodes[0].disconnect_p2ps()129# Now, *sending* to *outbound*.130self.log.info('SENDTXRCNCL sent to an outbound')131peer = self.nodes[0].add_outbound_p2p_connection(132SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="outbound-full-relay")133assert peer.sendtxrcncl_msg_received134assert_equal(peer.sendtxrcncl_msg_received.version, 1)135self.nodes[0].disconnect_p2ps()137self.log.info('SENDTXRCNCL should not be sent if block-relay-only')138peer = self.nodes[0].add_outbound_p2p_connection(139SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="block-relay-only")140assert not peer.sendtxrcncl_msg_received141self.nodes[0].disconnect_p2ps()143self.log.info("SENDTXRCNCL should not be sent if feeler")144peer = self.nodes[0].add_outbound_p2p_connection(P2PFeelerReceiver(), p2p_idx=0, connection_type="feeler")145assert not peer.sendtxrcncl_msg_received146self.nodes[0].disconnect_p2ps()148self.log.info("SENDTXRCNCL should not be sent if addrfetch")149peer = self.nodes[0].add_outbound_p2p_connection(150SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="addr-fetch")151assert not peer.sendtxrcncl_msg_received152self.nodes[0].disconnect_p2ps()154self.log.info('SENDTXRCNCL not sent if -txreconciliation flag is not set')155self.restart_node(0, [])156peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)157assert not peer.sendtxrcncl_msg_received158self.nodes[0].disconnect_p2ps()160self.log.info('SENDTXRCNCL not sent if blocksonly is set')161self.restart_node(0, ["-txreconciliation", "-blocksonly"])162peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)163assert not peer.sendtxrcncl_msg_received164self.nodes[0].disconnect_p2ps()166# Check everything concerning *receiving* SENDTXRCNCL167# First, receiving from *inbound*.168self.restart_node(0, ["-txreconciliation"])169self.log.info('valid SENDTXRCNCL received')170peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)171with self.nodes[0].assert_debug_log(["received: sendtxrcncl"], timeout=2):172peer.send_without_ping(create_sendtxrcncl_msg())173self.log.info('second SENDTXRCNCL triggers a disconnect')174with self.nodes[0].assert_debug_log(["(sendtxrcncl received from already registered peer), disconnecting peer=0"]):175peer.send_without_ping(create_sendtxrcncl_msg())176peer.wait_for_disconnect()178self.restart_node(0, [])179self.log.info('SENDTXRCNCL if no txreconciliation supported is ignored')180peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)181with self.nodes[0].assert_debug_log(['ignored, as our node does not have txreconciliation enabled'], timeout=2):182peer.send_without_ping(create_sendtxrcncl_msg())183self.nodes[0].disconnect_p2ps()185self.restart_node(0, ["-txreconciliation"])187self.log.info('SENDTXRCNCL with version=0 triggers a disconnect')188sendtxrcncl_low_version = create_sendtxrcncl_msg()189sendtxrcncl_low_version.version = 0190peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)191with self.nodes[0].assert_debug_log(["txreconciliation protocol violation"]):192peer.send_without_ping(sendtxrcncl_low_version)193peer.wait_for_disconnect()195self.log.info('SENDTXRCNCL with version=2 is valid')196sendtxrcncl_higher_version = create_sendtxrcncl_msg()197sendtxrcncl_higher_version.version = 2198peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)199with self.nodes[0].assert_debug_log(['Register peer=1'], timeout=2):200peer.send_without_ping(sendtxrcncl_higher_version)201self.nodes[0].disconnect_p2ps()203self.log.info('unexpected SENDTXRCNCL is ignored')204peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=False, wait_for_verack=False)205old_version_msg = msg_version()206old_version_msg.nVersion = 70015207old_version_msg.strSubVer = P2P_SUBVERSION208old_version_msg.nServices = P2P_SERVICES209old_version_msg.relay = 1210peer.send_without_ping(old_version_msg)211with self.nodes[0].assert_debug_log(['Ignore unexpected txreconciliation signal'], timeout=2):212peer.send_without_ping(create_sendtxrcncl_msg())213self.nodes[0].disconnect_p2ps()215self.log.info('sending SENDTXRCNCL after sending VERACK triggers a disconnect')216peer = self.nodes[0].add_p2p_connection(P2PInterface())217with self.nodes[0].assert_debug_log(["sendtxrcncl received after verack"]):218peer.send_without_ping(create_sendtxrcncl_msg())219peer.wait_for_disconnect()221self.log.info('SENDTXRCNCL without WTXIDRELAY is ignored (recon state is erased after VERACK)')222peer = self.nodes[0].add_p2p_connection(PeerNoVerack(wtxidrelay=False), send_version=True, wait_for_verack=False)223with self.nodes[0].assert_debug_log(['Forget txreconciliation state of peer']):224peer.send_without_ping(create_sendtxrcncl_msg())225peer.send_and_ping(msg_verack())226self.nodes[0].disconnect_p2ps()228# Now, *receiving* from *outbound*.229self.log.info('SENDTXRCNCL if block-relay-only triggers a disconnect')230peer = self.nodes[0].add_outbound_p2p_connection(231PeerNoVerack(), wait_for_verack=False, p2p_idx=0, connection_type="block-relay-only")232with self.nodes[0].assert_debug_log(["we indicated no tx relay, disconnecting peer=5"]):233peer.send_without_ping(create_sendtxrcncl_msg())234peer.wait_for_disconnect()237if __name__ == '__main__':238SendTxRcnclTest(__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.