介绍如何进行零知识证明
零知识证明就是你告诉别人你知道某个东西,但是不用真的跟别人说这个东西,有公式可以证明你的确知道这个东西,但不需要暴露你知道的东西
- 比如你和别人说你有100W,但不需要真的把钱给他们看
- 比如你说你知道某个地址的私钥,但你不需要把私钥给他们看
- 比如你说你知道某个hash值的原文是什么,但是不用把原文给别人看
如何实现
通过电路Circom来实现
Circom 电路与约束系统(R1CS)
Circom是一个专门为ZK设计的电路语言,用来描述计算逻辑
比如
pragma circom 2.1.6;
template Example() {
signal input a;
signal input b;
signal output c;
c <== a * b;
}
component main = Example();
这段代码就代表“我想证明我知道两个数 a、b,它们的乘积是 c”。但不需要暴露a和b的具体值。
零知识证明要证明某个东西,其实就是写一个电路,然后你提供一组input,让电路的output符合条件,即让电路的所有约束条件都能符合,同时可以不暴露input的具体值。所以就是将要证明的东西,以电路的形式写出来
有了电路后如何证明
比如你现在已经写了一个电路,然后通过这个电路,你可以生成proof,别人可以验证这个proof,整个流程如下
- 编译Circom(编译电路) 电路写完之后,后续是不直接用这个电路的,而是要编译这个电路,用这个编译后的文件
circom circuit.circom --r1cs --wasm --sym
| 文件名 | 作用 |
|---|---|
| circuit.r1cs | 电路的约束系统文件(Rank-1 Constraint System) |
| circuit.wasm | 用于生成 witness 的 WebAssembly 程序 |
| circuit.sym | 信号名与内部变量的映射关系(调试用) |
这里主要要用到的是r1cs和wasm文件
r1ls是约束系统,可以理解为是电路用数学公式表达的形式,r1ls里都是约束方程,用来表示电路的逻辑,证明者需要提供一组input,让r1ls里的所有约束方程都成立
wasm文件和可以理解为电路的另一种形式,用来将电路的逻辑完整的执行一遍,你输入一组input,通过执行这个wasm文件,即完整的执行了一遍电路,可以得到所有的中间变量(input、output),这些变量会使用witness格式的文件存储,这里只要知道这个文件是存储电路完整运行的中间输出的各种变量就好了
- 生成ptau文件
在 SNARK(尤其是 Groth16)中,需要一个可信初始化(Trusted Setup)。 这一步生成一些通用参数,用于构造后续的证明系统,包含这些通用参数的文件就是ptau文件。
ptau文件是一个和电路无关的文件,可以理解为一个底座,只要参数够用,电路就可以用这个文件,很多官方会将自己的ptau文件公开,所有人都可以使用。
过程类似如下:
# 生成一个通用的ptau文件
snarkjs powersoftau new bn128 12 pot12_0000.ptau
# 贡献随机性(可多次)
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contributor"
# 准备最终ptau文件
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau
pot12_final.ptau 就是最终可供电路使用的公共参数。
- 生成zkey文件 用ptau文件和r1ls文件,可以生成这个电路的vkey文件,vkey文件里包含了proving key和verifying key,这两个key可以用来生成这个电路的proof,proving key用来生车proof,verifying key用来验证proof
生成过程如下:
// 生成 proving/verifying key
snarkjs groth16 setup circuit.r1cs pot12_final.ptau circuit_0000.zkey
// 贡献随机性
snarkjs zkey contribute circuit_0000.zkey circuit_final.zkey
- 生成witness文件 witness文件是运行电路过程中生成的完整的所有中间变量,需要执行编译电路过程中生成的wasm文件,参数是一组input参数。 代码如下:
node generate_witness.js circuit.wasm input.json witness.wtns
其中generate_witness.js一般是编译电路的时候会一起生成的,用来执行wasm文件
- 生成proof文件和public signals 有了上面的文件之后,就可以用来生成proof和public文件了,public文件是电路里面可以暴露给其他人看的那些参数
如何生成proof.json和public.json
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json
参数是zkey文件和wtns文件,输出是proof.json public.json,这里用到的是proving key,proving key可以从zkey文件里拿到
- 验证proof 别人可以使用vkey文件验证这个proof是否满足条件,只要满足条件,就说明满足这个vkey文件对应的电路的所有约束条件
snarkjs groth16 verify verification_key.json public.json proof.json