用Circom和Snarkjs实践零知识证明技术

0、简介和安装

  1. Circom:它是用来定义和构建零知识证明电路的工具。当你使用 circom 编写一个电路(通常是一个用于验证某种计算过程的程序),它会生成一些所谓的“工件”(artifacts)。这些工件包括了用来生成证明和验证证明的关键数据,如电路的描述(通常是 .r1cs 文件)、公共输入、私有输入、证明生成的帮助函数(.wasm 文件)等。
  2. Snarkjs:它是用来操作这些“工件”的工具包。snarkjs 会使用由 circom 构建的工件来:
    • 生成证明(生成 ZK-proof):基于电路和私有输入生成零知识证明。
    • 验证证明(验证 ZK-proof):验证生成的证明是否正确,是否符合电路逻辑。

换句话说:

  • Circom:负责电路的构建(通过定义电路并生成相应的“工件”)。
  • Snarkjs:负责利用这些工件生成和验证证明。

所以,CircomSnarkjs 是配合使用的工具,前者用于构建电路和生成必要的工件,后者用于基于这些工件来进行证明的生成和验证。

1、环境准备
https://rustup.rs/ 下载rustup,然后选择通过vs安装器(Quick install via the Visual Studio Community installer)安装rust环境。
安装到最后会出现:

stable-x86_64-pc-windows-msvc installed - rustc 1.91.0 (f8297e351 2025-10-28)

Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload its PATH environment variable to include
Cargo's bin directory (%USERPROFILE%\.cargo\bin).

把这个加入到环境变量Path里。

然后也要确保有npm,以及nodejs,nodejs版本要10以后的。

2、安装circom
下载源代码
git clone https://github.com/iden3/circom.git
编译:

cargo build --release


Compiling circom v2.2.3 (D:\circom\circom\circom)
Finished `release` profile [optimized] target(s) in 2m 19s
warning: the following packages contain code that will be rejected by a future version of Rust: num-bigint-dig v0.8.4
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`

编译好的二进制文件在target目录。

然后安装。cargo install --path circom
circom --help 能正确运行,说明安装成功。

3、安装snarkjs
npm install -g snarkjs

1、创建和编译电路

新建文件multiplier2.circom,写入如下电路逻辑:

pragma circom 2.0.0;

template Multiplier2() {
	signal input a;
	signal input b;
	signal output c;
	c <== a*b;
 }

component main = Multiplier2(); 定义组件main,包含上面的信号和约束关系。

编译电路

circom .\multiplier2.circom --r1cs --wasm --sym --c
template instances: 1
non-linear constraints: 1
linear constraints: 0
public inputs: 0
private inputs: 2
public outputs: 1
wires: 4
labels: 4
Written successfully: .\multiplier2.r1cs
Written successfully: .\multiplier2.sym
Written successfully: .\multiplier2_cpp\multiplier2.cpp and .\multiplier2_cpp\multiplier2.dat
Written successfully: .\multiplier2_cpp/main.cpp, circom.hpp, calcwit.hpp, calcwit.cpp, fr.hpp, fr.cpp, fr.asm and Makefile
Written successfully: .\multiplier2_js\multiplier2.wasm
Everything went okay

2、创建witness

输入、中间信号和输出的集合称为见证witness

进入multiplier2_js目录
创建文件 input.json {"a": "3", "b": "11"}

node generate_witness.js multiplier2.wasm input.json witness.wtns

新生成了见证文件witness.wtns

3、证明的创建和验证

用wtns见证文件和r1cs约束文件,witness.wtns, multiplier2.r1cs
并使用Groth16 zk-SNARK协议来创建证明和验证证明。

(1)首先做可信设置:

阶段一,The powers of tau, which is independent of the circuit.

snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
[DEBUG] snarkJS: Calculating First Challenge Hash
[DEBUG] snarkJS: Calculate Initial Hash: tauG1
[DEBUG] snarkJS: Calculate Initial Hash: tauG2
[DEBUG] snarkJS: Calculate Initial Hash: alphaTauG1
[DEBUG] snarkJS: Calculate Initial Hash: betaTauG1
[DEBUG] snarkJS: Blank Contribution Hash:
                786a02f7 42015903 c6c6fd85 2552d272
                912f4740 e1584761 8a86e217 f71f5419
                d25e1031 afee5853 13896444 934eb04b
                903a685b 1448b755 d56f701a fe9be2ce
[INFO]  snarkJS: First Contribution Hash:
                9e63a5f6 2b96538d aaed2372 481920d1
                a40b9195 9ea38ef9 f5f6a303 3b886516
                0710d067 c09d0961 5f928ea5 17bcdf49
                ad75abd2 c8340b40 0e3b18e9 68b4ffef

参数解释
bn128 → 使用的椭圆曲线(BN254)
12 → 电路规模(2¹² ≈ 4096 个约束)

pot12_0000.ptau → 输出文件名,ptau = powers of tau

PS D:\circom\work\multiplier2_js> snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
Enter a random text. (Entropy): ieu39739mude73kuekg
[DEBUG] snarkJS: Calculating First Challenge Hash
[DEBUG] snarkJS: Calculate Initial Hash: tauG1
[DEBUG] snarkJS: Calculate Initial Hash: tauG2
[DEBUG] snarkJS: Calculate Initial Hash: alphaTauG1
[DEBUG] snarkJS: Calculate Initial Hash: betaTauG1
[DEBUG] snarkJS: processing: tauG1: 0/8191
[DEBUG] snarkJS: processing: tauG2: 0/4096
[DEBUG] snarkJS: processing: alphaTauG1: 0/4096
[DEBUG] snarkJS: processing: betaTauG1: 0/4096
[DEBUG] snarkJS: processing: betaTauG2: 0/1
[INFO]  snarkJS: Contribution Response Hash imported:
                b587291a d84b2da4 310050e2 5cf82cf2
                d5e0cf7a 2a5e5ff5 d9502362 2416b648
                cd875541 00b2fb74 ea44eb6f a3d7b293
                5c3243a0 6ceee2cc 4b1023e8 ebcfc91c
[INFO]  snarkJS: Next Challenge Hash:
                a92a0c7c 2d6578ca 2e30ce93 a86b6d1c
                90f1d5ed fbb50a2d 1853105f 65a76d1a
                a02cf5a4 fd5147af ae1acb7d 4ebcb7a0
                208d276e 061c84d3 7047149d 7130d096

在上面pot12_0000.ptau基础上,贡献了新的随机数r进行乘积,得到新输出文件 pot12_0001.ptau

在pot12_0001.ptau文件中做了贡献,接下来进行二阶段。

阶段二,The phase 2, which depends on the circuit.

snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v

上面是“冻结”Powers of Tau文件,把 Phase 1 的公共随机性 τ 固定下来,给出最终的 pot12_final.ptau文件,供具体电路使用。 这一步执行执行时间和控制台输出都较长。

snarkjs groth16 setup ..\multiplier2.r1cs pot12_final.ptau multiplier2_0000.zkey
[INFO]  snarkJS: Reading r1cs
[INFO]  snarkJS: Reading tauG1
[INFO]  snarkJS: Reading tauG2
[INFO]  snarkJS: Reading alphatauG1
[INFO]  snarkJS: Reading betatauG1
[INFO]  snarkJS: Circuit hash:
                0cd28c1b cfdc6fa4 c4a4f06c bdfaa74c
                45f14c90 17b2b751 214ec62d 7249b5c1
                1502b053 9fd6a89e da8046fc 9266c41f
                db3c408f 15c1f463 f028569f d3e4952d

上面是生成zkey文件(Zero-knowledge key file),这就是CRS(Common Reference String),也就是g^Aᵢ(τ), g^Bᵢ(τ), g^Cᵢ(τ)这些。

snarkjs zkey contribute multiplier2_0000.zkey multiplier2_0001.zkey --name="1st Contributor Name" -v
Enter a random text. (Entropy): duw62jjnde7383
[DEBUG] snarkJS: Applying key: L Section: 0/2
[DEBUG] snarkJS: Applying key: H Section: 0/4
[INFO]  snarkJS: Circuit Hash:
                0cd28c1b cfdc6fa4 c4a4f06c bdfaa74c
                45f14c90 17b2b751 214ec62d 7249b5c1
                1502b053 9fd6a89e da8046fc 9266c41f
                db3c408f 15c1f463 f028569f d3e4952d
[INFO]  snarkJS: Contribution Hash:
                b2594cc7 9803fc61 67a2805a 6bf1d140
                52178df5 3e7ae69a 111b6ad8 f0659cef
                909c8bda aacbcb59 8defa810 3db0f223
                ccaabc64 f0542bf9 f50b082e c286ff9b

就像阶段一需要多个参与者贡献τ一样,阶段二也需要多个参与者贡献随机性,每个参与者都在zkey上乘以自己的随机数。只要至少一个参与者是诚实的,整个CRS就是安全的,这是分散信任的关键步骤。

snarkjs zkey export verificationkey multiplier2_0001.zkey verification_key.json
[INFO]  snarkJS: EXPORT VERIFICATION KEY STARTED
[INFO]  snarkJS: > Detected protocol: groth16
[INFO]  snarkJS: EXPORT VERIFICATION KEY FINISHED

这一步是从multiplier2_0001.zkey中导出verification_key.json

(2)生成Proof

​ 证明者运行:

snarkjs groth16 prove multiplier2_0001.zkey witness.wtns proof.json public.json

这里生成proof.jsonpublic.json

(3)验证证明

证明者把verification_key.json , public.json, proof.json发给验证者。验证者执行如下命令:

snarkjs groth16 verify verification_key.json public.json proof.json
[INFO]  snarkJS: OK!

证明成功!

参考文档

Installation - Circom 2 Documentation

posted on 2025-10-30 17:12  肥兔子爱豆畜子  阅读(16)  评论(0)    收藏  举报

导航