Tor: TX circuit isolation #32235

issue learnwayback openend this issue on April 8, 2025
  1. learnwayback commented at 2:08 am on April 8, 2025: none

    Create an option that would forcefully terminate any connection that just sent a locally originating transaction.

    -proxyrandomize ensures that each connection corresponds to a tor circuit, therefore terminating connection = terminating circuit

    the same circuit will not send more than 1 transaction. achieving desirable privacy properties.

    this is a preferable solution to the proposed overengineered mess of #29415

  2. learnwayback added the label Feature on Apr 8, 2025
  3. mzumsande commented at 3:57 am on April 8, 2025: contributor

    That doesn’t seem private at all to me: Disconnecting right after sending your own tx means telling your peer that this is likely a local tx, which is only acceptable if there is no other information they can connect it with:

    Here, they can probe for various kinds of information about your node in advance, and when you finally disconnect them after sending an own tx they can use that.

    Also, the status quo is to send outgoing txns to all peers, in the tor-only case you’d drop all your peers at the same time without any adjustments such as in #29415 - this is very undesirable.

  4. learnwayback commented at 5:44 am on April 8, 2025: none

    That doesn’t seem private at all to me: Disconnecting right after sending your own tx means telling your peer that this is likely a local tx, which is only acceptable if there is no other information they can connect it with: Here, they can probe for various kinds of information about your node in advance, and when you finally disconnect them after sending an own tx they can use that.

    If your node is public (probed) then it seems to be too late to hide. TX circuit isolation only makes sense if you are running a probably small & pruned node behind tor for a private wallet and it’s unreachable (-listen=0). I suppose public nodes may moonlight as private wallets but that strikes me as a bad idea (and maybe not addressable, as any deviation in behavior will leak information).

    Also, the status quo is to send outgoing txns to all peers, in the tor-only case you’d drop all your peers at the same time without any adjustments such as in #29415 - this is very undesirable.

    This may indeed be a problem, sending behavior may need to be adjusted, send to one peer a time observing the status of the mempool in response (this may already be the default, wouldn’t it resend if it doesn’t go through?). In most cases a single send will be sufficient.

  5. vasild commented at 8:32 am on April 8, 2025: contributor

    If your node is public (probed) then it seems to be too late to hide.

    Wait. It is not that simple. I guess here you assume that if a node is “public” you mean “is accepting inbound connections”. So, if a node is accepting inbound connections on a clearnet IPv4 address and on a an .onion address, then it could have been that a spy has correlated and linked the IPv4 address and the .onion address as belonging to the same node. That is true, it is too late to “hide” as in un-correlate those.

    But. Tor connections do not have a source address, so the connections made by a node are not linked to its listening .onion address. Those outbound connections made by the node, even if without a source address could still be linked to the node (i.e. its IPv4 and .onion listening addresses) if the node leaks some information at the application level. Most blatant example is if the node is using a custom user agent string, e.g. “Jonny’s node” and sends that as the greeting over the IPv4, inbound and outbound Tor connections.

    So, outbound Tor connections are pretty anonymous at the network level (no source address) and if we are careful not to leak information at the application level then it is good for sending our own transactions. Best way to avoid application level leakage is to exchange the bare minimum of messages for sending the transaction - don’t send anything extra and ignore incoming messages not related to the transaction send.

    In addition it may happen that the transaction is sent to an adversary. They can hold it and start probing nodes’ mempools to see which one has the transaction. So the current behavior of putting the transaction in the mempool upfront has to be changed.

    So, have dedicated broadcast connections that do not leak info at the application level and don’t put the transaction in the mempool upfront. This is exactly what #29415 does.

    overengineered mess

    Thank you for looking into #29415! Concrete ideas into how to simplify things are very much welcome!

  6. learnwayback commented at 6:41 pm on April 8, 2025: none

    In that case wouldn’t it be better to just treat local transactions as standalone events:

    Whenever the local bitcoin node is RPC’ed to send a transaction then pick a public node at random from a list, connect to it using a fresh circuit, send the transaction and close the circuit. If transaction doesn’t show up in the mempool, repeat.

  7. mzumsande commented at 6:55 pm on April 8, 2025: contributor

    In that case wouldn’t it be better to just treat local transactions as standalone events:

    Whenever the local bitcoin node is RPC’ed to send a transaction then pick a public node at random from a list, connect to it using a fresh circuit, send the transaction and close the circuit. If transaction doesn’t show up in the mempool, repeat.

    That’s what #29415 does, what are you suggesting to do in a different way than proposed there?

  8. vasild commented at 8:34 am on April 9, 2025: contributor

    @learnwayback, yes, that is what #29415 tries to do, roughly. Now, some details could be different from what you describe, but they are up for discussion and improvement. For example what you describe is to send the transaction to 1 peer whereas #29415 sends to 3 peers “at once”, right away, without waiting for an outcome. Then it waits to see if the transaction will enter the mempool from the outside and if not, then it retries after 10-15 minutes. These numbers “3 peers” and “10-15 minutes” are more or less arbitrary, it works with them, will probably work with others as well.

    In my experiments (on mainnet) the transaction comes back in 2-3 seconds, sometimes before the node has completed sending it to all 3 initial peers. In that case, the e.g. 3rd connection is closed by the node before sending the transaction over it because it is not needed anymore. For these “happy” scenarios maybe it would be more optimal to send to 1 (or 2?) peer(s), wait a short period of time, e.g. 30 seconds and if not received back then retry to more peers, e.g. 5. That is, start small, optimistic and if it does not work, then increase the number of peers to send to during the retries. This would cause a delay in an “unhappy” scenario where the single recipient drops the transaction and does not resend it to the network for whatever reason (they are broken, or malicious or something else).

  9. learnwayback commented at 5:19 pm on April 9, 2025: none

    My bad I must have misunderstood what the PR was doing (and saw the dozens of modifications it was doing).

    transaction comes back in 2-3 seconds, sometimes before the node has completed sending it to all 3 initial peers. In that case, the e.g. 3rd connection is closed by the node before sending the transaction over it because it is not needed anymore

    That is called a side channel information leakage, while it may seem improbable that it could get exploited better not to do it.

    Do not alter privacy sensitive behavior based on immediate feedback. Sending twice instead of three times may get correlated with a particular node getting a particular transaction for example.

    I would recommend sending to 1 random peer at a time and waiting a random number of seconds before sending it again if it did not come back (while other transactions did).

  10. andrewtoth commented at 5:28 pm on April 9, 2025: contributor
    I suppose sending it to all 3 peers and not stopping early is the more reliable way.

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: 2025-04-16 15:12 UTC

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