Wallet Syncing
A shielded wallet has a problem an ordinary wallet does not. Your balance is not stored under your address — it is a set of encrypted notes sitting in a shared pool alongside everyone else's. Nothing in the pool is labelled. To know what you own, your wallet has to find your notes among all notes.
There are two ways to do that, and the choice between them is the single largest privacy decision a shielded wallet makes.
Two Ways To Find Your Notes
Ask a server to find them for you. The wallet sends a filter — derived from its viewing key — to an indexer, and the indexer returns the matching records. This is fast and cheap. It is also a complete disclosure: the filter is the question, and the server must read it to answer it.
Download the data and look yourself. The wallet fetches the pool's history in fixed, public pieces, then tries to decrypt each note locally. Only the notes that belong to you decrypt successfully. The server never learns which ones those were, because it was never asked.
Anon does the second. The mechanism is digest sync.
Why The Query Is The Leak
It is tempting to treat this as a logging problem — if the indexer promises not to retain queries, the exposure goes away. It does not.
An indexer query is a predicate: give me the commitments matching X. That predicate is computed from your viewing key. The server has to parse it to answer it, so your key material is in memory on someone else's machine on every single request, whatever the retention policy says. Turning off logs changes what is written to disk. It does not change what the server had to know.
Not logging is a promise. Not having the data is an architecture.
This is also why network-level anonymity does not rescue the query model. You can route an indexer request through Tor and the indexer still knows precisely which commitments you asked about — it just does not know your IP while learning it.
What A Digest Request Looks Like
Anon's history lives on a CDN as immutable, uniformly-chunked files. A request looks like this:
GET https://digest.anon.inc/v4/1/chunks/42/commitments.json.gz
There is nothing of yours in it. The path is a function of global chain state — chunk 42 covers a fixed range of Ethereum blocks and contains the same bytes for every person on earth who asks for it. A wallet holding nothing and a wallet holding a thousand notes issue the identical request for the identical file.
Two properties follow:
- There is no wallet-derived filter in the history request. A fully-logging, fully-compromised CDN learns that an IP downloaded a public file. It cannot learn what was in that file for you, because that is not a fact the CDN is in possession of.
- Hits and misses are invisible. Decryption happens on your device, after the bytes arrive. The edge cannot tell whether chunk 42 contained anything for you. An indexer knows by construction, because the filter was the question.
Sealed chunks are immutable and cached at the edge for a year, so in steady state your request is answered by a Cloudflare data centre near you and never reaches Anon's infrastructure at all.
What Each Server Can See
Syncing is not the wallet's only network activity, and the surfaces do not all have the same properties. Stated plainly:
| Surface | Operated by | Derived from your keys? | What it can learn |
|---|---|---|---|
Digest CDN (digest.anon.inc) |
Cloudflare | No | Your IP, and that you fetched public files covering a given block range |
Indexer head API (api.anon.inc) |
Anon | No | Your IP, and roughly how far behind the chain tip you are |
| PPOI proxy | Anon, in front of a third-party aggregator | Yes | Your IP, and which blinded commitments you asked about |
| RPC endpoints | Your configured provider | No | Your IP, and which public addresses and contracts you query |
| Broadcaster network (Waku) | Peer-to-peer | No | Encrypted transaction messages; peers may observe connection metadata |
| Paymaster through OHTTP | Anon gateway and an independent relay | No | The relay sees the client IP and encrypted request; the gateway sees the decrypted request and submission timing |
The head API keeps your wallet current between chunk seals. It takes a block or sequence cursor — your position in the chain, not a fact about your wallet — so what it reveals is "this IP had synced to about here," never "these notes are theirs."
The PPOI proxy is a separate privacy surface. Proof of Innocence asks an aggregator about blinded commitments derived from the viewing key. These requests are wallet-specific. The current proxy can cache reads and retain request parameters in cache metadata; a hashed cache key does not anonymize that data. Operators must review retention and network-metadata correlation. See PPOI Proxy. The bulk history path is query-free; the POI path is not.
What This Does Not Protect Against
- Timing and volume. A first-run sync pulling the entire history looks different on the wire from a returning wallet pulling only the tip. That leaks approximately "this IP last synced around block X." It does not leak which notes are yours.
- Connection metadata. The server that receives a direct connection can observe its IP address, timing, and TLS metadata. An OHTTP relay separates the client connection from the gateway that decrypts the request; the relay still sees the client IP.
- Blinded commitments on the PPOI path, as described above.
Network-layer exposure can be reduced by a correctly configured VPN or Tor, but this changes who you trust and does not eliminate traffic analysis or wallet-specific application data.
Network privacy does not erase wallet-specific request contents.
With a query-based indexer the leak sits at the application layer, where the user has no remedy short of not using the wallet. Uniform, content-independent history requests avoid that particular filter disclosure. Other wallet requests and network metadata still require separate consideration.
The Tradeoff
This is not private information retrieval. There is no cryptography hiding your request, because there is no request to hide — you are simply downloading the anonymity set and searching it yourself. You pay for that in bandwidth: a first sync transfers the pool's compressed history rather than a handful of matching rows.
For this workload it is the right trade. The data is public, immutable, and identical for every user, which makes it perfectly cacheable — so the cost is paid once per file per edge location rather than once per user, and it buys the elimination of an entire class of disclosure. But it is a trade, and it holds only as long as chunk selection stays independent of the user. A client that fetched only the ranges it "needed" based on wallet contents would have reinvented the query in a slower form. Chunk boundaries are therefore derived from block height alone.
Related
- Digest Sync — the wire format, chunking, and sync algorithm in full
- PPOI Proxy — the compliance path and its caching rules
- How Privacy Works — what observers see on-chain
- Security Overview — the full threat model