Groth16 trusted setup, multi-party (MPC) procedure
VDI uses Groth16 over BN128. Soundness requires that the proving key (*.zkey) was produced in a setup where at least one participant behaved honestly and destroyed their toxic waste (the randomness used during their contribution). A multi-party ceremony achieves this without trusting any single operator: if you trust that any one contributor was honest, the final parameters are secure.
This document is the operational counterpart to the “Trusted Setup Note” in protocol/NCC_REVIEW_PACKET.md.
What gets generated
| Phase | Artifact | Purpose |
|---|---|---|
| Phase 1 | pot{N}_*.ptau → pot{N}_final.ptau | Powers of Tau, shared across all circuits of sufficient constraint size |
| Phase 2 | {circuit}_0000.zkey → contributions → final zkey | Circuit-specific Groth16 proving key |
| Export | {circuit}.vkey.json | Verification key embedded in attestations |
The reference tool is snarkjs (via npx snarkjs). Phase 1 can be run once per curve/power level; Phase 2 is repeated per circuit (or per R1CS revision).
Prerequisites
npx/ Node.js ≥ 18jq(for JSON transcript)snarkjsresolved bynpx(project or global)- Circom already compiled the circuit to R1CS (and WASM for proving at runtime)
One-shot MPC (Phase 1 + Phase 2)
From the repository root, after R1CS exists (example: zk-cp causal circuit):
export MPC_CONTRIBUTORS=3
export MPC_CIRCUIT_NAME=causalProof
export MPC_R1CS_PATH=zk-cp/build/causalProof/causalProof.r1cs
export MPC_OUTPUT_DIR=zk-cp/build
export MPC_POWERS=14
bash protocol/scripts/mpc-snarkjs-ceremony.shAdjust paths if your Circom outputs live under zk-cp/build/causalProof/ (see zk-cp/scripts/build-all-circuits.sh).
The script:
- Runs Phase 1 with
MPC_CONTRIBUTORSsequential contributions (or skips ifMPC_PTAU_FINALis set; see below). - Runs Phase 2 Groth16 setup + the same number of zkey contributions.
- Exports
${MPC_CIRCUIT_NAME}.vkey.jsonintoMPC_OUTPUT_DIR. - Copies the final proving key to
${MPC_CIRCUIT_NAME}_0001.zkey(name kept for compatibility with existing prover paths). - Writes
mpc_transcript_${MPC_CIRCUIT_NAME}.jsonandmpc_ceremony.logunderMPC_OUTPUT_DIR. - Runs
snarkjs zkey verifyon the final key.
Minimum contributors: the script defaults to requiring at least 2 contributors (override with MPC_MIN_CONTRIBUTORS). Production ceremonies often use 3+ independent parties.
Reusing Powers of Tau (Phase 2 only)
If you already have a final Phase 1 file (e.g. pot14_final.ptau from a prior MPC or a published PTAU you audited):
export MPC_PTAU_FINAL=/absolute/path/to/pot14_final.ptau
export MPC_SKIP_PHASE1=1
# ... same MPC_CIRCUIT_NAME, MPC_R1CS_PATH, MPC_OUTPUT_DIR, MPC_CONTRIBUTORS
bash protocol/scripts/mpc-snarkjs-ceremony.shPhase 2 still uses multiple zkey contributions; only Phase 1 is skipped.
VDI policy circuits (protocol/build)
After compiling Circom sources under protocol/circuits/ to protocol/build/<Circuit>/, run the same script with:
MPC_R1CS_PATH=protocol/build/fairLending/fairLending.r1cs(example)MPC_OUTPUT_DIR=protocol/build/fairLendingMPC_CIRCUIT_NAME=fairLending
Then refresh protocol/ARTIFACT_MANIFEST.json using the steps in Post-ceremony: manifest pin below.
Operational security
- Entropy: Contributors should supply their own high-entropy secret (or HSM-backed randomness); the script supports interactive input when a TTY is available.
- Toxic waste: Each contributor must delete their contribution secret after the step; coordinators should not retain it.
- Transcripts: Publish
mpc_transcript_*.jsonand SHA-256 hashes of finalptau/zkey/vkeyfor third-party verification. - Hermez / community PTAU: Many teams reuse well-known
pot*_final.ptaufiles for BN128; if you use one, document the exact file hash you pinned.
Post-ceremony: manifest pin
After mpc-snarkjs-ceremony.sh completes, pin the transcript and (optionally) refresh on-disk artifact hashes in ARTIFACT_MANIFEST.json so verifiers can enforce circuit governance (VDI-SPEC §14).
1. Regenerate base manifest (optional)
If you added a new circuit or changed Circom sources:
cd protocol
node scripts/generate-artifact-manifest.js2. Merge MPC transcript + refresh hashes
Recommended after MPC (new zkey / vkey on disk, wasm/r1cs unchanged):
cd protocol
npm run manifest:post-mpc -- \
--transcript ../zk-cp/build/mpc_transcript_causalProof.json \
--circuit-dir ../zk-cp/build \
--refresh-hashes--transcript: path tompc_transcript_<circuit>.jsonfrom the ceremony.--circuit-dir: directory that contains{circuit}_js/,{circuit}.r1cs,{circuit}_0001.zkey,{circuit}.vkey.json(same layout asgenerate-artifact-manifest.js).--refresh-hashes: recomputesartifacts.*.sha256and sizes from files. Omit it if you only want to attachceremonymetadata (transcript SHA, ptau/zkey/vkey hashes from the transcript) without touching wasm/r1cs entries.
The merge adds a per-circuit ceremony object (contributor count, dates, transcript SHA-256, phase-1 skip flag, and hashes copied from the transcript).
3. Commit and publish
Commit ARTIFACT_MANIFEST.json, the transcript JSON, and (if you version them) final ptau / zkey references. Publish transcript + manifest hashes for third-party verification.
Relationship to existing scripts
verifier-api/circuits/scripts/mpc-ceremony.sh, older compliance_certificate–specific ceremony; preferprotocol/scripts/mpc-snarkjs-ceremony.shfor new work.protocol/scripts/compile-circuits.sh, dev single-contributor flow; replace proving keys with MPC outputs before production.
