Network-Layer Privacy on Bitcoin Cash

Hi all,

In anticipation of future Zero-Knowledge Proof (ZKP) Bitcoin Cash covenants, I’ve been reviewing how to make implementation in wallets as practical and private as possible.

I expect now that many ZKP covenant systems can be fully supported by generalized “wallet engine” infrastructure, such that software which supports wallet templates will be able to simply import and use ZKP covenant wallet templates (create a wallet, scan for matching UTXOs, and deposit, transfer within, and withdraw from public ZKP covenants) with minimal integration work.

However, the Bitcoin Cash P2P protocol has two areas requiring improvements for better privacy in real-world usage:

  • Transaction origin exposure – network-level adversaries can trivially identify the node which first broadcasts most transactions. Many end-user wallets are partially protected from network-level monitoring by broadcasting via a backend controlled by their wallet vendor or community-run Fulcrum servers (Electrum protocol), but this comes at the cost of leaking far more actionable data to those providers (who in many cases are also servicing “balance checks” and other queries that reliably de-anonymize all of the end-user’s activity).

  • Cleartext network communications – the Bitcoin Cash P2P protocol does not support any form of encryption, so network-level adversaries can very easily identify and track all activity, simplifying and lowering the cost of both censorship and attacks on privacy.

Some ideas I’ve reviewed:

A new “ONION_TX” P2P protocol extension

We could add a Tor-like transaction broadcast protocol where the transaction is encrypted to a path of known “onion-capable” nodes. However, powerful network-level attackers could likely still correlate traffic in and out of circuits with timing analysis – at the very least, much of the rest of the network would need to upgrade to encrypted connections as well.

Additionally, it’s very hard to protect against denial-of-service attacks: even if nodes temporarily remembered onion TXs they forwarded, and we added a backwards-propagating ONION_ABUSE message to allow nodes in the circuit to blame a misbehaving node, the originating node may always plausibly claim to be a victim of an earlier non-existent node. Compare that with our existing protocol, which is very byte-efficient at banning misbehaving peers for wasting bandwidth on invalid transactions. (And of course transaction fees impose a cost on valid transaction bandwidth.)

Note also that BCH nodes can already connect via Tor, which probably offers both better privacy and greater resistance to denial-of-service attacks (due to the size of the existing Tor network).

TLDR: An “onion” extension would be complex, easily abused, and ultimately less private than network-layer encryption + a deniable broadcast solution (Dandelion++ or Clover).

BIP324 opportunistic encryption

Bitcoin Cash nodes could implement BIP324, an upgrade to add encryption to the existing P2P protocol.

If our only goal were to better protect traffic against powerful network-level adversaries, this solution seems hard to beat: maximally simple, pseudorandom bytestream, shapable for better censorship resistance, etc.

However, implementation is not trivial: it’s a new, special-purpose protocol with relatively little ecosystem support (some scattered patches for various BTC software, early support in a few BTC-only libraries).

It’s quite hard to justify this additional work and maintenance when compared to adopting a more widely-used stack like Noise or libp2p, for which better-reviewed libraries already exist in a variety of language/programming environments.

Further, even if encryption support were successfully deployed to 100% of BCH nodes, we’d still need solutions for transaction origin exposure – well-connected adversaries could still trivially identify originating nodes on the main network, and the status quo of wallets leaking private info to backend servers would remain unchanged.

In fact, even coupled with a deniable broadcast solution like Dandelion++ or Clover, practical privacy for most end-users would still remain highly vulnerable to trusted backends. To create plausible deniability in light client transaction broadcast, you need the light clients to also be plausibly participating in the deniable broadcast protocol, i.e. light clients with peer connections to other nodes and light clients.

Implementing BIP324 would bring encryption to the existing P2P network, but it wouldn’t improve the privacy of most light wallets – for that we need to make it easier for light wallets to broadcast transactions directly over the P2P network (even if they still leak other info to backend servers rather than internally running pruned or SPV nodes).

TLDR: a partial solution to cleartext P2P communications, but at significant implementation cost, with little new practical privacy for most light wallets users.


Proposals for Bitcoin Cash

With that background, I’d like to outline some solutions I’m exploring.

These might eventually become CHIPs to make them easier to talk about, but note that they’re much “lower-stakes” than VM changes: there’s no consensus or widely-coordinated upgrade needed here, nodes and wallets can choose to start or stop speaking protocols whenever they like, and it’s likely we’ll iterate a bit.

Implement PTX messages (“Clover”)

In my opinion, Clover is a clear improvement over Dandelion++ – it’s simpler, faster, more byte efficient, has fewer pitfalls, and offers overall better privacy to all network participants.

In short: nodes add a new PTX (“proxy TX”) message type equivalent to the TX type, but quietly relayed based on some simple rules such that the network first “hears” it via the standard INV diffusion after a few hops.

When performed across encrypted connections, this offers similar privacy to an “onion” extension, but without the denial-of-service issues. Note that even the first peer to receive the PTX can’t know if the originator was simply forwarding it, so they learn only slightly more than if they were passing along an onion-encrypted payload. However, with the payload visible, they 1) are not wasting bandwidth (they won’t need to GETDATA the TX again later) and 2) can easily see if it’s invalid and ban misbehaving peers.

Without encryption, the originator of each PTX message is still easily tracked by powerful network-level adversaries, but when combined with some P2P layer encryption solution (and perhaps if messages are padded to a fixed length), we get Tor-like privacy without the DoS exposure.

One BCH-specific detail: nodes must advertise in service bits whether or not they support PTX, and only forward PTX to other supporting nodes. With BCH’s annual upgrades, we’re likely to have great support relatively quickly, but it’s important that PTX “support” remains optional (e.g. Chaingraph probably won’t implement the PTX forwarding behavior, so if nodes blindly selected a Chaingraph agent to send a PTX, propagation would be delayed until a previous node’s timeout was triggered.) Some node implementations will never bother to implement, and that’s fine.

Implement optional libp2p transport protocols

The libp2p project seems to have come a long way in standardizing various transport protocols, implementing native libraries in a variety of languages, and resolving earlier resource exhaustion issues. A number of cryptocurrency projects now use libp2p, including Ethereum since 2023.

I’d love to see BCH node implementations experiment with using libp2p libraries to support the TCP + Noise + Yamux, WebTransport, and WebRTC libp2p transport protocols. This would unlock P2P network compatibility with the web platform, enabling:

  • Web-based peers that can easily contribute bandwidth from any connection (like Tor’s Snowflake project),
  • Stronger censorship resistance: more protocol options to route around censorship/damage, and BCH traffic can blend in with video calls (WebRTC) and HTTP/3 (WebTransport),
  • Strongly-private, PWA-based nodes and severless wallets,
  • Simpler, JS-only P2P connection code in web-stack based software (e.g. wallets built with Electron, Tauri, Expo, React Native, Capacitor, etc.)

Rough sketch of the necessary parts:

  • Reuse most of existing P2P message formats after libp2p handshakes – can simply remove the checksum field (as the transport layers handle message integrity). The addr message format would continue to enable “v1” P2P discovery.
  • Extend the existing version handshake to enable immediately upgrading to libp2p WebTransport for the performance and encryption, or libp2p TCP for the encryption.
  • Seeder support for JSON responses – we need some seeder (like BCHN’s) to support listening on a port and returning the list via HTTP in JSON format. (Seeder operators should also set up HTTPS with Caddy or something.) The JSON response should only include nodes with libp2p interfaces enabled (and returned records should allow for opening WebTransport and/or WebRTC connections).

Long term, I hope to add support in Libauth for locally running a fast-syncing pruned node in the browser, Node.js, Deno, Bun, etc. such that Libauth-based wallets can both participate in PTX (“Clover”) broadcasting and scan for their own UTXOs without leaking info to backends (at the cost of local storage and bandwidth usage). Clients running in Node.js, Deno, or Bun would bridge between v1 P2P nodes and libp2p-connected nodes to help bootstrap the libp2p-based network (stretch goal: a PWA and/or browser extension-based full node with support for ~1MB sub-block progressive knockout filters).


Does anyone see areas for improvement over PTX messages + encrypted libp2p transports?

7 Likes

Ahh, 18 months tech. Keep repeating the idea often enough and people will slowly believe it will come! In just 18 months or so, right?

Have you tried CashFusion yet?

It already works today and indeed already gives you a better system than you’re attempting to design due to it’s simplicity and it’s dependency on nothing but decades (nah, centuries) long proven math.

Anyway, your post;

Onion-tx/dandelion/clover idea is not new here, has been discussed before. The idea comes from a different chain that has problems not existing on BCH and the attack vector is likewise not present on BCH. Good thing, though, is that you don’t need to discuss that here you can just implement it in the full node clients. It is permissionless to add new p2p messages.

Without considering privacy,

Right now, the project farthest along in the BCH space toward p2p networking in the browser is something called electrum-cash/application by @Jonathan_Silverblood .

The goal of the electrum/app project is to provide an SPV API to any app in the browser.

From an app building standpoint, the libauth node or SPV client should live in something called a SharedWorker which does not have support in Chrome for Android.

So I think, even if there were a pruned libauth indexeddb-backed rust full-node today, we couldn’t run in the v8 engine that most of the people of the world have available in their pockets.

Considering privacy,

A common trend that could be observed in many PWA technoligies or capabilities, is that it only takes one tech giant to ruin a feature for everyone.

Apple, google and firefox can take turns each not supporting: push notifications, persistent storage, background processes, wasm in workers or networking from workers, NFC read/writing, etc… until the whole PWA stack of bleeding edge features someone would want for a wallet or dapp is essentially rendered useless because each tech giant wouldn’t support one or two features.

It would be hard, but one strategy could be to direct users to a platform like the tor browser for android, or a dedicated browser, and not attempting to support devices that do not allow users to install a web browser (iOS in the US), or where an OS/Browser/Search/DNS/Account provider is using a vertically integrated monopoly to engage in trust-like anti-competitive behavior.

PWA users could get a splash page that says, “Please download tor for android” or a browser dedicated to libp2p support.

I don’t think we’re going to get anywhere building castles in the lands where 30% of every transaction is supposed to go towards a private trust that has inserted itself into all commerce.

1 Like

Additional possibility to add better-than-Tor resistance against timing analysis by powerful network adversaries (who can e.g. see the timings and sizes of packets going between most nodes): a DTX (“delayed TX”) message type which behaves mostly like PTX but is also allowed to introduce meaningful delay.

We want PTX messages to broadcast very quickly to preserve a fast user experience (we don’t want light wallets to avoid PTX broadcasting due to latency). However, for clients that don’t mind latency, DTX messages would be very resistant even to origin de-anonymization by global dragnet-level attackers. (And the existence of DTX broadcasts would also introduce uncertainty into timing analysis of PTX and TX messages.)

DTX messages should look approximately as if broadcast via a mix network: attempt to batch DTX broadcasts with other messages using fixed length chunks, allow a highly variable per-relay delay (maybe between 0 and 2 seconds – matching ~99% global TX diffusion time), much longer fallback timeouts, etc. The terminating node then rebroadcasts the DTX via PTX.

Like PTX, intermediate DTX relayers can see the payload, but this 1) improves efficiency and makes abuse easy to detect, and 2) reveals less to an active attacker than mixnets for 1-to-1 messages (and like Tor, even incentivizes would-be attackers to run nodes and subsidize our network bandwidth).

TLDR: give end users (or their software) the option between:

  • near-instant (TX),
  • fast + private (PTX), and
  • slow + anonymous (DTX).

Even if most wallets don’t use them, the simple existence of PTX and especially DTX broadcasts would significantly improve real-world privacy for all TXs.

3 Likes

Re Clover, it seems single-hop proxying to one randomly selected outbound peer can be done without a protocol change, and would reduce deanonymysation precision to D = |S|/|R|.

How does multi-hop with PTX improve on single-hop to a random outbound peer?

I’m missing something obvious here, just not sure what. :thinking:

2 Likes

Yes, PTX and P2P connectivity would improve the privacy of light wallets that support CashFusion but ultimately leak privacy info in earlier or later transactions.

Can you link to prior discussion of Clover? Also, which chain are you referring to?

Interesting, thanks for sharing! Have you all considered @markblundeberg’s progressive knockout filters? (I know this might be a lot of work in a different direction, so this is not necessarily a recommendation, I’m just curious if there’s any discussion on it that you can link.) I think a protocol using PKFs on “sub-block” portions of block merkle trees could be really efficient, even as block sizes get very large.

A SharedWorker seems reasonable, though it seem just as easy and performant to build the node as a website with an iframe postMessage interface. Compatibility there extends back decades, and you could also expose APIs for cross-domain access.

I agree that this is very frustrating. It’s hard right now to envision PWAs being fully competitive with native frameworks on the various mobile platforms.

A web-stack accessible P2P network would still improve the status quo by making direct P2P usage a bit more accessible and maintainable, even for Expo, React Native, etc. And of course, for use cases where PWAs are already sufficient (despite the nerfing), it’d be great to have an easy P2P network access!

1 Like

Hi Jason I believe the overall work is amazing you have my full support however, latency is a real issue here, especially for DTX it would be better if there is a way to improve PTX imo

1 Like

I might be misunderstanding the question, but right now, if you receive an unsolicited TX message from a random inbound peer, you know with almost 100% certainty that they are the origin (otherwise you’d have gotten it via INV).

Likewise, network analysis companies maintain lots of well-connected peers, and can generally pinpoint TX origin with high certainty by monitoring INV messages (even if they’re not selected for the random initial broadcast). Nodes even have a helpful GETADDR message + ADDR response to help attackers construct a clear network graph.

With PTX messages, you both introduce uncertainty at the first hop and mark the transaction for a brief phase of continued proxying by honest nodes.

1 Like

I believe this is what you talking about?

print(network. subscribed_addresses)

1 Like

I agree, we probably want to select PTX Clover parameters to aim for fast propagation and “ok privacy” rather than PTX messages being slow enough to risk wallet vendors resorting to TX or INV broadcast for user experience reasons. (Though note that payment protocol(s) still enable instant feedback at point-of-sale by skipping the P2P network round-trip, even if the wallet itself always broadcasts via PTX.)

I think acknowledging the difference in use cases – “everyday privacy” (PTX) vs. “privacy is critical” (DTX) – by offering both options to wallets can maximize the overall usefulness of BCH.

Note that the above DTX description is very half-baked/underspecified, I just wanted to clarify that a one-size-fits-all solution probably isn’t optimal. Overall usage (and therefore overall anonymity set) could be improved by offering two distinct options. Maybe to simplify node implementation, DTX messages would still perform the exact same protocol, just with Clover parameters selected to maximize privacy vs. latency. (Users needing global-dragnet resistance should still broadcast over Tor.)

2 Likes

Super excited to see where this leads – awesome stuff!

2 Likes

https://x.com/bitjson/status/1899735806337601903

If one wanted encrypting connections, why not just use TLS? Hasn’t this problem has been solved aeons ago?

The P2P network doesn’t necessarily “need” authentication, only encryption, and BIP324 makes some good arguments: bips/bip-0324.mediawiki at master · bitcoin/bips · GitHub.

But I agree, that was my conclusion too: we should use existing protocols with good language/library support rather than BIP324 or making our own.

E.g. libp2p WebTransport would use TLS, and libp2p standardizes other messy bits – browsers require the hash of self-signed certificates in advance of the connection, so libp2p “multiaddresses” + a security handshake lets browser clients connect. See: libp2p Connectivity

I’m not suggesting unsolicted TX messages. I’m suggesting the originating node X picks a random outbound peer Y and sends an INV message, waits for GETDATA then sends TX.

If Y is honest, how does the adversary A know that the transaction originated with X and not with Y? Surely the first-timestamp estimator will select Y.

To what extent does this rely on orignating node X broadcasting new transactions to all inbound and outbound peers?

Are there any strategies that can reduce deanonymisation precision without a protocol change?

There is a need to correct this widespread “misunderstanding” of CashFusion.

The fact is that a wallet that has used fusion properly will have severed its history completely. Any money held in that wallet is then no longer linked to any past transactions. And this is permanent. Barring bugs, there is no way that a user can mistakenly undo this.

This is a very important fact. After applying this simple privacy feature, you clean your coins. History is dropped, permanently. User error doesn’t have risks to undo this.

So, Jason claiming that this is good for “leaks in privacy in earlier (or later) transactions” is at best misunderstanding the tech. But with Jason doing this repeatedly it likely means he is aware and just spreading this misinformation to entice people to think his ideas are needed to solve those not existing problems.

CashFusion being applied properly is by far the best privacy tool we have. And it is available today.
It is not really much in demand in wallets and such because most people don’t really care.

This is indeed how it works in real life. In practically all of the actual software doing this.

If Jason is doing it differently, he’s doing it wrong.

I found the answer in the Dandelion paper, which calls it diffusion-by-proxy:

A natural strategy for breaking symmetry about the source is to ask someone else to spread the message. That is, for every transaction, the source node chooses a peer uniformly at random from the pool of all nodes. It transmits the message to that node, who then broadcasts the message.

Diffusion-by-proxy might seem like it should have low precision because the graph is so dynamic, but that intuition turns out to be false.

See the paper for more details on why. My observation is alluded in section 4.4:

Intuitively, this statement holds because each node delivers its own message to the adversary with probability p.

In Dandelion paper p refers to the ratio of colluding nodes to the total number of nodes in the network. The Clover paper calls this is |S|/|R| (or |A|/|R| in figures 2 and 3, perhaps a typo?) and uses p for broadcast probability.

In my initial analysis I was thinking only about detection probability (recall), not detection precision. Recall has a floor at p, precision has a floor at p2 (where p is as defined in Dandelion paper).

It’s a pity that the Clover doesn’t use the same terminology and graphs. It seems to be focussed on precision rather than recall, perhaps because Dandelion already achieves optimal recall but not optimal precision.

Anyway Clover does seem simpler than Dandelion and provides significant improvement over Diffusion so it could be a great addition to BCH.