better proof system checker

This commit is contained in:
erhant
2023-04-23 22:46:53 +03:00
parent 6cf5952952
commit a41d638358
4 changed files with 69 additions and 15 deletions

View File

@@ -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"

42
TODO.md Normal file
View File

@@ -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<bigint>;
};
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.

View File

@@ -57,7 +57,7 @@ describe('multiplier utilities', () => {
});
});
describe.skip('multiplier proofs', () => {
describe('multiplier proofs', () => {
const N = 3;
let fullProof: FullProof;

View File

@@ -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<FullProof> {
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<boolean> {
return await snarkjs[this.proofSystem].verify(this.verificationKey, publicSignals, proof);
return await snarkjs[this.protocol].verify(this.verificationKey, publicSignals, proof);
}
/**