Skip to content
VOUCHVOUCHDocs

Architecture

Anchors and chain data

A verifier accepts a proof only if its two roots are in the verifier’s anchor table. This page explains how those roots are built from public data, checked, published and re-derived.

On this page

#What an anchor is

An anchor is a finalised height and two roots: the Ironwood note-commitment tree root and the root of the indexed Merkle tree of every Ironwood nullifier revealed up to that height. Records look like this:

JSON
{
  "network":   "mainnet",
  "height":    3493000,
  "blockHash": "00000000000477d021dac9bc28de61cac47a915add870a5716e3865234229b0c",
  "ncRoot":    "1780a43b9054d8f45de3763df336ec4f443697b2762d397b014255d202b56a3c",
  "nfRoot":    "34da5ef38d024c99b8ec7fc464c78cda5543b06ba412c775ca9dfcb26453c336"
}

#How an anchor is built

How an anchor is built and publishedlightwalletdzec.rockstestnet.zec.rocksStream blockssince Ironwood activationto tip − 100, by 1,000Ironwood actionsnullifier · cmx · epkcompact ciphertextNote treefrom every cmx→ nc_rootNullifier IMTfrom every nullifier→ nf_rootCross-checktree root, sizeand block hashPublishanchors.<net>.jsonnever overwrites.vsnp snapshotfor provers;checkpoint / 1,000GetTreeState: must match, or the scan fails
pof-anchor scan rebuilds both roots from public compact blocks, refuses to continue unless its tree matches lightwalletd's own tree state and block hash, then writes a snapshot and merges the record into the anchor table. scripts/sync-fixtures.sh publishes it to the site and the attestor.
  1. Connect to lightwalletd and read the tip. The anchor height is floor((tip − 100) / 1000) × 1000, so it is finalised and on a checkpoint.
  2. Stream every compact block from Ironwood activation (mainnet 3,428,143, testnet 4,134,000). Each Ironwood action carries a nullifier, a note commitment, an ephemeral key and a 52-byte compact ciphertext.
  3. Build the note tree from every commitment and the dense IMT from every nullifier.
  4. Check the tree root, its size and the block hash against lightwalletd’s own tree state. A mismatch stops the scan.
  5. Write the snapshot (.vsnp) with a checkpoint every 1,000 blocks, and merge the record into anchors.<network>.json. Publishing refuses a different record at a height already published.

#Snapshots

A snapshot is everything a prover needs, from public data only: every action’s nullifier, commitment and encrypted output up to a height, and (version 2) a checkpoint table every 1,000 blocks plus the last block. Provers trial-decrypt it to find their notes, and take the tree at an earlier checkpoint for “unmoved since” proofs. Reading is bounds-checked and never panics. The mainnet snapshot at 3,493,000 is about 84 MB.

#Published anchors

The table served at /api/anchors merges the mainnet, testnet and demo tables. Earlier testnet heights are there for “unmoved since” proofs.

NetworkHeightnc_rootnf_root
demo3,481,0007cd9a74820ce97cb…689d7c0c743b83eb…
demo3,491,040da7e8a67b49fa924…689d7c0c743b83eb…
mainnet3,493,0001780a43b9054d8f4…34da5ef38d024c99…
testnet4,410,000dc487630542d8caa…322fcff2c72d6acb…
testnet4,459,00094f218a998772a28…f9468d5ccaef361e…
testnet4,460,391d83151878dc1976e…157230149dc27092…

#Re-derive any entry

Shell
pof-anchor scan --to 3493000                 # mainnet, from zec.rocks
pof-anchor scan --network testnet --to 4410000
pof-anchor scan --network testnet --to 4459000 --also-at 4410000   # earlier checkpoints of the same scan
pof-anchor check --snapshot target/snapshots/testnet-4459000.vsnp --at 4410000

A wrong entry cannot make a false proof pass: the roots must match the proof’s own public inputs. It can only make honest proofs fail. That is why an anchor table is trusted for availability, not for truth.

#Testnet and mainnet

The network is recorded in the anchor table, not in the proof or the attestation. pof-anchor refuses a lightwalletd that reports a different chain than --network, so a testnet anchor cannot be published as mainnet, and verdicts on a testnet anchor say so. A verifier that only wants mainnet should load only mainnet anchors.