该文档介绍了micro-zk-proofs库,一个用于并行创建和验证零知识SNARK证明的工具,它支持Groth16协议,并计划支持PLONK等。
使用 noble cryptography 并行创建和验证零知识 SNARK 证明。
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);
// 类型定义如下:
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;
// 多项式
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[];
}
总共有 4 个步骤:
查看 examples 目录。它包含 wasm-v2、wasm-v1 和 js 电路。
我们需要一个电路和一个编译器。
电路编译不在我们库的范围内,并且取决于电路语言。
Groth16 证明不在乎语言。我们在下面的例子中使用 circom,但你可以使用任何东西。
circom 没有 no common serialization format,但这没什么大不了的。
有三个 circom 编译器:
我们支持所有版本,原因是向后兼容:
v2 程序与 circom v1 不同,旧电路不总能用新编译器编译,并且它们的输出可能彼此不同。
Witness 生成:
[!NOTE]
当与现有项目一起使用时,证明/验证密钥、witness 计算程序和
电路信息应由作者提供。用稍微
不同版本的编译器编译同一电路将导致不兼容的电路,这将生成
无效的证明。
[!WARNING]
.setup
方法仅用于测试,在实际生产环境中,你需要进行多方仪式以避免有毒标量的泄漏。
查看 examples 目录。它包含 wasm-v2、wasm-v1 和 js 电路。
我们将使用一个测试电路。

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
);
})();

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')
})();
circom JS v1 旧程序生成的代码使用 new Function
进行 eval
。
我们必须 monkey-patch BigInt - 否则代码将无法运行。
对于 WASM 程序,不进行任何修补。

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
// 使用 circom_old 电路生成
// NOTE: 我们有这个小实用程序来删除 snarkjs 对 witness 生成的依赖
// 3. generate witness
const witnessJs = zkpWitness.generateWitness(sumCircuit)(input);
//deepStrictEqual(witness0, witnessJs); // -> 将失败,因为我们有不同的约束!
// 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');
})();
// 快速并行证明
(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();
})();
MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.
- 原文链接: github.com/paulmillr/mic...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!