Zcash Verifier
Verifiability
Understanding public outputs, replay protection, and proof verification
Overview
The SP1 zkVM runs the exact same Rust verifier that powers the CLI and browser flows. When a proof succeeds, verifiers know the prover supplied a HoldingsWitness whose Merkle paths, alternate nullifiers, and transparent commitments all match the canonical Zcash snapshot at height 3118950.
Public outputs consist only of snapshot metadata and the ProcessedHoldings commitment (rounded total value + hash lists). No UFVKs, note plaintexts, or real nullifiers escape the circuit, so proofs can be published without revealing wallet internals.
What Gets Verified
- 1.
Snapshot Binding
SnapshotMetadata(height, Sapling/Orchard roots, transparent UTXO root, nullifier root, and leaf counts) is a public input. Changing any root or the block height invalidates the proof. - 2.
Commitment Inclusion
Sapling/Orchard notes reconstructed from UFVKs must hash back to the snapshot roots using their Merkle witnesses. Transparent shielding transactions are re-parsed, signatures checked, and commitments recomputed inside the circuit.
- 3.
Nullifier Exclusion
Alternate nullifiers derived with
ZFUN_ORCH_ALT_NF/ZFUN_SAP_ALT_NF0must fall strictly between the predecessor and successor nodes supplied by the proof service, showing the real notes remained unspent at the snapshot. - 4.
Rounded Balance Commitment
The circuit sums all verified value, ensures it fits in
u64, then rounds down to the nearest 1,000,000 zatoshis before committing it. This prevents leaking exact balances while preserving proof correctness. - 5.
Domain Binding
All alternate nullifiers are derived using
PROOF_DOMAIN(currentlyb"Z.funAirdrop"). Re-using a proof in another context fails because the domain string would change the public hashes.
Public Outputs
After verification succeeds, the SP1 program commits a ProcessedHoldings struct. These bytes are stored by the prover server and surfaced to integrators:
Applications can store these hashes to detect reuse. Because alternate nullifiers are domain- separated, they cannot be mapped back to real nullifiers or spending keys.
Verification Process
Minimal Rust code is required to verify a proof and recover the public outputs: