Appearance
Groth16
Arithmetization 算数化
对于经过算数化的电路,我们可以表示为
其中,
记:
这里的角标是指矩阵列插值得出的多项式。
其他同理。
QAP
对于验证矩阵的正确性,我们可以很自然地想到把矩阵通过插值计算为多项式形式,然后通过 Schwartz-Zippel 引理 来验证其正确性。这里有
其中:
但是这里观察到左边的次数是
从而,通过公开的
这样,我们就有一个初步的 Sigma 协议雏形:
- Prover 对自己的多项式做出承诺
传输给 Verifier。 - Verifie 发送随机值
给 Prover. - Prover 计算出多项式
给 Verifier。 - Verifier 进行验证。
接下来,我们的目标就是如何确保 Provr 在第三部时确实是根据正确的多项式进行运算的。
利用SRS计算多项式
以下对于
这里我们使用可信设置(多方安全计算)生成一个秘密值
我们有:
Note that
验证 SRS 的正确性
记住我们同时给出了
和 证明生成
的正确性:e( ) = e( ) 证明生成
的正确性:e( ) = e( )
这样,当我们删除
对于多项式的计算,例如对于点
其中
使用SRS计算QAP多项式
我们的目标是计算:
此时的问题是,我们希望 rhs 是
这里我们考虑到
- 由于我们预先知道
, 并且在可信设置我们可以提前先计算 再丢弃 (这里是可信设置时直接计算的,不是低度扩展),得到:
- 从而有如下等式关系可以容易得到
:(这里的 是 的系数)
证明:
此时我们有:
SRS:
Prover:
Verifier: 需要验证
NOTE HERE
引入随机性,保证QAP正确性
之后,我们需要在可信设置随机生成两个秘密标量,
这时我们修改
并且为了同时使得上面的等式仍然成立 Here,修改
需要验证的式子变为:
易证:
这样变换
但是这里又出现了一个问题,计算
这里先把多项式展开回上面的形式:
注意到对于 Prover 之外的人来说,
再回顾一下我们的目标:计算
故而在可信设置时,我们还需计算:
然后再由 Prover 计算:
从而,通过可信设置(类似于承诺),我们可以确保 Prover 在生成证据时,使用的是正确的程序(QAP),这也解释了为什么在SnarkJS中Groth16要再Contribute一次,而Plonk不用。
说明
对于要验证的
- 注意到可信设置确保了
的不可伪造性,对于 rhs 来说 Prover 不可能在使用错误的 下计算得出目标 - 而对于:
来说,确保了计算的正确性
此时我们有
SRS:
- random
- random
Public
Prover
Verifier
支持公开输入
在Circom中,我们可以通过 signal public 来声明公开输入,例如下面的Hash代码:
js
template Hash() {
signal input a;
signal input b;
signal input value;
signal output c;
signal
conponent hasher = Poseidon(2);
hasher.inputs[0] <== a;
hasher.inputs[1] <== b;
c <== hasher.out;
value === c;
log("hash: ", c);
}
component main{ public [a, value] } = Hash();目前为止,我们所有的
注意这里的角标变了,既不隐藏
的前 个元素
然后由验证者计算:
这时候,验证的公式变为:
但是,我们该如何限制Prover只使用私有变量计算
这里我们设置
但是如果 Prover 想要作弊的话,假设这里把sym2给隐藏,既使得 Verifier 彻底无法知道程序的正确性,可以把
这样,把公开变量从
为了解决这个问题,我们继续在可信设置时引入两个变量
再把
同时修改验证的计算方式
这里的原因也是显然的,把
此时我们有
SRS:
- random
- random
\
Public
Prover
Verifier
实现零知识
由于运算程序是已知的,并且当前对于证明者,我们没有引入任何的随机性,故而攻击者可以通过遍历所有的输入可能从而获得证明者的输入和中间变量的值,造成隐私泄露。
为了保障零知识性质,Prover 在生成证据
这样,通过引入随机性,我们就可以保证 Prover 的输入和中间变量的值是不可预测的,从而保障了零知识性质。
推导
这时候我们的验证公式:
这时候,我们发现:
这样分母就被消掉了。
这样,我们就得到最终的Groth16
对于
的加法实际上是乘法(
SRS:
- random
- random
Public
Prover
Verifier