Repository navigation
Circomkit 0.4.0
Your v0.3 circomkit.json, circuits.json (including per-circuit dir) and new Circomkit({...}) calls keep working as they are.
Changed behaviour
ProofTester(circuit, protocol?): the protocol defaults to the circuit's protocol in the config, as its keys are named after it. A mismatching protocol throws an error that names both protocols, instead ofProtocol mismatch.- Node.js 18 or newer is required (
engines). It was effectively required before too, as Circomkit's CLI dependencycommanderneeds it, butenginessaid 12. - snarkjs
^0.7.5is required (peer dependency). Its PLONK verifier template no longer importshardhat/console.sol(#101). WitnessTesterskips compiling unchanged circuits. A circuit is recompiled when its main component, compiler options or any included file changed. Passrecompile: trueto always compile (#26).- Tester builds now live under
<dirBuild>/.witness-tester/<circuit>-<hash>, so tests no longer overwrite the build thatcompile/proveuse. clear(circuit)removes them too.
- Tester builds now live under
expectFailaccepts more failures. It now also counts these as a failed witness calculation, instead of throwing them (#107):- WASM traps such as modulo by zero, and deep recursion, which overflows the witness calculator's stack
- the remaining witness calculator errors, such as out-of-bounds array access
- any failure of the C witness calculator
- Reading a signal that the compiler optimized away throws a clear error, instead of returning
undefined(#80). circomkit initworks offline. It creates a small project from a template bundled with Circomkit, instead of cloning circomkit-examples. Existing files are never overwritten.--examplesis removed: the examples now live in examples, and the circomkit-examples repository is archived.- CLI errors print only their message, without a stack trace, and exit with code 1.
circomkit verifyexits with code 1 when the proof does not verify, instead of 0, so scripts & CI can rely on it.circom_testeris no longer a dependency.WitnessTestercompiles, computes witnesses and checks constraints itself, with the same API and error messages.- It now uses
circomPath, like the rest of Circomkit, instead of thecircomon yourPATH. - It computes witnesses with the
witness_calculator.jsthat your Circom version generates. - Importing Circomkit no longer sets
BigInt.prototype.toJSONglobally;circom_testerused to. If your own code callsJSON.stringifyon values that containbigints, it now throwsTypeError: Do not know how to serialize a BigInt. Pass a replacer, e.g.JSON.stringify(x, (_, v) => (typeof v === 'bigint' ? v.toString() : v)).
- It now uses
Circom versions
Circomkit does not require a minimum Circom version. Features that need a newer Circom check for it and tell you which version they need:
- C witness generation on non-x86-64 machines (Apple Silicon, ARM Linux) needs Circom 2.2.2 or newer. It uses
--no_asm, because Circom's assembly is x86-64 only. - Primes that Circom added later:
grumpkin,pallas&vestaneed Circom 2.1.6,secq256r1needs 2.1.8, and the newbls12377needs 2.2.2. Older versions only said "invalid prime number".
CI tests against Circom 2.1.5 and 2.2.3.
New
teardown()is exported: SnarkJS keeps worker threads running after proving & verifying, so callawait teardown()at the end of a script for it to exit. Test runners & the CLI exit anyway.- Inline circuits:
circuitsincircomkit.jsoncan be the circuits object itself, instead of a path tocircuits.json. - Per-circuit
overridesof any config option exceptcircuitsandlogLevel, e.g. a differentprotocoloroptimizationfor one circuit (#67).WitnessTesterstill caps the globaloptimizationat O1, but uses a circuit'soverrides.optimizationas given, e.g.WitnessTester(circuit, {...config, overrides: {optimization: 2}}).
- JSON schemas for editor completion and validation:
"$schema": "./node_modules/circomkit/schema.json"incircomkit.json, andcircuits.schema.jsonforcircuits.json(#111). - C witness calculator:
witness(circuit, input, data, 'c'), andcircomkit witness <circuit> [input] --cin the CLI (#113)WitnessTester(circuit, config, 'c')now works,expectFailincluded (#88)- Works on macOS, including Apple Silicon. Circom's C code passes
uint64_t*to GMP, which only compiles on macOS with-D_LONG_LONG_LIMB, so Circomkit adds it there when building, along with the headers & libraries of Homebrew, so thatbrew install gmp nlohmann-jsonis all the setup it needs.
- Default inputs: the input name is optional in the CLI and defaults to
default, e.g.circomkit prove multiplier_3. A circuit's default input can beinputs/<circuit>.jsoninstead ofinputs/<circuit>/default.json; if both exist,default.jsonis used.input()creates the input directory if needed. circomkit --version(#99).- Examples in examples, each a full stack: Sudoku with Node.js, with witness tests, Groth16, PLONK & FFLONK and the C witness calculator, and SHA-256 with Bun, with a Solidity verifier tested with Foundry.
- Readable constraints:
circomkit constraints <circuit>,circomkit.constraints(circuit)andWitnessTester.constraints()print a circuit's constraints as formulas such asin[0] * in[1] = inner[0], with signal names (#105, thanks to numtel).--filter <signal>/constraints(filter)keeps only the constraints using a signal or component, andcircomkit.streamConstraints()yields them one by one for large circuits. - Bun: snarkjs runs single-threaded under Bun, so proving and verifying no longer crash or hang (#114).
- Anonymous components can be read and edited by their symbol name,
<Template>_<line>_<offset>, e.g.main.Square_3_158.out(#115). - Less memory for constraints:
WitnessTesterpacks them into typed arrays, storing each distinct coefficient once. On a 300k-constraint circuit, it holds 18 MB instead of 254 MB.
Fixes
- Reading signals with
compute()/readWitnessSignals():- Signals of component arrays work, e.g.
hashers[0].out. They threw "No symbols matched" before. - Buses are returned as objects, e.g.
{x, y}, also in arrays. - Parts of a signal can be read, e.g.
out[1]. - Array elements are placed by their own index, instead of assuming that they are consecutive in the witness.
- Signals of component arrays work, e.g.
- Large
.symfiles are read in chunks, instead of as a single string, which fails beyond V8's maximum string length (~512 MB). circomkit info/circomkit.info()with goldilocks: the R1CS header was read as if field elements were always 32 bytes, so 8-byte goldilocks circuits got wrong wire & constraint counts and no prime name.primeis now abigint, as its type says, and broken or truncated R1CS files throw a clear error.- PTAU:
- Automatic downloads work again. The previous host returns HTTP 403, so PTAU files now come from
https://circom.info, as in the SnarkJS docs. This also affects 0.3. - A failed download no longer hangs, and no longer saves an error page as the
.ptaufile. - Concurrent downloads of the same file no longer write to the same temporary file.
- The automatic size also counts public inputs and outputs.
- PLONK & FFLONK get a PTAU large enough for their setup. It was sized for Groth16, which is too small for them: PLONK also counts an addition gate for each extra term in a constraint, and FFLONK needs a PTAU 8 to 16 times larger than PLONK.
- Non-bn128 primes no longer pick up a bn128 PTAU.
- Automatic downloads work again. The previous host returns HTTP 403, so PTAU files now come from
- Paths: paths with spaces work, because Circom is run without a shell.
clear()removes the main component from the circuit's owndir.instantiate()no longer rewrites an unchanged main component, so its modification time stays the same (#120).setup()throws when snarkjs fails, instead of continuing.- The CLI exits on
bls12381too. SnarkJS'sbls12381worker threads used to keep the process alive. WitnessTestercalls can run in parallel, e.g. withPromise.all. They used to overwrite each other's signals and fail (#62).- Concurrent
WitnessTesters of the same circuit, e.g. in parallel test files, compile into separate folders, so they no longer break each other's builds. Circuits with the same name in different folders no longer share a build either. - Concurrent
WitnessTesters of the same name with different parameters, e.g. withPromise.all, each get their own circuit. They used to compile whichevermaincomponent was written last. dirwith a trailing slash or a leading./, e.g.test/or./test, includes the circuit file correctly. It got one../too many.- Recompiling when an included file changes also works for includes written without a space, e.g.
include"lib.circom";. circomkit compile <pattern>matches the whole name for alternatives such asa|b, which also matched names starting withaor ending withb.- A per-circuit
verbosealso applies to the logs of SnarkJS. circomkit.path.ofCircuit(circuit, 'main')returns the main component under the circuit'sdir, instead of always undermain.- Commands that need a compiled circuit, such as
info,ptauandconstraints, say to compile it first, instead of a bareENOENTerror. - Expected outputs are compared in the field, so
expectPass(input, {out: -1})matchesp - 1, and hexadecimal strings such as'0xff'work. They used to be compared as decimal strings. WitnessTesterkeeps a circuit'soverrides.optimizationwith PLONK too. It was forced to O1, which PLONK needs to prove, but not to compute witnesses."type": "module"projects:WitnessTesterloads Circom's generatedwitness_calculator.jsas CommonJS, instead of failing.- Configs passed to Circomkit are no longer modified, e.g. inline circuits given to
WitnessTester. witness()with the C calculator creates the main component in the same folder ascompile().- Custom templates:
pragma custom_templatesis detected with any spacing, e.g.pragma custom_templates ;, and no longer in comments. contract()finds snarkjs' templates when snarkjs is hoisted, e.g. in monorepos.- CLI: the ESM build of the CLI no longer crashes on start.
prettyCalldatacan be set totruein TypeScript, its type wasfalse.
Full Changelog: v0.3.5...v0.4.0