Architecture
Where each part runs, why the wrapper that was supposed to join them cannot exist, what the attester quorum replaced it with, and why Zolar serves the Ironwood pool only.
Zcash proves on the Pallas and Vesta curves. The only pairing syscall Solana has is over BN254. Nothing bridges that gap, and the measurement that established it decides the shape of everything below.
So the proof is built where the secrets are, checked where the anchor is already being asserted, and settled where the audience is.
Three places
Each part runs where it does because of what it has to see.
- 01Your browser
Build the Halo 2 proof over Pallas
It needs the spending key and the Merkle witness, and neither is allowed to leave the device. Halo 2 proving in a browser is no longer theoretical: the shielding path does it in production.
8.3 s in a Web Worker, 4,960-byte proof
- 02An attester quorum
Verify the proof, then sign the claim and the anchor
Solana cannot follow Zcash consensus, so somebody has to assert what the Ironwood tree looked like. That is the quorum, and it is trusted for the anchor whether or not it also checks the proof.
6.4 ms per proof, measured
- 03Solana
Check the signatures, then write a non-transferable attestation
One Ed25519 precompile call, then an account carrying the claim, the anchor it was checked against, and when it expires. The proof is published beside it, so anyone can redo the arithmetic.
not yet written
The wrapper
An earlier version of this page had a fourth place: a service that re-proved the Halo 2 statement as Groth16 over BN254, so that Solana could verify the mathematics itself. That service cannot be built.
- Constraints in the wrapper circuit 1.55 to 8.97 B
- The lower figure is a floor that assumes free lookup tables and free multiplexers. It is a bound rather than a target.
- Proving key at the floor 0.7 TB
- Extrapolated from a measured slope of 448 bytes per constraint. Linear is the optimistic reading, because the FFT bends upward long before a billion.
- What the verifier actually folds 2,048 terms
- Halo 2's inner product argument ends by folding the whole reference string into one check, and every scalar in it is a nonzero product of challenges.
- What an earlier estimate counted 155 elements
- The proof's own elements. Ruling out tree depth was right; putting the proof in its place was not.
The spike does not ask you to take that from the source. Two proofs from one run: this circuit at k = 11 carries 155 elements and verifies in 6.4 ms, while a one-gate circuit at k = 13 carries 40 and takes 16.1 ms.
Four times fewer elements, two and a half times slower. Whatever paces verification, it is not the proof.
Verifying the Pallas proof on Solana directly does not rescue it either. There is no Pasta syscall, so the same multi-scalar multiplication runs as field arithmetic in BPF.
That is on the order of a hundred million compute units against a budget of 1.4 million per transaction, or roughly a hundred transactions per attestation. Worth revisiting later, and not worth five weeks now.
The witness
Before any of that runs, the browser has to hold the note's path through Ironwood's commitment tree. The siblings along that path are other people's notes.
Reading them is not a lookup. It means replaying every commitment the pool has ever appended, in order, and hashing each one.
Where the replay starts is the whole cost. A wallet created here starts one block below the height it was created at, because a note cannot have been paid to an account that did not exist yet.
The light server publishes a frontier for that height, and it folds every earlier commitment into thirty-two nodes. A phrase restored from elsewhere has no such block, so it walks from Ironwood's activation.
Once built, the tree is kept in a store of its own, so a second proof costs seconds.
That store is separate from the one an ordinary sync writes. A sync knows only the notes this wallet owns, and a tree is made mostly of the ones it does not.
The relay
A browser cannot speak to a Zcash light node. gRPC finishes a call in HTTP/2 trailers that JavaScript is never given, and a light server publishes no CORS headers.
The request is blocked before it is sent, and neither half of that is a question of picking a better server.
So Zolar runs one: a single Rust binary that translates gRPC-web and keeps no database. It streams instead of buffering, because a restore pulls tens of megabytes through it.
That relay is the only infrastructure Zolar operates today. The attester quorum will be the second, and the last.
- What the relay carries public data
- Compact blocks, which are public before it touches them, and the queries asking for them. A seed, a key, a witness and a note value are never part of a request, so it cannot see one.
- What it still learns metadata
- An IP address and a pattern of queries. Calling this zero-trust would be false, and the widget says so where somebody can act on it.
- Two other parties answer the page IP only
- A price oracle for the dollar figure, and a Solana RPC for the balance in the bar. The second is asked about a Solana account, which is public by design. Neither is ever asked anything about Zcash.
One Rust core
Everything cryptographic is one Rust workspace, compiled twice: to native binaries that test it and shield from a terminal, and to WebAssembly for the browser.
The browser runs that code. It does not run a second implementation of it.
That is a soundness decision before it is a convenience. The Orchard bug sat inside a circuit whose honest path was perfect for four years.
A second implementation of anything in that class is a second chance to be wrong in a way the tests agree with.
- Wallet module 346 KB
- Key derivation, scanning, trial decryption, and the commitment tree. Loaded by everybody who opens a wallet, so it is the one kept free of C dependencies.
- Prover module 1.42 MB
- The Layer 1 circuit and nothing else. Fetched on the first press of PROVE, because a visitor who never proves should not pay for it.
- Shield module 2.1 MB
- Assembles, proves and signs a real Zcash transaction. The same crate the command-line tool runs, which is how a browser came to move money on mainnet.
Programs on Solana
No Zolar program runs on Solana yet. Two are specified, and each is narrow for a reason of its own.
- The attestation account not written
- Checks the quorum's signatures through the Ed25519 precompile, then writes the claim, the anchor it was checked against, the expiry and the owner. It is not a Groth16 verifier, which is what the wrapper would have forced it to be.
- The receiving program not written
- Converts what lands at a Solana account and forwards it to that holder's own shielded address. Forwarding there is the only thing it can do, including to Zolar.
The boundary
Three lists. Everything in the first two is public before Zolar touches it, and everything in the third is why the first two can be.
- Goes on-chain public
- The claim, the anchor it was checked against, the account it belongs to, when it expires, and the quorum's signatures.
- Published beside it public
- The proof and its public inputs. Both are already public, and publishing them is what lets anybody re-check the quorum instead of trusting it.
- Never leaves the browser private
- The seed, the spending key, every viewing key, the Merkle witness, note values, note commitments, note positions, your Zcash address, and how far above the threshold you actually are.
Ironwood only
In June 2026 a soundness bug was disclosed in the Orchard Action circuit. An under-constrained element had survived four years and several audits.
It was never exploited, and the turnstile held: value leaving a pool is capped at the value verifiably entered, so a bug that could have counterfeited ZEC was contained as one pool's solvency problem instead of chain-wide inflation.
In July, Ironwood replaced Orchard with a formally verified pool, and 3.7M ZEC began migrating through that turnstile.
- Circuits to build and audit 1
- One instead of two or three, because only one pool is served.
- Trusted setup none
- None. Sapling would have brought one along with a second curve.
- Foundation pool verified
- Formally verified, which leaves Zolar's own circuit as the remaining surface.
- The cost: addressable base unknown
- Only migrated ZEC can be proved. Migration is mandatory and one-way so the number only rises, but the share as of today has not been checked, and it is the first thing a reviewer will ask.
- The cost: no proving from transparent funds accepted
- Funds at a transparent address are public, traceable, and outside this circuit. The wallet reads a transparent balance and can shield it into Ironwood, but it can never prove anything about it while it sits there.