#36608 Reimplement private broadcast as node state-less jobs + package relay support
| Reviewability | Reviews | Agreement | Size | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| #36608 Reimplement private broadcast as node state-less jobs + package relay support | Needs rebase | 0 | Crickets | 7,403 + 9,808 tests | |||||||||
|
|
|
|
| |||||||||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||||||||
Goal
Replaces Bitcoin Core's private broadcast implementation with an isolated, node-stateless job engine and introduces a standalone bitcoin-privbcast CLI tool. Transaction broadcast targets are selected by resolving DNS seeds through Tor SOCKS5 proxies using dedicated circuits, and connections are established strictly over BIP324 v2 transport according to a pre-calculated random schedule. The PR removes legacy private broadcast peer connections and relay code from net and PeerManager, and extends the new engine to support 1-parent-1-child package relay via submitpackage.
Problem: The existing in-tree private broadcast mechanism relies on node state (such as addrman, peer discouragement, and mempool arrival monitoring). This creates observable timing oracles and fingerprinting vectors that an adversary can probe or manipulate via address gossip. In addition, the existing subsystem lacks package relay support, preventing private broadcast of low-fee transactions needing child-pays-for-parent to enter the mempool.
Evidence of importance:
- PR description details historical privacy regressions and leaks from relying on node state, including a proxy bypass on v2-to-v1 retry (#35319/#35410), peer discouragement creating a clearnet oracle (#36312), mempool inspection leaking completion timing (#36277), and addrman state pollution (#35032)
- PR description notes that drawing destinations from addrman allows remote peers to influence and fingerprint destination selection through address gossip
- Commit ee5719d0c6 fixes a validation bug where two prioritisetransaction deltas near INT64_MIN caused a modified fee sum overflow in package handling
Scope: Very large multi-part change (+17k/-4.4k lines across 121 files) organized in four distinct sections: moving shared transport and BIP324 code into the common library, specifying and implementing the standalone bitcoin-privbcast tool, refactoring bitcoind -privatebroadcast into job queues while deleting legacy net/peerman broadcast logic, and adding 1P1C package validation and relay support. More than 11,000 lines are new unit, functional, and fuzz tests.
Risk of a mistake: high (p2p, validation, rpc_api, tooling). Mistakes in the new SOCKS5 proxy state machine or BIP324 attempt loops could leak user IP addresses or fail transaction broadcasting entirely, while changes to package acceptance logic in validation could introduce mempool inconsistencies or crashes.
What the assessment lacked:
- rest_of_diff
Reviewability
Ready · Needs rebase
Conflicts with master; review the logic now, the rebase is mechanical.
Waiting on (from the thread read):
- author: PR is in draft; author pointed reviewers to slimmed down PR #36628 2026-10-23
- author: PR is in draft; author directed review to PR #36628 instead 2026-10-23
Agreement
Crickets
no substantive comment either way
Thread notes: Initial record for draft PR. No reviewer comments yet; the author noted in c:6369998981 that #36628 is the branch to review. / Initial record. Draft PR with no reviewer comments; the author directed review to slimmed-down PR #36628.
Objections, suggestions, and questions: none recorded.
Files
11641 lines under test/bench/ci.
- src/test/privbcast_job_tests.cpp +1413/-0
- test/functional/p2p_private_broadcast.py +761/-388
- src/net.cpp +16/-1113
- src/net_transport.cpp +917/-0
- src/test/privbcast_attempt_tests.cpp +824/-0
- src/test/fuzz/privbcast_queue.cpp +712/-0
- src/test/privbcast_queue_tests.cpp +698/-0
- doc/design/private-broadcast-tool.md +589/-0
- src/net.h +7/-567
- src/test/fuzz/privbcast_attempt.cpp +513/-0
- src/test/privbcast_input_tests.cpp +511/-0
- test/functional/tool_privbcast.py +505/-0
- src/privbcast/job.cpp +504/-0
- src/net_transport.h +495/-0
- src/node/privbcast.cpp +464/-0
- src/test/fuzz/privbcast_discovery.cpp +462/-0
- src/rpc/mempool.cpp +320/-83
- src/privbcast/attempt.cpp +400/-0
- src/test/privbcast_rpc_tests.cpp +361/-0
- src/bitcoin-privbcast.cpp +353/-0
- test/functional/test_framework/privbcast.py +335/-0
- src/test/private_broadcast_tests.cpp +0/-334
- src/test/privbcast_discovery_tests.cpp +316/-0
- src/node/privbcast.h +310/-0
- src/private_broadcast.h +0/-271
- src/net_processing.cpp +15/-251
- test/functional/tool_privbcast_package.py +266/-0
- src/test/fuzz/p2p_private_broadcast.cpp +0/-258
- test/functional/tool_privbcast_peers.py +256/-0
- src/test/fuzz/private_broadcast.cpp +0/-250
- src/privbcast/stream.cpp +234/-0
- src/test/privbcast_assign_tests.cpp +233/-0
- src/privbcast/attempt.h +232/-0
- src/test/fuzz/privbcast_socks5.cpp +223/-0
- src/privbcast/job.h +215/-0
- src/test/util/net.cpp +209/-4
- src/private_broadcast.cpp +0/-209
- src/privbcast/socks5.cpp +204/-0
- test/functional/p2p_private_broadcast_retry_v1.py +0/-199
- src/privbcast/input.cpp +198/-0
- src/test/privbcast_plan_tests.cpp +185/-0
- src/test/privbcast_socks5_tests.cpp +178/-0
- src/privbcast/discovery.cpp +171/-0
- src/privbcast/report.cpp +171/-0
- test/functional/tool_privbcast_proxy.py +168/-0
- src/privbcast/params.h +153/-0
- doc/private-broadcast.md +137/-0
- test/functional/test_framework/test_framework.py +9/-120
- src/privbcast/report.h +118/-0
- src/privbcast/socks5.h +115/-0
- test/functional/p2p_private_broadcast_cap.py +43/-72
- test/functional/test_framework/socks5.py +101/-13
- src/privbcast/stream.h +111/-0
- src/test/txpackage_tests.cpp +109/-0
- src/privbcast/assign.cpp +106/-0
- src/privbcast/assign.h +104/-0
- src/test/util/net.h +103/-1
- src/validation.cpp +83/-20
- src/privbcast/discovery.h +101/-0
- test/functional/p2p_private_broadcast_completion.py +0/-99
- src/test/fuzz/privbcast_input.cpp +97/-0
- src/privbcast/input.h +94/-0
- src/init.cpp +55/-30
- src/privbcast/plan.cpp +75/-0
- src/test/util/privbcast.h +75/-0
- doc/release-notes-privbcast-node.md +58/-0
- src/privbcast/plan.h +53/-0
- src/test/fuzz/package_eval.cpp +44/-0
- doc/release-notes-privbcast-package.md +36/-0
- src/test/denialofservice_tests.cpp +1/-34
- src/bitcoin-privbcast-res.rc +33/-0
- test/functional/feature_config_args.py +17/-16
- src/test/net_tests.cpp +0/-30
- src/net_processing.h +0/-29
- src/test/util/privbcast_queue.h +29/-0
- src/CMakeLists.txt +24/-3
- src/privbcast/CMakeLists.txt +24/-0
- doc/release-notes-privbcast-tool.md +22/-0
- src/node/transaction.cpp +21/-1
- src/node/transaction.h +13/-0
- src/test/CMakeLists.txt +10/-1
- test/functional/feature_signet.py +9/-0
- src/test/fuzz/CMakeLists.txt +6/-2
- src/test/fuzz/connman.cpp +1/-7
- src/node/connection_types.h +0/-7
- src/rpc/net.cpp +3/-4
- src/validation.h +6/-1
- test/functional/test_runner.py +5/-2
- src/node/types.h +4/-2
- test/functional/feature_init.py +6/-0
- doc/man/bitcoin-privbcast.1 +5/-0
- src/qt/rpcconsole.cpp +1/-4
- test/functional/test_framework/util.py +5/-0
- src/common/messages.cpp +4/-0
- test/functional/test_framework/p2p.py +3/-1
- CMakeLists.txt +3/-0
- cmake/module/Maintenance.cmake +2/-1
- src/bitcoin.cpp +3/-0
- src/netbase.h +3/-0
- src/netmessagemaker.h +2/-1
- src/node/context.h +3/-0
- src/primitives/transaction.h +1/-2
- src/test/fuzz/p2p_transport_serialization.cpp +2/-1
- src/bench/txorphanage.cpp +1/-1
- src/bitcoin-cli.cpp +0/-2
- src/kernel/chainparams.cpp +2/-0
- src/netbase.cpp +1/-1
- src/node/connection_types.cpp +0/-2
- src/node/peerman_args.cpp +0/-2
- src/node/txorphanage.h +1/-1
- src/qt/guiutil.cpp +0/-2
- src/rpc/util.cpp +2/-0
- .github/ci-windows.py +1/-0
- contrib/devtools/gen-manpages.py +1/-0
- doc/README.md +1/-0
- src/interfaces/node.h +1/-0
- src/node/context.cpp +1/-0
- src/node/interfaces.cpp +1/-0
- src/rpc/blockchain.cpp +1/-0
- test/CMakeLists.txt +1/-0
- test/config.ini.in +1/-0
Card
The short text the ranking pass compares across PRs.
This PR completely replaces Bitcoin Core's private broadcast subsystem with an isolated, node-stateless job runner and introduces a standalone bitcoin-privbcast utility. The new design contacts DNS seeds over Tor to find IPv4/IPv6 peers and connects according to a predetermined randomized schedule over BIP324 v2 transport, eliminating reliance on internal node state and addrman. It removes the previous private broadcast peer connection and relay logic from net and PeerManager and adds 1-parent-1-child package relay support to submitpackage. The change is motivated by recurring privacy leaks in the legacy design, including direct proxy bypasses and clearnet peer discouragement oracles. It touches P2P networking, package validation, RPC, and CLI tooling across 121 files.
Data
thread read through 2026-10-23 (1 statements), head eec78ccf7d
labels: Needs rebase, Private Broadcast · draft
Categories
Mempool and policy
P3 · bug fix
Adds 1-parent-1-child package support to private broadcast and fixes a package fee overflow bug. Commit ee5719d0c6 fixes a validation bug where prioritisetransaction deltas near INT64_MIN caused a modified fee sum overflow in package handling.
Membership: Modifies package acceptance and relay handling in src/rpc/mempool.cpp and fixes package fee validation logic.
Factors: security/stability 2, bug 2, performance 0, user value 2, leverage 1
P2P
P1 · bug fix (ranking pass: Ranked just below its base #36628, which it depends on and whose direction must be decided first.)
Eliminates severe deanonymization risks and timing oracles in transaction broadcasting. Relying on node state creates observable timing oracles, addrman manipulation vectors, and proxy bypasses (#35319/#35410, #36312, #36277), making an isolated broadcast engine critical for network privacy.
Membership: Carries the 'Private Broadcast' label and rewrites P2P private broadcast connection, relay, and peer management logic in src/net.cpp and src/net_processing.cpp.
Factors: security/stability 2, bug 2, performance 0, user value 2, leverage 1
Tools and scripts
P2 · new feature (assessed alone as P3; Raised to P2: the problem is recurring privacy/deanonymization leaks in private broadcast, a security-class issue, not merely a new CLI utility.)
Introduces bitcoin-privbcast as an isolated standalone tool for private transaction relay. It allows broadcasting over Tor SOCKS5 and BIP324 without requiring bitcoind node state.
Membership: Adds the new standalone bitcoin-privbcast command-line utility in src/bitcoin-privbcast.cpp.
Factors: security/stability 2, bug 2, performance 0, user value 2, leverage 1