npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

micro-zk-proofs

v0.2.0

Published

Create & verify zero-knowledge SNARK proofs in parallel, using noble cryptography

Downloads

9

Readme

micro-zk-proofs

Create & verify zero-knowledge SNARK proofs in parallel, using noble cryptography.

  • Supports Groth16. PLONK and others are planned
  • Fast, even faster using optional parallel generation
  • Supports gnark, modern wasm and legacy js circom programs
  • Parse R1CS, WTNS

Usage

npm install micro-zk-proofs

deno add jsr:@paulmillr/micro-zk-proofs

import * as zkp from 'micro-zk-proofs';
const proof = await zkp.bn254.groth.createProof(provingKey, witness);
const isValid = zkp.bn254.groth.verifyProof(verificationKey, proof);

// Typed as following:
type Constraint = Record<number, bigint>;
type G1Point = [bigint, bigint, bigint];
type G2Point = [[bigint, bigint], [bigint, bigint], [bigint, bigint]];
type ProvingKey = {
  protocol?: 'groth';
  nVars: number;
  nPublic: number;
  domainBits: number;
  domainSize: number;
  // Polynominals
  polsA: Constraint[];
  polsB: Constraint[];
  polsC: Constraint[];
  //
  A: G1Point[];
  B1: G1Point[];
  B2: G2Point[];
  C: G1Point[];
  //
  vk_alfa_1: G1Point;
  vk_beta_1: G1Point;
  vk_delta_1: G1Point;
  vk_beta_2: G2Point;
  vk_delta_2: G2Point;
  //
  hExps: G1Point[];
};

type VerificationKey = {
  protocol?: 'groth';
  nPublic: number;
  IC: G1Point[];
  //
  vk_alfa_1: G1Point;
  vk_beta_2: G2Point;
  vk_gamma_2: G2Point;
  vk_delta_2: G2Point;
};

type GrothProof = {
  protocol: 'groth';
  pi_a: G1Point;
  pi_b: G2Point;
  pi_c: G1Point;
};
interface ProofWithSignals {
  proof: GrothProof;
  publicSignals: bigint[];
}

There are 4 steps:

  1. Compile circuit (outside of scope)
  2. Setup circuit (outside of scope)
  3. Generate witness (outside of scope)
  4. Create / verify proof

Check out examples directory. It contains wasm-v2, wasm-v1 and js circuits.

Compile, setup, generate witness

We need a circuit, and a compiler.

Circuit compilation is outside of scope of our library and depends on a circuit language. Groth16 proofs don't care about language. We use circom in examples below, but you can use anything.

There is no common serialization format for circom, but this is not a big deal.

There are three circom compilers:

  • WASM circom v2 v2.2.2 (github) - modern version
  • WASM circom v1 v0.5.46 (github) - legacy rewrite of v0.0.35
  • JS circom v1 v0.0.35 (github) - original JS version

We support all versions for backwards-compatibility reasons: v2 programs are different from circom v1, old circuits won't always compile with new compiler, and their output may differ between each other.

  • First, we need to write circuit in circom language.
  • Result of compilation:
    • constraints list/info:
      • json or r1cs format for circom2
      • embedded in circuit.json for old compiler
    • witness calculation program:
      • wasm/js for circom2
      • embedded in circuit.json for old compiler

Witness generation:

  • This step depends on language, but we just need array of bigints.
  • For wasm (circom2) there is nice zero-deps calculator generated by compiler itself
  • there also 'circom_tester' package to run these wasm witness calculation programs

[!NOTE] When using with existing project, proving/verify keys, witness calculation program and circuit info should be provided by authors. Compiling same circuit with slightly different version of compiler will result in incompatible circuit which will generate invalid proofs.

[!WARNING] .setup method is for tests only, in real production setup you need to do multi-party ceremony to avoid leaking of toxic scalars.

Create / verify proof

Check out examples directory. It contains wasm-v2, wasm-v1 and js circuits.

We will use a test circuit.

  • This is basic circuit that takes 3 variables: 'a, b, sum' (where a is private) and verifies that 'a + b = sum'. All variables are 32 bit. This allows us to prove that we know such 'a' that produces specific 'sum' with publicly known 'b' without disclosing which a we know.
  • This is a toy circuit and it is not hard to identify which 'a' was used, in real example there would be some hash.
  • Last version of sum.json: sum_last.json from snarkjs v0.2.0
  • this specific circuit compiles both with new compiler and old one, other circuits may not.

WASM v2

dir='circom-wasm'
git clone https://github.com/iden3/circom $dir
cd $dir
git checkout v2.2.2
cargo build --release
./circom-wasm/target/release/circom -o output --r1cs --sym --wasm --json --wat circuit-v2/sum_test.circom
cd output/sum_test_js
mv witness_calculator.js witness_calculator.cjs
import { bn254 } from '@noble/curves/bn254';
import * as zkp from 'micro-zk-proofs';
import * as zkpWitness from 'micro-zk-proofs/witness.js';
import { deepStrictEqual } from 'node:assert';
import { default as calc } from './output/sum_test_js/witness_calculator.cjs';

import { readFileSync } from 'node:fs';
import { dirname, join as pjoin } from 'node:path';
import { fileURLToPath } from 'node:url';
const _dirname = dirname(fileURLToPath(import.meta.url));
const read = (...paths) => readFileSync(pjoin(_dirname, ...paths));

console.log('# wasm circom v2');
(async () => {
  const input = { a: '33', b: '34' };
  // 2. setup
  const coders = zkpWitness.getCoders(bn254.fields.Fr);
  const setupWasm = zkp.bn254.groth.setup(
    coders.getCircuitInfo(read('output', 'sum_test.r1cs'))
  );
  // 3. generate witness
  // NOTE: circom generates zero-deps witness calculator from wasm.
  // In theory we can do small wasm runtime for it, but it depends on compiler version and can change!
  const c = await calc(read('output', 'sum_test_js', 'sum_test.wasm'));
  const binWitness = await c.calculateBinWitness(input, true);
  const wtns = await c.calculateWTNSBin(input, true);
  const witness0 = coders.binWitness.decode(binWitness);
  const witness1 = coders.WTNS.decode(wtns).sections[1].data; // Or using WTNS circom format
  deepStrictEqual(witness0, witness1);
  // 4. create proof
  console.log('creating proof');
  const proofWasm = await zkp.bn254.groth.createProof(setupWasm.pkey, witness0);
  console.log('created proof', proofWasm);
  // 4. verify proof
  console.log('verifying proof');
  deepStrictEqual(
    zkp.bn254.groth.verifyProof(setupWasm.vkey, proofWasm),
    true
  );
})();

WASM v1

dir='wasmsnark'
git clone https://github.com/iden3/wasmsnark.git $dir
cd $dir
git checkout v0.0.12
import * as zkp from 'micro-zk-proofs';
import { deepStrictEqual } from 'node:assert';

import { readFileSync } from 'node:fs';
import { dirname, join as pjoin } from 'node:path';
import { fileURLToPath } from 'node:url';
const _dirname = dirname(fileURLToPath(import.meta.url));
const read = (...paths) => readFileSync(pjoin(_dirname, ...paths));

console.log('# wasm circom v1');
(async () => {
  const bigjson = (path) => zkp.stringBigints.decode(
    JSON.parse(read('wasmsnark', 'example', 'bn128', path))
  );
  const pkey = bigjson('proving_key.json');
  const vkey = bigjson('verification_key.json');
  const witness = bigjson('witness.json');
  const oldProof = bigjson('proof.json');
  const oldProofGood = bigjson('proof_good.json');
  const oldProofGood0 = bigjson('proof_good0.json');
  const oldPublic = bigjson('public.json');
  // Generate proofs
  console.log('creating proof');
  const proofNew = await zkp.bn254.groth.createProof(pkey, witness);
  console.log('created proof', proofNew);
  console.log('verifying proof');
  deepStrictEqual(
    zkp.bn254.groth.verifyProof(vkey, proofNew),
    true
  );
  const { publicSignals } = proofNew;
  // Verify proofs
  console.log('verifying proof 2');
  deepStrictEqual(zkp.bn254.groth.verifyProof(vkey, { proof: oldProof, publicSignals }), true);
  console.log('verifying proof 3');
  deepStrictEqual(zkp.bn254.groth.verifyProof(vkey, { proof: oldProofGood, publicSignals }), true);
  console.log('verifying proof 4');
  deepStrictEqual(zkp.bn254.groth.verifyProof(vkey, { proof: oldProofGood0, publicSignals }), true);
  console.log('all proofs were correct')
})();

JS v1

circom JS v1 legacy programs produce code which is eval-ed using new Function. We have to monkey-patch BigInt - otherwise the code won't run. No patching is being done for WASM programs.

dir='circom-js'
git clone https://github.com/iden3/circom_old $dir
cd $dir
git checkout v0.0.35
npm install
import { bn254 } from '@noble/curves/bn254';
import * as zkp from 'micro-zk-proofs';
import * as zkpMsm from 'micro-zk-proofs/msm.js';
import * as zkpWitness from 'micro-zk-proofs/witness.js';
import { deepStrictEqual } from 'node:assert';
import sumCircuit from './sum-circuit.json' with { "type": "json" };

const groth = zkp.bn254.groth;
const input = { a: '33', b: '34' };
const setupJs = groth.setup(sumCircuit);

(async () => {
  // 2. setup
  // Generate using circom_old circuit
  // NOTE: we have this small util to remove dependencies on snarkjs for witness generation
  // 3. generate witness
  const witnessJs = zkpWitness.generateWitness(sumCircuit)(input);
  //deepStrictEqual(witness0, witnessJs); // -> will fail, because we have different constrains!
  // 4. create proof
  const proofJs = await groth.createProof(setupJs.pkey, witnessJs);
  console.log('proof created, signals:', proofJs.publicSignals)
  // 4. verify proof
  deepStrictEqual(
    groth.verifyProof(setupJs.vkey, proofJs),
    true
  );
  console.log('proof is valid');
})();

// Fast, parallel proofs
(async () => {
  console.log('testing fast parallel proofs, using web workers');
  const msm = zkpMsm.initMSM();
  const grothp = zkp.buildSnark(bn254, {
    G1msm: msm.methods.bn254_msmG1,
    G2msm: msm.methods.bn254_msmG2,
  }).groth;
  // 4. generate proof
  const proofJs2 = await grothp.createProof(setupJs.pkey, witnessJs);
  console.log('proof created, signals:', proofJs2.publicSignals)
  // 4. verify proof
  deepStrictEqual(
    grothp.verifyProof(setupJs.vkey, proofJs2),
    true
  );
  console.log('proof is valid');
  msm.terminate();
})();

Speed

Benchmarks measured on Apple M4:

  • Proof generation, no workers: 149ms
  • Proof verification, no workers: 24ms
  • Proof generation, with workers: 48ms
  • Proof verification, with workers: 24ms

License

MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.