以太坊之上的 2 层 EVM 协议(包括乐观rollup和 ZK rollup)依赖于 EVM 验证。然而,这要求他们信任大型代码库,如果该代码库中存在错误,这些虚拟机就有被黑客攻击的风险。此外,这意味着即使 ZK-EVM 想要与 L1 EVM 保持完全相同,也需要某种形式的治理,将对 L1 EVM 的更改复制到自己的 EVM 实现中。 这种情况不是最理想的,因为这些项目正在复制以太坊协议中已经存在的功能,并且以太坊治理已经负责进行升级和修复错误:ZK-EVM 基本上完成与验证 1 层以太坊区块相同的工作!此外,在接下来的几年里,我们预计轻客户端会变得越来越强大,并且很快就会达到使用 ZK-SNARK 来完全验证 L1 EVM 执行的程度。届时,以太坊网络将有效地拥有内置的ZK-EVM。那么问题来了:为什么不让ZK-EVM原生地用于rollup呢? 这篇文章将描述可以实现的“封装ZK-EVM(enshrined ZK-EVM)”的几个版本,并讨论权衡和设计挑战,以及不朝特定方向发展的原因。应权衡实现协议功能的好处与将事情交给生态系统并保持基本协议简单的好处。 我们希望从ZK-EVM中获得哪些关键属性?基本功能:验证以太坊区块。协议功能(到目前为止,无论是操作码、预编译还是其他机制,都处于开放状态)应该(至少)接受前状态根、区块和后状态根作为输入,并验证后状态根实际上是在前状态根之上执行区块的结果。 与以太坊的多客户端理念兼容。这意味着我们希望避免采用单一的证明系统,而是允许不同的客户端使用不同的证明系统。这又暗示了几点: 数据可用性要求:对于使用 ZK-EVM 进行验证的任何 EVM 执行,我们希望保证底层数据可用,以便使用不同证明系统的证明者可以重新证明执行,并且客户端依赖于该证明系统可以验证那些新生成的证明。 证明存在于 EVM 和区块数据结构之外:ZK-EVM 功能实际上不会将 SNARK 作为 EVM 输入,因为不同的客户端会期望不同类型的 SNARK。相反,它的工作方式可能类似于 blob 验证:交易可能包括需要证明的(前状态、区块体、后状态)语句,操作码或预编译可以访问这些语句的内容,并且客户端共识规则将分别检查数据可用性以及区块中提出的每个声明的证据是否存在。 可审计性。如果任何执行得到验证,我们希望底层数据可用,以便如果出现任何问题,用户和开发人员可以检查它。实际上,这又增加了数据可用性要求如此重要的一个原因。 可升级性。如果发现某个特定的 ZK-EVM 方案存在错误,我们希望能够快速修复它。这意味着不需要硬分叉来修复。这又增加了 EVM 和区块数据结构之外的证明如此重要的另一个原因。 支持almost-EVM(准EVM)。L2 的部分吸引力在于能够在执行层进行创新,并对 EVM 进行扩展。如果给定的 L2 的 VM 与 EVM 只有一点点不同,那么如果 L2 仍然可以使用原生协议内 ZK-EVM 来处理与 EVM 相同的部分,并且只依赖自己那部分不同的代码,那就太好了。这可以通过设计 ZK-EVM 功能来实现,允许调用者指定由外部提供的表而不是 EVM 本身处理的位字段或操作码列表或地址。我们还可以在一定程度上允许Gas成本进行定制。
“开放”vs“封闭”多客户端系统“多客户端理念”可能是此列表中最固执己见的要求。可以选择放弃它并专注于一种 ZK-SNARK 方案,这将简化设计,但代价是成为以太坊更大的“哲学支点”(因为这实际上是放弃以太坊的长期坚持的多客户理念)并以引入更大的风险为代价。在长远的未来,如果形式化验证技术变得更好,走这条路可能会更好,但目前风险似乎太大了。 另一种选择是封闭的多客户端系统,其中协议内有一组固定的证明系统。例如,我们可能决定使用三个 ZK-EVM:PSE ZK-EVM、Polygon ZK-EVM和Kakarot。一个区块需要提供这三个中两个的证明才有效。这比单一证明系统更好,但它使系统的适应性较差,因为用户必须为现有的每个证明系统维护验证者,合并新证明系统将不可避免地存在政治治理过程等。 这激发了我对开放式多客户端系统的偏好,其中证据被放置在“区块之外”并由客户端单独验证。个人用户可以使用他们想要验证区块的任何客户端,只要至少有一个证明者为该证明系统创建证明,他们就能够这样做。证明系统将通过说服用户运行它们来获得影响力,而不是通过说服协议治理流程。然而,正如我们将看到的,这种方法确实具有更高的复杂性成本。 我们希望ZK-EVM实现具有哪些关键属性?除了正确的功能和安全性的基本保证之外,最重要的属性是速度。可以设计一个异步的协议内 ZK-EVM 功能,仅在延迟 N 个slot后才返回每个声明的答案,如果我们能够保证可以在几秒钟可靠地生成证明,问题就变的简单,这样每个区块中发生的任何事情都是自给自足的。 虽然今天生成以太坊区块的证明需要花费几分钟或几小时,但我们知道没有任何理论上的原因阻止大规模并行化:我们总是可以组合足够的 GPU来分别证明区块执行的不同部分,然后使用递归 SNARK 来证明区块执行的不同部分。将证据放在一起。此外,通过 FPGA 和 ASIC 进行硬件加速有助于进一步优化证明。然而,实际上达到这一点是一项不容低估的重大工程挑战。 以太坊主网协议内ZK-EVM功能具体是什么样的?与EIP-4844 blob交易类似,我们引入了一种包含 ZK-EVM 声明的新交易类型: 与 EIP-4844 一样,在内存池中传递的对象将是交易的修改版本: 后者可以转化为前者,但反之则不行。我们还扩展了区块 sidecar 对象(在EIP-4844中引入)以包含区块中声明的证明列表。
请注意,在实践中,我们很可能希望将 sidecar 分成两个单独的 sidecar,一个用于 blob,一个用于证明,并为每种类型的证明拥有一个单独的子网(加上用于 blob 的附加子网)。 在共识层上,我们添加了一条验证规则,即只有当客户端看到区块中每个声明的有效证明后,才会接受该区块。证明必须是 ZK-SNARK,它证明串联transaction_and_witness_blobs是一对(Block, Witness)的序列化,并且在使用Witness在pre_state_root之上执行区块(i)是有效的,并且 (ii) 输出正确的post_state_root。客户可能会选择等待 M-of-N 种多种类型的证明。 这里的一个哲学注释是区块执行本身可以被简单地视为三元组,它需要与ZKEVMClaimTransaction对象中提供的三元组一起检查。因此,用户的 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)。 然而,好消息是,如果我们采用单slot最终确定性,我们几乎肯定可以将这种额外的延迟与 SSF 固有的多轮共识延迟一起“管道化”。例如,在这个 4 sub-slot提案中,“head vote”步骤可能只需要检查基本区块有效性,但“冻结并确认”步骤将需要存在证明。 扩展:支持“almost-EVM”ZK-EVM 功能的一个理想目标是支持“almost-EVM”:内置一些额外功能的 EVM。这可能包括新的预编译、新的操作码、在 EVM 或 EVM 中编写合约的选项。完全不同的VM(例如Arbitrum Stylus),甚至具有同步交叉通信的多个并行EVM。 可以通过简单的方式支持一些修改:我们可以定义一种语言,允许ZKEVMClaimTransaction传递修改后的 EVM 规则的完整描述。可以这样做: 为了允许用户通过引入新的预编译(或操作码)以更加开放的方式添加新功能,我们可以添加预编译输入/输出记录作为 blob 的一部分包含在ZKEVMClaimNetworkTransaction:
EVM 执行将修改如下。数组inputs将被初始化为空。used_precompile_addresses第 i 次调用in 中的地址时,我们将一个对象附加到输入InputsRecord(callee_address, gas, input_calldata),并将调用的RETURNDATA设置为outputs。最后,我们检查used_precompile_addresses是否总共被调用了len(outputs)次,并且inputs_commitments是否与inputs SSZ序列化生成的blob 承诺结果相匹配。暴露inputs_commitments的目的是为了方便外部SNARK证明输入和输出之间的关系。 请注意输入(存储在哈希中)和输出(存储在必须可用的字节中)之间的不对称性。这是因为执行需要由仅看到输入并理解 EVM 的客户端来执行。EVM 执行已经为它们生成了输入,因此它们只需要检查生成的输入是否与声明的输入匹配,这只需要哈希检查。然而,输出必须完整地提供给他们,因此数据必须可用。 另一个有用的功能可能是允许从任意发件人帐户进行呼叫的“特权交易”(privileged transactions)。这些交易可以在两个其他交易之间运行,也可以在调用预编译时的另一个(也可能是特权)交易期间运行。这可用于允许非 EVM 机制自call back EVM。 除了新的或修改的预编译之外,还可以修改此设计以支持新的或修改的操作码。即使只有预编译,这个设计也相当强大。例如: 通过设置used_precompile_addresses来包含在状态的帐户对象中设置了一些标志的常规帐户地址列表,并制作 SNARK 来证明其构造正确,你可以支持Arbitrum Stylus风格的功能,其中合约可以拥有其代码用 EVM 或 WASM(或其他 VM)编写。特权交易可用于允许 WASM 账户回调 EVM。 通过添加外部检查来确保多个 EVM 执行的输入/输出记录和特权交易以正确的方式匹配,你可以证明多个 EVM 的并行系统通过同步通道相互通信。 类型4 ZK-EVM可以通过多种实现来运行:一种将 Solidity 或另一种高级语言直接转换为 SNARK 友好的 VM,另一种将其编译为 EVM 代码并在所包含的 ZK-EVM 中执行。第二种(不可避免地较慢)实现只能在错误证明者发送断言存在错误的交易的情况下运行,如果他们可以提供两者不同对待的交易,则收集赏金。 纯异步虚拟机可以通过使所有调用返回零并将调用映射到添加到区块末尾的特权交易来实现。
扩展:支持有状态证明者上述设计的一个挑战是它是完全无状态的,这使得它的数据效率低下。通过理想的数据压缩,有状态压缩的ERC20 发送空间效率比无状态压缩高出 3 倍。
除此之外,有状态 EVM 不需要提供见证数据。在这两种情况下,原理是相同的:当我们已经知道数据可用时,要求数据可用是一种浪费,因为它是由 EVM 的先前执行输入或生成的。 如果我们想让 ZK-EVM 功能成为有状态的,那么我们有两个选择: 1、要求σpre要么为空,要么是预先声明的键和值的数据可用列表,要么是一些以前执行的σpost。 2、将 Blob 承诺添加到R收据,它由区块(σpre,σpost,R)元组生成。任何先前生成或使用的 blob 承诺,包括代表区块、见证人、收据甚至常规 EIP-4844 blob 交易的承诺,可能有一定的时间限制,都可以在ZKEVMClaimTransaction中引用并在其执行期间访问(可能通过一系列指令:“在区块+见证数据位置j上的承诺i插入N...N+k-1字节”) (1) 基本上是说:我们不会奉行无状态 EVM 验证,而是奉行EVM 子链。(2) 本质上是创建一个最小的内置状态压缩算法,该算法使用先前使用或生成的 blob 作为字典。两者都给证明节点带来了负担,并且只有证明节点需要存储更多信息;在情况(2)中,比情况(1)更容易使该负担有时间限制。
|