Skip to content

v0.4.0

Latest

Choose a tag to compare

@erhant erhant released this 05 Nov 12:53
· 3 commits to main since this release
3bf71aa

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 of Protocol mismatch.
  • Node.js 18 or newer is required (engines). It was effectively required before too, as Circomkit's CLI dependency commander needs it, but engines said 12.
  • snarkjs ^0.7.5 is required (peer dependency). Its PLONK verifier template no longer imports hardhat/console.sol (#101).
  • WitnessTester skips compiling unchanged circuits. A circuit is recompiled when its main component, compiler options or any included file changed. Pass recompile: true to always compile (#26).
    • Tester builds now live under <dirBuild>/.witness-tester/<circuit>-<hash>, so tests no longer overwrite the build that compile/prove use.
    • clear(circuit) removes them too.
  • expectFail accepts 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 init works offline. It creates a small project from a template bundled with Circomkit, instead of cloning circomkit-examples. Existing files are never overwritten. --examples is 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 verify exits with code 1 when the proof does not verify, instead of 0, so scripts & CI can rely on it.
  • circom_tester is no longer a dependency. WitnessTester compiles, computes witnesses and checks constraints itself, with the same API and error messages.
    • It now uses circomPath, like the rest of Circomkit, instead of the circom on your PATH.
    • It computes witnesses with the witness_calculator.js that your Circom version generates.
    • Importing Circomkit no longer sets BigInt.prototype.toJSON globally; circom_tester used to. If your own code calls JSON.stringify on values that contain bigints, it now throws TypeError: Do not know how to serialize a BigInt. Pass a replacer, e.g. JSON.stringify(x, (_, v) => (typeof v === 'bigint' ? v.toString() : v)).

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 & vesta need Circom 2.1.6, secq256r1 needs 2.1.8, and the new bls12377 needs 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 call await teardown() at the end of a script for it to exit. Test runners & the CLI exit anyway.
  • Inline circuits: circuits in circomkit.json can be the circuits object itself, instead of a path to circuits.json.
  • Per-circuit overrides of any config option except circuits and logLevel, e.g. a different protocol or optimization for one circuit (#67).
    • WitnessTester still caps the global optimization at O1, but uses a circuit's overrides.optimization as given, e.g. WitnessTester(circuit, {...config, overrides: {optimization: 2}}).
  • JSON schemas for editor completion and validation: "$schema": "./node_modules/circomkit/schema.json" in circomkit.json, and circuits.schema.json for circuits.json (#111).
  • C witness calculator:
    • witness(circuit, input, data, 'c'), and circomkit witness <circuit> [input] --c in the CLI (#113)
    • WitnessTester(circuit, config, 'c') now works, expectFail included (#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 that brew install gmp nlohmann-json is 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 be inputs/<circuit>.json instead of inputs/<circuit>/default.json; if both exist, default.json is 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) and WitnessTester.constraints() print a circuit's constraints as formulas such as in[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, and circomkit.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: WitnessTester packs 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.
  • Large .sym files 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. prime is now a bigint, 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 .ptau file.
    • 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.
  • Paths: paths with spaces work, because Circom is run without a shell.
  • clear() removes the main component from the circuit's own dir.
  • 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 bls12381 too. SnarkJS's bls12381 worker threads used to keep the process alive.
  • WitnessTester calls can run in parallel, e.g. with Promise.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. with Promise.all, each get their own circuit. They used to compile whichever main component was written last.
  • dir with 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 as a|b, which also matched names starting with a or ending with b.
  • A per-circuit verbose also applies to the logs of SnarkJS.
  • circomkit.path.ofCircuit(circuit, 'main') returns the main component under the circuit's dir, instead of always under main.
  • Commands that need a compiled circuit, such as info, ptau and constraints, say to compile it first, instead of a bare ENOENT error.
  • Expected outputs are compared in the field, so expectPass(input, {out: -1}) matches p - 1, and hexadecimal strings such as '0xff' work. They used to be compared as decimal strings.
  • WitnessTester keeps a circuit's overrides.optimization with PLONK too. It was forced to O1, which PLONK needs to prove, but not to compute witnesses.
  • "type": "module" projects: WitnessTester loads Circom's generated witness_calculator.js as 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 as compile().
  • Custom templates: pragma custom_templates is 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.
  • prettyCalldata can be set to true in TypeScript, its type was false.

Full Changelog: v0.3.5...v0.4.0