Z mark

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. 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. 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. 3.

    Nullifier Exclusion

    Alternate nullifiers derived with ZFUN_ORCH_ALT_NF / ZFUN_SAP_ALT_NF0 must fall strictly between the predecessor and successor nodes supplied by the proof service, showing the real notes remained unspent at the snapshot.

  4. 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. 5.

    Domain Binding

    All alternate nullifiers are derived using PROOF_DOMAIN (currently b"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:

Loading...

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:

Loading...