给定一个编码为 一阶约束系统(Rank 1 Constraint System) 的 算术电路(arithmetic circuit),我们有可能创建一个证明拥有见证(witness)的 ZK-proof,尽管它并不是简洁的(succinct)。本文将描述如何实现这一目标。
针对 R1CS 的零知识证明,其实现方式是将见证向量转换为 有限域椭圆曲线点,并将每一行的阿达马乘积(Hadamard product)替换为 双线性配对(bilinear pairing)。
给定一个 Rank 1 Constraint System,其中每个矩阵有 n 行和 m 列,我们将其写为
La∘Ra=Oa
其中 L、R、O 是具有 n 行和 m 列的矩阵,a 是见证向量(包含对算术电路中每个信号满足条件的赋值)。向量 a 有 1 列和 m 行,∘ 表示逐元素乘法(阿达马乘积)。
展开后如下所示:
l1,1⋮ln,1⋯⋱⋯l1,m⋮ln,ma1⋮am∘r1,1⋮rn,1⋯⋱⋯r1,m⋮rn,ma1⋮am=o1,1⋮on,1⋯⋱⋯o1,m⋮on,ma1⋮am
=a1l1,1+⋯+aml1,m⋮a1ln,1+⋯+amln,m∘a1r1,1+⋯+amr1,m⋮a1rn,1+⋯+amrn,m=a1o1,1+⋯+amo1,m⋮a1on,1+⋯+amon,m
=∑i=1mail1,i∑i=1mail2,i⋮∑i=1mailn,i∘∑i=1mair1,i∑i=1mair2,i⋮∑i=1mairn,i=∑i=1maio1,i∑i=1maio2,i⋮∑i=1maion,i
=∑i=1mail1,i∑i=1mair1,i=∑i=1maio1,i∑i=1mail2,i∑i=1mair2,i=∑i=1maio2,i⋮∑i=1mailn,i∑i=1mairn,i=∑i=1maion,i
在这种设置下,我们只需向验证者(verifier)提供向量 a,就可以向其证明我们拥有一个满足该 R1CS 的见证向量 a,但这里有一个明显的缺陷——这并不是一个零知识证明!
R1CS 的零知识证明算法
如果我们将见证向量“加密”,即将其每个元素乘以 G1 或 G2,数学运算依然能够正常进行!
为了理解这一点,考虑一下如果我们执行矩阵乘法:
[1324][45]=[1432]
以及:
[1324][4G15G1]=[14G132G1]
第二个矩阵乘法中两个椭圆曲线点的离散对数(discrete logs)与第一个矩阵乘法中元素的值完全相同。
换句话说,每次我们将列向量与方阵中的某一行相乘时,我们实际上是在执行两次椭圆曲线点乘法和一次椭圆曲线加法。
椭圆曲线的符号表示
我们用 [aG1]1 表示域元素 a 乘以 G1 生成的 G1 椭圆曲线点。我们用 [aG2]2 表示 a 乘以生成元 G2 生成的 G2 椭圆曲线点。由于离散对数问题,我们无法从已知的 [aG1]1 或 [aG2]2 中提取出 a。给定点 A∈G1 和 B∈G2,我们称这两个点的配对(pairing)为 A∙B。
证明者步骤
让我们将 a 向量中的每个元素乘以生成元点 G1 来对其进行加密,从而生成椭圆曲线点 [aiG1]1。
对于矩阵 L,我们执行以下操作:
l1,1⋮ln,1⋯⋱⋯l1,m⋮ln,m[a1G1]1⋮[amG1]1=l1,1[a1G1]1⋮ln,1[a1G1]1+⋯+⋱+⋯+l1,m[amG1]1⋮ln,m[amG1]1=∑i=1ml1,i[aiG1]1∑i=1ml2,i[aiG1]1⋮∑i=1mln,i[aiG1]1
考虑到阿达马乘积将变成一系列椭圆曲线配对,我们也可以使用 G2 点对 a 向量进行加密,以便验证者能够进行配对操作。
r1,1⋮rn,1⋯⋱⋯r1,m⋮rn,m[a1G2]2⋮[amG2]2=r1,1[a1G2]2⋮rn,1[a1G2]2+⋯+⋱+⋯+r1,m[amG2]2⋮rn,m[amG2]2=∑i=1mr1,i[aiG2]2∑i=1mr2,i[aiG2]2⋮∑i=1mrn,i[aiG2]2
在此操作之后,我们得到了一列源自乘法 La 的 G1 椭圆曲线点,以及一列源自 Ra 的 G2 点。
下一步最朴素的想法是用 G12 点加密 a 向量,这样验证者就可以将 La 的结果与 Ra 的结果进行配对,以检验其是否等于 Oa。但是 G12 点的数据量非常庞大,因此我们更倾向于让验证者对位于 G1 上的 Oa 椭圆曲线点进行配对,然后将每个元素与一个 G2 点进行配对。与一个 G2 点配对在某种意义上等同于“乘以 1”,但会将 G1 点转化为 G12 点。
然后证明者将 G1 向量和 G2 向量提交给验证者。
验证步骤
因此,验证步骤变为:
∑i=1mli,1[aiG1]1∑i=1mli,1[aiG1]1⋮∑i=1mli,1[aiG1]1∙∙⋮∙∑i=1mri,1[aiG2]2∑i=1mri,1[aiG2]2⋮∑i=1mri,1[aiG2]2=?∑i=1moi,1[aiG1]1∑i=1moi,1[aiG1]1⋮∑i=1moi,1[aiG1]1∙∙⋮∙G2G2⋮G2
=∑i=1mli,1[aiG1]1∙∑i=1mri,1[aiG2]2∑i=1mli,2[aiG1]1∙∑i=1mri,2[aiG2]2⋮∑i=1mli,n[aiG1]1∙∑i=1mri,n[aiG2]2=?∑i=1moi,1[aiG1]1∙G2∑i=1moi,2[aiG1]1∙G2⋮∑i=1moi,n[aiG1]1∙G2
当且仅当证明者提供了一个有效的见证时,上述 G12 元素向量才会逐元素相等。
嗯,差不多是这样。我们将在后续小节中探讨剩下的问题。
首先我们需要提及一个重要的实现细节。
公开输入
如果我们的知识声明是“我知道 x,使得 x3+5x+5=y,其中 y=155”,那么我们的见证向量可能如下所示:
[1,y,x,v]
其中 v=x2。在这种情况下,我们需要 [1,y] 是公开的。为了实现这一点,我们只需不对见证向量的前两个元素进行加密。验证者将检查公开输出,然后通过将公开输入与 G1 或 G2 点相乘来进行加密,从而保持验证公式不变。
应对恶意证明者
由于向量被加密了,验证者无法立刻知晓由 G1 点组成的向量与由 G2 点组成的向量所加密的值是否相同。
也就是说,证明者提供的是 aG1 和 aG2。由于验证者不知道这些点向量的离散对数,验证者如何确信 G1 点向量的离散对数与 G2 点向量的离散对数相同呢?
验证者可以通过将这两个点向量分别与由 相反 生成元组成的向量进行配对,并观察生成的 G12 点是否相等,来检查离散对数是否相等(而无需知道具体的离散对数值)。具体公式为:
a1G1a2G1⋮amG1∙∙⋮∙G2G2⋮G2=?a1G2a2G2⋮amG2∙∙⋮∙G1G1⋮G1
这个算法主要停留在学术层面
该算法对验证者来说非常低效。如果 R1CS 中的矩阵很大(对于有实际意义的算法而言必然如此),那么验证者将需要执行大量的配对和椭圆曲线加法运算。椭圆曲线加法相当快,但椭圆曲线配对却很慢(并且在 Ethereum 上会消耗大量的 Gas)。
然而,很高兴能看到在现阶段,零知识证明在数学上是可行的。如果你对椭圆曲线运算有很好的掌握(并且没有忘记矩阵算术),它们其实并不难理解。
让这项技术实现真正的零知识
就目前而言,我们的见证向量无法被解密,但它可以被猜测。如果攻击者(试图发现未加密见证的人)利用某些辅助信息对见证进行了合理的猜测,他们可以将猜测的见证向量乘以椭圆曲线点生成元,看看结果是否与证明者的见证向量相同,以此来验证他们的猜测。
我们将在关于 Groth16 的内容中学习如何防御见证猜测。
此外,请记住,现实世界中没有人会使用这里描述的算法,因为它实在太低效了。不过,如果你动手实现它,这将有助于你练习编写有意义的椭圆曲线算术逻辑,并构建一个功能完善的端到端(准)零知识证明系统。
你可以在 此代码库 中看到 Obront 对本文描述算法的一个示例实现。
通过 RareSkills 了解更多
本材料节选自我们的 零知识课程。
原文首发于 2023 年 8 月 26 日