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) 2019-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 p2p blocksonly mode & block-relay-only connections."""7import time9from test_framework.messages import msg_getdata, msg_tx, msg_inv, CInv, MSG_WTX10from test_framework.p2p import P2PInterface, P2PTxInvStore11from test_framework.test_framework import BitcoinTestFramework12from test_framework.util import assert_equal13from test_framework.wallet import MiniWallet16class P2PBlocksOnly(BitcoinTestFramework):17def set_test_params(self):18self.num_nodes = 119self.extra_args = [["-blocksonly"]]21def run_test(self):22self.miniwallet = MiniWallet(self.nodes[0])24self.blocksonly_mode_tests()25self.blocks_relay_conn_tests()27def blocksonly_mode_tests(self):28self.log.info("Tests with node running in -blocksonly mode")29assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)31self.nodes[0].add_p2p_connection(P2PInterface())32tx, txid, wtxid, tx_hex = self.check_p2p_tx_violation()34self.log.info('Check that tx invs also violate the protocol')35self.nodes[0].add_p2p_connection(P2PInterface())36with self.nodes[0].assert_debug_log(['transaction (0000000000000000000000000000000000000000000000000000000000001234) inv sent in violation of protocol, disconnecting peer']):37self.nodes[0].p2ps[0].send_without_ping(msg_inv([CInv(t=MSG_WTX, h=0x1234)]))38self.nodes[0].p2ps[0].wait_for_disconnect()39del self.nodes[0].p2ps[0]41self.log.info('Check that txs from rpc are not rejected and relayed to other peers')42tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())43assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)45assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True)46with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer'.format(wtxid)]):47self.nodes[0].sendrawtransaction(tx_hex)48tx_relay_peer.wait_for_tx(txid)49assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)51self.log.info("Restarting node 0 with relay permission and blocksonly")52self.restart_node(0, ["-persistmempool=0", "[email protected]", "-blocksonly"])53assert_equal(self.nodes[0].getrawmempool(), [])54first_peer = self.nodes[0].add_p2p_connection(P2PInterface())55second_peer = self.nodes[0].add_p2p_connection(P2PInterface())56peer_1_info = self.nodes[0].getpeerinfo()[0]57assert_equal(peer_1_info['permissions'], ['relay'])58assert_equal(first_peer.relay, 1)59peer_2_info = self.nodes[0].getpeerinfo()[1]60assert_equal(peer_2_info['permissions'], ['relay'])61assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True)63self.log.info('Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer)')64with self.nodes[0].assert_debug_log(["received getdata"]):65# Note that normally, first_peer would never send us transactions since we're a blocksonly node.66# By activating blocksonly, we explicitly tell our peers that they should not send us transactions,67# and Bitcoin Core respects that choice and will not send transactions.68# But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to69# second_peer since we gave relay permission to first_peer.70# See https://github.com/bitcoin/bitcoin/issues/19943 for details.71first_peer.send_without_ping(msg_tx(tx))72self.log.info('Check that the peer with relay-permission is still connected after sending the transaction')73assert_equal(first_peer.is_connected, True)74second_peer.wait_for_tx(txid)75assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)76self.log.info("Relay-permission peer's transaction is accepted and relayed")78self.nodes[0].disconnect_p2ps()79self.generate(self.nodes[0], 1)81def blocks_relay_conn_tests(self):82self.log.info('Tests with node in normal mode with block-relay-only connections')83self.restart_node(0, ["-noblocksonly"]) # disables blocks only mode84assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], True)86# Ensure we disconnect if a block-relay-only connection sends us a transaction87self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only")88assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False)89_, txid, _, tx_hex = self.check_p2p_tx_violation()91self.log.info("Tests with node in normal mode with block-relay-only connection, sending an inv")92conn = self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only")93assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False)94self.check_p2p_inv_violation(conn)96self.log.info(97"Check that getdata(tx) from a block-relay-only connection is ignored"98)99conn = self.nodes[0].add_outbound_p2p_connection(100P2PInterface(), p2p_idx=0, connection_type="block-relay-only"101)102conn.send_and_ping(msg_getdata([CInv(t=MSG_WTX, h=0x12345)]))103assert_equal(self.nodes[0].getpeerinfo()[0]["relaytxes"], False)104assert "notfound" not in conn.last_message106self.log.info("Check that txs from RPC are not sent to blockrelay connection")107conn = self.nodes[0].add_outbound_p2p_connection(P2PTxInvStore(), p2p_idx=1, connection_type="block-relay-only")109self.nodes[0].sendrawtransaction(tx_hex)111# Bump time forward to ensure m_next_inv_send_time timer pops112self.nodes[0].setmocktime(int(time.time()) + 60)114conn.sync_with_ping()115assert int(txid, 16) not in conn.get_invs()117def check_p2p_inv_violation(self, peer):118self.log.info("Check that tx-invs from P2P are rejected and result in disconnect")119with self.nodes[0].assert_debug_log(["inv sent in violation of protocol, disconnecting peer"]):120peer.send_without_ping(msg_inv([CInv(t=MSG_WTX, h=0x12345)]))121peer.wait_for_disconnect()122self.nodes[0].disconnect_p2ps()124def check_p2p_tx_violation(self):125self.log.info('Check that txs from P2P are rejected and result in disconnect')126spendtx = self.miniwallet.create_self_transfer()128with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol, disconnecting peer=0']):129self.nodes[0].p2ps[0].send_without_ping(msg_tx(spendtx['tx']))130self.nodes[0].p2ps[0].wait_for_disconnect()131assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)132self.nodes[0].disconnect_p2ps()134return spendtx['tx'], spendtx['txid'], spendtx['wtxid'], spendtx['hex']137if __name__ == '__main__':138P2PBlocksOnly(__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.