Problem Statement / Feature Objective
Privacy regulations in certain jurisdictions require that meter reading submissions do not reveal the individual meter's identity or precise consumption to the central server. A zero-knowledge proof module must allow field operators to submit an encrypted meter reading along with a zk-SNARK proof that the reading is within an acceptable range (e.g., between 0 and 10,000 kWh) and was generated by a registered meter, without revealing the actual value or meter ID. The verifier runs client-side during audit, using a proving key cached from the Soroban contract's verification key registry.
Technical Invariants & Bounds
- Proof system: Groth16 over BLS12-381 curve, compiled via circom + snarkjs to WASM. Proving key size: ~150 MB (must be cached in IndexedDB, not re-downloaded per proof).
- Circuit constraints: ~50,000 R1CS constraints (range check + membership check + hash). Proving time target: under 30 seconds on a field tablet.
- Input private signals: meterId (248 bits), consumption (64 bits), salt (248 bits). Public signals: merkleRoot (256 bits), encryptedCiphertext (512 bits), timestamp (32 bits).
- Encryption: reading is encrypted using the server's public key (x25519) before submission. The proof ensures ciphertext corresponds to the plaintext without revealing it.
- Verification key: stored on-chain in the Soroban contract under DataEntry key zk_verification_key; fetched and cached locally for 24 hours.
- Freshness: each proof must include a recent block hash (last 10 ledgers) as a public signal to prevent replay attacks.
Codebase Navigation Guide
- src/services/zkProver.ts - Orchestrator: loads proving key, constructs witness, invokes snarkjs Groth16 prover.
- src/workers/zkProver.worker.ts - Web Worker running the WASM prover off the main thread; receives circuit inputs, returns proof + publicSignals.
- public/wasm/zk/circuit_final.zkey - Proving key (symlink or placeholder; actual file hosted on CDN due to size).
- src/services/zkVerifier.ts - Client-side verifier using snarkjs; loads verification key from Soroban contract and verifies proof.
- src/hooks/useZKSubmission.ts - React hook: prepareInputs, generateProof, submitEncrypted, verify.
- src/components/panels/ZKSubmissionPanel.tsx - UI panel for encrypted meter reading submission with proof generation progress bar.
- src/services/keyCache.ts - IndexedDB store for caching the proving key (~150 MB) with LRU eviction and integrity check.
Implementation Blueprint
- Design the circom circuit: RangeCheck(consumption, 0, 10000), MembershipCheck(meterId, merkleRoot), HashEquality(consumption, ciphertext). Compile to WASM with circom and snarkjs.
- Host the proving key (circuit_final.zkey) on a CDN (e.g., S3 with CloudFront). In src/services/keyCache.ts, download it on demand to IndexedDB with a SHA-256 integrity check. Implement resumable download using Range requests.
- In src/services/zkProver.ts, prepareInputs(): gather meterId, consumption, salt, merkleRoot, ciphertext, blockHash. Serialize as JSON for the worker.
- The worker zkProver.worker.ts loads the WASM circuit and proving key using snarkjs, creates a witness, runs groth16Prove, returns { proof, publicSignals } as Transferable objects.
- Back on the main thread, package { encryptedReading, proof, publicSignals } into a Soroban transaction payload and submit via src/services/soroban.ts.
- implement src/services/zkVerifier.ts: fetch verification key from Soroban (data entry zk_verification_key), cache for 24h, call snarkjs groth16Verify.
- useZKSubmission exposes: submit(reading) returns { progress: 0-100, status: 'downloading-key' | 'proving' | 'submitting' | 'confirmed' | 'rejected', proofHash }. ZKSubmissionPanel renders a stepper UI showing each phase.
Problem Statement / Feature Objective
Privacy regulations in certain jurisdictions require that meter reading submissions do not reveal the individual meter's identity or precise consumption to the central server. A zero-knowledge proof module must allow field operators to submit an encrypted meter reading along with a zk-SNARK proof that the reading is within an acceptable range (e.g., between 0 and 10,000 kWh) and was generated by a registered meter, without revealing the actual value or meter ID. The verifier runs client-side during audit, using a proving key cached from the Soroban contract's verification key registry.
Technical Invariants & Bounds
Codebase Navigation Guide
Implementation Blueprint