circomkit
Version:
A Circom testing & development environment
468 lines (459 loc) • 20.2 kB
text/typescript
import loglevel, { LogLevelDesc } from 'loglevel';
import { Groth16Proof, PublicSignals, PlonkProof, FflonkProof } from 'snarkjs';
/**
* Some fields for the R1CS information, many other fields are omitted in this type.
*/
type R1CSInfoType = {
wires: number;
constraints: number;
privateInputs: number;
publicInputs: number;
publicOutputs: number;
useCustomGates: boolean;
labels: number;
prime: bigint;
primeName: string;
};
/** An integer value is a numerical string, a number, or a bigint. */
type IntegerValueType = `${number}` | number | bigint;
/** A signal value is a number, or an array of numbers (recursively). */
type SignalValueType = IntegerValueType | SignalValueType[];
/**
* An object with string keys and array of numerical values.
* Each key represents a signal name as it appears in the circuit.
*
* By default, signal names are not typed, but you can pass an array of signal names
* to make them type-safe, e.g. `CircuitSignals<['sig1', 'sig2']>`
*/
type CircuitSignals<T extends readonly string[] = []> = T extends [] ? {
[signal: string]: SignalValueType;
} : {
[signal in T[number]]: SignalValueType;
};
/** A witness is an array of `bigint`s, corresponding to the values of each wire in the evaluation of the circuit. */
type WitnessType = bigint[];
/**
* Symbols are a mapping of each circuit `wire` to an object with three keys. Within them,
* the most important is `varIdx` which indicates the position of this signal in the witness array.
*/
type SymbolsType = {
[symbol: string]: {
labelIdx: number;
varIdx: number;
componentIdx: number;
};
};
/** A configuration object for circuit main components. */
type CircuitConfig = {
/** File to read the template from */
file: string;
/** The template name to instantiate */
template: string;
/** Directory to instantiate at */
dir?: string;
/** Target version */
version?: `${number}.${number}.${number}`;
/** An array of public input signal names, defaults to `[]` */
pubs?: string[];
/** An array of template parameters, defaults to `[]` */
params?: (number | bigint)[];
/** Include `pragma custom_templates;` in instantiation */
usesCustomTemplates?: boolean;
};
/**
* A simple type-wrapper for `circom_tester` WASM tester class.
* Not all functions may exist here, some are omitted.
* @see https://github.com/iden3/circom_tester/blob/main/wasm/tester.js
*/
type CircomTester = {
checkConstraints: (witness: WitnessType) => Promise<void>;
release: () => Promise<void>;
assertOut: (actualOut: WitnessType, expectedOut: CircuitSignals) => Promise<void>;
calculateWitness: (input: CircuitSignals, sanityCheck: boolean) => Promise<WitnessType>;
loadConstraints: () => Promise<void>;
constraints: unknown[] | undefined;
loadSymbols: () => Promise<void>;
symbols: SymbolsType | undefined;
getDecoratedOutput: (witness: WitnessType) => Promise<string>;
dir: string;
baseName: string;
};
declare const PROTOCOLS: readonly ["groth16", "plonk", "fflonk"];
declare const PRIMES: readonly ["bn128", "bls12381", "goldilocks", "grumpkin", "pallas", "vesta", "secq256r1"];
type CircomkitConfig = {
/** Protocol (proof system) to be used. */
protocol: (typeof PROTOCOLS)[number];
/**
* Primes supported by Circom, as described for the `-p` option.
* @see https://github.com/iden3/circom/blob/master/program_structure/src/utils/constants.rs
*/
prime: (typeof PRIMES)[number];
/** Circuit configurations path. */
circuits: string;
/** Directory to read circuits from. */
dirCircuits: string;
/** Directory to read inputs from. */
dirInputs: string;
/** Directory to download PTAU files. */
dirPtau: string;
/** Directory to output circuit build files. */
dirBuild: string;
/** Path to circom executable */
circomPath: string;
/** Number of contributions */
groth16numContributions: number;
/** Ask user input to create entropy */
groth16askForEntropy: boolean;
/** Version number for main components. */
version: `${number}.${number}.${number}`;
/**
* [Optimization level](https://docs.circom.io/getting-started/compilation-options/#flags-and-options-related-to-the-r1cs-optimization).
* See [`circom/src/input_user.rs`](https://github.com/iden3/circom/blob/master/circom/src/input_user.rs#L249).
* - `undefined`: Follow Circom default. (<v2.2.0: 2, >=v2.2.0: 1)
* - `0`: No simplification is applied.
* - `1`: Only applies `var` to `var` and `var` to `constant` simplification.
* - `2`: Full constraint simplificiation via Gaussian eliminations.
* - `>2`: Any number higher than 2 will use `--O2round` with the number as simplification rounds.
*/
optimization: number | undefined;
/** Does an additional check over the constraints produced. */
inspect: boolean;
/** Include paths as libraries during compilation. */
include: string[];
/** Pass logger to SnarkJS to see its logs in addition to Circomkit. */
verbose: boolean;
/** Log level used by the internal logger. */
logLevel: LogLevelDesc;
/** Whether to generate the C witness calculator. */
cWitness: boolean;
/** Whether to generate the WASM witness calculator. */
wasmWitness: boolean;
/** Whether to print Solidity copy-pasteable calldata. */
prettyCalldata: false;
};
/** A utility class to test your circuits. Use `expectFail` and `expectPass` to test out evaluations. */
declare class WitnessTester<IN extends readonly string[] = [], OUT extends readonly string[] = []> {
/** The underlying `circom_tester` object */
private readonly circomTester;
/** A dictionary of symbols, see {@link loadSymbols} */
private symbols;
/** List of constraints, see {@link loadConstraints} */
private constraints;
constructor(
/** The underlying `circom_tester` object */
circomTester: CircomTester);
/** Assert that constraints are valid for a given witness. */
expectConstraintPass(witness: WitnessType): Promise<void>;
/**
* Assert that constraints are NOT valid for a given witness.
* This is useful to test if a fake witness (a witness from a
* dishonest prover) can still be valid, which would indicate
* that there are soundness errors in the circuit.
*/
expectConstraintFail(witness: WitnessType): Promise<void>;
/** Compute witness given the input signals. */
calculateWitness(input: CircuitSignals<IN>): Promise<WitnessType>;
/** Returns the number of constraints. */
getConstraintCount(): Promise<number>;
/** Asserts that the circuit has enough constraints.
*
* By default, this function checks if there **at least** `expected` many constraints in the circuit.
* If `exact` option is set to `true`, it will also check if the number of constraints is exactly equal to
* the `expected` amount.
*
* If first check fails, it means the circuit is under-constrained. If the second check fails, it means
* the circuit is over-constrained.
*/
expectConstraintCount(expected: number, exact?: boolean): Promise<void>;
/** Expect a witness computation to fail in the circuit.
*
* See [here](https://github.com/iden3/circom/blob/master/code_producers/src/wasm_elements/common/witness_calculator.js#L21)
* for the list of errors that may occur during witness calculation.
* Most of the time, you will be expecting an assertion error.
*
* @returns the error message.
*/
expectFail(input: CircuitSignals<IN>): Promise<string>;
/** Expect an input to pass assertions and match the output.
*
* If `output` is omitted, it will only check for constraints to pass.
*/
expectPass(input: CircuitSignals<IN>, output?: CircuitSignals<OUT>): Promise<void>;
/**
* Computes the witness.
* This is a shorthand for calculating the witness and calling {@link readWitnessSignals} on the result.
*/
compute(input: CircuitSignals<IN>, signals: string[] | OUT): Promise<CircuitSignals>;
/**
* Override witness value to try and fake a proof. If the circuit has soundness problems (i.e.
* some signals are not constrained correctly), then you may be able to create a fake witness by
* overriding specific values, and pass the constraints check.
*
* The symbol names must be given in full form, not just as the signal is named in the circuit code. In
* general a symbol name looks something like:
*
* - `main.signal`
* - `main.component.signal`
* - `main.component.signal[n][m]`
*
* You will likely call `expectConstraintPass` on the resulting fake witness to see if it can indeed fool
* a verifier.
* @see {@link expectConstraintPass}
*/
editWitness(witness: Readonly<WitnessType>, symbolValues: {
[symbolName: string]: bigint;
}): Promise<WitnessType>;
/** Read symbol values from a witness. */
readWitness(witness: Readonly<WitnessType>, symbols: string[]): Promise<Record<string, bigint>>;
/**
* Read signals from a witness.
*
* This is not the same as {@link readWitness} in the sense that the entire value represented by a signal
* will be returned here. For example, instead of reading `main.out[0], main.out[1], main.out[2]` with `readWitness`,
* you can simply query `out` in this function and an object with `{out: [...]}` will be returned.
*
* To read signals within a component, simply refer to them as `component.signal`. In other words, omit the `main.` prefix
* and array dimensions.
*/
readWitnessSignals(witness: Readonly<WitnessType>, signals: string[] | OUT): Promise<CircuitSignals>;
/**
* Assert the output of a given witness.
* @param actualOut expected witness
* @param expectedOut computed output signals
*/
private assertOut;
/** Loads the list of R1CS constraints to `this.constraints`. */
private loadConstraints;
/**
* Loads the symbols in a dictionary at `this.symbols`
* Symbols are stored under the .sym file
*
* Each line has 4 comma-separated values:
*
* 1. symbol name
* 2. label index
* 3. variable index
* 4. component index
*/
private loadSymbols;
/** Faster alternative to this.circomTester.loadSymbols(),
* stops once all the requested symbols are found */
private loadSpecificSymbols;
/**
* @deprecated this is buggy right now
* @param witness witness
*/
private getDecoratedOutput;
/**
* Cleanup directory, should probably be called upon test completion (?)
* @deprecated this is buggy right now
*/
private release;
}
/** A tester that is able to generate proofs & verify them.
* Use `expectFail` and `expectPass` to test out evaluations. */
declare class ProofTester<IN extends string[] = [], P extends CircomkitConfig['protocol'] = 'groth16'> {
readonly wasmPath: string;
readonly pkeyPath: string;
readonly vkeyPath: string;
readonly protocol: P;
readonly verificationKey: any;
constructor(wasmPath: string, pkeyPath: string, vkeyPath: string, protocol: P);
/** Generate a proof for the witness computed from the given input signals. */
prove(input: CircuitSignals<IN>): Promise<{
proof: Groth16Proof;
publicSignals: PublicSignals;
}>;
prove(input: CircuitSignals<IN>): Promise<{
proof: PlonkProof;
publicSignals: PublicSignals;
}>;
prove(input: CircuitSignals<IN>): Promise<{
proof: FflonkProof;
publicSignals: PublicSignals;
}>;
/** Returns the verification result of a proof for some public signals. */
verify(proof: Groth16Proof, publicSignals: PublicSignals): Promise<boolean>;
verify(proof: PlonkProof, publicSignals: PublicSignals): Promise<boolean>;
verify(proof: FflonkProof, publicSignals: PublicSignals): Promise<boolean>;
/** Expects a verification to pass for this proof and public signals. */
expectPass(proof: Groth16Proof, publicSignals: PublicSignals): Promise<void>;
expectPass(proof: PlonkProof, publicSignals: PublicSignals): Promise<void>;
expectPass(proof: FflonkProof, publicSignals: PublicSignals): Promise<void>;
/** Expects a verification to fail for this proof and public signals. */
expectFail(proof: Groth16Proof, publicSignals: PublicSignals): Promise<void>;
expectFail(proof: PlonkProof, publicSignals: PublicSignals): Promise<void>;
expectFail(proof: FflonkProof, publicSignals: PublicSignals): Promise<void>;
}
/** Utility class to handle path abstractions.
*
* This class takes in a reference to the Circomkit configuration and provides the correct pathing.
*/
declare class CircomkitPath {
private readonly config;
constructor(config: CircomkitConfig);
/**
* Computes a path that requires a circuit name.
*
* @param circuit The name of the circuit.
* @param kind The kind of file to compute the path for.
*/
ofCircuit(circuit: string, kind: 'main' | 'sym' | 'pkey' | 'vkey' | 'wasm' | 'sol' | 'dir' | 'r1cs'): string;
/**
* Computes a path that requires a circuit and an input name.
*
* @param circuit The name of the circuit.
* @param input The name of the input.
* @param kind The kind of file to compute the path for.
*/
ofCircuitWithInput(circuit: string, input: string, kind: 'pubs' | 'proof' | 'wtns' | 'in' | 'dir'): string;
/**
* Given a PTAU name, returns the relative path.
*
* @param ptauName The name of the PTAU file, e.g. `powersOfTau28_hez_final_08.ptau`.
*/
ofPtau(ptauName: string): string;
/**
* Given a circuit & id name, returns the relative path of the phase-2 PTAU.
*
* This is used in particular by Groth16's circuit-specific setup phase.
*
* @param circuit The name of the circuit.
* @param id The id of the zKey.
*/
ofZkey(circuit: string, id: number): string;
}
/**
* Circomkit is an opinionated wrapper around many SnarkJS functions.
*
* It abstracts away all the path and commands by providing a simple interface,
* built around just providing the circuit name and the input name.
*
* ```ts
* const circomkit = new Circomkit()
* ```
*
* It also provides a **WitnessTester** and a **ProofTester** module which use Chai assertions within.
*
* ```ts
* const witnessTester = await circomkit.WitnessTester(circuitName, circuitConfig)
* const proofTester = await circomkit.ProofTester(circuitName)
* ```
*/
declare class Circomkit {
readonly config: CircomkitConfig;
readonly log: loglevel.Logger;
readonly path: CircomkitPath;
/** A logger reference to be passed into SnarkJS functions. If `verbose` is set to `false`, this logger will be undefined. */
private readonly snarkjsLogger;
constructor(overrides?: Partial<CircomkitConfig>);
/** Returns the contents of `circuits.json`. */
readCircuits(): Record<string, CircuitConfig>;
/** Returns a single circuit config from `circuits.json`. */
readCircuitConfig(circuit: string): CircuitConfig;
/** Clear build files and the `main` component of a circuit. */
clear(circuit: string): Promise<void>;
/** Export a verification key (vKey) from a proving key (zKey). */
vkey(circuit: string, pkeyPath?: string): Promise<string>;
/** Returns circuit information. */
info(circuit: string): Promise<R1CSInfoType>;
/** Downloads the phase-1 setup PTAU file for a circuit based on it's number of constraints.
*
* The downloaded PTAU files can be seen at [SnarkJS docs](https://github.com/iden3/snarkjs#7-prepare-phase-2).
* Note that this may take a while if the circuit is large and thus a larger PTAU is needed.
*
* This function only works when the used prime is `bn128`.
*
* @returns path of the downloaded PTAU file
*/
ptau(circuit: string): Promise<string>;
/** Compile the circuit.
*
* A circuit configuration can be passed optionally; if not, the
* config will be read from `circuits.json` at the working directory.
*
* @returns path of the build directory
*/
compile(circuit: string, config?: CircuitConfig): Promise<string>;
/** Exports a solidity contract for the verifier.
* @returns path of the exported Solidity contract
*/
contract(circuit: string): Promise<string>;
/** Export calldata to call a Verifier contract.
*
* @returns calldata
*/
calldata(circuit: string, input: string, pretty?: boolean): Promise<string>;
/** Instantiate the `main` component.
*
* If `circuitConfig` argument is omitted, this function will look for it at `circuits.json`
* in the working directory, and throw an error if no entry is found for the circuit.
*
* When config is read from file, `dir` defaults to `main`, otherwise `dir` defaults to `test`.
* This is done to make it so that when CLI is used circuits are created under `main`, and when
* we use Circomkit programmatically (e.g. during testing) circuits are created under `test`
* unless specified otherwise.
*
* @returns path of the created main component
*/
instantiate(circuit: string, circuitConfig?: CircuitConfig): string;
/** Generate a proof.
*
* If `data` is not passed, the input data will be read from `inputs/<circuit>/<input>.json`.
*
* @returns path of the directory where public signals and proof are created
*/
prove(circuit: string, input: string, data?: CircuitSignals): Promise<string>;
/** Commence a circuit-specific setup.
*
* If `ptauPath` argument is omitted, this function will try to automatically download it.
* See the {@link ptau} method for more information about this.
*
* @returns path of the verifier key and prover key
*/
setup(circuit: string, ptauPath?: string): Promise<{
proverKeyPath: string;
verifierKeyPath: string;
}>;
/** Verify a proof for some public signals.
* @returns `true` if verification is successful, `false` otherwise.
*/
verify(circuit: string, input: string): Promise<boolean>;
/** Calculates the witness for the given circuit and input.
*
* If `data` is not passed, the input data will be read from `inputs/<circuit>/<input>.json`.
*
* @returns path of the created witness
*/
witness(circuit: string, input: string, data?: CircuitSignals): Promise<string>;
/** Exports a JSON input file for some circuit with the given object.
*
* This is useful for testing real circuits, or creating an input programmatically.
* Overwrites an existing input.
*
* @returns path of the created input file
*/
input(circuit: string, input: string, data: CircuitSignals): string;
/** Export a circuit artifact in JSON format.
*
* Returns the JSON object itself, and the path that it would be exported to with
* respect to the Circomkit configuration.
*
* @returns a JSON object or the path that it would be exported to.
*/
json(target: 'r1cs' | 'zkey', circuit: string): Promise<{
json: object;
path: string;
}>;
json(target: 'wtns', circuit: string, input: string): Promise<{
json: object;
path: string;
}>;
/** Compiles the circuit and returns a witness tester instance. */
WitnessTester<IN extends string[] = [], OUT extends string[] = []>(circuit: string, circuitConfig: CircuitConfig & {
recompile?: boolean;
}, tester?: 'wasm' | 'c'): Promise<WitnessTester<IN, OUT>>;
/** Returns a proof tester. */
ProofTester<IN extends string[] = [], P extends CircomkitConfig['protocol'] = 'groth16'>(circuit: string, protocol: P): Promise<ProofTester<IN, P>>;
}
export { Circomkit, type CircomkitConfig, type CircuitConfig, type CircuitSignals, ProofTester, WitnessTester };