From a41d6383586aa93a13aff742a447bb346ae581d0 Mon Sep 17 00:00:00 2001 From: erhant Date: Sun, 23 Apr 2023 22:46:53 +0300 Subject: [PATCH] better proof system checker --- .cli.env | 2 +- TODO.md | 42 ++++++++++++++++++++++++++++++++++++++++ tests/multiplier.test.ts | 2 +- utils/proofTester.ts | 38 +++++++++++++++++++++++------------- 4 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 TODO.md diff --git a/.cli.env b/.cli.env index dc5a4c1..932df36 100644 --- a/.cli.env +++ b/.cli.env @@ -1,5 +1,5 @@ # compiler args, can add --inspect and -c for example -CIRCOMKIT_COMPILER_ARGS="-l ./node_modules --r1cs --wasm --sym" +CIRCOMKIT_COMPILER_ARGS="-l ./node_modules --r1cs --wasm --sym -c" # proof system to be used, can be: groth16 | plonk | fflonk CIRCOMKIT_PROOF_SYSTEM="groth16" diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..11ff0d9 --- /dev/null +++ b/TODO.md @@ -0,0 +1,42 @@ +# TODOs + +The two features in plan right now is the following: + +- [ ] **Multiple Backends**: We only use WASM & SnarkJS right now, but we would like to export prover libraries for other backends such as mobile. +- [ ] **Type Generation**: Generate input & output signal type declarations for a given circuit, [work in progress](./scripts/functions/type.sh). + +## Multiple Backends + +The default code generates a WASM circuit and does tests in NodeJS environment. We would like to have an easy guide to show how proof generation can be done on other backends for a given circuit too. + +Some resources on this: + +- [iden3 Rapidsnark](https://github.com/iden3/go-rapidsnark) computes witness, generates & verifies proofs in Go. +- [polygonId witnesscalc](https://github.com/0xPolygonID/witnesscalc) has witness computation build files for many backends. + +## Type Generation + +We would like to generate types from input and output signals. For example, + +```c +template Multiplier(N) { + signal input in[N]; + signal output out; + // ... +} +``` + +could have input and output signals as + +```ts +type inputs = { + in: Array; +}; +type outputs = { + out: bigint; +}; +``` + +We could ignore the input size (`in` array should precisely have `N` elements for the example above) because the array size can be computed at runtime within Circom, which makes things a lot more complicated. + +Our `type` script almost does the job, but so far I couldn't find a good way to determine the output signal itself, or ignore intermediate signals. diff --git a/tests/multiplier.test.ts b/tests/multiplier.test.ts index ea6e796..97b3262 100644 --- a/tests/multiplier.test.ts +++ b/tests/multiplier.test.ts @@ -57,7 +57,7 @@ describe('multiplier utilities', () => { }); }); -describe.skip('multiplier proofs', () => { +describe('multiplier proofs', () => { const N = 3; let fullProof: FullProof; diff --git a/utils/proofTester.ts b/utils/proofTester.ts index e958f03..8156e28 100644 --- a/utils/proofTester.ts +++ b/utils/proofTester.ts @@ -3,38 +3,50 @@ const snarkjs = require('snarkjs'); import {expect} from 'chai'; import type {CircuitSignals, FullProof, ProofSystem} from '../types/circuit'; +// Make a const assertion +// see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions +const PROOF_SYSTEMS = ['groth16', 'plonk', 'fflonk'] as const; + /** * A more extensive Circuit class, able to generate proofs & verify them. * Assumes that prover key and verifier key have been computed. */ export class ProofTester { - public readonly proofSystem: ProofSystem; + public readonly protocol: (typeof PROOF_SYSTEMS)[number]; private readonly wasmPath: string; private readonly proverKeyPath: string; - private readonly verificationKey: object; + private readonly verificationKeyPath: string; + private readonly verificationKey: object & { + protocol: ProofSystem; + }; /** - * Sets the paths & loads the verification key + * Sets the paths & loads the verification key. The underlying proof system is checked by looking + * at `verificationKey.protocol`. * @param circuit a proof tester - * @param proofSystem proof system to use, defaults to `groth16` */ - constructor(circuit: string, proofSystem: ProofSystem = 'groth16') { + constructor(circuit: string) { // find paths (computed w.r.t circuit name) this.wasmPath = `./build/${circuit}/${circuit}_js/${circuit}.wasm`; this.proverKeyPath = `./build/${circuit}/prover_key.zkey`; - const verificationKeyPath = `./build/${circuit}/verification_key.json`; + this.verificationKeyPath = `./build/${circuit}/verification_key.json`; // ensure that paths exist - const missing = [this.wasmPath, this.proverKeyPath, verificationKeyPath].filter(p => !fs.existsSync(p)); + const missing = [this.wasmPath, this.proverKeyPath, this.verificationKeyPath].filter(p => !fs.existsSync(p)); if (missing.length !== 0) { - throw new Error('Missing files for' + circuit + '\n' + missing); + throw new Error('Missing files for' + circuit + ':\n' + missing.join('\n')); } // load verification key - this.verificationKey = JSON.parse(fs.readFileSync(verificationKeyPath).toString()); + this.verificationKey = JSON.parse( + fs.readFileSync(this.verificationKeyPath).toString() + ) as typeof this.verificationKey; - // set proof system - this.proofSystem = proofSystem; + // check proof system + if (!PROOF_SYSTEMS.includes(this.verificationKey.protocol)) { + throw new Error('Unknown protocol in verification key: ' + this.verificationKey.protocol); + } + this.protocol = this.verificationKey.protocol; } /** @@ -44,7 +56,7 @@ export class ProofTester { * @returns a proof and public signals */ async prove(input: CircuitSignals): Promise { - return await snarkjs[this.proofSystem].fullProve(input, this.wasmPath, this.proverKeyPath); + return await snarkjs[this.protocol].fullProve(input, this.wasmPath, this.proverKeyPath); } /** @@ -54,7 +66,7 @@ export class ProofTester { * @returns `true` if proof verifies, `false` otherwise */ async verify(proof: object, publicSignals: string[]): Promise { - return await snarkjs[this.proofSystem].verify(this.verificationKey, publicSignals, proof); + return await snarkjs[this.protocol].verify(this.verificationKey, publicSignals, proof); } /**