Merge bitcoin/bitcoin#34329: rpc,net: Add private broadcast RPCs

2a1d0db799 doc: Mention private broadcast RPCs in release notes (Andrew Toth)
c3378be10b test: Cover abortprivatebroadcast in p2p_private_broadcast (Andrew Toth)
557260ca14 rpc: Add abortprivatebroadcast (Andrew Toth)
15dff452eb test: Cover getprivatebroadcastinfo in p2p_private_broadcast (Andrew Toth)
996f20c18a rpc: Add getprivatebroadcastinfo (Andrew Toth)
5e64982541 net: Add PrivateBroadcast::GetBroadcastInfo (Andrew Toth)
573bb542be net: Store recipient node address in private broadcast (Andrew Toth)

Pull request description:

  Follow up from #29415

  Sending a transaction via private broadcast does not have any way for a user to track the status of the transaction before it gets returned by another peer. The default logs have been removed as well in #34267. Nor is there any way to abort a transaction once it has been added to the private broadcast queue.

  This adds two new RPCs:
  - `getprivatebroadastinfo` returns information about what transactions are in the private broadcast queue, including all the peers' addresses we have chosen and timestamps.
  - `abortprivatebroadcast` stops broadcasting a transaction in the private broadcast queue.

ACKs for top commit:
  nervana21:
    tACK 2a1d0db799
  achow101:
    ACK 2a1d0db799
  l0rinc:
    ACK 2a1d0db799
  danielabrozzoni:
    tACK 2a1d0db799
  sedited:
    ACK 2a1d0db799

Tree-SHA512: cc8682d0be68a57b42bea6e3d091da2b80995d9e6d3b98644cb120a05c2b48a97c2e211173289b758c4f4e23f1d1a1f9be528a9b8c6644f71d1dd0ae5f673326
This commit is contained in:
Ava Chow
2026-02-19 13:42:11 -08:00
9 changed files with 294 additions and 9 deletions

View File

@@ -36,6 +36,7 @@ from test_framework.test_framework import (
)
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_not_equal,
assert_raises_rpc_error,
p2p_port,
@@ -301,6 +302,16 @@ class P2PPrivateBroadcast(BitcoinTestFramework):
self.log.info(f"{label}: ok: outbound connection i={i} is private broadcast of txid={tx['txid']}")
broadcasts_done += 1
# Verify the tx we just observed is tracked in getprivatebroadcastinfo.
pbinfo = self.nodes[0].getprivatebroadcastinfo()
pending = [t for t in pbinfo["transactions"] if t["txid"] == tx["txid"] and t["wtxid"] == tx["wtxid"]]
assert_equal(len(pending), 1)
assert_equal(pending[0]["hex"].lower(), tx["hex"].lower())
peers = pending[0]["peers"]
assert len(peers) >= NUM_PRIVATE_BROADCAST_PER_TX
assert all("address" in p and "sent" in p for p in peers)
assert_greater_than_or_equal(sum(1 for p in peers if "received" in p), NUM_PRIVATE_BROADCAST_PER_TX)
def run_test(self):
tx_originator = self.nodes[0]
tx_receiver = self.nodes[1]
@@ -366,6 +377,30 @@ class P2PPrivateBroadcast(BitcoinTestFramework):
self.log.info("Waiting for normal broadcast to another peer")
self.destinations[1]["node"].wait_for_inv([inv])
self.log.info("Checking getprivatebroadcastinfo no longer reports the transaction after it is received back")
pbinfo = tx_originator.getprivatebroadcastinfo()
pending = [t for t in pbinfo["transactions"] if t["txid"] == txs[0]["txid"] and t["wtxid"] == txs[0]["wtxid"]]
assert_equal(len(pending), 0)
self.log.info("Checking abortprivatebroadcast removes a pending private-broadcast transaction")
tx_abort = wallet.create_self_transfer()
tx_originator.sendrawtransaction(hexstring=tx_abort["hex"], maxfeerate=0.1)
assert any(t["wtxid"] == tx_abort["wtxid"] for t in tx_originator.getprivatebroadcastinfo()["transactions"])
abort_res = tx_originator.abortprivatebroadcast(tx_abort["txid"])
assert_equal(len(abort_res["removed_transactions"]), 1)
assert_equal(abort_res["removed_transactions"][0]["txid"], tx_abort["txid"])
assert_equal(abort_res["removed_transactions"][0]["wtxid"], tx_abort["wtxid"])
assert_equal(abort_res["removed_transactions"][0]["hex"].lower(), tx_abort["hex"].lower())
assert all(t["wtxid"] != tx_abort["wtxid"] for t in tx_originator.getprivatebroadcastinfo()["transactions"])
self.log.info("Checking abortprivatebroadcast fails for non-existent transaction")
assert_raises_rpc_error(
-5,
"Transaction not in private broadcast queue",
tx_originator.abortprivatebroadcast,
"0" * 64,
)
self.log.info("Sending a transaction that is already in the mempool")
skip_destinations = len(self.destinations)
tx_originator.sendrawtransaction(hexstring=txs[0]["hex"], maxfeerate=0)