本文介绍了 LambdaWorks 库的开发目标,该库旨在提供易于使用的零知识证明工具,并重点介绍了 STARKs 证明系统的原理和实现步骤,包括算术化、多项式方程转换和 FRI 协议,并通过一个玩具示例展示了 FRI 协议的运行过程,最后总结了 STARKs 的核心思想。
我们认为目前大多数 ZK 库还不够易于使用。它们大多假定用户具有深厚的密码学背景,这使得新手很难从中学习,即使他面前有所有代码。我们还发现,一些常用的库对于初学者来说文档不足或示例难以理解。除此之外,一些库没有遵循对于构建可靠的生产系统至关重要的先进工程实践。有很多像 Cairo、Noir 这样的项目没有这些问题,但它们是成熟的编程语言。我们想要一个工具来构建像那些语言、新的证明系统或任何我们需要的东西。
因此,我们决定开始构建我们的 LambdaWorks 库,并牢记以下目标:
鉴于 STARKs 的重要性和应用,我们决定从实现 STARKs 的证明器开始构建我们的库。我们必须实现有限域算术和基本的密码学内容,例如 Merkle 树和哈希函数。我们将继续学习椭圆曲线和 SNARKs。
STARKs(可扩展的、透明的知识论证)是密码学原语,是达到目的的便捷手段。我们追求的目标是计算完整性,即证明计算已正确执行(根据一组指令)。例如,我们想要证明我们正确计算了一个序列的前 5000 个值,或者我们运行了一个给定的机器学习算法,或者我们在区块链中处理了 4000 个交易。STARKs 为我们提供了计算完整性的简短证明。STARKs 给我们的优势是,检查证明比执行朴素验证(验证者重新执行程序)要快得多。
有很多有趣的资源可以学习 STARKs 的基础知识,例如 Starkware's STARK 101、Anatomy of a STARK、Ministark,以及 Starkware 关于算术化的博客(第一部分 和 第二部分)。
STARK 协议包含以下步骤:
执行轨迹是一个包含 $w$ 列(寄存器)和 $T$ 行的表格,代表系统的每个状态。一个轨迹看起来像这样:
| 寄存器 1 | 寄存器 2 | …… | 寄存器 w |
|---|---|---|---|
| x1,0x1,0 | x2,0x2,0 | …… | xw,0xw,0 |
| x1,1x1,1 | x2,1x2,1 | …… | xw,1xw,1 |
| ⋮⋮ | ⋮⋮ | ⋱⋱ | ⋮⋮ |
| x1,Tx1,T | x2,Tx2,T | …… | xw,Txw,T |
我们将每一列(寄存器)解释为多项式在域上的求值(我们称之为轨迹求值域)。例如,我们可以说 $f1(x)f1(x)$ 是表示第 1 列的多项式,因此:
f1(0)=x1,0f1(0)=x1,0
f1(1)=x1,1f1(1)=x1,1
⋮⋮
f1(T)=x1,Tf1(T)=x1,T
为了使事情更容易和更快,我们将使用大小为 $2n2n$ 的乘法子群 $Zp⋆Zp⋆$ 作为轨迹求值域,使得 $2n≥T2n≥T$。该群有一个生成器 ωω,它跨越子群中的所有元素。子群可以用 ωω 的幂表示,{1,ω,ω2,ω3,...,ωN}{1,ω,ω2,ω3,...,ωN}。我们的轨迹多项式满足
f1(1)=x1,0f1(1)=x1,0
f1(ω)=x1,1f1(ω)=x1,1
⋮⋮
f1(ωT−1)=x1,Tf1(ωT−1)=x1,T
执行轨迹中的元素满足由计算和边界条件给出的某些关系。我们称这些关系为约束。它们可以大致分为两组:
边界约束非常简单:它们指定给定时间寄存器的值。例如,当我们初始化计算时,每个寄存器都有一个给定的值。在斐波那契数列的情况下,
a0=a1=1a0=a1=1
如果我们的轨迹由表示序列的单列组成,则前两个元素等于 1:
x1,0=1x1,0=1
x1,1=1x1,1=1
我们可以将约束转换为多项式关系。我们知道 $x1,0=f1(1)x1,0=f1(1)$ 和 $x1,1=f1(ω)x1,1=f1(ω)$。如果约束成立,例如在 $x=ωx=ω$ 时,则单项式 $x−ωx−ω$ 除 $f1(x)−1f1(x)−1$。这意味着 $f(x)−1f(x)−1$ 除以 $x−ωx−ω$ 的结果是一个多项式,
QBC,1(x)=f1(x)−1x−ωQBC,1(x)=f1(x)−1x−ω
类似地,
QBC,0(x)=f1(x)−1x−1QBC,0(x)=f1(x)−1x−1
这种方法的一个缺点是,如果我们有 $n$ 个边界约束,我们会得到 $n$ 个多项式。一种优化方法是插值边界约束并获得一个新多项式。在这种情况下,
fBC(1)=1fBC(1)=1
fBC(ω)=1fBC(ω)=1
结合所有内容,我们得到
QBC(x)=f(x)−fBC(x)ZBC(x)QBC(x)=f(x)−fBC(x)ZBC(x)
其中 $ZBC(x)ZBC(x)$ 是在强制边界条件的点上消失的多项式:
ZBC(x)=(x−1)(x−ω)ZBC(x)=(x−1)(x−ω)
转移约束是可以在各种计算点应用的,不同行之间的关系。在斐波那契数列的情况下,我们有 $an+2=an+1+anan+2=an+1+an$,对于每个 $n=0,1,...T−2n=0,1,...T−2$。就轨迹多项式而言,
f1(ω2x)−f1(ωx)−f1(x)=0f1(ω2x)−f1(ωx)−f1(x)=0
如果满足约束,则以下函数应该是一个多项式,
QT(x)=f1(ω2x)−f1(ωx)−f1(x)ZT(x)QT(x)=f1(ω2x)−f1(ωx)−f1(x)ZT(x)
其中 $ZT(x)ZT(x)$ 是在强制执行转移约束的地方消失的多项式,
ZT(x)=∏T−2k=0(x−ωk)ZT(x)=∏k=0T−2(x−ωk)
转移约束通常表示为连接执行轨迹中两个连续行的多元多项式。例如,如果我们用 $x$ 表示给定的行,而 $y$ 是下一行,则约束可能是这样的
P(x,y)=y−x2=0P(x,y)=y−x2=0
如果我们用轨迹多项式组合约束多项式,我们有 $x=t(x)x=t(x)$,$y=t(ωx)y=t(ωx)$,所以
t(ωx)−t(x)2=0t(ωx)−t(x)2=0
如果我们正确地进行了计算,那么 $QBC(x)QBC(x)$ 和 $QT(x)QT(x)$ 应该是多项式;如果不是,它们是有理函数(两个多项式的商)。我们可以通过取一个随机线性组合来减少证明它们每个都是多项式
CP(x)=αBCQBC(x)+αTQT(x)CP(x)=αBCQBC(x)+αTQT(x)
如果 $QBC(x)QBC(x)$ 和 $QT(x)QT(x)$ 都是多项式,那么 $CP(x)CP(x)$ 也是。但如果它们中至少有一个是有理函数,那么 $CP(x)CP(x)$ 不太可能是多项式。
鉴于证明 $CP(x)CP(x)$ 是一个多项式很困难,我们将证明它接近一个低次多项式。为此,我们将 $CP(x)CP(x)$ 投影到一个新的函数上,该函数的次数较小。我们将继续进行投影,直到我们得到一个常数多项式。关键在于投影操作Respects这个距离。如果原始函数远离低次多项式,那么投影也将远离它。在跳转到这个过程(称为 FRI)之前,我们必须承诺到轨迹多项式。
我们需要在更大的域上评估轨迹多项式;域的大小是 $β2nβ2n$,其中 $ββ$ 是 blowup 因子。为了避免问题,我们通过将元素乘以 $h$ 来移动域,其中 $h$ 属于陪集。低次扩展域(简称域)由下式给出
D={h,hη,hη2,...,hη2n−1}D={h,hη,hη2,...,hη2n−1}
这里 ηη 是阶数为 $β2nβ2n$ 的子群的生成器,这样它就不会与 ωω 混淆(尽管我们可以通过取 $ω=ηβω=ηβ$ 来关联它们)。
我们在这个大域上评估轨迹多项式,并获得表示每次求值的向量:
[f1(h),f1(hη),...,f1(hη2n−1)][f1(h),f1(hη),...,f1(hη2n−1)]
[f2(h),f2(hη),...,f2(hη2n−1)][f2(h),f2(hη),...,f2(hη2n−1)]
[fw(h),fw(hη),...,fw(hη2n−1)][fw(h),fw(hη),...,fw(hη2n−1)]
为了承诺到这些求值,我们构建 Merkle 树,prover 将 Merkle 树的根发送给 verifiers。为了使事情更容易,轨迹的低次扩展的每一行的元素被分组到单个叶子中。
我们使用相同的域 $D={h,hη,hη2,...,hη2n−1}D={h,hη,hη2,...,hη2n−1}$ 来评估组合多项式。然后,我们可以从这些求值创建一棵 Merkle 树,并将根发送给 verifier。
在某个时刻,verifier 将要求 prover 在某个点 $z$ 处给出组合多项式的值,即 $CP(z)CP(z)$。Verifier 需要确保组合多项式是由将多项式约束应用于轨迹多项式而产生的。给定值 $z∈Dz∈D$(在 DEEP 中,$z$ 的值是在域外采样的),prover 需要在给定点发送轨迹多项式的值,以便 verifier 可以检查计算。例如,在斐波那契的情况(为了简单起见,我们将忽略所有其他约束),
P(u,v,w)=w−v−u=0P(u,v,w)=w−v−u=0
P(t(x),t(ωx),t(ω2x))=t(ω2x)−t(ωx)−t(x)=0P(t(x),t(ωx),t(ω2x))=t(ω2x)−t(ωx)−t(x)=0
要创建组合多项式,我们必须将先前的多项式除以相应的消失多项式。因此,如果我们选择 $x=zx=z$,我们有
Q(z)=t(ω2z)−t(ωz)−t(z)ZD(z)Q(z)=t(ω2z)−t(ωz)−t(z)ZD(z)
Prover 需要发送这三个值。注意 $z=hηkz=hηk$,所以 prover 需要发送值 $t(ω2hηk)t(ω2hηk)$,$t(ωhηk)t(ωhηk)$,$t(hηk)t(hηk)$,他们在 Merkle 树中被 $ββ$ 个元素分隔开。Verifier 取这三个值,评估消失多项式,并检查
Q(z)=CP(z)Q(z)=CP(z)
这样,verifier 相信组合多项式通过约束多项式与执行轨迹相关。
Prover 必须证明 $CP(x)CP(x)$ 接近于低次多项式。为此,他会随机折叠多项式,从而降低次数,直到得到一个常数多项式(在优化中,获得一个常数多项式是不必要的,因为 prover 可以发送一个多项式的所有系数,并让 verifier 检查它)。FRI 协议有两个步骤:承诺和解承诺。
Prover 取 $CP(x)CP(x)$ 并按以下方式拆分它:
g(x2)=CP(x)+CP(−x)2g(x2)=CP(x)+CP(−x)2
xh(x2)=CP(x)−CP(−x)2xh(x2)=CP(x)−CP(−x)2
以便
CP(x)=g(x2)+xh(x2)CP(x)=g(x2)+xh(x2)
Verifier 选择一个随机值 α0α0,并且 prover 形成多项式,
P1(x)=g(x2)+α0h(x2)P1(x)=g(x2)+α0h(x2)
新的域 $D1={h2,h2η2,...,h2ηm}D1={h2,h2η2,...,h2ηm}$ 的大小是 $DD$ 的一半。
Prover 可以通过在 $D1D1$ 上评估 $P1(x)P1(x)$ 来执行低次扩展,然后通过创建 Merkle 树并发送根来承诺到它。他可以通过在每一步将次数减半,继续执行该过程。对于步骤 $k$,我们有
Pk(y2)=Pk−1(y)+Pk−1(−y)2+αk−1(Pk−1(y)−Pk−1(−y)2)Pk(y2)=Pk−1(y)+Pk−1(−y)2+αk−1(Pk−1(y)−Pk−1(−y)2)
和
Dk={h2k−1,(hη)2k−1,...,(ηlh)2k−1}Dk={h2k−1,(hη)2k−1,...,(ηlh)2k−1}
Prover 在 $DkDk$ 上评估 $Pk(x)Pk(x)$ 并承诺到它,发送 Merkle 根。
Verifier 随机选择一个属于 $DD$ 的点 $q$。Prover 需要说服他,轨迹多项式和组合多项式是相关的(我们之前已经介绍过),并且连续 FRI 层的元素也是相关的。对于每一层,prover 需要向 verifier 发送两个元素,$Pk(z)Pk(z)$ 和 $Pk(−z)Pk(−z)$。他还必须证明这些元素属于相应的 Merkle 树,因此还需要每个元素的身份验证路径。
Verifier 可以通过执行共线性检查来检查 FRI 层的正确性。给定 $Pk(z)Pk(z)$,$Pk(−z)Pk(−z)$ 和 $Pk+1(z2)Pk+1(z2)$,verifier 可以计算
gk+1(z2)=Pk(z)+Pk(−z)2gk+1(z2)=Pk(z)+Pk(−z)2
hk+1(z2)=Pk(z)−Pk(−z)2zhk+1(z2)=Pk(z)−Pk(−z)2z
并获得下一层的值
uk+1=gk+1(z2)+αkhk+1(z2)uk+1=gk+1(z2)+αkhk+1(z2)
如果 prover 正确地进行了计算,那么
uk+1=Pk+1(z2)uk+1=Pk+1(z2)
我们将使用一个简单的示例来了解 FRI 上的一切是如何工作的。我们选择 $p=17p=17$,它的乘法群的阶数为 $16=2^416=24$,并设置 $η=3η=3$,这是一个本原单位根(即,$3^{16}=13^{16}=1$ 并且对于 $0<k<160<k<16$ 有 $3^k≠13k≠1$)。我们的组合多项式是 $P0(x)=x3+x2+1P0(x)=x3+x2+1$。LDE 的域简单地是 $D0=Z17⋆={1,2,3,4,5,6,...,16}D0=Z17⋆={1,2,3,4,5,6,...,16}$。下表包含 $P0(x)P0(x)$ 的LDE:
| 索引 | xx | P0(x)P0(x) | 索引 | xx | P0(x)P0(x) |
|---|---|---|---|---|---|
| 0 | 1 | 3 | 8 | 16 | 1 |
| 1 | 3 | 3 | 9 | 14 | 0 |
| 2 | 9 | 12 | 10 | 8 | 16 |
| 3 | 10 | 13 | 11 | 7 | 2 |
| 4 | 13 | 4 | 12 | 4 | 13 |
| 5 | 5 | 15 | 13 | 12 | 3 |
| 6 | 15 | 14 | 14 | 2 | 13 |
| 7 | 11 | 8 | 15 | 6 | 15 |
假设 verifier 采样 $β0=3β0=3$。Prover 对 $P0(x)P0(x)$ 执行随机折叠,
g1(x2)=1+x2g1(x2)=1+x2
xh1(x2)=x3xh1(x2)=x3
所以
P1(x2)=1+(1+β0)x2P1(x2)=1+(1+β0)x2
为了使事情更简单,
P1(y)=1+4yP1(y)=1+4y
与 $y=x2y=x2$。新的域是通过对 $D0D0$ 的元素进行平方而获得的。$P1(y)P1(y)$ 的 LDE 是
| 索引 | yy | P1(y)P1(y) | 索引 | yy | P1(y)P1(y) |
|---|---|---|---|---|---|
| 0 | 1 | 5 | 4 | 16 | 14 |
| 1 | 9 | 3 | 5 | 8 | 16 |
| 2 | 13 | 2 | 6 | 4 | 0 |
| 3 | 15 | 10 | 7 | 2 | 9 |
Verifier 采样 $β1=2β1=2$,并且 prover 折叠 $P1(y)P1(y)$ 以获得 $P2(z)P2(z)$,
P2(z)=1+4β1=9P2(z)=1+4β1=9
这是一个常数多项式。域 $D2={1,13,16,4}D2={1,13,16,4}$。LDE 中的所有元素都评估为 9,因此不需要表格。
多项式 $P0(x)P0(x)$,$P1(x)P1(x)$ 和 $P2(x)P2(x)$ 的评估分别使用 Merkle 树进行承诺并发送给 verifier。
假设 verifier 在 LDE 中选择索引 4 以检查 FRI 层的正确性。Prover 需要向他发送以下内容:
我们可以看到,对于第一层,prover 传递位置 4 和 12 的值,然后传递 4 和 1(这是 index+|D1|/2index+|D1|/2,其中 |D1||D1| 是 D1D1 中的元素数,但由于 8 超过了最大值,因此我们环绕)。
Verifier 进行以下计算,
u=P0(13)+P0(4)2+β0(P0(13)−P0(4)2×13)u=P0(13)+P0(4)2+β0(P0(13)−P0(4)2×13)
回想一下,除以 $t$ 只是乘以 $t^{-1}t^{-1}$。在 22 的情况下,我们有 $2^{-1}=92^{-1}=9$,因为 $2×9=18≡1(mod17)2×9=18≡1(mod17)$。因此,
u=2−1(4+13)+3×9−1(4−13)u=2−1(4+13)+3×9−1(4−13)
第一项是 00,而第二项是 $48≡14(mod17)48≡14(mod17)$,所以
u=14u=14.
接下来,他检查
u=P1(16)u=P1(16)
两者都是 1414,因此第一层是正确的。
Verifier 继续到下一层。他需要计算
u=P1(16)+P1(1)2+β1(P1(16)−P1(1)2×16)u=P1(16)+P1(1)2+β1(P1(16)−P1(1)2×16)
如果我们进行计算,
u=22+2(92×16)u=22+2(92×16)
但这只是
u=1+(−9)=1+8=9u=1+(−9)=1+8=9
现在,
P2(4)=9=uP2(4)=9=u
因此所有层都已检查。你应该尝试选择一个索引并表明所有计算都匹配。
STARKs 是强大的密码学原语,允许一方证明计算的完整性。为了生成证明,我们获得了程序的执行轨迹,并将轨迹的每一列解释为多项式在“nice”域上的求值。执行轨迹的行通过低次多项式相关联,这决定了约束。当我们用轨迹多项式组合约束多项式时,我们在执行轨迹上强制执行约束。然后我们可以通过它们有效域上的消失多项式来除以它们(强制执行每个约束的地方);如果约束成立,则除法是精确的并产生一个多项式。STARKs 没有证明结果是一个多项式,而是表明结果接近于低次多项式。FRI 随机折叠该函数,在每一步将次数减半;关键是这种折叠保留了与低次多项式的距离。该协议包含两个阶段:承诺,其中证明者将自己绑定到多项式在其相应域上的评估,以及解承诺,其中他生成允许验证者检查计算的证明。在即将发布的文章中,我们将介绍一些优化和示例。
- 原文链接: blog.lambdaclass.com/lam...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!