SNARK性能及安全——Prover篇

1. 引言

SNARK (Succinct Non-interactive Arguments of Knowledge) 是实现:

  • 1)区块链扩容(如L2 rollups)
  • 2)隐私

的重要密码学原语。

SNARK中有2个重要角色:

  • 1)Prover P;
  • 2)Verifier V。

同时本文将SNARK Prover 工作分为前后端:

  • 1)前端:将计算程序转换为电路;
  • 2)后端:基于电路使用某种SNARK方案生成证明。

关于SNARK的更多知识,可参看:

SNARKs的verification cost根据具体的实现方案,会各有不同,但是普遍认为其verification cost不错。如:

  • Plonk proof 在以太坊上的验证开销约为29万gas
  • Groth16 proof的验证开销约为23万gas;
  • 而StarkWare的proof验证开销约为500万gas。

SNARK可用于除区块链之外的其它领域,如:

由于SNARK的验证开销较低,影响SNARK可用性的决定性因素在于SNARK prover P的开销。

2. SNARK是如何部署的?

SNARK部署通常为:

  • 开发者编写a computer program ψ \psi ψ,Prover P声称其知道某输入数据 w w w w w w表示witness),并检查了 w w w是有效的。

如在rollups中,该program为检查 w w w中的所有交易都已有效签名且account balance不会为负值等等。然后program ψ \psi ψ作为某SNARK frontend的输入被编译为某种有利于SNARK技术应用的格式。这种SNARK友好的格式称为intermediate representation(IR)。

通常,IR为某种与 ψ \psi ψ等价的circuit-satisfiability实例。即有某circuit C C C,其输入为数据 w w w 以及 一些称为“non-deterministic advice”的额外输入,然后基于 w w w运行 ψ \psi ψ。advice输入帮助 C C C运行 ψ \psi ψ,同时keeping C C C small。如,每次 ψ \psi ψ计算 x / y x/y x/y,将商 q q q和余数 r r r作为advice提供给 C C C C C C仅需要简单的检查 x = q y + r x=qy+r x=qy+r即可。相对于重新做除法运算,该检查的开销要没那么贵。

最后,对 C C C应用circuit-satisfiability SNARK,这称为SNARK backend。对于一些高度结构化的问题,如:

已知的SNARK可以避免这种frontend/backend范式,从而实现更快的Prover。但这篇文章的重点是通用SNARK。

同时,将发现SNARK backend的Prover cost将随 C C C size增长。保持 C C C small是有调整的,因circuit是表示计算的特别有限的格式。circuit中包含了gates,这些gates通过wires连接。每个gate g g g基于 某些输入值 通过 某个很简单的函数运算 获得 某些输出值。 g g g这些输出值 又通过wires 作为后续gate的输入值。

3. SNARK扩容:用时究竟多少?

关键在于,相比于基于data重新执行 ψ \psi ψ,SNARK prover的用时要多多少呢?
答案为:

  • “SNARK prover开销” 减去 “direct witness checking开销”。

所谓“direct witness checking开销”是指:P将 w w w发送给V,V通过执行 ψ \psi ψ来检查 w w w的有效性。

可将"SNARK prover开销"分解为:

  • SNARK frontend开销:若逐gate对circuit C C C进行evaluate 的开销 为 运行 ψ \psi ψ F F F倍,则称frontend开销为 F F F
  • SNARK backend开销:若对 C C C应用backend prover的开销 为 逐gate对circuit C C C进行evaluate开销 的 B B B倍,则称backend开销为 B B B

总的"SNARK prover开销"为 F ⋅ B F\cdot B FB,即使 F , B F,B F,B各自为适度的,但乘积之后的开销也可能是相当大的。

事实上, F , B F,B F,B都可能约为1000或更大,这意味着总的prover开销 相对于 “direct witness checking” 将是百万级甚至千万级的。笔记本电脑上运行仅需1秒的程序,SNARK prover在笔记本电脑上使用单线程的情况下将需要数十或数百天。幸运的是,SNARK prover的工作看具体的方案,通常可不同程度的并行化。

总之,若想在当今应用中使用SNARK,需要以下三者之一成立:

  • 1)“Direct witness checking”在笔记本上用时小于1秒;
  • 2)“Direct witness checking”特别适用于电路中的表示,因此前端开销较小;
  • 3)愿意等数天待SNARK prover完成;或(和)为大型并行计算资源付费。

本文接下来将解释前端和后端开销的来源,以及不同SNARK方案相应的开销评估。然后将讨论改进的前景。

4. SNARK前后端分离

完全将前后端分离是有调整的,因不同的后端支持不同类型的circuits,因此,前端会根据其交互的后端而不同。

SNARK后端支持所谓的arithmetic circuit,即该circuit的输入为某有限域的元素,且circuit的gate会对2个域元素进行加法和乘法运算。大致对应于本质上是代数的直线计算机程序(无分支、条件语句等),也就是说,它们的原始数据类型是域元素。

大多数SNARK后端都支持名为Rank-1 Constraint Satisfaction (R1CS)的通用arithmetic circuit。除了Groth16及其前身之外,这些SNARK也可用于支持其他IRs。如StarkWare使用名为“Algebraic Intermediate Representation (AIR)”,一种类似于PlonK和其它后端所支持从名为PlonKish arithmetization。某些后端支持更通用的IR可减少生成这些IR所需的前段开销。

根据所支持的有限域,后端也会有所不同。

4.1 SNARK前端的各种方法

某些非常特别的计算机程序天然对应arithmetic circuit,如某程序对某域进行直接的矩阵乘法运算。但大多数计算机诚实既不是straight-line的,也不是algebraic的,它们通常包含了条件声明、整数除法运算、浮点数运算这些并不天然对应有限域的运算 等等。此时,前端开销将是大量的。

一种流行的前端方法是,按单一CPU逐步执行的方式来生成电路,也称为virtual machine(VM)。前端设计者会定义一组类似于真实计算机处理器的汇编指令的“基本操作”(“primitive operations”)。想要使用前端的开发人员要么直接用汇编语言编写“witness-checking programs”,要么用Solidity等高级语言编写,然后让他们的程序自动编译成汇编代码。

如:

  • 1)StarkWare的Cairo是一种功能非常有限的汇编语言,其汇编指令仅支持基于有限域的加法和乘法、函数调用、对某immutable(即write-once)memory的读写。
    Cairo VM为von Neumann架构,即意味着前端生成的circuit,以某Cairo program为public input,并基于witness “运行”该程序。Cairo语言为图灵完备的——除了其指令集有限,Cairo可模拟更标准的架构,尽管可能更昂贵。
    Cairo前端将 执行 T T T个primitive指令的Cairo程序 转换为 名为“degree-2 AIR with T T T rows and about 50 columns”。其具体含义见:ethSTARK Documentation – Version 1.1。但是只要考虑SNARK prover足够快,每个Cairo CPU的 T T T steps对应电路中的50到100个gates。

  • 2)RISC Zero的方案与Cairo类似,但其virtual machine为RISC-V架构。RISC-V架构为一种开源架构,具有丰富的软件生态并广受欢迎。由于其指令集非常简单,设计高效的SNARK前端 相对易处理(至少相对于复杂的架构,如x86或ARM)。截止2022年5月,RISC Zero是 图灵程序——ZK7: RISC Zero: Encoding Von-Neumann Architectures in Zero-Knowledge Proof Systems - Risc0的,可将 T T T primitive RISC-V指令 执行转换为 “degree-5 AIRs with 3T rows and 160 columns”。对于RISC-V CPU的每个step,对应的circuit中至少有500 gates。未来将对其进行改进。

不同的zkEVM项目(如zkSync 2.0,Scroll,Polygon的zkEVM)将虚拟机视为以太坊虚拟机。将每个EVM指令 转换为 等价“gadget” (即针对该指令的某优化电路)的过程,相比于简单的Cairo和RISC-V架构,这种转换的开销将更大。为此,某些zkEVM项目(见Vitalik The different types of ZK-EVMs),并不直接实现EVM指令集,而是将高层Solidity程序 变意思为其它 汇编语言,然后将汇编语言转换为电路。这些项目的性能效果尚待确定。

RISC-V和Cairo这样的“CPU仿真器”,可生成单一电路 来 处理 相关汇编语言中的所有程序。
备选方案有“RISC-like”方法:为不同的程序 生成 不同的电路。这样可为某程序生成更小的电路,特别是当程序在每个timestep的汇编指令不依赖程序输入时。如,对于naive矩阵乘法这类straight-line程序,可能可完全避免前端开销。

但是ASIC方法仍然有局限,迄今为止,其无法支持具有固定上限的循环计算。

前端开销的最后一部分来自于,所有的SNARK都是用基于有限域的circuit运算。笔记本上的CPU可通过单一机器指令来对2个整数求和或求积。若前端输出的circuit基于的域具有足够大的characteristic,则可通过相应的域运算来模仿加法或乘法运算。但是,基于real CPU来做域运算通常需要很多机器指令(尽管当今有某些处理器原生支持特定域)。

某些SNARK后端所采用的域选择很灵活。如,若后端使用密码学群 G G G,则其circuit的域必须与 G G G群中的元素数量相匹配,这就是限制。此外,并不是所有的域都支持实用FFT算法。

当前的情况为:

某些项目会选择某些可快速运算的域,如Plonky2等项目使用了characteristic为 2 64 − 2 32 + 1 2^{64}-2^{32}+1 264232+1的域(即 Goldilocks域),因该域内的计算可比其他less structured域快数倍。但是,使用如此小的characteristic可能会为通过域操作高效表达整数运算带来风险。(如,32-bit无符号整数 与 任意大于 2 32 2^{32} 232 的整数相乘时,结果将大于该field characteristic。因此,选择该域意味着仅支持32-bit number运算。)

但不管怎样,对于当今所有流行的SNARK来说,要实现128位的安全性(在不显著增加验证成本的情况下),它们必须在大于 2 128 2^{128} 2128的域上工作。据我所知,这意味着每个域操作将需要至少10个64位机器乘法,以及相当多的加法和按位操作。因此,对于需要在有限域上工作的电路,应考虑至少一个数量级的前端开销。

总而言之,使用虚拟机抽象的现有前端在虚拟机的每一步生成100x到1000x个gates的电路,对于更复杂的虚拟机可能更多。除此之外,有限域运算至少比现代处理器上的类似指令慢10倍(如果处理器内置了对某些域的支持,则可能会有例外)。“ASIC前端方法”可能会减少一些开销,但目前其支持的程序种类有限。

5. SNARK后端的瓶颈是什么?

SNARKs for circuit-satisfiability通常设计为:

的结合。大多数情况下,Prover的具体瓶颈在polynomial commitment scheme。特别的,这些SNARK的prover会对某个或某些degree(最多)为 ∣ C ∣ |C| C的多项式进行密码学承诺,其中 ∣ C ∣ |C| C表示circuit C C C中的gate数。

继而,polynomial commitment scheme中的具体拼接在于:

  • 所选用的具体多项式承诺方案;
  • 以及 circuit size。

但是polynomial commitment scheme中通常有如下3种操作之一:

  • 1)FFT计算
  • 2)密码学群内的exponentiation计算
  • 3)Merkle-hashing计算:通常仅当circuit为small时,Merkle-hashing计算才是瓶颈,因此本文后续不再讨论该情况。

5.1 基于discrete log的多项式承诺方案

基于某密码学群 G G G内的discrete logarithm problem难度的多项式承诺方案有:

Prover需要对多项式的系数计算Pedersen vector commitment。这包含了size等于多项式degree的multi-exponentiation运算。在SNARK中,多项式degree通常为circuit C C C的size ∣ C ∣ |C| C

直接计算的话,size为 ∣ C ∣ |C| C的multi-exponentiation需要约 1.5 ⋅ ∣ C ∣ ⋅ log ⁡ ∣ G ∣ ≈ 400 ⋅ ∣ C ∣ 1.5\cdot |C|\cdot \log |G|\approx 400\cdot |C| 1.5ClogG400C次群运算,其中 ∣ G ∣ |G| G表示群 G G G内的元素数。但是,Pippenger算法可将其reduce大约 log ⁡ ∣ C ∣ \log |C| logC因子。对于大电路(也就是说, ∣ C ∣ ≥ 2 25 |C|\geq 2^{25} C225), log ⁡ ∣ C ∣ \log |C| logC因子具体为25或更多,即,对于搭电路,Pedersen vector commitment计算需约略多于 10 ⋅ ∣ C ∣ 10\cdot |C| 10C次群操作。单个群操作要比单个慢约10x倍。采用这种多项式承诺机制的SNARK prover,需要约 100 ⋅ ∣ C ∣ 100\cdot |C| 100C次域操作(昂贵的)。

除了以上100x倍因子的放大之外,现有的SNARK还有额外的开销,如:

  • 1)Spartan prover:采用Hyrax多项式承诺,需要做 ∣ C ∣ 1 / 2 |C|^{1/2} C1/2次multi-exponentiations运算,每个multi-exponentiations运算size为 ∣ C ∣ 1 / 2 |C|^{1/2} C1/2,从而将Pippenger算法的加速效果减半。
  • 2)Groth16 prover:需基于pairing-friendly group,该群内操作 要比 非pairing-friendly群操作 慢至少2x倍。同时,Prover还需要做3次而不是1次multi-exponentiations运算。因此,相比于上面评估的 100 ⋅ ∣ C ∣ 100\cdot |C| 100C,实际会更慢,还需要至少附加一个 6 6 6因子。
  • 3)MarlinPlonK prover:也需要pairing,且需要对多于3个多项式进行commit。
  • 4)而对于任意使用Bulletproofs的SNARK(如Halo2使用的为PlonK+Bulletproofs,而不是PlonK+KZG) prover:在承诺机制的“opening”阶段,需计算多达logarithmically次multi-exponentiations运算,这在很大程度上消除了任何Pippenger加速效果。

总之,现有使用Pedersen vector commitment的SNARK后端开销至少要200x倍甚至多于1000x倍。

5.2 其它多项式承诺

使用其它多项式承诺机制(如FRILigero-commit:2017年论文 Ligero: Lightweight Sublinear Arguments Without a Trusted Setup)的SNARK,其prover瓶颈在于需要做large FFT计算。StarkWare部署的prover每列至少需要做2次FFT计算,每列的长度在 16 ⋅ T 16\cdot T 16T 32 ⋅ T 32\cdot T 32T之间。其中常量 16 16 16 32 32 32取决于StarkWare设置的FRI内部参数,可减少该常量值,但将增加验证开销。

乐观地是,长度 32 ⋅ T 32\cdot T 32T的FFT约需 64 ⋅ T ⋅ log ⁡ ( 32 T ) 64\cdot T\cdot \log(32T) 64Tlog(32T)次field multiplication。这意味着即使选择相对小的 T T T(如, T ≥ 2 20 T\geq 2^{20} T220),每列所需的field操作次数最少为 64 ⋅ 25 ⋅ T = 1600 ⋅ T 64\cdot 25\cdot T=1600\cdot T 6425T=1600T,因此后端开销至少为千倍级。此外,large FFT更大的瓶颈可能来自于内存带宽而不是域操作。

在某些上下文,执行large FFT的SNARK的后端开销,可借助proof aggregation技术进行缓解。如,对于rollups,这意味着P(rollup service)将a big batch of transactions 切分为10个更小的batches。对于每个small batch i i i,P会为该batch的有效性生成SNARK证明 π i \pi_i πi。但P并不将这些证明提交到以太坊,因为这意味着近10倍的gas开销。相反,会再次应用SNARK,为“P knows π 1 , ⋯   , π 10 \pi_1,\cdots,\pi_{10} π1,,π10”生成证明 π \pi π。P声称知道的witness为该10个证明 π 1 , ⋯   , π 10 \pi_1,\cdots,\pi_{10} π1,,π10,且direct witness checking为 对每个证明应用SNARK验证流程。然后将证明 π \pi π提交到以太坊。

在类似FRI和Ligero-commit的多项式承诺机制中,P time 和 V cost 之间存在强烈的紧张关系,内部参数充当一个旋钮,可以相互权衡。由于 π 1 , ⋯   , π 10 \pi_1,\cdots,\pi_{10} π1,,π10不提交到以太坊,因此可将旋钮调整,使得生成证明更快,证明更大。仅在最后一次应用SNARK aggregate π 1 , ⋯   , π 10 \pi_1,\cdots,\pi_{10} π1,,π10为单一证明 π \pi π,使得其为small proof。

StarkWare计划近期部署proof aggregation,参见"Recursive STARKs with Cairo" (Avihu Levy & Shahar Papini)
Plonky2也关注proof aggregation。

6. SNARK扩容的其他瓶颈?

本文主要关注prover time,但其它prover开销也可能是扩容瓶颈。如,很多SNARK后端的prover需为circuit C C C中的每个gate存储多个field elements,这样的space cost也很大。如,在笔记本上运行为1秒的程序 ψ \psi ψ,在中等处理器上可能需要执行10亿次primitive操作。正如我们所看到的,一般来说,我们应该期望 C C C在每次操作中需要超过100个gates。这意味着1000亿个gate,具体取决于SNARK,可能意味着P需数十或数百TB的空间。

另一个例子,很多流行SNARKs(如PlonK,Marlin,Groth16)需要复杂的“trusted setup ceremony”来生成结构化的“proving key”——必须由Prover存储。如:

这些大的ceremony生成的proving key支持的约 2 28 ≈ 250 2^{28}\approx 250 228250 million gates(2.5亿个gate)的circuit。相应的proving key size为几十GB。

若proof aggregation可行,这些瓶颈可大幅缓解。

7. 展望:更多可扩展SNARK的前景

前端和后端开销都可以是三个数量级或更多。我们能预期在不久的将来这些数字会大幅下降吗?
答案是肯定的:

  • 1)首先,当今最快的后端(如Spartan何Brakedown,以及其他 sum-check protocol + 多项式承诺的SNARK),都具有large proofs——通常proof size为circuit size的平方根,使得人们实际上并不使用如此大的proof。未来,期望通过深度为1的small-proof SNARK组合,来有效降低proof size和verifier time。类似proof aggregation,这意味着Prover将首先生成具有“fast-prover, large-proof”的SNARK证明 π \pi π,但并不将 π \pi π直接发送给V。相反,P将使用某small-proof SNARK来生成proof π ′ \pi' π,以证明其知道 π \pi π,并将 π ′ \pi' π发送给V。这可以将当前流行的SNARK后端开销削减一个数量级。
  • 2)借助硬件加速。通常GPU速度比CPU快10倍,而ASIC又比GPU快10倍。但是需考虑以下三个前提:
    • 2.1)large FFT的瓶颈可能在内存带宽而不是field操作,因此,需执行FFT的SNARK受限于特定的硬件;
    • 2.2)本文重点关注多项式承诺瓶颈,很多SNARK prover还需做一些相对没那么昂贵的操作,解决了多项式承诺瓶颈之后(如discrete-log-based SNARKs的multi-exponentiation加速,详细见2022年论文PipeMSM: Hardware Acceleration for Multi-Scalar Multiplication),这些相对没那么昂贵的操作将成为新的不易解决的瓶颈。(如Groth16,Marlin,PlonK等SNARK方案中,Prover除需做FFT计算之外,还需做multi-exponentiation计算)。
    • 2.3)寻找SNARK的最有效域和椭圆曲线可能会持续发展一段时间,这可能会对基于ASIC的prover加速带来挑战。
  • 3)在前端方面,我们可能会越来越多地发现,Cairo、RISCZero、zkEVMs等项目的“CPU仿真器”方法实际上可以很好地适应CPU指令集的复杂性(在性能方面)。事实上,这正是各种zkEVM项目的希望所在。这可能意味着,虽然前端开销保持三个数量级或更多,但前端能够支持越来越符合实际CPU架构的VM。一个相反的担忧是,随着实现越来越复杂指令的hand-coded gadgets激增,前端可能会变得复杂和难以审计。形式化验证方法可能在解决这一问题方面发挥重要作用。
  • 4)最后,至少在区块链应用程序中,我们可能会发现,大多数智能合约主要使用简单、SNARK友好的指令。这可以在实践中实现较低的前端开销,同时保持通用性和改进开发人员体验,这与支持高级编程语言如Solidity和丰富的指令集(包括EVM的指令集)有关。

参考资料

[1] a16z团队2022年8月博客 Measuring SNARK performance: Frontends, backends, and the future
[2] a16z团队2022年9月博客 SNARK security and performance
[3] twitter SNARK security

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/126890596