1. 引言
一年前,V神博客The different types of ZK-EVMs中指出:
- 以太坊初始设计未围绕ZK友好性,因此,以太坊协议的很多部分都需要大量计算来做ZK-prove。Type 1 zkEVM致力于精准复制以太坊,因此它没有办法减轻这些低效率。目前,以太坊区块的证明需要数小时才能生成。
不过,用时数小时来生成以太坊区块证明已成历史,Zeth——可在数分钟,而不是数小时,生成以太坊区块证明。
Zeth为“Type 0” zkEVM,其使用RISC Zero zkVM来证明以太坊区块的有效性。
开源代码实现见:
Zeth构建在RISC Zero zkVM之上,是针对以太坊的开源ZK blockprover:
- 无需依赖validator或sync committees,可证明某特定以太坊区块的有效性
- 其在zkVM完成构建新区块所需的所有工作。
当前Zeth已在以太坊主网构建了多个真实区块,并通过了官方以太坊testsuite所有相关测试。
RISC Zero团队可在4周实现兼容性级别的Zeth,因为当前RISC Zero zkVM:
Zeth可在数分钟内生成相应的区块证明,因为有RISC Zero zkVM的:
同时由于RISC Zero zkVM支持链上验证,任何人都可很便宜地在链上验证这些proofs:
1.1 Zeth:verifiable Ethereum block construction
Zeth,可在不依赖validator或sync committees的情况下,证明某以太坊区块是有效的(即,是对某父区块应用一组交易列表)。原因在于,Zeth可在RISC Zero zkVM内做构建新区块所需的所有工作,包括:
- 1)验证交易签名
- 2)基于父区块的state root,验证账号状态 & storage状态
- 3)应用一组交易列表
- 4)给产块者(block author)支付手续费
- 5)更新state root
- 6)等等。
构建完该新区块之后,Zeth会计算并输出新区块的哈希值。通过在zkVM中运行以上流程,可获得该新区块是有效的ZK proof。
由于Zeth构建了标准以太坊区块,可将Zeth看成是Type 1 zkEVM。但是,远不止此:
- 因为Zeth采用了标准的Rust crates(与当前流行的全节点,如Reth一样,采用了完全相同的crates),可将其看成是Type 0 zkEVM。
所谓Type 0 zkEVM,是指:
- 与以太坊协议完全兼容,并复用了流行以太坊节点的大量代码。
- 包含Type 1 zkEVM的所有优势。
- 生成proof用时不太长。
Zeth对ZK技术和以太坊生态具有里程碑意义。本文重点关注:
- 如何在数周内实现了Zeth?
- Zeth的性能
- Zeth的工作原理
- Zeth对ZK项目的意义
2. RISC Zero正让zkRollups、zkEVMs、轻节点和bridges的构建更简单
写Zeth的原因有2个:
- 1)为让其他团队更容易构建其自己的ZK赋能的基础设施:如zkRollups、zkEVMs、轻节点和bridges等等。
Zeth提供了为EVM-based区块生成ZK proofs所需的一切。这是任意zkEVM或bridge的关键要素。
Zeth已开源并基于revm,可很容易对其修改并用于自己的项目中。proofs:- 可在链上验证(适于bridges和L2s)。
- 或在本地app上验证(适于全节点和轻节点)。
- 2)为评估在RISC Zero zkVM内的EVM性能,特别是以太坊相关任务性能。
2.1 zkRollups和zkEVMs
作为Type 0 zkEVM,Zeth使得开发者可构建完全原生支持EVM和以太坊的zkRollups。同时借助RISC ZERO的链上proof验证,很容易就构建由ZK加持的L2。
现有的zkRollups和zkEVM电路设计是monolithic(庞大而单一的),开发者若不对ZK密码学高度理解,很难对这样的电路进行升级。而,Zeth使得任何开发者都可构建其自己的区块打包逻辑。
希望借助Zeth,未来让,之前需要数年开发以及数十亿资金的,zkRollups和zkEVM更民主。
2.2 轻节点和bridges
beacon chain的引入,无疑有益于轻节点和bridges。这些技术,基于以太坊的现有构建完好的PoS模式,使得轻节点和bridges在(假设所有人都遵循规则)无需重构区块的情况下,更容易验证最近的区块。
质押的目的是提供游戏规则的经济激励措施。stake被slash掉的风险,并不能保证坏事永远不发生。外部激励可“颠覆规模”,有利于恶作剧——而设计一个能够正确处理这些恶作剧的轻节点或bridges是很困难的。
有了像Zeth这样的工具,恶作剧的风险大大降低了。轻节点只需向RISC Zero zkVM添加几个调用,就可以与Zeth集成;而链上应用程序,如bridges,可使用RISC Zero的链上验证合约与Zeth集成。
在不久的将来,人们可以想象使用ZK证明来确定特定区块是有效的轻节点和bridge。这种方法将在不显著增加验证区块的成本的情况下显著降低风险。
这对于应用链、模块化生态系统以及尚未具有以太坊大型全节点社区所提供的安全级别的新链来说尤其重要。
3. 感谢现有RISC Zero zkVM基础设施,让Zeth很容易实现
Zeth基于RISC Zero zkVM构建,RISC Zero zkVM提供了RISC-V指令集架构所支持的熟悉的编程体验。
RISC Zero zkVM不只有RISC-V core,还有:
这种通用CPU内核 + accelerator circuits 的混合模式,可提供以下2种最好的世界:
- 支持主流编程语言
- 对关键密码学操作不牺牲性能。
如,利用现有的revm、ethers、alloy Rust crates,工程师用时不到4周就开发了Zeth。这种工程化速度不亚于任何任何现有成熟生态。
性能方面,Zeth利用了RISC Zero的:
- accelerator circuit for ECDSA signature verification
- continuations:借助GPU集群快速并行证明大型计算。当前对Nvidia GPU和Apple Metal都支持。
continuations很容易使用,可透明提供给所有运行在zkVM的guest程序。而无需修改程序代码。
4. 性能
本节重点关注Zeth block builder的性能,主要考虑如下因素:
- 生成proof所需的计算资源
- 生成proof所需的“Wall time”(即,用户需等待多久才能获得其proof)
- proof生成总开销($)
4.1 continuations
RISC Zero zkVM可通过使用continuations来调整性能。
RISC Zero zkVM实现的是标准的RISC-V处理器。因此,其执行以cycles为单位(在电路中,大多数RISC-V指令执行仅需1 cycle,但有某些例外)。简单的程序通常仅需要成百上千个cycles来执行,但复杂的程序很容易需要数十亿cycles。
在经典的ZK系统中,执行的这些cycles会收集到单个proof内,随着cycles数量的增加,生成该proof所需时间和内存也将增加。但RISC Zero zkVM部署经典ZK系统。2023年年初,RISC Zero团队上线了continuations新特性,从而大幅改进了该范式。
借助continuations,证明过程分为3大阶段:
- 1)在non-proving emulator中执行所需的计算。从而可计算出目前为止运行所需的cycle数。在配置间隔,对该程序的状态做快照。这会将该执行高效切分为多个segments。每个segment都很小,通常代表100万个cycle或更少。
- 2)将这些segments分发给a pool of workers。这些workers会为各自的segment生成ZK proofs。最重要的是,这些works是并行工作的。只要有足够的workers,所有segments的证明用时,与单个segment的证明用时是相当的。因为segment很小,证明用时通常很短(几十秒)。
- 3)segment proofs生成之后,需将这些segment proofs “rolled up”。每个rollup操作会取一组顺序的segment proofs,然后为该segments组合生成一个新的proof。如,Segment 1证明了程序由状态A到状态B,Segment 2证明了程序由状态B到状态C,则rollup可证明该程序由状态A到状态C。借助足够的workers,rollup用时可为 log N \log N logN,其中 N N N为segments数量。
4.2 构建一个以太坊区块有多难?
以实际以太坊真实区块为例,使用zkVM中的Zeth重构这些区块的复杂度为:
如,区块17606771生成了2131个segments。每个segment表示最多
2
20
2^{20}
220个执行cycles,因此,整个计算需要最多2,234,515,456执行cycles。
通常,一个经典的以太坊区块需要20亿到40亿个cycles来构建,但优势,甚至需要95亿个cycles。(一开始可能会惊讶这些差异不体现在交易gas上,但这是有道理的:gas系统是针对传统执行的,而不是针对ZK proving的。)
借助continuations,这种规模的cycle执行很容易管理。基于这些数据,具有1万个节点的运行zkVM prover的P2P网络,就足以为最大区块实现最大并行化证明性能。这1万个节点,相比于以太坊现有的70万个validators来说,只是很小的一部分。
4.3 生成一个proof需用时多长?
为收集一些基础性能数据,搭建了一个具有64 GPU workers的Bonsai测试实例。然后让该测试实例用Zeth证明第17735424区块(其包含182笔交易,对应3242个segments,约34个cycles)。
为生成该proof,RISC Zero zkVM首先比如将该执行切分为segments。如下图所示,其通过Executor任务来完成,用时约10分钟。(大多数时间都用于做AWS事务,如写网络storage。在本地机器,相同的任务用时小于6分钟。未来将致力于进一步缩短该时间。)
Executor最终会将该执行切分为3242个segments。这对于64GPU来说,数量有点多。因此,每个worker节点需生成50个segment proofs,如上图所示。在rollup早期阶段,work数量要多于workers数量,因此第一个stage用时1分钟,第2个阶段用时35秒钟,第3个用时25秒钟等等。到第7个阶段,用时仅需5秒钟。
最后,在rollup完成之后,对结果进行finalize,用时需要一分钟。
因此,在小规模集群情况下,仍能在50分钟内生成该proof(giving an effective speed of 1.1MHz)。若有合适规模的集群,生成该proof将快得多:
- 若足够并行化,proving step将用时42+12*5+60秒钟,或2分钟42秒。
- 若保守估计,并包含executor用时,则总时长为9到12分钟 (giving an effective speed of 4.7 MHz - 6.3 MHz.)。
随着未来对Executor和proving框架的优化,生成proof的时长将进一步缩短。
4.4 生成proof有多昂贵?
4.3节提到的测试集群部署在AWS上,其包含64个g5.xlarge proving节点和1个m5zn.xlarge executor节点。根据Amazon,每个g5.xlarge节点有:
- 1个 GPU + 24GB GPU内存
- 4个 vCPU + 16GB内存
2023年8月,g5.xlarge实例的on-demand价格为1.006美金每小时,以及0.402美金每小时for reserved instances。
同时,根据Amazon spec sheet,单个m5zn.xlarge节点具有:
- 4个 vCPU + 16GB内存
2023年8月,m5zn.xlarge实例的on-demand价格为0.3303美金每小时。
利用以上价格数据,来粗略评估为17735424区块生成proof的开销。
注意部署了64个proving节点,该proof需要50分钟来生成。忽略空闲的worker时间,64个proving节点,加,1个executor节点,持续50分钟,金额为50/60 * (64 * $0.402 + $0.3303) = $21.72。不过这个金额是过量的,因为假设为空闲worker付费了。若排除空闲workers的开销(如关闭),相应开销约为$19.16:
- 该区块具有182笔交易,即每笔交易对应0.11美金的成本。
- 总交易金额为1.125045057 Eth,价值约2137.59美金。即所支付的每1美金,守护了用户的109.01美金。
- 该区块支付了0.117623263003047027 Eth in rewards(不包括交易手续费)。对应约223.48美金。因此proof开销约占生成block reward的8.7%。
- 交易手续费总额为0.03277635 Eth,约62.28美金,为proof开销的3倍多。
注意,以上开销与集群size无关。仅与segments数量有关。因为1台机器顺序做2个jobs的开销,与2台机器并行做1个job的开销,是一样的。结果就是,借助更大的集群,proof生成速度将更快,但不是更贵。
未来有多种方式来降低该开销。此外,随着RISC Zero zkVM性能的持续改进,以及未来可能增加Keccak accelerator,有望能采用更便宜的实例。重要的是,考虑到我们使用的机器规格较低(以及我们的zkVM对nVidia Cuda和Apple Metal的支持),这项工作可以通过由普通消费PC和Mac组成的p2p网络轻松完成。
4.5 链上验证
之前已提到,RISC Zero zkVM支持链上验证,任何人都可很便宜地在链上验证这些proofs:
- https://sepolia.etherscan.io/tx/0x274eba5b49b54d352f104c531334ac1a9747d07fdf77042c42d9cb8338d13b91
(使用的为RISC Zero Groth16 verifier)。
链上验证是RISC Zero stack相对新的内容,于2023年8月发布。其使用Bonsai将RISC Zero zkVM的原生STARK proofs,转换为等价的SNARK proof,然后将该SNARK proof提交到链上SNARK verifier。
若将该交易输入转换为UTF-8 data ,可知该proof对应区块17735424:
使用Bonsai,将STARK转换为SNARK,用时40秒钟。在链上验证该SNARK需245, 129 gas(即约5.9美金)。
RISC Zero zkVM很棒的点还在于,其可将多个proof rollup into为一个proof。借助该rollup特性,整个proofs集合都可在链上验证,而无需额外的gas。这样,链上验证的开销就可在该集合摊销,对单个proof来说手续费就更低了。
4.6 Zeth对以太坊的意义
以太坊设计时未考虑ZK友好性。正如zkeVM的前景所示,有很多事情可以用不同的方式来做,尤其是在opcodes、数字签名和哈希函数方面。
虽然这些变化确实提高了性能,但没有它们,我们仍然能够实现稳定的性能。当Vitalik去年写下不同类型的zkEVM时,证明以太坊区块的有效性需要几个小时;今天我们可以在几分钟内完成。ZK的业绩正在快速改善,有充分的理由相信这一趋势将在未来几年继续下去。
5. 展望
Zeth项目的目的是调研区块构建的性能,为此,限定范围为post-merge blocks。
此外,Zeth可证明特定区块是有效的,其当前无法证明共识(即,该区块实际被包含在canonical chain中)。可能通过在zkVM内添加所源自的verifier或sync committee签名检查,未来能支持共识证明。
Zeth是新的项目软件,可能存在bug。其目前处于试验阶段。
参考资料
[1] RISC Zero团队2023年8月22日博客 Announcing Zeth: the first Type Zero zkEVM
[2] RISC Zero zkVM docs
[3] RISC Zero团队2023年10月视频 Intro to Zeth (RISC Zero Study Club)
附录A:Zeth工作原理
Zeth构建区块的方式与全节点一样:
- 基于某父区块、一组交易列表和block author
- 做一系列计算:验签、运行交易、更新global state
- 然后,返回该新区块的哈希值。
与全节点不同之处在于,Zeth运行在RISC Zero zkVM内。这意味着可获得一个ZK proof——具有given hash的区块是有效的。
本节将重点介绍实现Zeth所面临的挑战:
- 1)密码学挑战
- 2)accounts和storage的性能和安全挑战
附录A.1 密码学挑战
第一个挑战是密码学挑战。构建一个以太坊区块需要做大量密码学操作,如:
- 哈希(Keccak-256)
- 验签(ECDSA with secp256k1)
RISC Zero zkVM正加速致力于支持椭圆曲线:
因此,ECDSA签名验签问题不大。
RISC Zero zkVM已支持SHA2-256,但当前还没经历支持Keccak-256:
当前为简化使用,采用的是sha3 Rust crate中的Keccak实现。经评估,Keccak-256会大量的cycles,并未优化。未来团队将致力于增加Keccak accelerator。
附录A.2:accounts和storage的性能和安全挑战
以太坊中,accounts和storage通过全局Merkle Patricia Trie(MPT)来跟踪。
根据Etherscan,到2023年8月,该MPT树包含了将近2.5亿个独特的以太坊地址。总的来说,这并不是很多数据,但这已经足够多了,人们必须小心其存储和使用方式。特别是,MPT性能至关重要。
而且,性能不是唯一因素,还需考虑安全性。
Zeth的block builder在RISC Zero zkVM内运行a guest。这意味着其无法直接访问以太坊P2P网络或其它RPC提供商。相反,其必须依赖某独立运行与zkVM之外的程序来提供数据。
对于Zeth block builder,这意味着验证所有相关accounts和storage状态(即,运行特定交易列表所需的accounts和storage)。
幸运地是,EIP-1186: RPC-Method to get Merkle Proofs - eth_getProof #1186提供了一种机制,其定义了通过Merkle inclusion 证明某特定账号(及其storage)的标准方式。
原则上,Zeth的block builder可通过简单验证一组EIP-1186 inclusion proofs来验证account和storage状态,但这种方式并非最优的。
相反,更优的方式是:
- 使用EIP-1186 inclusion proofs中的数据有构建a partial MPT。即,该MPT仅包含与特定交易列表相关的节点,不相关的branches简单以相应的哈希表示。可将该partial MPT看成是某种“union” of Merkle inclusion proofs。也可将其看成是a Merkle subset proof。
验证partial MPT的流程,与验证常规EIP-1186 proof的流程基本一致:
- 计算root哈希,并比较父区块的state root。若二者相等,则可信任所持有的accounts和storage完整性。
验证完partial MPT之后,可应用交易并更新该partial MPT,通过计算该partial MPT的新root hash,来获得新的state root。
最终,Zeth block builder的流程为:
- 1)在运行Zeth block builder之前,在sandbox内运行该交易列表,以确定相关的accounts和storage。(该流程还可帮助确定相关的最老前辈区块——为支持blockhash()查询需要。)
- 2)为每个相关的accounts和storage获取相应的EIP-1186 inclusion proofs(同时,也获取相应的前辈区块)。
- 3)使用这些inclusion proofs来构建包含所有相关数据的partial MPT。
- 4)启动RISC Zero zkVM,让其运行Zeth block builder,并提供相应输入(父区块、交易列表等)以及相应的partial MPT。
在RISC Zero zkVM内,Zeth block builder会:
- 1)验证该partial MPT root与其父区块state root匹配。
- 2)验证到服区块之前的相关前辈区块的hash chain。
- 3)应用交易列表。
- 4)更新该partial MPT。
- 5)使用该partial MPT的新root哈希作为新区块的state root。
当Zeth block builder运行完成,其会输出该新区块的哈希。该哈希包含了:
- 对该服务情况的承诺,以及该服区块的state root(可用于验证原始partial MPT)。
- 这意味着恶意prover不能在不提供无效服务器的情况下,提供无效的accounts和storage数据。
- 换句话说,若父区块是有效的,则由Zeth生成的新区块也是有效的。
因此,由Zeth生成的新区块和相应的ZK proof,则可做如下3项检查以确认该区块的有效性:
- 1)确认该ZK proof是有效的并源自Zeth。对于链下应用,可通过使用RISC Zero zkVM Rust crate提供的函数接口来检查;对于链上应用,可使用RISC Zero的链上verifier来检查。
- 2)确认该ZK proof对新区块的哈希进行了commit。
- 3)确认该服区块的哈希与期待的一致。
若以上3项检查均通过,则该新区块有效。