前序博客见:
本文重点关注Polygon zkEVM的架构设计。
本节,借助递归、聚合和组合,提供具体的blocks和steps来证明Polygon zkEVM中a batch of transactions(或多个batches)的正确执行。
如之前所属,生成proof分为2个阶段:
proving阶段总体架构流程见下图:
注意,首个STARK会生成big proof π b a t c h \pi_{batch} πbatch,该big proof中包含大量的多项式,因此其attached FRI使用low blowup factor,因此:
1)Compression Stage:对每个batch proof π b a t c h \pi_{batch} πbatch做压缩变成 π c 12 a \pi_{c12a} πc12a,其目的是:
2)Normalization Stage:需要该Normalization Stage的原因在于:
因此Normalization Stage负责将 π b a t c h \pi_{batch} πbatch Verifier circuit中的constant root转换public,即 π b a t c h \pi_{batch} πbatch经Normalization Stage之后,变为了 π r e c 1 \pi_{rec1} πrec1, π r e c 1 \pi_{rec1} πrec1的Verifier circuit中的constant root是public的。该step使得每个aggregator Verifiers和normalization Verifiers是完全相同的,从而支持通过递归实现聚合:
3)Aggregation Stage:负责将多个batches proofs (实际是经Normalization Stage之后的 π r e c 1 \pi_{rec1} πrec1)聚合为 一个proof——该proof可一次性证明所有batches proofs。
不过,Aggregation Stage需设计为,支持已聚合proofs π r e c 2 \pi_{rec2} πrec2,或仅仅是已压缩和标准化proofs π r e c 1 \pi_{rec1} πrec1。
实际实现方式是构建proof组成的二进制树,依次两两聚合。
可聚合多次,直到剩下一个proof。为此,需创建一个可聚合2个Verifiers的电路(recursive2 Prover),其输入可为:
4)Final Stage:为递归流程的最后一个STARK step,负责在一个完全不同的有限域内验证 π r e c 2 \pi_{rec2} πrec2 proof。Polygon zkEVM中该完全不同的有限域,是指:bn128椭圆曲线。更准确来说,就是:
这么做的原因在于,Final Stage之后的流程中,会基于bn128曲线来生成一个SNARK Groth16 proof。
Final Stage与之前的stage类似,会为 π r e c 2 \pi_{rec2} πrec2实例化一个Verifier circuit,只不过,需提供2个constants roots(分别对应前一step所聚合的proof的constant root)。
5)SNARK Stage:整个流程的最后一步,旨在为 验证前面的 π r e c f \pi_{recf} πrecf proof 生成Groth16 proof π g r o t h 16 \pi_{groth16} πgroth16。事实上,可将Groth16替换为任何其它SNARK proof。此处选择SNARK的目的在于:
将 π g r o t h 16 \pi_{groth16} πgroth16 proof发送给Verifier验证。
需注意的是,会将整个public inputs集合 作为 整个递归流程中每个proof的输入。整个public inputs集合为:【详情见 Polygon zkEVM bridge技术文档 和 Polygon zkEVM Trustless L2 State Management 技术文档】
setup阶段为预处理阶段,会创建声称proofs所需的所有artifacts——包括:
rom.json
。ROM程序中包含executor运行的指令,以生成zkEVM的execution trace。zkevm.pil
。rom.json
、zkevm.starkstruct
、zkevm.pil
,输出为rom.const
。之前也提到过,setup阶段无需committed polynomials,所以此时也不需要运行zkEVM executor来生成committed polynomials。rom.const
(constant polynomials的evaluations值),输出为:
zkevm.consttree
。zkevm.consttree
的root zkevm.verkey
(constRoot)。该root值为对计算中所有固定参数的密码学总结,为哈希值。zkevm.starkstruct
和zkevm.pil
,输出为zkEVM.starkinfo
——为自动生成“验证zkEVM STARK”电路所需的starkInfo。Polygon zkEVM方案在本build阶段时,设置:
生成proofs需用到上图灰色的artifacts(zkevm.pil
、rom.const
、zkEVM.starkinfo
、zkevm.consttree
)。(详细的proof生成细节见4.3节)
Setup S2C for the zkEVM STARK——Compression Stage part1的目的在于:
1)pil2circom 流程:会向名为stark_verifier.circom.ejs
的EJS模板中,注入 验证zkEVM STARK 所需的所有信息。其输入有:
zkevm.pil
:以获取多项式名zkevm.starkinfo
:定义了blowup factor、query次数、以及FRI验证流程的stepszkevm.verkey
(constRoot):其中的constRoot用于自动生成Circom电路。其输出为:Circom文件zkevm.verifier.circom
。
2)compile circom 流程:Circom文件zkevm.verifier.circom
编译为 R1CS约束系统 zkevm.verifier.r1cs
——该约束将后续用于生成下一个proof的PIL和constant polynomials。
除此之外,compile circom 流程还会输出:
zkevm.verifier.witnesscalc
:为witness calculator程序,上图中将其标记为灰色,是因为需将其用于proof生成。Compression Stage的目的是降低proof size,因此,需将build时的“blowup factor 2和query次数128” 修改为 “blowup factor 4和query次数64”,该信息见proverjs repo中的c12a.starkstruct
文件。
验证zkEVM STARK的电路称为zkevm.verifier
Circom电路(或c12a
PIL电路)。原因在于:
zkevm.verifier
Circom电路 的电路为PIL电路c12a
,该PIL电路为PlonKish电路,具有定制门和12个多项式。1)c12a setup 流程:对应名为compressor12_setup的服务。
其输入为:
zkevm.verifier.r1cs
输出有:
c12a.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。c12a.const
:为定义c12a.pil
的所有constant polynomials。c12a.exec
:为helper文件。包含了 shuffle所有witness值 所需的所有规则,后续会计算到execution trace的相应位置中。c12a.const
中定义的connections,一起,对于honest Prover,只要前一电路有效,则新生成的execution trace也是有效的。2)Build c12a constants tree 流程:
输入有:
c12a.const
:c12a.starkstruct
:具有所有FRI相关参数(其中“blowup factor为4和query次数为64”)输出有:
c12a.constTree
:为constants Merkle treec12a.verkey
(constRoot):为constants Merkle tree的root3)generate c12a strakInfo 流程:对应generate_starkinfo服务。
输入有:
c12a.starkstruct
:c12a.pil
:输出为:c12a.strarkinfo
。
截止目前,有验证首个big STARK proof π b a t c h \pi_{batch} πbatch的STARK proof π c 12 a \pi_{c12a} πc12a。
接下来的思路,就是为 验证 π c 12 a \pi_{c12a} πc12a 生成Circom电路,来模拟FRI验证流程。
其中:
1)c12a generate circom 流程:输入有之前的c12a.pil
文件、c12a.starkInfo
文件和constant root c12a.verkey
(constRoot),与之前类似,通过向stark_verifier.circom.ejs模板注入来生成verifier circuit c12a.verifier.circom
。
2)recursive1 generate circom 流程:为实现normalization,需要修改verifier circuit c12a.verifier.circom
,使得constant root为public input。此时还未聚合,此处实际并未用到该constant root。不过让constant root称为public input,对于后续Aggregation Stage至关重要。因为Aggregation Stage中前一电路计算中所有constants,都必须作为public inputs。
其输入为verifier circuit c12a.verifier.circom
,输出为recursive1.circom
(即,将verifier circuit c12a.verifier.circom
实例化在 recursive1.circom
中,连接所有所需的wires,并将constant root放入publics set中)。
3)compile circom 流程:将circom文件recursive1.circom
编译为:
recursive1.r1cs
recursive1.witnesscalc
这2个输出后续用于构建和注入下一execution trace。
1)recursive1 setup 流程:
其输入为:
recursive1.r1cs
:前一验证电路的R1CS描述输出有:
recursive1.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursive1.const
:定义recursive1.pil
的所有constant polynomials的值。recursive1.exec
:为helper文件,提供了execution trace中相应位置witness值。2)generate recursive1 starkInfo 流程:
其输入有:
recursive1.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursive.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【注意,此时的blowup factor 为 2 4 = 16 2^4=16 24=16,query次数为32。】输出有:
recursive1.starkinfo
:通过generate_starkinfo 服务生成。3)build recursive1 constants tree 流程:
其输入有:
recursive1.const
:定义recursive1.pil
的所有constant polynomials的值。recursive.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【注意,此时的blowup factor 为 2 4 = 16 2^4=16 24=16,query次数为32。】输出有:
recursive1.consttree
:为constants Merkle tree。recursive1.verkey
(constRoot):为constants Merkle tree的root。目前为止,已有STARK proof π r e c 1 \pi_{rec1} πrec1来验证proof π c 12 a \pi_{c12a} πc12a。之前“4.2.4 Setup S2C for recursive1——Normalization Stage part1” 中,通过模拟验证 π r e c 1 \pi_{rec1} πrec1中的FRI验证流程 来生成Circom电路。本节采用类似的方式:
其中:
1)recursive1 generate circom 流程:输入有之前的recursive1.pil
文件、recursive1.starkInfo
文件和constant root recursive1.verkey
(constRoot),与之前类似,通过向stark_verifier.circom.ejs模板注入来生成verifier circuit recursive1.verifier.circom
。
2)recursive2 generate circom 流程:不过与之前“4.2.4 Setup S2C for recursive1——Normalization Stage part1” 不同之处在于:
在stark_verifier.circom.ejs模板生成verifier circuit recursive1.verifier.circom
之后,还需要使用另一模板来创建聚合2个Verifiers的Cirom电路。
之前“4.2.4 Setup S2C for recursive1——Normalization Stage part1” 中的constant root是通过外部文件传入硬编码在电路中的——这样做的目标是为实现normalization目的,以 支持在本步骤中所验证的每个proof具有相同的格式,从而使得recursion成为可能。
recursive2 generate circom 流程 输出的recursive2.circom
电路具有2个Verifiers,有2个multiplexors来实际绝对每个Verifier的格式:
若upper proof为 π r e c 2 \pi_{rec2} πrec2格式:则Multiplexor不会将constant root rootC 提供给Verifier A进行硬编码,因为Verifier A应通过前一电路的public input来获取。
若lower proof为 π r e c 1 \pi_{rec1} πrec1格式:则在做相应模板注入时,Multiplexor会将constant root rootC 提供给Verifier B进行硬编码。
3)compile circom 流程:通过运行名为genrecursive的不同脚本,将circom文件recursive2.circom
编译为:
recursive2.r1cs
recursive2.witnesscalc
这2个输出后续用于构建和注入下一execution trace。
1)recursive2 setup 流程:
其输入为:
recursive2.r1cs
:前一验证电路的R1CS描述输出有:
recursive2.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursive2.const
:定义recursive2.pil
的所有constant polynomials的值。recursive2.exec
:为helper文件,提供了execution trace中相应位置witness值。2)generate recursive2 starkInfo 流程:
其输入有:
recursive2.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursive.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【注意,此时的blowup factor 为 2 4 = 16 2^4=16 24=16,query次数为32。即,Aggregation Stage与Normalization Stage具有完全相同的参数。】输出有:
recursive2.starkinfo
:通过generate_starkinfo 服务生成。3)build recursive2 constants tree 流程:
其输入有:
recursive2.const
:定义recursive2.pil
的所有constant polynomials的值。recursive.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【注意,此时的blowup factor 为 2 4 = 16 2^4=16 24=16,query次数为32。即,Aggregation Stage与Normalization Stage具有完全相同的参数。】输出有:
recursive2.consttree
:为constants Merkle tree。recursive2.verkey
(constRoot):为constants Merkle tree的root。截止目前,有STARK proof π r e c 2 \pi_{rec2} πrec2 来验证 另一STARK proof π r e c 2 \pi_{rec2} πrec2(或 π r e c 1 \pi_{rec1} πrec1)。
接下来的思路,就是为 验证 π r e c 2 \pi_{rec2} πrec2(或 π r e c 1 \pi_{rec1} πrec1,若实际未聚合) 生成Circom电路,来模拟FRI验证流程。
其中:
1)recursive2 generate circom 流程:输入有之前的recursive2.pil
文件、recursive2.starkInfo
文件和constant root recursive2.verkey
(constRoot),与之前类似,通过向stark_verifier.circom.ejs模板注入来生成verifier circuit recursive2.verifier.circom
。
2)recursive2 generate circom 流程:输入有 recursive2.verifier.circom
,以及前2个proof的constant roots recursive2_a.verkey.constRoot
和 recursive2_b.verkey.constRoot
,通过向stark_verifier.circom.ejs模板注入获得recursivef.circom
。
3)compile circom 流程:通过运行另一名为genrecursivef的脚本,将circom文件recursivef.circom
编译为:
recursivef.r1cs
recursivef.witnesscalc
这2个输出后续用于构建和注入下一execution trace。
1)recursivef setup 流程:
其输入为:
recursivef.r1cs
:前一验证电路的R1CS描述输出有:
recursivef.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursivef.const
:定义recursivef.pil
的所有constant polynomials的值。recursivef.exec
:为helper文件,提供了execution trace中相应位置witness值。2)generate recursivef starkInfo 流程:运行generate_starkinfo服务。
其输入有:
recursivef.pil
:为machine-like construction,其正确执行,等价为,前一电路的有效性。recursivef.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【输出有:
recursivef.starkinfo
:通过generate_starkinfo 服务生成。3)build recursivef constants tree 流程:
其输入有:
recursivef.const
:定义recursive2.pil
的所有constant polynomials的值。recursivef.starkstruct
:存储了本步骤要用到的所有FRI相关参数。【输出有:
recursivef.consttree
:为constants Merkle tree。recursivef.verkey
(constRoot):为constants Merkle tree的root。截止目前,有STARK proof π r e c f \pi_{recf} πrecf 来验证 另一STARK proof π r e c 2 \pi_{rec2} πrec2。
接下来的思路,就是为 验证 π r e c f \pi_{recf} πrecf 生成Circom电路,来模拟FRI验证流程。
其中:
1)recursivef generate circom 流程:输入有之前的recursivef.pil
文件、recursivef.starkInfo
文件和constant root recursivef.verkey
(constRoot),与之前类似,通过向stark_verifier.circom.ejs模板注入来生成verifier circuit recursivef.verifier.circom
。
2)final generate circom 流程:输入有 recursivef.verifier.circom
,通过向stark_verifier.circom.ejs模板注入获得final.circom
。
3)compile circom 流程:将circom文件final.circom
编译为:
final.r1cs
final.witnesscalc
这2个输出后续用于构建Groth16证明。
整个proof生成分为6大阶段:
zkevm.inputs
、zkevm.pil
、rom.json
来生成execution trace zkevm.commit
、zkevm.const
、zkevm.constTree
。zkevm.commit
、zkevm.const
、zkevm.constTree
、zkevm.pil
、zkevm.starkinfo
来生成 STARK proof zkevm.proof
、zkevm.public
、zkevm.zkin.proof
(包含了STARK proof和相应的public inputs)。生成zkEVM STARK proof π b a t c h \pi_{batch} πbatch时的blowup factor为2,因此 π b a t c h \pi_{batch} πbatch很大,具有大量的多项式。为此,需要后续的c12a 压缩步骤,通过增大blowup factor 来 减少多项式数量。
生成zkEVM STARK proof π b a t c h \pi_{batch} πbatch 不同于后续的步骤,为递归的开始准备阶段。不过,为统一代码风格,Main Prover流程对证明过程做了抽象,使得后续递归中的每个步骤看起来都是一样的。
为验证之前的big STARK proof π b a t c h \pi_{batch} πbatch 生成新的压缩版 STARK proof π c 12 a \pi_{c12a} πc12a。
为 验证之前的STARK proof π c 12 a \pi_{c12a} πc12a 生成新的normalized STARK proof π r e c 1 \pi_{rec1} πrec1。
为 验证之前的2个STARK proof π r e c 1 \pi_{rec1} πrec1(和 π r e c 2 \pi_{rec2} πrec2) 生成聚合STARK proof π r e c 2 \pi_{rec2} πrec2。
为 验证之前的聚合STARK proof π r e c 2 \pi_{rec2} πrec2 生成新的STARK proof π r e c f \pi_{recf} πrecf。
为 验证 π r e c f \pi_{recf} πrecf,使用 递归流程的最后一个电路——final.circom
来生成SNARK proof π g r o t h 16 \pi_{groth16} πgroth16。
[1] Polygon zkEVM技术文档 Recursion, aggregation and composition of proofs v.1.1