引介 micro-zk-proofs : 用于创建和验证零知识 SNARK 证明的开源库

该文档介绍了micro-zk-proofs库,一个用于并行创建和验证零知识SNARK证明的工具,它支持Groth16协议,并计划支持PLONK等。

micro-zk-proofs

使用 noble cryptography 并行创建和验证零知识 SNARK 证明。

  • 支持 Groth16。计划支持 PLONK 和其他算法
  • 可选,使用 Web Worker 实现快速证明生成
  • 支持现代 wasm 和旧版 js circom 程序
  • 解析 R1CS,WTNS

用法

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 个步骤:

  1. 编译电路(不在本库的范围内)
  2. 设置电路(不在本库的范围内)
  3. 生成 witness(不在本库的范围内)
  4. 创建/验证证明

查看 examples 目录。它包含 wasm-v2、wasm-v1 和 js 电路。

编译、设置、生成 witness

我们需要一个电路和一个编译器。

电路编译不在我们库的范围内,并且取决于电路语言。
Groth16 证明不在乎语言。我们在下面的例子中使用 circom,但你可以使用任何东西。

circom 没有 no common serialization format,但这没什么大不了的。

有三个 circom 编译器:

  • WASM circom v2 v2.2.2 (github) - 现代版本
  • WASM circom v1 v0.5.46 (github) - v0.0.35 的旧版重写
  • JS circom v1 v0.0.35 (github) - 原始 JS 版本

我们支持所有版本,原因是向后兼容:
v2 程序与 circom v1 不同,旧电路不总能用新编译器编译,并且它们的输出可能彼此不同。

  • 首先,我们需要用 circom 语言编写电路。
  • 编译结果:
    • constraints 列表/信息:
      • circom2 的 json 或 r1cs 格式
      • 嵌入在旧编译器的circuit.json 中
    • witness 计算程序:
      • circom2 的 wasm/js
      • 嵌入在旧编译器的circuit.json 中

Witness 生成:

  • 此步骤取决于语言,但我们只需要 bigint 数组。
  • 对于 wasm(circom2),编译器本身生成了不错的零依赖计算器
  • 还有 'circom_tester' 包来运行这些 wasm witness 计算程序

[!NOTE]
当与现有项目一起使用时,证明/验证密钥、witness 计算程序和
电路信息应由作者提供。用稍微
不同版本的编译器编译同一电路将导致不兼容的电路,这将生成
无效的证明。

[!WARNING]
.setup 方法仅用于测试,在实际生产环境中,你需要进行多方仪式以避免有毒标量的泄漏。

创建/验证证明

查看 examples 目录。它包含 wasm-v2、wasm-v1 和 js 电路。

我们将使用一个测试电路。

  • 这是一个基本电路,它接受 3 个变量:“a、b、sum”(其中 a 是私有的),并验证
    'a + b = sum'。所有变量都是 32 位的。这允许我们证明我们知道这样一个 'a',它可以产生特定的
    'sum' 与公开已知的 'b' 而不泄露我们知道哪个 a。
  • 这是一个玩具电路,不难识别使用了哪个'a',在真实的例子中,会有一些哈希。
  • sum.json 的最新版本:snarkjs v0.2.0 中的 sum_last.json
  • 这种特定电路可以使用新编译器和旧编译器进行编译,其他电路可能无法编译。
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 旧程序生成的代码使用 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();
})();

License

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

  • 原文链接: github.com/paulmillr/mic...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
paulmillr
paulmillr
江湖只有他的大名,没有他的介绍。