Skip to main content

The Proof Chain

Every registration produces one proof. These proofs form a chain of state transitions from genesis:

genesis_root (empty tree -- known constant)
|
+-- Proof 1: "alice" registered at block 100, txid abc...
| old_root = genesis_root --> new_root_1
|
+-- Proof 2: "bob" registered at block 105, txid def...
| old_root = new_root_1 --> new_root_2
|
+-- Proof 3: "carol" registered at block 110, txid 789...
old_root = new_root_2 --> new_root_3 (= current root)

Each proof's old_root must equal the previous proof's new_root. The first proof's old_root must equal the known genesis root (an empty Sparse Merkle Tree).

Verifying the Chain

A verifier checks the entire registration history in five steps:

  1. Get the SP1 verification key -- deterministically derived from the guest program binary
  2. Fetch all proofs from the API
  3. Verify each SP1 proof using the verification key
  4. Check the chain -- genesis == P1.old_root, P1.new_root == P2.old_root, ..., PN.new_root == current_root
  5. Spot-check on-chain -- look up any committed txid on a Zcash node and confirm the memo, value, and block match

If all checks pass, the entire registration history is correct -- no trust in the indexer needed.

Proof Pipeline

The scanner and prover run independently:

Zcash Blockchain
|
ZNS Scanner (validates, captures SMT state)
|
SQLite DB (status: "pending")
|
SP1 Prover (generates ZK proof, verifies it)
|
SQLite DB (status: "proved")
|
API Server (GET /v1/proof/{name})

The scanner never blocks on proof generation. The prover daemon polls for pending records and can run on separate hardware.