Figuring out ways to make light clients with better privacy is interesting. Even if people figure out how to do it with minimal trade-offs, there is the concern that fewer people will have an incentive to run a full node.
Anyhow, here is an interesting scheme:
Today's light wallets leak every address you own to their server, enabling full surveillance of your balances, transactions, and spending habits. Bitcoin PIR uses Private Information Retrieval — a family of cryptographic protocols where the server(s) provably learn nothing about which records you look up — to give light wallets the same privacy as a full node, without the storage cost.
It sounds like the light client would need a few servers to connect to, or at 1 running a specific kind of service:
| Backend | Trust Model | Best For |
| DPF (2-server) | Privacy holds as long as two servers don't collude | Fast, lightweight queries |
| OnionPIR (1-server, FHE) | Single server, cryptographic privacy from lattice hardness | Strongest single-server guarantee |
| HarmonyPIR (1 or 2-server, stateful) | Offline setup phase; deployed here as 2-server (query server + dedicated hint server) | Fast online queries after initial sync |
All three backends expose the same high-level API — clients can switch between them without changing their code.
I don't fully understand how this achieves privacy, but I think that it has to do with querying merkle roots and sending queries in batches.
Each UTXO lookup can be paired with a Merkle proof query that verifies the result against a published root hash. The server can refuse to answer, but it cannot lie about your balance. Verification is batched across all addresses in a wallet sync — one proof round covers the whole batch.Privacy note on Merkle verification. Within each PIR round, queries are padded to a fixed count (e.g. 75 for index, 80 for chunk, 25 for Merkle siblings) so the server cannot tell which group is real. However, the current implementation does not pad the number of rounds: the server can observe whether chunk and Merkle-sibling rounds are sent at all, and how many. This reveals whether the address was found and roughly how many UTXOs it has. Fixing this requires sending dummy chunk and Merkle rounds even when no results were found. This is a known trade-off — the lookup itself remains private, but the coarse shape of the result (found/not-found, small/large) is visible to the server.
Nice! I've been eagerly waiting for someone to actually implement this for some time. FHE is magic.