以太坊的下一个变革:Vitalik 探讨 L1 内置 ZK-EVM 蓝图
作者:Vitalik Buterin
编译:TinTinLand
原文:https://notes.ethereum.org/@vbuterin/enshrined_zk_evm
建立在以太坊之上的 Layer2 EVM 协议,包括 Optimistic Rollups 和 ZK Rollups,都依赖于 EVM 验证。然而,这需要它们信任庞大的代码库,如果代码库中存在漏洞,这些虚拟机就有被黑客攻击的风险。此外,即便是想要与 L1 EVM 完全等效的 ZK-EVM,也需要某种形式的治理机制,以便将 L1 EVM 的更改复制到其自身的 EVM 实现中。
这种情况并不理想,因为这些项目在复制已存在于以太坊协议中的功能,并且以太坊治理已经负责升级和修复错误的地方:ZK-EVM 基本上做的是验证 Layer1 以太坊区块的工作!此外,在未来几年中,我们预计「轻客户端」(https://ethereum.org/en/developers/docs/nodes-and-clients/light-clients/)会变得越来越强大,很快就会使用 ZK-SNARKs 完全验证 L1 EVM 执行。到那时,以太坊网络将有效地拥有内置的 ZK-EVM。因此,一个问题就出现了:为什么不将这个 ZK-EVM 本地化为 Rollups 呢?
本文将描述可以实施的「内置的 ZK-EVM」的几个版本,并详细介绍权衡和设计挑战,以及不采取特定方向的原因。实施协议特性的优势应该与留给生态系统并保持基础协议简单性的好处进行权衡。
我们希望「内置的 ZK-EVM」
具有哪些关键特性?
基本功能
基本功能
验证以太坊区块。协议特性(暂未确定是否为操作码、预编译或其他机制)应接受(至少)一个预状态根、一个区块和一个后状态根作为输入,并验证后状态根实际上是在预状态根上执行区块的结果。
与以太坊多客户端理念的兼容性
与以太坊多客户端理念的兼容性
详情可查看:https://vitalik.ca/general/2023/03/31/zkmulticlient.html
这意味着我们希望避免将单一证明系统固化,而是允许不同的客户端使用不同的证明系统。这又涉及一些要点:
数据可用性要求
对于通过「内置的 ZK-EVM」进行的任何 EVM 执行,我们希望能保证底层数据可用性,这样使用不同证明系统的证明者可以重新证明执行,并且依赖该证明系统的客户端可以验证新生成的证明。
证明存放在 EVM 和区块数据结构之外
ZK-EVM 功能不会将 SNARK 直接作为 EVM 输入,因为不同客户端期望不同类型的 SNARK。相反,它可能类似于 blob 验证:交易可以包括(预状态、区块主体、后状态)需要证明的声明,一个操作码或预编译可以访问这些声明的内容,客户端共识规则将分别检查数据可用性和区块中所作每个声明的证明存在性。
可审计性
可审计性
如果任何执行被证明,我们希望底层数据可用,以便用户和开发者可以检查。实际上,这增加了对数据可用性要求的重要性。
可升级性
可升级性
如果发现某个特定的 ZK-EVM 方案存在漏洞,我们希望能够迅速修复,而不需要硬分叉。这又增加了证明存放在 EVM 和区块数据结构之外的重要性。
支持 almost - EVMs
支持 almost - EVMs
L2 的吸引力之一是在执行层面进行创新,并对 EVM 进行扩展。如果给定 L2 的虚拟机与 EVM 仅略有不同,那么当 L2 与 EVM 相同的部分仍然可以使用原生的协议内 ZK-EVM 时,这将是很好的,只有对于与 EVM 不同的部分才会依赖他们自己的代码。这可以通过设计 ZK-EVM 功能的方式来完成,使其允许调用者指定位字段、操作码或地址列表,由外部提供表来处理而不是由 EVM 本身。我们还可以在有限的范围内使 gas 成本可自定义。
「开放式」与「封闭式」
多客户端系统
「多客户端理念」可能是上述列表中最有主张的要求。有放弃它并专注于一个 ZK-SNARK 方案的选择,这将简化设计,但代价是成为以太坊更大的「理念转向」(因为它实际上是放弃以太坊长期以来的多客户端理念),并引入更大的风险。在长期的未来,例如形式验证技术变得更加完善时,可能更好的做法是走这条路线,但目前看来风险似乎太大。
另一种选择是封闭的多客户端系统,在协议中已知一组固定的证明系统。例如,我们可能决定使用三个 ZK-EVM:PSE ZK-EVM(https://privacy-scaling-explorations.github.io/zkevm-docs/)、Polygon ZK-EVM(https://polygon.technology/polygon-zkevm) 和 Kakarot(https://www.kakarot.org/)。一个区块需要来自这三个中的两个的证明才能被视为有效。这比单一证明系统要好,但它使系统更少适应性,因为用户必须维护每个证明系统的验证器,必然会对纳入新证明系统的政治治理程序等产生影响。
这促使我倾向于开放的多客户端系统,其中证明被放置在「区块之外」并由客户端单独验证。个体用户将使用他们想要的客户端来验证区块,只要有一个证明者为该证明系统创建证明就可以。证明系统通过说服用户运行它们来获得影响力,而不是通过说服协议治理流程。然而,这种方法的复杂性成本更高,正如我们将看到的那样。
我们希望 ZK-EVM 实现
具备哪些关键特性?
除了正确功能和安全性的基本保证外,最重要的特性是速度。虽然可以设计一个异步的协议内 ZK-EVM 功能,只有在经过 N 个槽之后才返回每个声明的答案,但是如果我们可以可靠地保证在几秒钟内生成一个证明,那么每个区块中发生的任何事情都将是自包含的,问题就会变得容易得多。
虽然今天为以太坊区块生成证明需要很多分钟甚至小时,但我们知道没有理论上的理由阻止大规模并行化:我们总是可以组装足够多的 GPU 来分别证明区块执行的不同部分,然后使用递归 SNARKs 将证明合并在一起。此外,通过 FPGA 和 ASIC 的硬件加速可以帮助优化证明。然而,要实现这一点是一个重大的工程挑战,不应低估。
在协议中的 ZK-EVM 功能
具体是什么样的?
类似于 EIP-4844 blob(https://eips.ethereum.org/EIPS/eip-4844#blob-transaction)交易,我们引入了一种包含 ZK-EVM 声明的新交易类型:
class ZKEVMClaimTransaction(Container):
pre_state_root: bytes32
post_state_root: bytes32
transaction_and_witness_blob_pointers: List[VersionedHash]
...
与 EIP-4844 类似,传播于内存池中的对象将是交易的修改版本:
class ZKEvmClaimNetworkTransaction(Container):
pre_state_root: bytes32
post_state_root: bytes32
proof: bytes
transaction_and_witness_blobs: List[Bytes[FIELD_ELEMENTS_PER_BLOB * 31]]
后者可以转换为前者,但反之则不然。我们还扩展了区块 side car 对象(在 EIP-4844 中引入,https://eips.ethereum.org/EIPS/eip-4844)以包含区块中所做声明的证明列表。
请注意,在实际操作中,我们很可能希望将 sidecar 分为两个独立的 sidecars,一个用于 blob,另一个用于证明,并为每种类型的证明设立一个独立的子网(另外还有一个子网用于 blobs)。
在共识层上,我们添加了一个验证规则:只有当客户端看到区块中每个声明的有效证明后,才能接受该区块。证明必须是一个 ZK-SNARK,证明了 transaction_and_witness_blobs 的串联是(Block, Witness)对的序列化,以及使用 Witness(i)在 pre_state_root 上执行区块是有效的,并且(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 的 proof 版本。Polygon ZK-EVM 的证明节点看到了这一情况,计算并重新发布了具有 Polygon ZK-EVM proof 的对象。
这增加了最早接受区块的诚实节点和最后接受同一区块的最新诚实节点之间的总最大延迟,从 δ 到 2δ+Tprove(在这里假设 Tprove < 5s)。
然而,好消息是,如果我们采用单个槽最终确定性,我们几乎可以肯定地将这种额外延迟与 SSF 中固有的多轮共识延迟一起「流水线」处理。例如,在这个 4 子槽提案(https://ethresear.ch/t/a-simple-single-slot-finality-protocol/14920)中,「head vote」步骤可能只需要检查基本区块的有效性,但「冻结和确认」步骤需要存在一个证明。
扩展:支持「almost-EVM」
ZK-EVM 功能的一个理想目标是支持「almost-EVM」:具有一些额外特性的 EVM。这可能包括新的预编译器、新的操作码、合同可以用 EVM 或完全不同的 VM(例如 Arbitrum Stylus 中的情况,https://docs.arbitrum.io/stylus/stylus-gentle-introduction)编写的选项,甚至具有同步交叉通信的多个并行 EVM。
一些修改可以以简单方式支持:我们可以定义一种语言,允许 ZKEVMClaimTransaction 传递修改后的 EVM 规则的完整描述。这可以应用于:
自定义 gas 成本表(用户不允许减少 gas fee,但可以增加)
禁用特定操作码
设置区块编号(这将意味着根据硬分叉而有不同的规则)
设置激活专门用于 L2 但不用于 L1 的一整套 EVM 变更的标志,或者其他简单的变更
为了更加开放地让用户引入新功能,我们可以通过将预编译器(或操作码)的输入/输出记录作为 ZKEVMClaimNetworkTransaction 中 blob 的一部分来增加预编译器的输入/输出记录:
class PrecompileInputOutputTranscript(Container):
used_precompile_addresses: List[Address]
inputs_commitments: List[VersionedHash]
outputs: List[Bytes]
EVM 执行将进行修改。一个数组 inputs 将被初始化为空。第 i 次调用 used_precompile_addresses 中的地址时,我们向 inputs 添加 InputsRecord(callee_address, gas, input_calldata),并将调用的 RETURNDATA 设置为 outputs[i]。最后,我们检查 used_precompile_addresses 总共被调用了 len(outputs) 次,以及 inputs_commitments 是否与对 inputs 的 SSZ 序列化产生的 blob 承诺的结果相匹配。暴露 inputs_commitments 的目的是为了便于外部 SNARK 证明输入与输出之间的关系。
请注意,输入被存储在哈希中,而输出被存储在必须提供的字节中,存在一定的不对称性。这是因为执行需要能够由只看到输入并理解 EVM 的客户端完成。EVM 执行已经为它们生成了输入,因此它们只需要检查生成的输入是否与声称的输入匹配,这只需要进行哈希检查。然而,输出必须完整提供给它们,因此必须具有数据可用性。
另一个有用的特性可能是允许「特权交易」,可以从任意发送方帐户进行调用。这些交易可以在两个其他交易之间运行,或者在另一个(可能也是特权的)交易中调用预编译器时运行。这可以用于允许非 EVM 机制回调到 EVM。
此设计可以修改以支持新的或修改后的操作码,除了新的或修改后的预编译器。即使只有预编译器,这种设计也非常强大。例如:
通过将 used_precompile_addresses 设置为包含在状态中具有某些标志的常规帐户地址列表,并生成一个 SNARK 证明以证明其正确构造,您可以支持像 Arbitrum Stylus(https://docs.arbitrum.io/stylus/stylus-gentle-introduction)风格的特性,其中合同可以在 EVM 或 WASM(或其他 VM)中编写其代码。特权交易可用于允许 WASM 帐户回调到 EVM。
通过添加外部检查来验证多个 EVM 执行的输入/输出记录和特权交易以正确方式匹配,您可以证明一种多个 EVM 的并行系统,这些系统通过同步通道进行通信。
Type 4 的 ZK-EVM(https://vitalik.ca/general/2022/08/04/zkevm.html)可以通过多个实现来操作:一个将 Solidity 或其他更高级别的语言直接转换为 SNARK 友好的 VM 的实现,并另一个将其编译为 EVM 代码并在「内置的 ZK-EVM」中执行。第二个(不可避免地更慢)实现只能在出现故障证明者发送事务断言存在漏洞的情况下运行,并在提供一个两者处理不同的交易时收集赏金。
可以通过使所有调用返回零并将调用映射到添加到区块末尾的特权交易来实现纯异步 VM。
扩展:支持有状态的证明者
上述设计的一个挑战是它完全是无状态的,这使得它数据效率低下。使用理想的数据压缩,与仅使用无状态压缩相比,一个 ERC20 发送可以是有状态压缩的 3 倍空间效率。
除此之外,有状态的 EVM 不需要使见证数据可用。在两种情况下,原则都是相同的:要求数据可用是一种浪费,因为我们已经知道该数据是可用的,因为它是在先前的 EVM 执行中输入或生成的。
如果我们想使 ZK-EVM 功能有状态,则有两种选择:
1. 要求 σpre 为空,或者是一个数据可用的预先声明的密钥和值列表,或者是先前执行的某个执行的 σpost。
2. 向(σpre,σpost,Proof)元组添加一个与区块生成的 receipt R 的 blob 承诺。在 ZKEVMClaimTransaction 中可以引用任何先前生成或使用的 blob 承诺,包括代表区块、见证、receipt,甚至常规的 EIP-4844 blob 交易,或许还有一些时间限制,可以在其执行期间访问(可能通过一系列指令:「在区块+见证数据的位置 j 插入承诺 i 的字节 N...N+k-1」)
(1) 基本上表示:与其固化无状态 EVM 验证,我们将固化 EVM 子链。(2)本质上是创建了一个最小的内置有状态压缩算法,该算法使用先前使用或生成的 blobs 作为字典。这两种方式都会增加对证明节点,仅对证明节点的负担,并且仅存储更多信息;在情况(2)中,比情况(1)更容易使负担受到时间限制。
封闭式多证明系统
和链下数据的论据
封闭式多证明者系统,其中具有 M-of-N 结构的固定数量的证明系统,避免了上述许多复杂性。封闭式多证明者系统特别是不需要担心确保数据在链上的问题。此外,封闭式多证明者系统将允许 ZK-EVM 证明链下执行;这使其与 EVM Plasma 解决方案(https://vitalik.eth.limo/general/2023/11/14/neoplasma.html)兼容。
然而,封闭式多证明者系统增加了治理复杂性并消除了可审计性,这些是需要权衡这些好处的高成本。
如果我们将 ZK-EVM 确立为协议功能,
那么「Layer2 项目」的持续作用是什么?
当前由 Layer2 团队实现的 EVM 验证功能将由协议处理,但 Layer2 项目仍将负责许多重要功能:
快速预确认:单个槽最终性可能会使 Layer1 槽变慢,而 Layer2 项目已经为其用户提供了由 Layer2 自身安全性支持的「预确认」,延迟远低于一个槽。这项服务将继续纯粹由 Layer2 负责。
MEV 缓解策略:这可能包括加密内存池、基于声誉的序列选择以及 Layer1 不愿意实施的其他功能。
对 EVM 的扩展:Layer2 项目可以包含对 EVM 的重要扩展,为其用户提供巨大价值。这包括「alomost-EVM」 和诸如 Arbitrum Stylus(https://docs.arbitrum.io/stylus/stylus-gentle-introduction)的 WASM 支持以及 SNARK-friendly Cairo(https://www.cairo-lang.org/)语言等完全不同的方法。
面向用户和开发者的便利性:Layer2 团队致力于吸引用户和项目进入其生态系统并使其感到受欢迎;他们通过在其网络内捕获 MEV 和拥堵费用来获得补偿。这种关系将继续存在。
往期精彩
探讨 Rust 向量数据库开发潜力,本周三 Rust 唠嗑室见
账户抽象之外,深入探讨 WebAuthn 与 Passkey 的密钥体验创新
主网上线一年来,公链新星 Aptos 如何培育亚太和华语开发生态?
关于我们
ABOUT US
TinTinLand 是赋能下一代开发者的技术社区,通过聚集、培育、输送开发者到各开放网络,共同定义并构建未来。
Discord: https://discord.gg/kmPnTDSFu8
Twitter: https://twitter.com/OurTinTinLand
Bilibili: https://space.bilibili.com/1152852334
Medium: https://medium.com/@tintin.land2021
YouTube: https://www.youtube.com/channel/UCfHiMcFt-4btbC75FsReQh