feat(net): optional anonymous transaction broadcast over Tor#6866
Open
tanderbold wants to merge 1 commit into
Open
feat(net): optional anonymous transaction broadcast over Tor#6866tanderbold wants to merge 1 commit into
tanderbold wants to merge 1 commit into
Conversation
df7fc12 to
60dd0cd
Compare
60dd0cd to
aacb35f
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)
aacb35f to
972ac35
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.