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
GETDATAandMEMPOOLrequests, as if we don't have them.Once we get an
INVfrom somebody, request the transaction withGETDATA, as if we didn't have it before.After we receive the full transaction as a
TXmessage, in reply to ourGETDATArequest, only then consider the transaction has propagated through the network and remove it from the unbroadcast list.INVit to others as a result of theTXmessage we get, like what we do with transactions that are not ours.
The messages exchange should look like this:
tx-sender >--- connect -------> tx-recipient
tx-sender >--- VERSION -------> tx-recipient
tx-sender <--- VERSION -------< tx-recipient
tx-sender <--- WTXIDRELAY ----< tx-recipient (maybe)
tx-sender <--- SENDADDRV2 ----< tx-recipient (maybe)
tx-sender <--- SENDTXRCNCL ---< tx-recipient (maybe)
tx-sender <--- VERACK --------< tx-recipient
tx-sender >--- VERACK --------> tx-recipient
tx-sender >--- TX ------------> tx-recipient
tx-sender >--- PING ----------> tx-recipient
tx-sender <--- PONG ----------< tx-recipient
tx-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
-connectis 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
VERSIONmessage. 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
masterwhich 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: OneINVshould 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