The Filter That Was Supposed to Hide You

You open a lightweight Bitcoin wallet on your phone. The app asks a full node for only the transactions relevant to you, and rather than handing that node a plain list of your addresses, it sends something that looks cleverer: a bloom filter, a probabilistic data structure that lets the node answer "does this transaction match?" without, in theory, knowing exactly what it is matching against.

The mechanism sounds elegant. The problem is that the math does not deliver what the privacy promise implies, and nodes can, with modest effort, reconstruct a close approximation of your wallet's address set. Not sometimes. Routinely.

What a Bloom Filter Actually Does

A bloom filter is a bit array, say 36,000 bits long, paired with a handful of hash functions. To add an address, you run it through each hash function, get a position in the array, and flip that bit to 1. To check whether a transaction matches, the node runs the transaction's addresses through the same functions and checks whether all the corresponding bits are 1. All 1s means a probable match. Any 0 means definitely not.

Probable. That word is doing a lot of work here.

Bloom filters have false positives by design. Your filter might flag an address you don't own, and the wallet relies on that slop to obscure which addresses are real. The node sees matches but cannot, in a single query, tell the genuine hits from the noise.

Consider a worked scenario with specific numbers.

Your wallet holds 40 addresses. You construct a filter with a 1% false-positive rate. The node has the full UTXO set. It probes your filter by running every address it knows through your filter and recording every hit. At 1% across roughly 50 million known UTXO addresses, the node collects around 500,000 false positives alongside your 40 real addresses. That sounds like impenetrable noise.

But the node doesn't need to identify your addresses in one shot.

The Intersection Attack, Spelled Out

Two users. Call them Priya and Marcus. Both run the same lightweight wallet software.

Priya's phone is two years old, low on storage, connecting from a coffee shop IP. She connects to a node, sends her bloom filter, and the node logs which transactions it serves her. She disconnects. An hour later she reconnects from a different IP, but her filter is functionally identical because her wallet hasn't changed. She gets another session's worth of data.

The node operator takes the intersection of transactions matched across both sessions. False positives are random each time the filter is applied to new blocks, so they don't repeat reliably. Real addresses do repeat, because they always produce genuine hits. After three or four sessions, the intersection shrinks toward Priya's actual address set. The noise cancels. The signal doesn't.

This is the intersection attack. It was described in the Bitcoin research literature as early as 2014, and it is precisely why BIP 37, which defined bloom filters for SPV, is now widely regarded as a failed privacy mechanism. Not a flawed one that needs tuning. Failed.

Marcus, by contrast, uses the same node for six months without rotating. His filter barely changes because his wallet is stable. The node has a static fingerprint of his addresses from day one, no intersection math required.

Why the False-Positive Rate Is a Trap

The obvious fix seems to be cranking up the false-positive rate. Set it to 20%. Bury the signal in noise.

Two problems, and neither is subtle.

First, bandwidth. At a 20% false-positive rate over tens of millions of UTXO entries, the node ships you an enormous volume of irrelevant transactions. A lightweight wallet on a mobile connection grinds to a halt, which defeats the entire purpose of SPV.

Second, and this is the part that tends to surprise people: a high false-positive rate still leaks. The filter's size and element count are transmitted to the node as explicit parameters. A wallet with 40 addresses and a 20% false-positive rate has a mathematically predictable filter size, and the node can estimate how many addresses are inside. Beyond that, a patient operator can run the full UTXO set through your filter and apply statistical clustering to separate real hits from noise, because real addresses cluster around your transaction history in ways that random false positives simply do not.

The filter is less a mask than a frosted window: the shape behind it remains visible to anyone willing to look carefully.

Ask yourself whether you'd accept that tradeoff knowingly. The designers of BIP 37 presumably hoped you wouldn't need to.

What the Better Alternatives Actually Do Differently

BIP 37 bloom filters have largely given way to two approaches that take the opposite philosophical stance, and the distinction in architecture is worth understanding precisely.

Client-side block filtering, specified in BIP 157 and BIP 158, inverts the model entirely. Instead of the client sending a filter to the node, the node publishes compact filters of each block and the client downloads those filters, checking them locally. The node never sees what the client is searching for. It learns only which blocks the client subsequently fetches in full, a far weaker signal. Neutrino, implemented in btcd and used in wallets built on Lightning Labs' LND-based stack, works this way.

The second approach is running your own node. If your wallet queries only infrastructure you operate, the problem of a third party learning your addresses does not arise. The node is you. Bitcoin Core developers have pushed consistently toward this model for users who treat privacy as a serious requirement, and that consistency reflects a considered position, not mere preference.

Electrum's server model deserves mention, though it carries its own tradeoffs. The Electrum server you connect to learns your addresses explicitly. Trust in the server operator replaces the probabilistic leak of bloom filters: a known, consented relationship rather than a covert one. Whether that is preferable depends entirely on how much you trust the operator, which is at least an honest question to be asking.

The Honest Accounting

Bloom filters in SPV clients were a genuine engineering attempt to solve a genuinely hard problem: give lightweight clients meaningful privacy without requiring them to download gigabytes of blockchain data. The attempt was not foolish. It was insufficient, and the insufficiency took several years of careful analysis to fully characterize, which is itself a lesson about how long privacy failures can hide in plain sight.

The practical upshot is straightforward. If your wallet still relies on BIP 37 bloom filters against a third-party node you don't control, that node operator has a workable statistical picture of your address set. Not a perfect picture. Not necessarily a malicious one. But a real one, built from math that was always there in the specification, waiting for someone to use it.

Privacy in Bitcoin is not delivered by clever filters. It is earned by limiting what you reveal to counterparties who have no structural reason to keep your secrets.