用Circom和Snarkjs实践零知识证明技术
0、简介和安装
- Circom:它是用来定义和构建零知识证明电路的工具。当你使用 circom 编写一个电路(通常是一个用于验证某种计算过程的程序),它会生成一些所谓的“工件”(artifacts)。这些工件包括了用来生成证明和验证证明的关键数据,如电路的描述(通常是
.r1cs文件)、公共输入、私有输入、证明生成的帮助函数(.wasm文件)等。 - Snarkjs:它是用来操作这些“工件”的工具包。
snarkjs会使用由 circom 构建的工件来:- 生成证明(生成 ZK-proof):基于电路和私有输入生成零知识证明。
- 验证证明(验证 ZK-proof):验证生成的证明是否正确,是否符合电路逻辑。
换句话说:
- Circom:负责电路的构建(通过定义电路并生成相应的“工件”)。
- Snarkjs:负责利用这些工件生成和验证证明。
所以,Circom 和 Snarkjs 是配合使用的工具,前者用于构建电路和生成必要的工件,后者用于基于这些工件来进行证明的生成和验证。
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.json ,public.json
(3)验证证明
证明者把verification_key.json , public.json, proof.json发给验证者。验证者执行如下命令:
snarkjs groth16 verify verification_key.json public.json proof.json
[INFO] snarkJS: OK!
证明成功!
浙公网安备 33010602011771号