ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Groth16 vs PLONK:SNARK选型中性能与灵活性如何权衡

Groth16 vs PLONK:SNARK选型中性能与灵活性如何权衡 1. 为什么把 PLONK 和 Groth16 摆在一起而不是跟别的 SNARK 比过去两年里我经常在技术群里看到同一个问题新项目选型到底用 Groth16 还是 PLONK两边各有拥趸争论起来谁都说服不了谁。从我自己的经历来说这个问题的确绕不开。我在隐私交易、zkEVM 和证明聚合相关项目上都踩过坑两种方案都实际部署过。今天这篇不打算带节奏说谁一定更好而是把两者的核心差异、底层逻辑、工程代价放在同一张桌子上让读者自己做判断。先说结论框架Groth16 是性能标杆单证明体积小、验证快但它是电路绑定的一次性设置电路一改整套仪式就得重来。PLONK 是通用性标杆一套参考字符串能复用于任意电路还支持可更新仪式但证明体积和证明者开销都更大。这个性能 vs 灵活性的拉锯正是所有选型纠结的根源。我见过不少团队在选型时只看证明大小和 Gas 费忽略了更隐蔽的环节仪式成本、电路升级频率、递归证明的复杂度、工具链成熟度。这些因素往往在生产环境里才爆发出来一旦上线再想换证明系统代价极高。所以我建议把这篇当作一个系统性的比较框架来看而不是背几个参数就完事。另外说明一下这篇是上篇主要讲两种证明系统的核心算法逻辑、可信设置路线、以及工程层面的宏观差异。至于完整的多项式承诺方案推导、验证方程的逐项拆解、以及具体实现里的 O(1) 验证细节我会在下篇里展开。先把骨架打好后面填肉才不会散。顺便交代一下我的使用背景我主要工作在基于配对的椭圆曲线BN254、BLS12-381 这类上论文会用 libsnark、snarkjs、arkworks 和 plonky2 这一箩筐工具。所以这篇谈的是可以落地的 PLONK 和 Groth16而不是纯理论推演。2. Groth16 的高效秘诀R1CS、QAP 到三个群元素2.1 R1CS 是如何把计算变成一道方程题的几乎所有基于 R1CS 的 SNARK起点都是同一个东西把程序或电路拍扁成一堆约束。你写好的电路逻辑比如某个余额大于转账金额两个输入做了一次椭圆曲线运算最终都会被翻译成一个向量方程组。每个约束长得像这样a, w × b, w c, wa、b、c 是固定系数向量w 是见证向量包含了公开输入和秘密输入。一个约束代表一条规则所有约束叠在一起就组成了 R1CSRank-1 Constraint System。以证明者知道 x 和 y且 x × y z为例。设见证向量是 w (1, x, y, z)那么一个约束可以写成行 a[-1, 0, 0, 0] 这里是占位示意实际系数取决于怎么排 行 b[0, 1, 0, 0] 行 c[0, 0, 1, 0]更标准的构造其实不复杂把 a 取成 [0, 1, 0, 0]b 取成 [0, 0, 1, 0]c 取成 [0, 0, 0, 1] 就能表达左边的乘积等于右边的输出。而约束矩阵 A、B、C 就是把所有这样的行向量堆起来。为什么要这样折腾因为多项式约束比任意程序加起来更容易做密码学承诺。一旦计算被拍成这种整齐的代数结构你就能用高次多项式把它整体编码进去。2.2 QAP 变换逐条校验变成单点抽查得到 R1CS 之后Groth16 并不会逐条约束去证明而是把它们转换成一个 QAP二次算术程序。这里面做了这样一件事把约束矩阵的每一行看作一组点值例如矩阵 A 的第 i 行在横坐标 x i 处对应一个取值。然后对每个矩阵列做拉格朗日插值得到三个多项式 A(x)、B(x)、C(x)。转换之后有一个关键性质原本每个约束都要验证的方程等价于存在一个多项式 H(x)使得下面这个恒等式对所有 x 成立A(x) * B(x) - C(x) Z(x) * H(x)其中 Z(x) 是已知的零多项式在所有插值点 x 1, 2, ..., n 上取值为 0。因为当 x 等于任何一个门编号时等号右边被 Z(x) 乘成 0左边就退化回原来的单条约束。但验证者不可能逐点检查所有点那还不如直接跑电路。所以验证者只抽查一个随机点。这个随机点就是 Fiat-Shamir 变换里常见的那种挑战值通常由承诺值哈希出来。只要多项式度数是有限的两个不同的多项式在某一个点碰巧相等的概率极低这就在计算可靠性上完成了全部检查到单点检查的压缩。这里我要加一条实践提醒QAP 随机点取样的安全性高度依赖 Fiat-Shamir 的实现质量。我在审计第三方代码时见过因为把某些公开输入漏进哈希导致的可伪造攻击。PLONK 和 Groth16 都逃不掉这一类问题不是算法本身的锅是工程实现的锅。2.3 Groth16 的证明为什么能做到三个群元素QAP 转换之后Groth16 用了一个非常紧凑的构造。证明者的输出只有三个元素A一个椭圆曲线群 G1 上的点B一个椭圆曲线群 G2 上的点C一个椭圆曲线群 G1 上的点在验证端验证方程用双线性配对 e: G1 × G2 → GT 来组织。直观解释是A、B、C 本身就是聚合了大量多项式信息后的压缩包。A、B 每一项都是对秘密设置参数做群指数运算后的线性组合C 则是用来平衡方程里多余项的关键部分。为什么 Groth16 能压到 3 个元素这是它针对 QAP 的代数结构做了专门优化。在它之前Pinocchio 协议大约需要 7 个群元素Groth16 通过精心设计的线性组合把所有校验多项式除尽关系的证据揉进同一个点 C 里。你可以把 A、B、C 想象成三个坐标值它们拼起来才能确定一个完整的多项式图像任何一个坐标都不可或缺。这也是为什么 Groth16 被认为在通用模型下的证明长度已经接近最优后续很多工作都是在换安全性假设或改证明风格而不是单纯地压缩长度。实际部署中BN254 曲线上一个 G1 点压缩后是 32 字节左右G2 点约 64 字节所以 Groth16 单证明通常就是 128 字节上下。这个体量成就了它在链上验证场景的统治地位。3. PLONK 的底层逻辑Plonkish 约束、置换论证与通用 SRS3.1 Plonkish 算术化电路是一张可以查重的表格PLONK 最大的不同是从底层算术化就跟 R1CS 分道扬镳了。它不把电路转换成 QAP而是直接定义在一张表上每一行是一个门每一列是一根线。表里通常会有左输入、右输入、输出三列必要时还可以扩展更多列。拿我常用的 Plonkish 结构举例一个乘法门可以写成左输入 右输入 输出 a b c门约束要求 a × b - c 0。这个形式比 R1CS 直白得多工程师一眼就能对上电路图。PLONK 里把这些门约束写成多项式恒等式q_L(x) * w_L(x) q_R(x) * w_R(x) q_O(x) * w_O(x) q_M(x) * w_L(x) * w_R(x) q_C(x) 0其中 q_L、q_R、q_O、q_M、q_C 是选择器多项式它们决定了电路长什么样w_L、w_R、w_O 是见证多项式它们保存了具体输入值。验证者把挑战点代入 x就能一次性确认所有行的门约束。但只有门约束不够。电路中很多约束是这两个位置必须相等比如一个变量被用了三次本质上是同一根线。R1CS 体系里这是靠变量在见证向量里只出现一次引用同一位置实现的但 Plonkish 是表结构每次单独写成一行会产生不同位置。PLONK 为此设计了第二类约束复制约束。3.2 置换论证PLONK 里最硬核的一块复制约束要回答的问题是我怎么在不暴露内容的前提下让验证者相信位置 5 和位置 13 的值相同PLONK 从这里引入了置换论证permutation argument。核心思路很巧妙。给每个位置的变量多加一个标签比如用多项式 ID 将 (位置, 值) 做一个独一无二的编码。把一组需要相等的变量视为同一个等价类先对所有变量做一次公开的洗牌再检查被洗过的表和原表一致。具体实现里PLONK 构造了一个累积乘积多项式 z(x)。它把所有标签编码后的比值一层一层乘进去然后通过一个多项式恒等式检查整体结果。如果所有复制约束都满足累积乘积的最终值会等于一个固定常数通常是 1只要有一个标签对不上乘积就会乱掉。而多项式本身的值是保密的所以验证者看不到具体标签内容只知道整体通过。我最初学置换论证的时候特别绕后来找到一个生活化类比你把一批球贴上编号按顺序放进盒子然后告诉验证者盒子里的球虽然重新排过序但编号集合没变。验证者不能偷看编号只能靠你提供的一个全局校验和来判断。置换论证里的 z(x) 就是这个校验和的密码学版本。这样想会容易得多。这套机制也给 PLONK 带来了一个额外优势想自定义约束时不需要改底层证明框架只要往门类型里加新的选择器表达式就行。zkEVM 这类需要大量自定义指令的场景就是看中这一点才选的 PLONK 系。3.3 PLONK 的证明体量和验证方程PLONK 的证明体量比 Groth16 大是因为它需要承诺更多的多项式。通常在基于配对的实现里证明者要提交三个见证多项式 w_L、w_R、w_O一个累积乘积多项式 z以及三个用来处理除余和打开检查的多项式。加起来大约是 7 个群元素还要附带若干打开值。验证方程也不再是 Groth16 那种一对 e(A, B) 加上零散的配对就行而是要把门约束、置换约束、除余性质整合进一个或少数几个配对等式里。验证者要做多项式的线性组合、计算商多项式在挑战点上的取值再配合若干次配对检查。整个过程比 Groth16 多出不少公开计算这也是链上 Gas 更高的直接原因。PLONK 的协议本身不强制指定某一种多项式承诺方案你可以用 KZG基于配对、FRI基于哈希、或 IPA基于内积。这个概念很关键我们常说的PLONK其实是一个通用框架真正决定证明大小和安全假设的是它底下那层承诺方案。比如 plonky2 就是把 PLONK 的算术化与 FRI 承诺结合起来走完全不用配对的路线。4. 可信设置的路线分歧电路专属仪式 vs 可更新全局仪式4.1 Groth16 的毒废料到底毒在哪用过 snarkjs 的人都知道跑 Groth16 的仪式时会提示你这是可信设置请保管好并销毁秘密。所谓毒废料就是设置阶段生成的几个秘密参数秘密的随机值、QAP 秘密求值点、线性组合偏移量。这些参数全部隐藏在公共参数pk/vk的指数里但要保证安全就必须让原始秘密永远没人知道。只要有人拿到了这些废料他就能伪造任意证明。哪怕你仪式跑了三百个参与者只要最终协调者保留了废料前面所有轮次的安全性全部白搭。这也是 Groth16 社区多年来的心病。更麻烦的是这套公共参数和电路强绑定。你改了电路里的任何一个系数设备的 QAP 多项式就变了原有的公共参数不能复用。你必须为新的电路重新跑一次完整仪式。项目快速迭代阶段每改一次业务逻辑都要拉一帮人重新做仪式非常糟心。4.2 PLONK 的通用可更新 SRS一次仪式到处使用PLONK 把可信设置分成了两层一层是通用参考字符串URS一层是电路专属配置。URS 里只存放与具体电路无关的一组点幂比如某个秘密 tau 的各次幂在 G1、G2 里的群元素。任何电路都可以使用同一套 URS只需要在证明和验证时把自己的选择器多项式乘以对应的系数即可。这一点是质变。这意味着你在项目第一天跑完一次仪式之后无论怎么改电路逻辑都不用重新跑仪式。我在实际项目中体会特别深迭代期可以随改随测改完逻辑立刻重新生成证明不需要上线前再找十个机构开视频会议做联合仪式。更关键的是可更新性。任何一个参与者都可以在已有 URS 的基础上加入自己的秘密随机数把整个公共参数重新洗一遍同时不破坏已存在参数的正确性。只要所有参与者里存在一个诚实的人最终 URS 就是安全的。这个性质让仪式能从集中式高风险变成长期分布式低风险。Polygon 当年做的 powersOfTau 仪式就是想让整个行业共用一套高安全性的全局 URS。4.3 一场仪式背后的工程代价对比下面这组比较来自我的真实经验环节Groth16PLONK公共参数针对单个电路生成全行业通用可复用仪式次数每改一次电路就要重跑一次生成永久复用参与者要求必须信任至少一个诚实方同样但可无限追加轮次废料风险必须在仪式后彻底销毁新增轮次后旧秘密自动失效电路升级影响需要完整重新设置选择器可变SRS 不变以我参与过的一个 DeFi 隐私协议为例早期用 Groth16每次调整电路都要重新协调多家机构参与仪式周期以周为单位。后来换到 PLONK仪式问题彻底消失团队可以把精力全部放到电路正确性上。这种效率提升在早期快速迭代期是决定性的。当然Groth16 的优势也不会被抹掉如果电路定型、不再改动它那近乎极限的证明压缩和极低验证成本让它依然是链上主频段的王者。像一些固定隐私转移逻辑的项目用 Groth16 再合适不过。5. 工程视角的实战权衡证明大小、验证成本、递归友好度5.1 证明大小不是一个孤立的数字先给一张我常用的对比表基于 BN254 曲线、压缩点表示维度Groth16PLONKKZG 承诺证明大小约 128 字节2 个 G1 1 个 G2约 300~600 字节7 个群元素加打开值验证者配对次数约 3 次通常 5~8 次证明者 FFT 次数通常 1~2 轮2~4 轮叠加多个多项式承诺设置类型电路专属通用可更新递归嵌入难度较高较低只看字节数Groth16 几乎碾压。但工程里必须考虑上下文如果你的场景是一个证明被上万个验证者反复验证那么 Groth16 在验证端省下的每次几十微秒很值钱如果你的场景是同一个电路被大量用户各自生成证明且电路频繁升级那么 Groth16 每次升级都要重跑仪式这个隐性成本往往比证明多出来的那几百字节更贵。我在链上做过 Gas 测试Groth16 验证大概在 20 万 Gas 上下PLONK 通常到 30 万以上具体取决于实现和曲线。差距确实存在但对许多应用而言一次交易总成本本来就有几十万 Gas差值不算压倒性。5.2 递归证明隐藏的选型逻辑递归证明即证明的证明是这两年特别火的扩展方向。基础思路是让某个电路内部去验证另一个证明系统的验证算法最终输出一个对聚合后证明的小证明。为什么 PLONK 在递归上更顺手因为 PLONK 的通用 SRS 支持在电路内部构建验证任意 PLONK 证明的子电路且不需要为每一种被验证电路单独准备设置。验证者电路可以在同一个 PLONK 体系内自我嵌套。这在构造递归聚合的时候省了很多事。而 Groth16 也不是不能递归业界早有成熟的循环方案。但问题还是回到它的老毛病如果你想递归验证一个 Groth16 证明你需要为这个特定 Groth16 验证器电路再跑一次可信设置如果被验证的电路变了你的递归验证器电路也得跟着变又得重新跑仪式。在多层递归场景里这是不可接受的复杂度。这不是说 Groth16 不能做递归而是说它做递归时的工程摩擦远大于 PLONK 系。如果你的产品路线图里有证明聚合跨链证明zkEVM这类需求我应该劝你直接往 PLONK 方向走。5.3 实际部署中的一个坑多项式承诺选择带来的性能方差PLONK 框架下不同承诺方案的性能差异非常大。KZG 承诺基于配对和可信设置证明小验证快但依赖二次方指数预计算FRI 承诺基于哈希不需要可信设置但证明大一个量级验证端也要跑很多次哈希计算。我在一个 Layer 2 项目里遇到过这样的情况一开始用 plonky2FRI 承诺证明生成速度确实快但最终证明有两百多 KB在链上做聚合证明时 Gas 昂贵到不可接受。后来把核心聚合层换成 KZG 承诺的 PLONK 实现证明缩到一千字节以内验证 Gas 大幅下降。代价是需要重新评估可信设置的风险。这个案例说明PLONK 的通用性并不意味着性能自动令人满意承诺层的选择往往比 PLONK 本身更影响指标。6. 选型建议我从项目中总结出的判断框架看完上面这些比较我相信你已经明白没有一劳永逸的答案只有适合当前约束的方案。总结出我认为最实用的判断框架电路会不会变会变就选 PLONK。不会变、长期冻结再看别的指标。链上验证成本是不是最敏感如果每次验证交易都有严格 Gas 预算上限Groth16 更稳。要不要做递归或聚合要的话优先 PLONK 系不然后期你会被 Groth16 的仪式副作用反复折磨。团队的工具链熟悉度如何Groth16 因为出道时间更久在 libsnark、snarkjs 生态里资料最多PLONK 的近两年工具链也已经非常成熟且大量新项目都在向它汇聚。按我个人的经验2021 年到 2023 年我主导选型的几个项目大部分最终都选择了 PLONK 系不是因为 Grove16 不行而是因为我们的电路迭代频率高、且需要递归。反过来一个固定逻辑的小型隐私支付模块我到现在依然会推荐 Groth16因为它简单、可靠、省 Gas。最后分享一个很多人没意识到的点证明系统的选择不只是一个技术决策还是一个治理决策。Groth16 要求你相信仪式组织方在仪式后销毁了废料这在公司内部或封闭联盟中容易做到PLONK 的公开可更新仪式则更适合面向公众的项目它让安全性的最终保障落到至少一个诚实的参与者上。如果你的项目未来要上开放的 Layer 2 网络这点尤其值得放在心上。这篇先把比较框架讲清楚。下篇我会从多项式承诺层切入逐项拆 PLONK 的证明验证方程并实测同一电路下 Groth16 与 PLONK 的 Gas 和时延数据到时见。
返回列表