This directory contains the Circom circuit for Opaque's Programmable Stealth Reputation (PSR) system. The circuit enables a user to prove they own a stealth address with a specific attestation (trait/badge) without revealing the address itself or any other identifying information.
Proof system: Groth16 (BN254 / alt_bn128) Tree depth: 20 (~1,048,576 announcement capacity) Constraint count: ~50,000
Given public inputs (merkle_root, attestation_id, external_nullifier), the prover demonstrates:
- They know a
stealth_private_keythat derives a valid BabyJubJub public key. - An ECDH shared secret between that key and an
ephemeral_pubkeyproduces a stealth address commitment. - A leaf
Poseidon(commitment, attestation_id)is included in the Merkle tree at the givenmerkle_root. - The announcement's
attestation_idmatches the claimed publicattestation_id. - A nullifier
Poseidon(stealth_private_key, external_nullifier)is output — binding the proof to a specific action context and preventing replay.
| Signal | Visibility | Description |
|---|---|---|
merkle_root |
Public input | Root of the announcement Merkle tree |
attestation_id |
Public input | The trait/badge ID being proven |
external_nullifier |
Public input | Action-scoped nonce (vote ID, campaign ID, etc.) |
stealth_private_key |
Private input | Scalar on BabyJubJub |
ephemeral_pubkey[2] |
Private input | BabyJubJub point (x, y) from the announcement |
announcement_attestation_id |
Private input | Attestation ID stored in the announcement leaf |
merkle_path_elements[20] |
Private input | Sibling hashes for Merkle inclusion proof |
merkle_path_indices[20] |
Private input | Direction bits (0=left, 1=right) for each level |
nullifier |
Output | Poseidon(stealth_private_key, external_nullifier) |
is_valid |
Output | 1 if all checks pass, 0 otherwise |
- BabyJubJub — Elliptic curve for in-circuit scalar multiplication (key derivation + ECDH)
- Poseidon — SNARK-friendly hash for Merkle tree, address commitments, and nullifiers
- EscalarMulAny — Arbitrary-base scalar multiplication from circomlib
# Install dependencies (circomlib)
npm install
# Compile the circuit (generates R1CS, WASM witness generator, and C++ witness generator)
npm run build
# Run Groth16 trusted setup with Powers of Tau
npm run setup
# Contribute to the ceremony (adds randomness)
npm run contribute
# Export verification key (JSON)
npm run export-vkey
# Export Solidity verifier contract
npm run export-sol# Generate witness (requires input.json with private + public signals)
node build/stealth_attestation_js/generate_witness.js \
build/stealth_attestation_js/stealth_attestation.wasm \
input.json \
build/witness.wtns
# Generate Groth16 proof
npm run prove
# Verify locally
npm run verifyIn the frontend, proof generation happens entirely in-browser via snarkjs, using the WASM witness generator and the final zkey verified against artifacts/manifest.json. Fetch release assets with npm run fetch:circuits (see artifacts/README.md).
| File | Description |
|---|---|
build/stealth_attestation.r1cs |
Rank-1 Constraint System |
build/stealth_attestation_js/ |
WASM witness generator |
build/stealth_attestation_cpp/ |
C++ witness generator |
build/sa_final.zkey |
Final zkey (after ceremony contributions) |
build/verification_key.json |
Verification key (used by on-chain verifier) |
build/Groth16Verifier.sol |
Auto-generated Solidity verifier (reference for the Anchor program) |
The .ptau files in this directory are from the Hermez Phase 1 Powers of Tau ceremony. The .zkey files include Phase 2 contributions specific to this circuit. These are for development only — a production deployment requires a properly audited multi-party ceremony.