本文介绍了zkEVM,一种EVM兼容的zk-Rollup方案,旨在解决当前zk-Rollup在通用DApp构建和可组合性方面的挑战。zkEVM通过生成EVM验证的zk证明,使得现有的以太坊应用可以轻松迁移到zk-Rollup上,同时详细讨论了zkEVM的设计挑战以及当前技术背景下实现zkEVM的可行性,并阐述了从头开始构建zkEVM的高级思路和关键技术。
感谢 Vitalik Buterin、Barry Whitehat、Chih-Cheng Liang、Kobi Gurkan 和 Georgios Konstantopoulos 的评论和深刻见解。
我们认为 zk-Rollup 是圣杯——一流的 Layer 2 扩展解决方案,它非常便宜且安全。然而,现有的 zk-Rollup 是特定于应用程序的,这使得在 zk-Rollup 内部构建通用的可组合 DApp 和迁移现有应用程序变得困难。我们引入了 zkEVM,它可以为一般的 EVM 验证生成 zk 证明。这允许我们构建一个完全 EVM 兼容的 zk-Rollup,任何现有的以太坊应用程序都可以轻松迁移到该 Rollup。
在本文中,我们确定了 zkEVM 的设计挑战以及为什么现在可以实现它。我们还给出了更具体的直觉,并描述了从头开始构建它的高级思想。
zk-Rollup 被认为是 以太坊的最佳扩展解决方案。 它与以太坊 Layer 1 一样安全,并且与所有其他 Layer 2 解决方案相比,具有最短的最终确定时间(详细比较此处)。
从中长期来看,随着 ZK-SNARK 技术的改进,ZK rollup 将在所有用例中胜出。 – Vitalik Buterin
zk-Rollup 的基本思想是将大量交易聚合到一个 Rollup 区块中,并为该区块生成一个简洁的链下证明。然后,Layer 1 上的智能合约只需要验证该证明并直接应用更新的状态,而无需重新执行这些交易。这可以帮助节省一个数量级的 Gas 费用,因为证明验证比重新执行计算便宜得多。 另一种节省来自数据压缩(即,仅保留最少的链上数据用于验证)
虽然 zk-Rollup 安全且高效,但其应用仍然仅限于支付和交换。由于以下两个原因,很难构建通用的 DApp。
简而言之,zk-Rollup 对开发者不友好,而且目前功能有限。
这是我们想要解决的最大问题。我们希望通过直接支持原生 EVM 验证来提供最佳的开发者体验并支持 Layer 2 中的可组合性,以便现有的以太坊应用程序可以按原样简单地迁移到 zk-Rollup 上。
有两种方法可以在 zk-Rollup 中构建通用 DApp。
"电路" 是指零知识证明中使用的程序表示形式。例如,如果你想证明 hash(x) = y,你需要使用电路形式重写哈希函数。电路形式仅支持非常有限的表达式(即,R1CS 仅支持加法和乘法)。因此,使用电路语言编写程序非常困难——你必须使用 add 和 mul 构建所有程序逻辑(包括 if else、循环等)。
第一种方法要求开发人员为不同的 DApp 设计专用的“ASIC”电路。 这是使用零知识证明的最传统方法。 每个 DApp 都将通过定制的电路设计来减少开销。 然而,这带来了可组合性的问题,因为电路是“静态的”,并且糟糕的开发者体验,因为它需要在电路设计方面具有很强的专业知识[2]。
第二种方法不需要开发人员进行任何特殊的设计或专业知识。这种基于机器的证明的高级思想是,任何程序最终都将在 CPU 上运行,因此我们只需要构建一个通用的 CPU 电路来验证底层的 CPU 步骤。然后我们可以使用这个 CPU 电路来验证任何程序的执行。在我们的场景中,程序是智能合约,CPU 是 EVM。然而,这种方法在过去几年中并没有被普遍采用,因为它开销很大。 例如,即使你只想证明 add
的结果在一个步骤中是正确的,你仍然需要承担整个 EVM 电路的开销。 如果你的执行跟踪中有数千个步骤,那么在证明者方面将有 1000 倍的 EVM 电路开销。[3]
最近,人们一直在进行大量研究,以优化以下两种方法的 zk 证明,包括 (i) 提出新的 zk 友好的原语,即 Poseidon hash 在电路中可以实现比 SHA256 高 100 倍的效率,(ii) 正在进行的工作,以提高通用可验证 VM 的效率,如 TinyRAM,以及 (iii) 越来越多的通用优化技巧,如 Plookup,甚至更一般的更快的密码库。
在我们之前的文章中,我们建议为每个 DApp 设计“ASIC”电路,并让他们通过加密承诺进行通信。然而,根据社区的反馈,我们改变了我们的优先级,并将首先专注于构建通用的 EVM 电路(所谓的“zkEVM”),遵循第二种方法。zkEVM 将允许与在 Layer 1 上开发完全相同的开发者体验。我们将接管设计复杂性并解决通过定制 EVM 电路设计带来的效率问题,而不是将设计复杂性留给开发人员。
zkEVM 很难构建。即使多年来直觉很清晰,但没有人成功构建过原生 EVM 电路。 与 TinyRAM 不同,由于以下原因,设计和实现 zkEVM 更具挑战性:
CALL
,并且它还有与执行上下文和 gas 相关的错误类型。 这将给电路设计带来新的挑战。add
这样简单的操作码也可能导致整个 EVM 电路的开销。感谢研究人员在该领域取得的巨大进展,越来越多的效率问题在过去两年中得到解决,zkEVM 的证明成本最终是可行的!最大的技术改进来自以下几个方面:
除了强大的直觉和技术改进之外,我们仍然需要更清楚地了解我们需要证明什么,并找出更具体的架构。 我们将在后续文章中介绍更多技术细节和比较。 在这里,我们描述了总体工作流程和一些关键思想。
对于开发者来说,他们可以使用任何 EVM 兼容的语言实现智能合约,并将编译后的字节码部署在 Scroll 上。 然后,用户可以发送交易以与那些已部署的智能合约进行交互。 用户和开发人员的体验将与 Layer 1 完全相同。 但是,Gas 费用显着降低,并且交易在 Scroll 上立即获得预确认(提款仅需几分钟即可完成)。
即使外部的工作流程保持不变,Layer 1 和 Layer 2 的底层处理过程也完全不同:
让我们更详细地解释一下 Layer 1 和 Layer 2 上的交易是如何不同进行的。
在 Layer 1 中,已部署智能合约的字节码存储在以太坊存储中。 交易将在 P2P 网络中广播。 对于每笔交易,每个完整节点都需要加载相应的字节码并在 EVM 上执行它以达到相同的状态(交易将用作输入数据)。
在 Layer 2 中,字节码也存储在存储中,用户将以相同的方式运行。 交易将链下发送到中心化的 zkEVM 节点。 然后,zkEVM 不仅仅是执行字节码,而是生成一个简洁的证明来证明在应用交易后状态已正确更新。 最后,Layer 1 合约将验证该证明并更新状态,而无需重新执行交易。
让我们更深入地了解执行过程,看看 zkEVM 最终需要证明什么。 在原生执行中,EVM 将加载字节码并从头开始逐个执行字节码中的操作码。 每个操作码都可以被认为是在执行以下三个子步骤:(i)从堆栈、内存或存储中读取元素(ii)对这些元素执行一些计算(iii)将结果写回堆栈、内存或存储。[5] 例如,add
操作码需要从堆栈中读取两个元素,将它们相加并将结果写回堆栈。
因此,很明显,zkEVM 的证明需要包含与执行过程相对应的以下几个方面
(你正在运行从给定地址加载的正确操作码)
(字节码按顺序执行,不会遗漏或跳过任何操作码)
(每个操作码中的三个子步骤都已正确执行,R/W + 计算)
在设计 zkEVM 的架构时,我们需要逐个处理/解决上述三个方面。
这部分就像一个“可验证存储”,我们需要一些技术来证明我们正在正确读取。 可以使用密码累加器来有效地实现此目的。[6]
让我们以 Merkle 树为例。 已部署的字节码将作为 Merkle 树中的一个叶子存储。 然后,验证者可以使用简洁的证明来验证字节码是否已从给定地址正确加载(即,在电路中验证 Merkle 路径)。 对于以太坊存储,我们需要电路与 Merkle Patricia Trie 和 Keccak 哈希函数兼容。
将字节码移动到静态电路中的一个问题是条件操作码,如 jump
(对应于智能合约中的循环、if else 语句)。 它可以跳到任何地方。 在使用特定输入运行字节码之前,无法确定目标。 这就是我们需要验证实际执行跟踪的原因。 执行跟踪可以被认为是“展开的字节码”,它将包括实际执行顺序中的操作码序列(即,如果你跳到另一个位置,则跟踪将包含目标操作码和位置)。
证明者将直接提供执行跟踪作为电路的 witness。 我们需要证明提供的执行跟踪确实是从具有特定输入的字节码“展开”的。 这个想法是强制程序计数器的值保持一致。 为了处理不确定的目标,想法 是让证明者提供所有内容。 然后,你可以使用查找参数有效地检查一致性(即,证明具有适当全局计数器的操作码包含在“总线”中)。
这是最重要的部分——证明执行跟踪中的每个操作码都是正确且一致的。 如果你将所有内容直接放在一起,将会带来巨大的开销。 这里重要的优化思想是
此架构首先由以太坊基金会指定。 它仍处于早期阶段,并且正在积极开发中。 我们正在与他们密切合作,以找到实现 EVM 电路的最佳方法。 到目前为止,已定义了最重要的特征,并且一些操作码已在此处实现此处(在 Halo2 存储库中使用 UltraPlonk 语法)。 更多详细信息将在后续文章中介绍。 我们建议感兴趣的读者阅读此文档。 开发过程将是透明的。 这将是一项社区工作,并且是完全开源的设计。 希望更多的人能够加入并为此做出贡献。
zkEVM 不仅仅是 Layer 2 扩展。 它可以被认为是通过 Layer-1 有效性证明来扩展以太坊 Layer 1 的直接方式。 这意味着你可以扩展现有的 Layer 1,而无需任何特殊的 Layer 2。
例如,你可以将 zkEVM 用作完整节点。 该证明可用于直接证明现有状态之间的转换——无需将任何内容移植到 Layer 2,你可以直接为所有 Layer 1 交易进行证明! 更广泛地说,你可以使用 zkEVM 为整个以太坊(如 Mina)生成一个简洁的证明。 你唯一需要添加的是证明递归(即,将一个区块的验证电路嵌入到 zkEVM 中)[7]。
zkEVM 可以为开发者和用户提供相同的体验。它在不牺牲安全性的前提下,便宜了几个数量级。已经提出了以模块化方式构建它的架构。它利用零知识证明的最新突破来减少开销(包括自定义约束、查找参数、证明递归和硬件加速)。我们期待看到更多人加入 zkEVM 社区并与我们集思广益!
Scroll Tech 是一家新建的科技驱动型公司。我们的目标是构建一个与 EVM 兼容的 zk-Rollup,并具有强大的证明网络(在此处查看概述)。整个团队现在专注于开发。我们正在积极招聘更多充满激情的开发者,请通过 hire@scroll.tech 与我们联系。如果你对技术内容有任何疑问,请通过 yezhang@scroll.tech 与我联系。DM 也开放。
[1]:Starkware 几天前声称实现了可组合性(此处参考)
[2]:电路是固定的和静态的。例如,在将程序实现为电路时,你不能使用可变上限循环。上限必须固定为其最大值。它无法处理动态逻辑。
[3]:为了更清楚起见,我们在此处详细说明了 EVM 电路的成本。正如我们之前描述的,电路是固定的和静态的。因此,EVM 电路需要包含所有可能的逻辑(比纯 add
大 10000 倍)。这意味着即使你只想证明 add
,你仍然需要承担 EVM 电路中所有可能逻辑的开销。它将使成本扩大 10000 倍。在执行跟踪中,你需要证明一系列操作码,并且每个操作码都会有如此大的开销。
[4]:EVM 本身并不与 Merkle Patricia 树紧密绑定。 MPT 只是目前存储以太坊状态的方式。 可以轻松插入另一个(即,当前提议用 Verkle 树 替换 MPT)。
[5]:这是一个高度简化的抽象。 从技术上讲,“EVM 状态”列表更长,包括 PC、剩余 gas、调用堆栈(所有上述内容加上堆栈中每次调用的地址和静态性)、一组日志和交易范围的变量(热存储槽、退款、自毁)。 可以通过为不同的调用上下文添加额外的标识符来直接支持可组合性。
[6]:我们使用accumulator存储,因为存储量很大。 对于内存和堆栈,可以使用可编辑的 Plookup(可以通过这种方式有效地实现“RAM”)。
[7]:向 zkEVM 电路添加完整的递归证明并非易事。 进行递归的最佳方法仍然是使用循环椭圆曲线(即 Pasta 曲线)。 需要一些"包装"过程才能使其在以太坊 Layer 1 上可验证。
- 原文链接: hackmd.io/@yezhang/S1_KM...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!