To improve privacy, relay locally submitted transactions (from the wallet or via RPC) to the P2P network only via Tor or I2P short-lived connections.
-
Introduce a new connection type for relaying sensitive data (our own transactions) with the following properties:
- started whenever there are local unbroadcast transactions to be sent
- only opened to Tor or I2P peers
- opened regardless of max connections limits
- after handshake is completed one local transaction is pushed to the peer and the connection is closed
- ignore all incoming messages after handshake is completed
-
Relay locally submitted transactions (from the wallet or via RPC) using this new mechanism, to a few Tor or I2P peers.
-
Hide those transactions from
GETDATA
andMEMPOOL
requests, as if we don’t have them. -
Once we get an
INV
from somebody, request the transaction withGETDATA
, as if we didn’t have it before. -
After we receive the full transaction as a
TX
message, in reply to ourGETDATA
request, only then consider the transaction has propagated through the network and remove it from the unbroadcast list. -
INV
it to others as a result of theTX
message we get, like what we do with transactions that are not ours.
The messages exchange should look like this:
0tx-sender >--- connect -------> tx-recipient
1tx-sender >--- VERSION -------> tx-recipient
2tx-sender <--- VERSION -------< tx-recipient
3tx-sender <--- WTXIDRELAY ----< tx-recipient (maybe)
4tx-sender <--- SENDADDRV2 ----< tx-recipient (maybe)
5tx-sender <--- SENDTXRCNCL ---< tx-recipient (maybe)
6tx-sender <--- VERACK --------< tx-recipient
7tx-sender >--- VERACK --------> tx-recipient
8tx-sender >--- TX ------------> tx-recipient
9tx-sender >--- PING ----------> tx-recipient
10tx-sender <--- PONG ----------< tx-recipient
11tx-sender disconnects
Whenever a new local transaction is received (from the wallet or RPC), the node will send it to 5 (SENSITIVE_RELAY_NUM_BROADCAST_PER_TX
) recipients right away. If after 10-15 mins we still have not heard anything about the transaction from the network, then it will be sent to 5 more peers (see PeerManagerImpl::ReattemptInitialBroadcast()
).
A few considerations:
- The short-lived sensitive relay connections are very cheap and fast wrt network traffic. It is expected that some of those peers will blackhole the transaction. Just one honest/proper peer is enough for successful propagation.
- The peers that receive the transaction could deduce that this is initial transaction broadcast from the transaction originator. This is ok, they can’t identify the sender.
TODO:
- Disable if
-connect
is used or Tor and I2P are not reachable. - Use I2P transient connections even if listening on I2P.
- Do something with the user-agent string (could reveal identity if it has been personalized) and make sure no identifying information in the
VERSION
message. Done: took the suggestion below. - Improve the condition when we consider the transaction seen by the network: right now a single INV from somebody suffices (this is still an improvement over
master
which is ok right after pushing the tx to a peer). We could wait for more than one INV, from peers that we have selected (outbound) and that are specifically not the ones we broadcasted to (via short-lived connection). Edit: OneINV
should be enough because after that we broadcast the transaction to everybody we are connected to (like if it is not ours). - Idea: start with one connection per transaction (not 5 as it is now), optimistically assuming it will be sufficient. (numbers are examples) After 1 minute, if still not received from the network, try 2 connections, after a few minutes try more. If still unsuccessful after 10 minutes, then fall back to the old mechanism.
This is supposed to address: #3828 #14692 #19042 #24557 #25450