Skip to main content

Trusted Setup

Every Groth16 circuit needs a one-time setup that produces its proving key and its verifying key. This page explains what that setup guarantees, what it does not guarantee, and how Orbinum's keys were produced.


What a Groth16 setup is​

The setup runs in two phases:

PhaseScopeOutput
1. Powers of TauAny circuit up to a size boundA shared file of structured parameters
2. Circuit-specificOne circuit, one versionThe proving key (.zkey, and .ark for arkworks) and the verifying key

Each contribution mixes in secret random values. Together these values are the setup's toxic waste. If they are discarded, nobody can produce a valid proof without a valid witness. In a multi-party setup, the keys are sound as long as one contributor discarded their share.

Phase 2 cannot be skipped. A key from phase 1 alone has delta equal to ±gamma or ± the G2 generator, and with it anyone can forge a proof from public key data. The chain refuses such a key at registration (see Extrinsics).


What the toxic waste could and could not do​

Whoever held the complete toxic waste for a circuit could forge proofs, meaning valid proofs for public values that have no valid witness.

A forger couldBecause
Shield a note worth more than the depositThe shield proof is all that ties a commitment's value to the amount paid in
Spend notes that do not exist, minting private valueThe transfer and unshield proofs are all that show membership and conservation of value
Withdraw that value through unshieldThe pool pays out from the deposits backing every note, so the loss falls on all depositors together

A forger could not:

  • Deanonymise anyone. A Groth16 proof under a well-formed key reveals nothing about its witness, whoever generated the key. Note contents are protected by the commitment hash and the memo encryption, and neither depends on the setup.
  • Spend a particular person's note. That requires the note's nullifier, which is Poseidon2(commitment, spending_key), and so requires the owner's spending key.

So a compromised setup is a soundness failure (inflation), not a privacy failure.


Current state​

The keys registered on the chain today come from a single-contributor setup:

PhaseHow it was run
1The Hermez Powers of Tau (powersOfTau28_hez_final_16.ptau, up to 2^16 constraints), a public multi-party ceremony. The file is checked against its published blake2b-512 hash before use.
2One contributor per circuit, using fresh random entropy, finished with a public beacon: the hash of a finalized Orbinum testnet block, with 2^10 iterations.
CircuitVersionBeacon block
Transfer (1)3#1190708 0x63dca3b61d162679b1acad67bbd7276bfb235a40972792e377fe1ba62910f81f
Unshield (2)3#1190708 0x63dca3b61d162679b1acad67bbd7276bfb235a40972792e377fe1ba62910f81f
Shield (3)1#1118341 0x4a194296be34a87bdb073fa8f678380d3e45c790ffca9fc0ce94de2ec0568ed8
Single-contributor setup

The soundness of phase 2 rests on one contributor having discarded their entropy. This is acceptable for a testnet. A public multi-party ceremony is planned before mainnet. It will produce new keys, which will be registered as new circuit versions through the normal rotation.


Checking the keys​

Two independent checks are possible:

  1. The chain uses the published key. @orbinum/circuits ships a manifest.json with a vk_hash for every circuit version. The chain reports the hash of each registered key through zkVerifier_getAllCircuitVersions. Both are blake2_256 of the arkworks-compressed verifying key, so they must be equal. The app runs this comparison before it proves.
  2. The published key comes from the circuit. The package also contains each circuit's .r1cs and .zkey. Running snarkjs zkey verify <circuit>.r1cs powersOfTau28_hez_final_16.ptau <circuit>.zkey checks that the proving key was derived from that circuit and that Powers of Tau file, with the recorded contributions and beacon.

Neither check can prove that the contributor discarded their entropy. Only a multi-party ceremony removes the need to trust that.