ProofGuard
Privacy-first zk credential attestation on Citrea: batch proofs, instant verification.
Problem Statement
What it is (one-liner)A privacy-preserving credential attestation and verification system: issuers batch-commit records to a Bitcoin-secured chain (Citrea) and anyone can verify an individual claim with a zero-knowledge proof—without exposing personal data.The ProblemCredentials (degrees, certificates, employment letters) are easy to forge and hard to verify globally.Verifiers (employers, platforms) either trust PDFs/emails or must contact the issuer directly—slow, manual, and privacy-leaking.On-chain storage of PII is unacceptable; off-chain databases are centralized and reversible.What ProofGuard SolvesAuthenticity: The issuer proves “this record was part of our official batch” using zk-SNARKs.Privacy: Verifiers get a binary result (Valid/Invalid) without seeing raw personal data.Global Verifiability: Anyone can independently verify against an immutable on-chain commitment (Merkle root).Cost Efficiency: Batch attestation compresses 100s/1000s of records into a single on-chain commitment.Core Concepts (plain English)Merkle Tree: Off-chain, we hash each credential into a leaf. Leaves form a Merkle tree; its root uniquely commits to the entire batch.zk-SNARK (Groth16): A tiny proof that a specific leaf (the student’s credential) exists in the committed tree and satisfies the circuit’s rules—without revealing the underlying data.On-chain Contracts:ProofGuardRegistry: Stores Merkle tree state (via incremental insert). Emits current root after each batch/insert; exposes getters.ProofGuardCoreVerifier: Stateless verifier that checks Groth16 proofs (generated off-chain) via Ethereum precompiles (pairing checks).Citrea (EVM on Bitcoin): We post the root/updates to Citrea, leveraging Bitcoin security while using EVM tooling (Remix, MetaMask, ethers).Stakeholders & JourneysIssuer (e.g., a University)Onboarding: Connect SIS (student information system) to ProofGuard via API or simple upload.Batch Attestation:Normalize records (stable JSON) → encrypt each record’s payload (so on-chain storage is never raw PII).Compute the leaf for each record (consistent hash inputs that match the circuit).Insert leaves into the on-chain Merkle tree (or call a batch function), which emits a new root.(Optional) Generate a proof template or pre-compute per-record proofs if the flow needs it.Result: One or a few on-chain transactions represent a large set of credentials, time-stamped and immutable.Holder (Student/Graduate)Gets a Claim Bundle: { registry, verifier, root, publicInputs[], proof }.publicInputs[] typically includes the Merkle root + disclosed statement hashes.proof is the Groth16 proof generated from the student’s encrypted record and the tree path.Can share the claim as a QR code or short JSON.Verifier (Employer/Platform)Opens the public dApp:Pasts/scans the Claim Bundle.dApp fetches getCurrentMerkleRoot() from ProofGuardRegistry.Compares provided root vs on-chain root (or verifies against a specific historical root).Calls ProofGuardCoreVerifier.verifyProof(a,b,c,input) via eth_call.Sees Valid / Invalid. No PII is exposed at any point.Repo Components (minimal logic approach)ContractsProofGuardCoreVerifier.sol: Groth16 verifier (IC points hard-coded; generated from the circuit).ProofGuardRegistry.sol: Ownable + Incremental Merkle Tree (zk-kit library). Functions to store encrypted records and insert leaves; exposes root and leaves.CircuitsDefine the exact leaf hash and public inputs layout (e.g., Poseidon hash of selective fields).Set depth (e.g., 16) to match the on-chain registry’s TREE_DEPTH.claimsN (or equivalent) must match your schema so inputs/constraints line up.Off-chain ToolingA small batch uploader (CSV/JSON → canonicalize → encrypt → leaf → on-chain insert).A prover that, for a given holder, computes the Merkle path and generates the Groth16 proof.A claim packer that exports the Claim Bundle JSON + QR.Data Model & PrivacyOn-chain: Only encrypted strings (ciphertexts), Merkle leaves, events, and roots.Off-chain: The cleartext record lives with the holder/issuer; proofs and public inputs reveal only what is necessary.No PII on-chain: All raw data is encrypted before touching the registry.Selective Disclosure: Circuits can be designed to prove properties (e.g., “degree=BS, year≥2024”) without revealing the entire record.API/UX Surface (example)Issuer Admin UIUpload CSV/JSON → map fields to schema → “Generate leaves” → “Attest batch”Shows tx status, final root, and per-record status.Student Portal“Create Claim” → choose which attributes to disclose → download JSON/QR claim bundle.Verifier dAppTextarea / QR scanner → “Verify” → instant Valid/Invalid.Trust & Threat ModelTrust: You trust the issuer’s policy (they control what is attested) but not their storage; proof + chain root prevents tampering.Tamper Resistance: Once a root is on-chain, issuer cannot later modify past records without changing the root.Privacy: Verifier learns only the validity of the claimed statement; no raw data leakage.Key Management: Issuer/holder encryption keys must be handled securely; losing keys can make claims unverifiable or undecryptable.Performance & CostBatching drastically reduces on-chain cost (one/few transactions per N records).Groth16 verification gas is low and deterministic; proofs are tiny (a few hundred bytes).Citrea: Native coin (cBTC) for gas; EVM RPC compatibility streamlines tooling.Constraints & AssumptionsCircuit/Contract Parity: Merkle depth, leaf hash function, and public input ordering must match exactly across:the circom circuit,the generated verifier,the off-chain leaf builder,the on-chain registry.Updates/Revocations:Easiest pattern: append new leaf representing revocation or updated status and verify against latest root.A dedicated revocation list circuit can be added later (roadmap).Availability:Encrypted data is on-chain (durable), but decryption depends on holder/issuer keys.Off-chain re-generation of proofs is required when root changes (e.g., periodic batch inserts).
Solution
ChatGPT said:Smart contracts in Solidity: ProofGuardRegistry (incremental Merkle tree via zk-kit) + ProofGuardCoreVerifier (Groth16 verifier with hard-coded VK IC points).OpenZeppelin (Ownable, ReentrancyGuard) for access + safety; deployed on Citrea testnet (Bitcoin-secured EVM).Circuits in circom; proofs generated with snarkjs; public inputs include Merkle root + selective claim hashes.Off-chain Node/TypeScript batch uploader: canonicalizes JSON, encrypts payloads, computes Poseidon leaves, and submits inserts.Encryption (ECIES/NaCl style): on-chain only stores ciphertext; PII never appears in clear.Frontend dApp (Next.js/React + ethers.js): paste/scan a “claim bundle” (a,b,c + inputs + root), auto-reads on-chain root, calls verifyProof via eth_call.Minimal gas by batching many records into one on-chain commitment (root), with per-record proofs verified client-side.“Hacky but handy”: a single attestBatch wrapper (loop in-contract) to compress UX into one tx without changing semantics.Tooling lint/format/test: pnpm, TypeScript, eslint/prettier, and a tiny prover CLI for local proof generation.Partner value: Citrea gives Bitcoin-level security with EVM tooling, so Remix/MetaMask/ethers “just work” while anchoring to BTC.
Hackathon
ETHGlobal New Delhi
2025
Contributors
- achiit
2 contributions