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) 2026-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 the private-broadcast queue size cap: submissions beyond the cap are6rejected (the queue is not modified), rather than evicting existing entries.7"""9from test_framework.test_framework import BitcoinTestFramework10from test_framework.util import assert_equal, assert_raises_rpc_error11from test_framework.wallet import MiniWallet14# Must match PrivateBroadcast::MAX_TRANSACTIONS15MAX_TRANSACTIONS = 10_00016OVER_CAP = 519class PrivateBroadcastCapTest(BitcoinTestFramework):20def set_test_params(self):21self.num_nodes = 122# -privatebroadcast is incompatible with the framework's default23# -connect=0; allow autoconnect (no actual peers will succeed though).24self.disable_autoconnect = False25self.extra_args = [[26"-privatebroadcast",27# Fake I2P reachability so the privatebroadcast startup precondition passes.28"-i2psam=127.0.0.1:1",29"-proxy=127.0.0.1:1",30]]32def setup_network(self):33# Skip the framework's default connect_nodes loop. We have a single34# node and don't need any peer connections.35self.setup_nodes()37def run_test(self):38node = self.nodes[0]39wallet = MiniWallet(node)40# Mature one coinbase to spend.41self.generate(wallet, 101)43# Build a parent that fans out to MAX_TRANSACTIONS + OVER_CAP outputs.44# Inject it directly via generateblock since -privatebroadcast bypasses45# the mempool46utxo = wallet.get_utxo()47parent = wallet.create_self_transfer_multi(48utxos_to_spend=[utxo],49num_outputs=MAX_TRANSACTIONS + OVER_CAP,50fee_per_output=500,51)52self.generateblock(node, wallet.get_address(), [parent["hex"]])54children = [wallet.create_self_transfer(utxo_to_spend=u)55for u in parent["new_utxos"]]56assert_equal(len(children), MAX_TRANSACTIONS + OVER_CAP)58# Fill the queue exactly to the cap; every distinct submission succeeds.59self.log.info(f"Filling private broadcast queue to cap ({MAX_TRANSACTIONS} txns)")60for child in children[:MAX_TRANSACTIONS]:61node.sendrawtransaction(child["hex"])63pbinfo = node.getprivatebroadcastinfo()64assert_equal(len(pbinfo["transactions"]), MAX_TRANSACTIONS)65present_wtxids = {t["wtxid"] for t in pbinfo["transactions"]}66for i, child in enumerate(children[:MAX_TRANSACTIONS]):67assert child["wtxid"] in present_wtxids, \68f"tx index {i} (wtxid={child['wtxid']}) should be in the queue"70# Further distinct submissions are rejected with an RPC error, and the71# queue is left unchanged (nothing evicted to make room).72self.log.info(f"Submitting {OVER_CAP} more; each should be rejected (queue full)")73for child in children[MAX_TRANSACTIONS:]:74assert_raises_rpc_error(-37, "Private broadcast queue is full",75node.sendrawtransaction, child["hex"])77assert_equal(pbinfo["transactions"], node.getprivatebroadcastinfo()["transactions"])79self.log.info("Checking abortprivatebroadcast frees a slot for a new submission")80abort_res = node.abortprivatebroadcast(children[1]["txid"])81assert_equal([t["wtxid"] for t in abort_res["removed_transactions"]],82[children[1]["wtxid"]])83wtxids = {t["wtxid"] for t in node.getprivatebroadcastinfo()["transactions"]}84assert_equal(len(wtxids), MAX_TRANSACTIONS - 1)85assert children[1]["wtxid"] not in wtxids, "aborted tx should be gone from the queue"87new_child = children[MAX_TRANSACTIONS] # first previously-rejected tx88node.sendrawtransaction(new_child["hex"])89wtxids = {t["wtxid"] for t in node.getprivatebroadcastinfo()["transactions"]}90assert_equal(len(wtxids), MAX_TRANSACTIONS)91assert new_child["wtxid"] in wtxids, "freed slot should now hold the new tx"92assert children[1]["wtxid"] not in wtxids, "aborted tx should not reappear"94# Re-submitting an already-queued transaction is a no-op, not an error,95# even when the queue is full.96self.log.info("Re-submitting an already-queued tx should not error")97node.sendrawtransaction(children[0]["hex"])98wtxids = {t["wtxid"] for t in node.getprivatebroadcastinfo()["transactions"]}99assert_equal(len(wtxids), MAX_TRANSACTIONS)100assert children[0]["wtxid"] in wtxids, "re-submitted tx should remain queued"102if __name__ == "__main__":103PrivateBroadcastCapTest(__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.