feat(net): optional anonymous transaction broadcast over Tor - #6866
feat(net): optional anonymous transaction broadcast over Tor#6866tanderbold wants to merge 1 commit into
Conversation
df7fc12 to
60dd0cd
Compare
972ac35 to
f70a15c
Compare
When node.tor.enabled is true, transactions submitted to this node's own broadcast API are validated locally as before but, instead of being advertised to the node's directly-connected sync peers (which exposes the origin node's IP), are relayed via the native TRON P2P protocol through the local Tor SOCKS5 proxy to standard, unmodified nodes learned via the (UDP) discovery layer that are NOT currently used as sync peers. Those nodes re-gossip the transaction as usual, hiding the origin. Block sync and normal P2P traffic are unaffected. The feature is off by default. Reaching a Tor peer and completing the P2P handshake costs several seconds, which is paid per connection, not per transaction. To broadcast quickly even at a high transaction rate, the service keeps a pool of persistent Tor tunnels to verified, at-head relay nodes: each tunnel is handshaked once, kept alive (answering keep-alive pings) and reused for every broadcast. The read timeout of an established tunnel is set well above the peer keep-alive interval so an idle but healthy tunnel is not torn down between pings. Transactions are buffered and flushed as a single TransactionsMessage batch (capped) over broadcastCount tunnels in parallel, so a burst is delivered in about one round-trip; broadcastTransaction enqueues and returns immediately. Expired transactions are dropped from the buffer. Each tunnel advertises a fresh random public IP (never our real one, and a fresh random node id per hello, so tunnels can't be clustered or linked to our clear-net node) and echoes the peer's head block so the peer marks the connection sync-complete before it will fetch a transaction. Relay candidates are discovered nodes minus current sync peers; only nodes at (or near) our head are kept. Resilience: no transaction is lost if Tor or a relay is down or degraded — it stays buffered and is delivered once a tunnel can be (re)built. A flush never blocks the pool on a silently-dead tunnel (writes are bounded; a stalled tunnel is dropped and rebuilt). Pool maintenance refills only the missing tunnels (plus a small margin) and backs off exponentially when Tor is congested, so it does not flood a struggling Tor with circuit requests. If no tunnel can be built for several rounds and node.tor.controlPort is set, the node signals NEWNYM over the Tor control port to rebuild circuits with fresh exit IPs. libp2p is used as a library only; it is not modified. - NodeConfig/Args/CommonParameter: parse and hold node.tor.* config (incl. optional controlPort/controlPassword for NEWNYM recovery) - TronNetService: expose discovery-table nodes (getTableNodes) - TorBroadcastService: persistent Tor tunnel pool + batched native-P2P relay, with bounded writes, adaptive refill/backoff, NEWNYM recovery, idle-safe read timeout, expired-tx eviction, and public-only advertised addresses - Wallet.broadcastTransaction: route through Tor when enabled - config.conf: documented node.tor block - Tests: delivery over a persistent tunnel via a SOCKS5 forwarder; Tor-outage and relay-degradation recovery; NEWNYM on a stuck pool; and routing (Tor path when enabled, normal P2P broadcast when disabled)
f70a15c to
193755a
Compare
|
Whether to support Tor network broadcasting should be thoroughly discussed in the issues section. |
|
@tanderbold Thank you for the contribution and the detailed implementation. After evaluation, java-tron does not plan to support Tor integration at this time. Transactions are already propagated through the P2P gossip network, which makes reliably identifying the original sender difficult in practice. Although this does not provide a formal anonymity guarantee, routing transaction broadcasts through Tor offers limited additional benefit for the current threat model and user demand. Meanwhile, Tor integration introduces considerable implementation, configuration, testing, security, and long-term maintenance complexity. We do not believe the incremental benefit currently justifies that cost. We may revisit this decision if there is clear user demand and a well-defined threat model that cannot be adequately addressed by the existing P2P propagation mechanism. For now, we may close this PR. Thank you again for your work and understanding. |
What
Adds an optional feature to anonymize the origin of transactions a node broadcasts, by
relaying them over Tor instead of advertising them to directly-connected P2P peers. Off by
default (
node.tor.enabled = false) — no behaviour change unless explicitly enabled.Why
When a node submits a transaction through its own broadcast API, it advertises that transaction
to its sync peers, which exposes the node's IP as the transaction's origin. For users who need
origin privacy this links their IP to their transactions. Block sync and normal P2P traffic are
too heavy to run over Tor, but a single transaction is tiny — so only the outbound transaction
broadcast is routed through Tor, while block sync and everything else keep running on the clear
net.
How
When
node.tor.enabledis true, a submitted transaction is still validated and pushed locally asusual, but instead of being advertised to sync peers it is relayed via the native TRON P2P
protocol through the local Tor SOCKS5 proxy to standard, unmodified full nodes learned from the
discovery layer that are NOT current sync peers. Those nodes re-gossip it as they would any peer's
transaction, hiding the origin.
seconds, paid per connection, not per transaction. The service keeps a pool of persistent,
handshaked Tor tunnels to verified at-head relays, reused for every broadcast and kept alive by
answering keep-alive pings.
TransactionsMessagebatch (capped) over
broadcastCounttunnels in parallel, so a burst is delivered in about oneround-trip;
broadcastTransactionenqueues and returns immediately.random node id per hello, so tunnels can't be clustered or linked to the clear-net node; SOCKS
connects by raw IP (no DNS leak); there is no clear-net fallback on failure.
until a tunnel can be (re)built; expired transactions are evicted; a stalled tunnel's write is
bounded and the tunnel is rebuilt; pool refill is adaptive with exponential backoff so a
congested Tor isn't flooded with circuit requests; and, optionally, the node signals
NEWNYMover the Tor control port to rebuild circuits with fresh exit IPs when no tunnel can be built.
libp2p is used as a library only; it is not modified.
Configuration (
node.tor)Testing
and a stub node; Tor-outage and relay-degradation recovery; NEWNYM on a stuck pool; and routing
(Tor path when enabled, normal P2P broadcast when disabled). All tests and checkstyle pass.
spikes every 5 minutes delivered every transaction on-chain, while block sync continued on the
clear net.