Skip to content
VOUCHVOUCHDocs

Guides

Answering a request

For holders: from a request link to a proof in the verifier’s hands, without your keys, your balance or your history leaving your machine.

On this page

#What happens

Inside pof-prove proveHolder's machine · offlineRead the requestdecode, validate, print thesentence, ask to confirm1Open the snapshotchecksum-verified; its anchormust be published2Derive keysseed → ZIP 32 spending keycoin type from the network3Find your notestrial-decrypt every actionin parallel; drop spent ones4Pick notesfewest, largest firstat most 5, sum ≥ X5Fix the envelopeevery field final beforeanything is bound6Build witnessesMerkle paths + IMTnon-membership proofs7ProveHalo2 over PastaK = 12, no trusted setup8SignRedPallas over statement‖ public inputs9Writeproof.pof + history(0600; secret kept locally)10
Everything runs on the holder's machine. The prover library links no network client (a test enforces it); the CLI touches the network only for pof-prove revoke, and only when asked.

The prover is a single binary. It reads three things: the request, a public chain snapshot, and your seed. It writes one file. It never connects to anything while proving; the only command that touches the network is pof-prove revoke, and only when you run it.

#Read the request

A request is a link to /prove?r=…. The r value is base64url JSON, and the whole request is in it; nothing is stored on a server. Open the link at /prove to read, in plain sentences, what the verifier will and will not learn.

FieldMeaningRule
vRequest format versionmust be 1
idRequest id; the default deposit intent for an exithex
claim · zatoshiThe threshold, as a decimal stringHoldsAtLeast; a multiple of 0.125 ZEC
audienceThe verifier’s identifierprintable ASCII
expiryDaysHow long the proof stays valid1–365 days
respondByOptional deadline for an answerunix seconds
bindsolana: bind the proof to your Solana accountoptional
kindexit (an exit certificate) or reserves (a batch)optional
epochThe verifier’s period: reuse within it is visible to themoptional
dormantSinceThe block your notes must be unmoved sincea multiple of 1,000

The same rules are implemented in Rust and TypeScript and checked against one shared vector file in CI.

#1 · Install the prover

Each release has pof-prove, pof-verify and pof-anchor for macOS (Apple silicon), Linux and Windows, with a SHA-256 beside every archive.

Shell
curl -fL https://github.com/SATISH-JALAN/Vouch/releases/latest/download/vouch-linux-x86_64.tar.gz | tar xz   # or vouch-macos-arm64.tar.gz, vouch-windows-x86_64.zip
# or with Rust 1.91 or newer
cargo install --git https://github.com/SATISH-JALAN/Vouch pof-prove --locked
# or from source
git clone https://github.com/SATISH-JALAN/Vouch && cd Vouch/backend && cargo build --release -p pof-prove

#2 · Get the chain snapshot

The prover needs the public Ironwood data up to a published anchor: every note commitment and nullifier, and the encrypted outputs your wallet finds its notes in. Build it yourself from any lightwalletd:

Shell
cargo install --git https://github.com/SATISH-JALAN/Vouch pof-anchor --locked
pof-anchor scan --server https://zec.rocks:443 --to 3493000
# ✓ nc_root matches lightwalletd's Ironwood tree state at 3493000

It writes target/snapshots/mainnet-<height>.vsnp. The anchor it prints must match one in the verifier’s table, or the verifier answers AnchorNotFound. On testnet add --network testnet: the server, the start height and the table switch, and pof-prove takes the network (ZIP 32 coin type 1) from the snapshot.

#3 · Review, then prove

Shell
pof-prove prove --request <encoded> --snapshot target/snapshots/mainnet-3493000.vsnp \
                --seed-file ~/.vouch/seed.txt --out proof.pof

  ✓ account 0 on mainnet
  ✓ 565,704 Ironwood actions to block 3493000
  ✓ 2 notes found
  ✓ anchor at block 3493000
  ✓ 1 note used; balance not revealed
  wrote proof.pof (11,886 bytes). Nothing was broadcast.

The seed file holds a BIP 39 mnemonic or a hex seed. The prover prints the same sentence as /prove and asks before doing anything. It picks the fewest notes that clear the bar, largest first, up to five, and fails fast if the best five cannot.

Variants

You wantAdd
A proof a Solana program can use--bind-solana <your pubkey>
“Unmoved since block H”--since <H>
An exit certificate--exit [--intent <deposit id>]
A reserves batch (more than five notes)--batch-size 5 --out reserves.pofb

Exits: send from exactly the certified notes

An exit certificate names its notes. Send the deposit from those notes in a transaction with no dummy spends, or the rail will see a nullifier the certificate never named and answer mismatch. Make the certificate only when you are ready to send.

#4 · Check it, hand it over

Drop proof.pof on /prove or /verify: you see exactly the verdict the other side will see. Then send the file, or the verify link, which carries the proof after the # so it never reaches a server log.

#5 · Revoke it

Shell
pof-prove history                     # your proofs: claim, audience, expiry, status
pof-prove revoke <id> --endpoint https://vouch-zcash.vercel.app/api/revocations

Revoking publishes the proof’s 32-byte revocation secret. Every verifier that checks the list answers Revoked from then on. History is saved with owner-only permissions; if saving fails, the secret is printed so it is never lost.

#No ZEC? The demo ledger

pof-prove demo prove --world fixtures/demo-world.json --request … answers any request as the demo holder: 4,018.2 ZEC in five real Ironwood notes under a real key, in a published demo tree. The proofs are real; the anchor is labelled demo everywhere it appears.