wallet: add private broadcast support for wallet transactions #34457

pull w0xlt wants to merge 4 commits into bitcoin:master from w0xlt:wprv_29012 changing 11 files +563 −47
  1. w0xlt commented at 8:06 am on January 30, 2026: contributor

    Summary

    Extends the private broadcast feature (#29415) to wallet transactions. When -privatebroadcast=1 is enabled, wallet RPCs (sendtoaddress, send, sendall, sendmany) now broadcast transactions through short-lived Tor/I2P connections instead of announcing to all connected peers.

    Key Changes

    Centralized availability check (commit 1)

    • Moves the Tor/I2P reachability check from sendrawtransaction RPC to BroadcastTransaction()
    • All broadcast callers now get consistent error handling

    Wallet integration (commit 2)

    • CommitTransaction(): Uses private broadcast when enabled, fails loudly if Tor/I2P unavailable
    • ResubmitWalletTransactions(): Rebroadcasts using the original broadcast method
    • Persists a private_broadcast flag in the wallet transaction’s mapValue

    Functional tests (commits 3-4)

    • Tests wallet send via SOCKS5 proxy
    • Tests flag persistence across restarts
    • Tests error handling when Tor/I2P unavailable

    Behavior Matrix

    Scenario TX Flag Node Setting Expected Behavior Tested
    New tx Private - Via SOCKS5, flag set Test 1 ✓
    Resubmit Private Public Skip Test 2 ✓
    Resubmit Public Private Skip Test 3 ✓
    Resubmit Private Private Rebroadcast Test 3 ✓
    New tx Private - (no Tor) RPC error Test 4 ✓
    Resubmit Private Private (no Tor) Log error Test 4 ✓

    Why skip on mode mismatch? Rebroadcasting via a different method than originally used may allow correlation of transaction to origin, or may indicate origin has -privatebroadcast enabled.

  2. DrahtBot added the label Wallet on Jan 30, 2026
  3. DrahtBot commented at 8:07 am on January 30, 2026: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK andrewtoth

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    LLM Linter (✨ experimental)

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • assert_raises_rpc_error(-1, “none of the Tor or I2P networks is reachable”, self.sender_wallet.sendtoaddress, dest, 0.1) in test/functional/wallet_private_broadcast.py

    Possible places where comparison-specific test macros should replace generic comparisons:

    • test/functional/wallet_private_broadcast.py: assert balance > 0, f"Wallet should have balance, got {balance}" -> recommendation: use assert_greater_than(balance, 0, f"Wallet should have balance, got {balance}")
    • test/functional/wallet_private_broadcast.py: assert tx_info[“confirmations”] == 0, \ … -> recommendation: use assert_equal(tx_info[“confirmations”], 0, f"tx {txid} should be unconfirmed{context_str}, got {tx_info[‘confirmations’]} confirmations")

    No other added bare comparison asserts needing helper replacement were found.

    2026-01-31 03:21:27

  4. DrahtBot added the label Needs rebase on Jan 30, 2026
  5. andrewtoth commented at 9:29 pm on January 30, 2026: contributor

    Concept ACK

    I don’t think we need to skip rebroadcast if we initially created the transaction with private broadcast disabled. This is only important the other way - if we initially broadcast via private broadcast, but then restart with private broadcast disabled.

    may indicate origin has -privatebroadcast enabled.

    Ideally every node has this enabled in the future. But also, one cannot conclude this if they receive a tx both from a persistently connected node and then later via a private broadcast. This would actually make the original broadcast more private, since this would indicate that perhaps the originator is not actually the originator.

    Sending decoys will also help here.

  6. node: move private broadcast availability check to BroadcastTransaction()
    Move the Tor/I2P network reachability check from the sendrawtransaction RPC
    to BroadcastTransaction(). This centralizes the check so that all callers
    (including wallet RPCs) get consistent behavior when using private broadcast.
    
    Changes:
    - Add TransactionError::PRIVATE_BROADCAST_UNAVAILABLE enum value
    - Add corresponding error message in common/messages.cpp
    - Add RPC error code mapping in rpc/util.cpp
    - Move the check from rpc/mempool.cpp to node/transaction.cpp
    - Set err_string when the error occurs for proper error propagation
    7760fab486
  7. wallet: integrate private broadcast for wallet transactions
    Enable private broadcast for wallet transaction submission when
    -privatebroadcast is set. This includes both initial sends and
    rebroadcasts, with persistent tracking to prevent privacy leaks.
    
    Changes to CommitTransaction():
    - Use NO_MEMPOOL_PRIVATE_BROADCAST method when -privatebroadcast enabled
    - Store "private_broadcast" flag in wallet transaction mapValue
    - Throw on broadcast failures (e.g., Tor/I2P not reachable) instead of
      silently continuing
    
    Changes to ResubmitWalletTransactions():
    - Use private broadcast for resubmission when -privatebroadcast enabled
    - Skip privately-sent txs when node has public setting (prevents IP leak)
    - Allow publicly-sent txs to rebroadcast privately (provides plausible deniability)
    - Log errors when rebroadcast fails due to Tor/I2P unavailability
    
    The persistence logic is essential for privacy: without it, a node
    restart with -privatebroadcast disabled could cause private transactions
    to be rebroadcast publicly, leaking information about the transaction
    origin.
    1c96ab167e
  8. test: add Socks5ProxyHelper and add_addresses_to_addrman helpers
    Add helper utilities to test_framework/socks5.py to reduce code duplication
    in private broadcast tests:
    
    - Socks5ProxyHelper: Simplifies SOCKS5 proxy setup with ephemeral ports,
      thread-safe destination tracking, and simple redirect factory
    - add_addresses_to_addrman(): Helper to populate a node's addrman with
      test addresses
    - FAKE_ONION_ADDRESSES: Shared list of fake .onion addresses for tests
    
    Update p2p_private_broadcast.py to use ephemeral ports and the new
    add_addresses_to_addrman helper.
    75ab4aec5b
  9. test: add wallet_private_broadcast.py functional test
    Add a functional test for wallet-specific private broadcast behavior:
    
    - Test 1: sendtoaddress with -privatebroadcast sends via SOCKS5 proxy
    - Test 2: private_broadcast flag persists and prevents public rebroadcast
    - Test 3: Public txs rebroadcast privately (provides plausible deniability)
    - Test 4: Error when Tor/I2P not reachable
    
    Uses Socks5ProxyHelper for simplified proxy setup and connection tracking.
    d9debb3348
  10. w0xlt force-pushed on Jan 31, 2026
  11. w0xlt commented at 3:26 am on January 31, 2026: contributor

    @andrewtoth Thanks for the insight about plausible deniability.

    Updated to only skip private→public (the other direction is now allowed):

    Scenario TX Flag Node Setting Behavior
    Resubmit Private Public Skip
    Resubmit Public Private Rebroadcast privately
    Resubmit Private Private Rebroadcast privately
    • Private→Public: Skip - Protects origin from being correlated
    • Public→Private: Allow - Provides plausible deniability
  12. DrahtBot removed the label Needs rebase on Jan 31, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-01-31 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me