Vitalik:探讨多种“内置 ZK-EVM”版本类型及设计挑战
撰文:Vitalik Buterin
编译:1912212.eth, Foresight News
以太坊之上的二层 EVM 协议,包括 optimistic rollups 和 ZK rollups,都依赖于 EVM 验证。然而,这要求他们信任庞大的代码库,如果该代码库中存在错误,这些 VM 就有被黑客攻击的风险。此外,这意味着即使是希望与 L1 EVM 完全等效的 ZK-EVM 也需要某种形式的治理,以将 L1 EVM 的更改复制到他们自己的 EVM 实践中。
这种情况并不理想,因为这些项目正在复制以太坊协议中已经存在的功能,而以太坊治理已经负责进行升级和修复错误:ZK-EVM 基本上与验证第一层以太坊块的工作相同!此外,在未来几年,我们预计轻客户端将变得越来越强大,很快就会达到使用 ZK-SNARKs 来完全验证第一层 EVM 执行的程度。到那时,以太坊网络将有效构造内置的 ZK-EVM。因此,问题出现了:为什么不让 ZK-EVM 本身也适用于 rollups 呢?
本文将介绍几种可以实现的「内置 ZK-EVM」版本,并讨论权衡和设计挑战,以及不采用特定方向的原因。实施协议特性的好处应该与将事情留给生态系统并保持基础协议简单的好处进行权衡。
我们希望从内置 ZK-EVM 中获得哪些关键特性?
- 基本功能:验证以太坊区块。协议功能(目前尚不明确是操作码、预编译或其他机制)应接受至少一个前状态根、一个块和一个后状态根作为输入,并验证后状态根实际上是执行块后的结果。
- 与以太坊的多个客户端兼容。这意味着我们希望避免只采用一个证明系统,而是允许不同的客户端使用不同的证明系统。这又引出以下几点:
- 数据可用性要求:对于任何使用内置 ZK-EVM 进行证明的 EVM 执行,我们希望保证底层数据的可用性,以便使用不同证明系统的证明者可以重新证明执行,并允许依赖该证明系统的客户端验证新生成的证明。
- 证明存在于 EVM 和区块数据结构之外:内置 ZK-EVM 功能不会将 SNARK 作为 EVM 内的输入,因为不同的客户端会期望不同类型的 SNARK。相反,它可能类似于 blob 验证:交易可以包括(前状态、区块主体、后状态)需要证明的声明,一个操作码或预编译可以访问这些声明的内容,客户端共识规则将分别检查每个声明的数据可用性和存在证明。
- 可审计性。如果任何执行得到证明,我们希望底层数据是可用的,以便在出现问题时,用户和开发人员可以检查它。实际上,这增加了为什么数据可用性要求如此重要的另一个原因。
- 可升级性。如果某个 ZK-EVM 方案被发现有 bug,我们希望能够快速修复它。这意味着不需要硬分叉来修复。这增加了为什么证明存在于 EVM 和区块数据结构之外的原因。
- 支持几乎所有的 EVM。L2 的吸引力之一是在执行层进行创新,并扩展 EVM。如果给定的 L2 的 VM 与 EVM 只有一点点不同,那么如果 L2 仍然可以在与 EVM 相同的部分使用本机协议内 ZK-EVM,并在不同的部分仅依赖于自己的代码,那将是一件好事。这可以通过设计 ZK-EVM 功能来实现,该功能允许调用者指定位字段或操作码列表或地址,这些位字段、操作码列表或地址由外部提供的表而不是 EVM 本身处理。我们还可以在一定程度上使 Gas 成本开放编辑。
「开放」与「封闭」的多客户端系统
「多客户端理念」可能是这个列表中最具主观性的要求。可以选择放弃它,专注于 ZK-SNARK 方案,这将简化设计,但代价是成为以太坊更大的「哲学转折点」(因为这实际上是放弃了以太坊长期以来的多客户端理念),并带来更大的风险。未来如果形式验证技术变得更好的时候,选择这条路可能会更好,但现在看来风险似乎太大了。
另一个选择是封闭的多客户端系统,在协议中已知有一组固定的证明系统。例如,我们可能会决定使用三个 ZK-EVM:PSE ZK-EVM、Polygon ZK-EVM 和 Kakarot。一个区块需要这三个中的两个提供的证明才有效。这比单一的证明系统要好,但它使系统的适应性降低,因为用户必须为每个存在的证明系统维护验证器,并且不可避免地会有政治治理过程来纳入新的证明系统等。
这促使我更喜欢开放的多客户端系统,在这个系统中,证明被放置在「区块外部」,并由客户端单独验证。个人用户可以使用他们想要的任何客户端来验证区块,只要至少有一个证明者为该证明系统创建证明就可以。证明系统将通过说服用户运行它们来获得影响力,而不是通过说服协议治理过程。然而,这种方法确实有更高的复杂性成本,正如我们将看到的那样。
我们希望从 ZK-EVM 实现中获得哪些关键属性?
除了基本的功能正确性和安全性保证之外,最重要的属性是速度。虽然我们可以设计在协议内的 ZK-EVM 特性,它是异步的,在 N 个插槽的延迟后只返回每个声明的答案,但如果我们能够可靠地保证在几秒钟内生成一个证明,那么无论每个块中发生什么都是自包含的,问题就会变得容易得多。
虽然今天为以太坊区块生成证明需要很多分钟或小时,但我们知道没有理论上的原因阻止大规模并行化:我们总是可以将足够的 GPU 组合起来,分别证明一个块执行的各个部分,然后使用递归 SNARK 将证明放在一起。此外,通过 FPGA 和 ASIC 的硬件加速可以帮助进一步优化证明。然而,真正达到这一步却是不容小觑的浩大工程挑战。
协议内 ZK-EVM 特性的具体形式可能是什么样?
与 EIP-4844 blob 交易类似,我们引入了新的包含 ZK-EVM 声明的交易类型:
需要注意的是,在实践中,我们可能想要将侧载拆分为两个单独的侧载,一个用于 blobs,一个用于证明,并为每种类型的证明(以及 blobs 的附加子网)设置一个单独的子网。
在共识层,我们添加了验证规则,即只有当客户端看到块中每个声明的有效证明时,才接受该块。证明必须是一个 ZK-SNARK,证明 transaction_and_witness_blobs 的串联是 (Block, Witness) 对的序列化,并且在 Witness 上使用 pre_state_root 执行该块
(i) 是有效的,并且
(ii) 输出正确的 post_state_root。可能的情况是,客户端可以选择等待多种类型证明的 M-of-N。
这里需要注意的是,块执行本身可以被简单地视为需要与 ZKEVMClaimTransaction 对象中提供的三元组一起检查的三元组之一(σpre,σpost,Proof)。因此,用户的 ZK-EVM 实现可以替换其执行客户端;执行客户端仍将由
(i) 证明者和块构建者以及
(ii) 关心索引和存储数据以供本地使用的节点使用。
此外,由于这种架构将执行与验证分开,因此它可能为以太坊生态系统中的不同角色提供更多灵活性和效率。例如,证明者可以专注于生成证明,而无需担心执行的具体细节,而执行客户端可以被优化以满足特定用户的需求,例如快速同步或高级索引功能。
验证和重新证明
假设有两个以太坊客户端,其中一个使用 PSE ZK-EVM,另一个使用 Polygon ZK-EVM,这个时候,这两个实现都已经发展到可以在 5 秒内证明以太坊块执行的程度,并且对于每个证明系统,就存在足够多的独立志愿者运行硬件以生成证明。
不幸的是,因为个别证明系统没有被正式确立,它们无法在协议中获得激励;不过,我们预计与研究和开发相比,运行证明的成本将较低,因此我们可以轻松通过面向公共物品资助的机构为证明者提供资金。
假设有人发布了一笔 ZKEvmClaimNetworkTransaction,但他们只发布了 PSE ZK-EVM 版本的证明。Polygon ZK-EVM 的证明节点看到了这一点,计算并重新发布该对象,附带 Polygon ZK-EVM 的证明。
这将使得最早的诚实节点接受一个块和最晚的诚实节点接受相同块之间的总最大延迟从δ增加到 2δ+Tprove(在这里假设 Tprove<5s)。
然而,好消息是,如果我们采用单个时隙最终性,我们几乎可以肯定将这个额外的延迟与 SSF 中固有的多轮共识延迟一起「流水线」进行。例如,在这个 4 个子时隙的提案中,「头部投票」步骤可能只需要检查基本块的有效性,但「冻结和确」步骤则需要存在一个证明。
扩展:支持「almost-EVMs」
ZK-EVM 功能的可取目标是支持「almost-EVMs」:具有额外功能的 EVMs。这可能包括新的预编译,新的操作码,合约可以用 EVM 或完全不同的 VM(例如在 Arbitrum Stylus 中),甚至具有同步交叉通信的多个并行 EVMs。
一些修改可以以简单的方式支持:我们可以定义一种语言,允许 ZKEVMClaimTransaction 传递修改后的 EVM 规则的完整描述。这可以用于以下情况:
- 自定义的 Gas 成本表(用户不被允许减少 Gas 成本,但他们可以增加它们)
- 禁用某些操作码
- 设置块号(这将意味着根据硬分叉有不同的规则)
- 设置标志,激活已经为 L2 使用标准化但不适用于 L1 使用的整套 EVM 更改,或其他更简单的更改
为了允许用户以更加开放的方式添加新功能,例如通过引入新的预编译(或操作码),我们可以在 ZKEVMClaimNetworkTransaction 的 blob 部分中添加一个包含预编译输入 / 输出记录的方式:
class PrecompileInputOutputTranscript(Container):used_precompile_addresses: List[Address]inputs_commitments: List[VersionedHash]outputs: List[Bytes]
EVM 执行将被修改如下。一个名为 inputs 的数组将被初始化为空。每当被调用 used_precompile_addresses 中的地址时,我们向 inputs 添加一个 InputsRecord(callee_address, Gas, input_calldata) 对象,并将调用的 RETURNDATA 设置为 outputs[i]。最后,我们检查 used_precompile_addresses 总共被调用了 len(outputs) 次,以及 inputs_commitments 是否与生成的 blob 对 inputs 的 SSZ 序列化的承诺的结果匹配。暴露 inputs_commitments 的目的是为了使外部 SNARK 能够证明 inputs 和 outputs 之间的关系。
请注意 inputs 和 outputs 之间的不对称性,inputs 存储在哈希中,而 outputs 存储在必须提供的字节中。这是因为执行需要由仅看到输入并理解 EVM 的客户端执行。EVM 执行已经为它们生成了输入,因此它们只需要检查生成的输入是否与声明的输入匹配,这只需要进行哈希检查。然而,必须完全提供输出给它们,因此必须具备数据可用性。
另一个有用的功能可能是允许「特权交易」从任意发送方账户进行调用。这些交易可以在两个其他交易之间运行,或者在另一个(可能也是特权)交易中,同时调用预编译。这可以用于允许非 EVM 机制调用回 EVM。
该设计可以修改以支持新的或修改的操作码,除了新的或修改的预编译。即使只有预编译,该设计也非常强大。例如:
通过设置 used_precompile_addresses 以包含在状态中的其帐户对象中设置了某个标志的一组常规帐户地址的列表,并生成证明其正确构建的 SNARK,可以支持像 Arbitrum Stylus 一样的功能,其中合约可以在 EVM 或 WASM(或其他 VM)中编写其代码。特权交易可用于允许 WASM 账户回调 EVM。
通过添加外部检查,确保多个 EVM 执行的输入 / 输出记录和特权交易以正确的方式匹配,可以证明通过同步通道相互通信的多个 EVM 的并行系统。
类型为 4 的 ZK-EVM 可以通过具有多个实现来运作:一个将 Solidity 或另一种更高级别语言直接转换为 SNARK 友好 VM 的实现,另一个将其编译为 EVM 代码并在规定的 ZK-EVM 中执行的实现。第二个(不可避免地较慢的)实现只能在故障证明者发送一笔交易声称存在错误的情况下运行,如果他们能够提供两者处理不同的交易,则可以收集赏金。
通过使所有调用返回零并将调用映射到添加到块末尾的特权交易,可以实现纯异步 VM。
扩展:支持状态证明者
上述设计的挑战是它完全是无状态的,这使得它在数据效率上表现不佳。使用理想的数据压缩,相对于仅使用无状态压缩,有状态压缩可以使 ERC20 发送在空间利用上更高效,最多可以提高 3 倍。
除此之外,有状态的 EVM 不需要提供证人数据。在两种情况下,原则是相同的:当我们已经知道数据可用,因为它是由 EVM 的先前执行输入或产生的数据时,要求数据可用是一种浪费。
如果我们希望使 ZK-EVM 功能具有状态,则有两种选择:
要求σpre 要么为空,要么数据可用的预先声明的键和值列表,要么是某个先前执行的σpost。
为由块生成的收据 R 添加一个 blob 承诺到 (σpre, σpost, Proof) 元组。ZKEVMClaimTransaction 中可以引用并在其执行过程中访问任何先前生成或使用的 blob 承诺,包括表示块、证人、收据或甚至常规 EIP-4844 blob 事务的承诺(可能有一些时间限制,可以通过一系列指令进行引用:“在块 + 证人数据的位置 j 处插入承诺 i 的字节 N…N+k-1”)
(1)基本意思是:与其确立无状态的 EVM 验证,我们将确立 EVM 子链。
(2)本质上是创建了最小的内置有状态压缩算法,该算法使用先前使用或生成的 blob 作为字典。这两者都对证明者节点提出了负担,只对证明者节点提出了负担,以存储更多的信息;
在情况(2)中,更容易对这种负担进行时间限制,而在情况(1)中则较难。
封闭多证明者系统和离链数据的论点
封闭多证明者系统,其中在 M-of-N 结构中有固定数量的证明系统,避免了上述很多复杂性。尤其是,封闭多证明者系统无需担心确保数据存在于链上。此外,封闭多证明者系统将允许 ZK-EVM 证明链下执行;这使其与 EVM Plasma 解决方案兼容。
然而,封闭多证明者系统增加了治理复杂性并削弱了可审计性,这些是需要权衡与这些优势相对应的高代价。
如果我们内置 ZK-EVM 并将其作为协议特性,那么 L2 项目的持续作用是什么?
目前由 L2 团队自行实施的 EVM 验证功能将由协议处理,但 L2 项目仍将负责许多重要功能:
- 快速预确认: 单时隙最终性可能会使 L1 时隙变慢,而 L2 已经通过其自身的安全性为用户提供了由预确认支持的服务,延迟远低于一个时隙。这项服务将继续完全由 L2 负责。
- MEV 缓解策略: 这可能包括加密的内存池、基于声望的顺序选择等特性,而这些是 L1 不愿意实施的。
- 对 EVM 的扩展: 第二层项目可以引入对 EVM 的重要扩展,为其用户提供显著的价值。这包括“几乎 -EVMs”和完全不同的方法,例如 Arbitrum Stylus 的 WASM 支持和 SNARK 友好的 Cairo 语言。
- 面向用户和开发者的便利性: 第二层团队在吸引用户和项目进入其生态系统并让他们感到受欢迎方面付出了很多努力;他们通过在其网络内捕获 MEV 和拥塞费用来获得报酬。这种关系将继续存在下去。