用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 → 電路規模(212 ≈ 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號