特别感谢 PSE、Polygon Hermez、Zksync、Scroll、Matter Labs 和 Starkware 团队的探讨与校对。
最近,很多“ZK-EVM”项目很快相继发出公告。Polygon 开源了他们的 ZK-EVM 的项目,ZKSync 发布了 ZKSync 2.0 的计划,而 Scroll 作为相对的新秀,最近也宣布了他们的 ZK-EVM 项目。还有 Privacy and Scaling Explorations(隐私和扩容探索)团队、Nicolas Liochon et al 的团队,以及 Nethermind 团队致力于将 EVM 的 Solidity 语言转译为 StarkWare 的 ZK 友好语言 Cairo 的一个 Alpha 编译器,这些团队都在为此不断努力,当然,还有一些项目我没有例举出来。
这些项目的核心目标是一致的:利用 ZK-SNARK 技术对以太坊类交易的执行生成加密证明,不是让证明更易于验证以太坊链本身,就是构建(接近)相当于以太坊所提供的一些功能,但比它更具有可扩展性的 ZK-rollups。但这些项目之间也有些细微区别,并且他们在实用性和速度之间所做的权衡也有差异。这篇文章将描述不同 EVM 等效“类型”的分类学,以及每种类型的优势和开销。
概览(图表形式)
类型 1:完全以太坊等效的 ZK-EVM
第一类 ZK-EVM 力图成为完全不妥协的以太坊等效 ZK-EVM 。他们不会更改以太坊系统的任何部分来让它更容易生成证明:不会替换哈希、状态树、交易树、预编译或是共识中的其他逻辑,不管这些部分多么次要。
优势:完美的兼容性
ZK-EVM 的目标在于,能够像目前以太坊验证区块那样 —— 或者至少,验证执行层上面的区块(因此,虽然信标链的共识逻辑没有囊括进来,但是所有执行的交易、智能合约和账户逻辑都包含在内)。
第一类的 ZK-EVM 正是我们最终所需要的,能让以太坊 L1 本身更具有可扩展性。长期来看,在第二类和第三类的 ZK-EVM 中试验得出的对以太坊的修改,可能会被合理引入至以太坊上,但是这种重新架构本身具有复杂性。
第一类 ZK-EVM 对 Rollup 来说也是理想的,因为它们让 Rollup 可以重复利用基础设施。比如,以太坊执行层客户端可以像原来那样生成和处理 Rollup 的区块(至少,一旦实现提款功能,它们可以重新使用该功能来支持 ETH 存入 Rollup),所以区块浏览器、区块生成等工具可以轻松重新使用。
劣势:证明生成时间慢
以太坊原本的设计并不是 ZK 友好的,因此,以太坊协议中的许多部分都需要花费大量的计算来生成 ZK 证明。第一类 ZK-EVM 旨在完全复制以太坊的环境,因此,它无法缓解这些计算低效性问题。目前,以太坊区块证明需要花费多个小时才能生成。这可以通过巧妙的工程(如大规模并行生成证明),或从长远来看,通过 ZK-SNARK ASIC 来缓解低效性问题。
谁在构建第一类 ZK-EVM?
Privacy and Scaling Explorations 团队正在努力构建第一类 ZK-EVM。
类型 2:完全 EVM 等效的 ZK-EVM
第二类 ZK-EVM 力争成为完全 EVM 等效但也不那么以太坊等效的 ZK-EVM。也就是说,“从内部来看”,他们完全就像以太坊一样,但是在外部与以太坊有一些区别,尤其是区块结构和状态树等数据结构。
它的目标在于,与现有的应用完全兼容,但它会对以太坊做一些较小的修改,让开发更容易,也让证明生成得更快。
优势:在虚拟机层面完全等效
第二类 ZK-EVM 会改变存储着如以太坊状态的数据结构。幸运的是,因为 EVM 本身就不能直接访问这些数据结构,所以对原本在以太坊上运行的应用程序没什么影响,它们仍能在第二类的 ZK-EVM rollup 上运行。你可能无法像原来那样使用以太坊执行层客户端,但你可以通过一些修改来使用客户端,并且依旧能使用 EVM 调试工具和大部分其他开发者基础设施。
还存在少数例外情况。当验证以太坊历史区块的默克尔树证明来验证历史交易、收据或状态的声明(比如,桥接有时候也会这么做)时,应用程序出现了不兼容。如果 ZK-EVM 用不同的哈希函数来替换 Keccak,那就会损坏这些证明。而且,我经常建议不要这样构建应用程序,因为未来的以太坊更新(如 Verkle tree)甚至会影响以太坊上的应用。更好的选择是以太坊自己添加不会过时的 (future-proof) 历史访问预编译。
劣势:已经改进过了但还是证明时间太慢
第二类 ZK-EVM 能比第一类提供更快的证明生成时间,它主要通过移除部分以太坊堆栈,这部分堆栈依赖于具有不必要复杂性和 ZK 不友好的加密学。特别是,这些堆栈可能会更改以太坊的 Keccak 和基于 RPL 的 Merkle patricia tree,也许还会更改区块和收据结构。第二类 ZK-EVM 则是会使用不同的哈希函数,如 Poseidon。自然而然地,它会修改状态树以储存哈希码和 Keccak,不需要验证哈希以处理 EXTCODEHASH 和 EXTCODECOPY 操作码。
这些修改极大地改善了证明生成的时间,但它们没有解决所有问题。这类 ZK-EVM 继承了 EVM 本身带来的低效性和 ZK 不友好问题,所以像原本那样基于 EVM 生成证明的低效情况依然存在。内存就是最简单的例子:因为 MLOAD 可以读取任何 32 字节,包括“无序的”代码段(其开头和结尾都不是 32 的倍数), MLOAD 也不能单纯理解为对一段代码的读取;确切地说,它可能需要读取两个连续的代码段和执行位操作来结合运行结果。
谁在构建第二类 ZK-EVM?
Scroll 的 ZK-EVM 项目正在构建第二类 ZK-EVM,Polygon Hermez 也是。即使如此,还没有项目真的成为第二类 ZK-EVM;尤其是很多更加复杂的预编译还没实现。所以,目前来说,这两个项目应该说属于第三类 ZK-EVM。
类型 2.5:EVM 等效,除了 gas 开销
在最糟糕的情况下,有一种能够极大地改善证明生成时间慢的方式是,大大提高那些难以在 EVM 中生成 ZK 证明的执行所花的 gas 开销。这些执行可能涉及预编译、KECCAK 操作码,还可能涉及调用合约的特定模式、访问内存/存储或是回滚。
更改 gas 开销可能会降低开发者工具的兼容性,损坏一些应用,但总体上,它的风险比“更深入地”变更 EVM 来说更少。开发者需要注意,不要消耗超出一个区块所容纳的 gas 上限,也永远都不要用硬编码 gas 数来进行调用(这已经是长期以来对开发者的标准建议了)。
另一种管理资源限制的方式是,只要对每个操作能被调用的次数设定硬限制就好了。这在电路中的实现很简单,但是对 EVM 的安全假设就不太好了。我更愿意将这种方法称作第三类 ZK-EVM,而不是类型 2.5。
类型 3:几乎是 EVM 等效的
第三类 ZK-EVM 几乎是 EVM 等效的,但需要对完全等效性做一些牺牲,以进一步改善证明生成时间,并促进 EVM 更易于开发。
优势:易于构建,证明生成时间更快
第三类 ZK-EVM 也许会取消一些格外难以在 ZK-EVM 实现中实现的功能。预编译通常会是这类功能中最难实现的;此外,这类 ZK-EVM 有时也在处理合约代码、内存和堆栈方面有些许不同。
劣势:兼容性更差
第三类 ZK-EVM 的目标是与大部分应用程序兼容,它只需要对剩下的应用进行极少的改写。即使是这样,也需要对一些应用进行改写,因为这些应用会使用第三类 ZK-EVM 已经取消的预编译,或是因为它们对边缘情况有着微妙依赖性,而 VM 会以不同的方式处理。
谁来构建第三类 ZK-EVM?
Scroll 和 Polygon 现在的形式都属于第三类 ZK-EVM,尽管他们预计会随着时间改善兼容性。Polygon 的设计很独特,他们用着自己的内部语言 zkASM 验证 ZK,并且他们会使用 zkASM 的实现来转译 ZK-EVM 代码。虽然其实现细节是这样的,但我还是愿意把它称为真正的第三类 ZK-EVM。它依旧能够验证 EVM 代码,只是用着一些不同的内部逻辑罢了。
现在,还没有 ZK-EVM 团队想要成为第三类 ZK-EVM;该类型仅仅是完成预编译添加这一复杂工作和项目能够转为类型 2.5 之前的过渡阶段。然而,通过添加新的 ZK-SNARK 友好的预编译,为开发者提供证明生成时间短、gas 开销低的功能,第一类和第二类 ZK-EVM 在未来可能会自发成为第三类 ZK-EVM。
类型 4:高级语言(high-level-language)等效
第四类 ZK-EVM 系统的工作原理是,采用高级语言编写智能合约源码(如 Solidity、Vyper,或一些由两者编译而成的中间语言(intermediate)),并将这些源码编译为一些明确设计成 ZK-SNARK 友好的其他语言。
优势:极快的证明生成时间
不将 EVM 的每个执行步骤的所有环节生成 ZK 证明,而是直接开始证明高级语言编写的代码,这样你可以避免掉很多开销。
在本文,虽然我只用了一句话来描述这种优势(对比以下兼容性相关的劣势要点列表来说),但这句话不应该被解读为价值判断!从高级语言直接编译真的可以极大地减少开销,并通过让证明过程变得容易而推动去中心化。
劣势:兼容性更差
一个用 Vyper 或 Solidity 编写的“正常”应用程序能够被编译出来,并且它“可以运行”,但在很多重要情况下,很多应用会变得不“正常”:
第四类 ZK-EVM 的系统中的合约地址与 EVM 中的可能不一样,因为 CREATE2 合约地址取决于具体的字节码。这破坏了依赖于尚未部署的“反事实合约”的应用、ERC-4337 钱包、EIP-2470 单例和许多其他应用程序。
手动编写的 EVM 字节码更难投入使用。很多应用程序为了效率,会使用手动编写部分 EVM 字节码。尽管有很多种方式可以实现对这类有限制的 EVM 字节码的支持,可以在无需完全成为第三类 ZK-EVM 的情况下将这些用例应用起来,但第四类 ZK-EVM 的系统可能不会支持这种手动编写的字节码。
很多调试基础设施无法继续生存,因为这种基础设施都基于 EVM 字节码运行。尽管如此,但我们可以通过“传统”高级语言或中间语言更轻松地访问调试基础设施,以减轻这种劣势(比如 LLVM)。
开发者应该留心这些问题。
谁在构建第四类 ZK-EVM?
ZKSync 系统就是第四类 ZK-EVM,虽然它可能会随着时间提高 EVM 字节码的兼容性。Nethermind 的 Warp 项目正在构建从 Solidity 语言转译为 StarkWare Cairo 语言的编译器,这个编译器将会把 StarkNet 变成真正的第四类 ZK-EVM 系统。
各个 ZK-EVM 类型的未来
并不是说这些类型比其它类型“更好”或“更差”。相反,相较之下他们各有不同:从类型 1 至类型 4,编号较低的 ZK-EVM 类型和现有的基础设施更加兼容,但运行得更慢;而编号较高的 ZK-EVM 类型则和现有的基础设施不那么兼容,但运行得更快。总之,对所有 ZK-EVM 类型的探索有益于该领域的健康发展。
另外,ZK-EVM 项目可以随着时间的推移,轻松地从编号高的 ZK-EVM 开始,然后转为编号低的类型(反之亦然)。例如:
ZK-EVM 可以在一开始作为第三类 ZK-EVM 投入使用,不去加入一些特别难以生成 ZK 证明的功能。之后,他们可以随着时间的推移而加入那些功能,继而转变为第二类。
一开始作为第二类别的 ZK-EVM,通过在完全兼容以太坊的模式下运行,或使用在修改后能更快生成证明的状态树,这类 ZK-EVM 可以在之后变成第二类和第一类 ZK-EVM 的混合类型。Scroll 就正在考虑向这个方向发展。
一些一开始属于第四类系统的 ZK-EVM 项目,可以通过之后添加 EVM 代码处理的功能,继而随着时间变成第三类 ZK-EVM(尽管开发者还是会被鼓励直接从高级语言编译,以此减少费用和证明生成的时间)。
如果以太坊自身为了变得更加 ZK 友好而采用一些修改,那么第二类和第三类可以成为第一类 ZK-EVM。
第一类或第二类 ZK-EVM 可以通过增加验证 ZK-SNARK 友好语言代码的预编译,变成第三类 ZK-EVM。这为开发者在以太坊兼容性和运行速度之间提供了一个选择。这可以算是第三类 ZK-EVM,因为它会破坏完美的 EVM 等效,但出于实际意图和目的,它可能还会具有很多第一类和第二类 ZK-EVM 的优势。它不好的地方可能是,一些开发者工具无法理解 ZK-EVM 的自编译,虽然这点也可以修复:开发者工具可以通过支持包括 EVM 代码等效的预编译实现在内的配置格式,以此增加通用的预编译支持。
就个人而言,通过结合 ZK-EVM 中的改进与让以太坊变得更加 ZK-SNARK 友好的改进,我希望这些项目全部慢慢变成第一类 ZK-EVM。在这样的未来里,我们也会有多种 ZK-EVM 实现,可以用于 ZK rollup,也能用来验证以太坊链本身。理论上,以太坊没有必要标准化单个 ZK-EVM 的实现来供 L1 使用;不同客户端可以使用不同的证明,我们才能继续从代码冗余中获益。
不论如何,我们还需要一些时间来迎接这种未来。同时,我们也会在扩容以太坊和开发基于以太坊 ZK rollup 的不同赛道上看到大量创新。