Z mark

Zcash Verifier

Overview

Understand the end-to-end system that turns a Zcash wallet into a verifiable SP1 proof

What Ships in `zfun`

The private `zfun` monorepo backs this documentation. It bundles the snapshot builder, witness extraction tooling, SP1 verifier program, prover service, and the public-facing claim experience into a single end-to-end flow for proving shielded and transparent Zcash holdings.

`crates/lib`

Core verification logic exposed as `verify_holdings`. It reconstructs Sapling and Orchard notes, checks Merkle witnesses against the canonical snapshot, verifies alternate-nullifier exclusion proofs, enforces transparent shielding signatures, and derives the public `ProcessedHoldings` commitment that the SP1 program emits.

Exports: `verify_holdings`, `SNAPSHOT`, `PROOF_DOMAIN`, `ProcessedHoldings`

`crates/host-lib` & `crates/host`

Turns a Zcash light-wallet SQLite database into a `HoldingsWitness`. It pulls Merkle checkpoints from the wallet, fetches nullifier exclusion and UTXO inclusion proofs from the proof service, and can be run via the `zfun-host` CLI (`cargo run --bin zfun-host wallet.db [--prove]`).

Env: `ZFUN_PROOF_SERVICE_URL` defaults to `http://localhost:3000`

`crates/program` & `crates/wasm`

The SP1 zkVM program simply reads the witness and calls `verify_holdings`, committing the result. The WASM wrapper (`load_processed_input`, `request_proof`) runs inside the browser to deserialize wallet bytes, build the witness, and hand it to the prover network without the wallet ever leaving the device.

Public API: `get_snapshot_metadata`, `load_processed_input`, `request_proof`

`crates/server` & `crates/setup-script`

`setup-script` derives the canonical snapshot from a Zebra node, producing Merkle forests for Sapling, Orchard, transparent UTXOs, and nullifiers. The Axum-based prover server exposes `/nullifier-exclusion`, `/utxo-proof`, `/hashes`, and `/proof` endpoints, brokers requests to the SP1 Reserved network, and deduplicates claims by storing alternate nullifiers and transparent commitments.

Env: `SERVER_BIND_ADDR`, `NETWORK_PRIVATE_KEY`, `NETWORK_RPC_URL`, `DATA_DIR`

End-to-End Flow

1 · Snapshot

Run `cargo run --bin zfun-setup-script /path/to/zebra-cache` to materialize Merkle trees and populate `SnapshotMetadata`. The resulting JSON drives both the verifier library and the browser UI.

2 · Wallet Ingest

`create_witness` (shared by CLI and WASM) opens the SQLite wallet, builds Sapling & Orchard Merkle witnesses, fetches exclusion proofs, and records shielding transactions for any post-snapshot spends.

3 · Local Verification

`verify_holdings` runs identically in native Rust, WASM, and inside the SP1 zkVM. It reconstructs notes via UFVKs, enforces Merkle inclusion, derives alternate nullifiers, and rounds the total to the nearest 1,000,000 zatoshis for privacy.

4 · Proving

The browser uploads the serialized `SP1Stdin` to the prover server. The server queues a Groth16 proof on the SP1 Reserved network and keeps state in SQLite (`proof_requests.db`). Once the proof is fulfilled, it verifies it locally before releasing it.

5 · Reuse

Verifiers consume `ProcessedHoldings` ( total zatoshis + alternate nullifiers + transparent commitments) and the snapshot height as public values. The server also rejects duplicate claims using these hashes.

Snapshot Constants

The current production snapshot baked into `SNAPSHOT` is derived from height 3118950. Every proof is bound to these commitments and the domain string `b"Z.funAirdrop"`:

Merkle Roots

  • Sapling · `39c5…7111`
  • Orchard · `79e9…0f12`
  • Transparent UTXO · `b414…45fe`
  • Nullifier · `2494…9c26`

Set Sizes

  • UTXOs: 27,980,676
  • Nullifiers: 52,937,578
  • Sapling leaves: 73,819,618
  • Orchard leaves: 49,332,279

Learn More

Dive deeper into the technical details and implementation: