以太坊 - zkEVM - Yezhang

  • yezhang
  • 发布于 2022-03-15 14:57
  • 阅读 27

本文介绍了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 的评论和深刻见解。

TL;DR

我们认为 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 中开发 DApp,你需要使用一种特殊的语言(即 R1CS)编写所有智能合约逻辑。不仅所需语言的语法复杂,而且这样做还需要零知识证明方面的极强专业知识。
  • 其次,当前的 zk-Rollup 不支持可组合性[1]。这意味着不同的 zk-Rollup 应用程序无法在 Layer 2 中相互交互。这种性质会严重损害 DeFi 应用程序的可组合性。

简而言之,zk-Rollup 对开发者不友好,而且目前功能有限。

这是我们想要解决的最大问题。我们希望通过直接支持原生 EVM 验证来提供最佳的开发者体验并支持 Layer 2 中的可组合性,以便现有的以太坊应用程序可以按原样简单地迁移到 zk-Rollup 上。

在 zk-Rollup 中构建通用 DApp

有两种方法可以在 zk-Rollup 中构建通用 DApp。

  • 一种是为不同的 DApp 构建特定于应用程序的电路(“ASIC”)。
  • 另一种是为智能合约执行构建通用的“EVM”电路。

"电路" 是指零知识证明中使用的程序表示形式。例如,如果你想证明 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 的设计挑战

zkEVM 很难构建。即使多年来直觉很清晰,但没有人成功构建过原生 EVM 电路。 与 TinyRAM 不同,由于以下原因,设计和实现 zkEVM 更具挑战性:

  • 首先,EVM 对椭圆曲线的支持有限。 目前,EVM 仅支持 BN254 配对。 由于循环椭圆曲线 不被直接支持,因此很难进行证明递归。 在这种设置下,也很难使用其他专门的协议。 验证算法必须对 EVM 友好。
  • 其次,EVM 字长为 256 位。 EVM 在 256 位整数上运行(很像大多数常规 VM 在 32-64 位整数上运行),而 zk 证明最“自然地”在素数域上工作。 在电路中进行“不匹配的域算术”需要范围证明,这将为每个 EVM 步骤增加约 100 个约束。 这将使 EVM 电路的大小增加两个数量级。
  • 第三,EVM 有许多特殊的操作码。 EVM 与传统的 VM 不同,它有许多特殊的操作码,如 CALL,并且它还有与执行上下文和 gas 相关的错误类型。 这将给电路设计带来新的挑战。
  • 第四,EVM 是一种基于堆栈的虚拟机。 SyncVM (zksync) 和 Cairo (starkware) 架构在基于寄存器的模型中定义了自己的 IR/AIR。 他们构建了一个专门的编译器,将智能合约代码编译成一个新的 zk 友好的 IR。 他们的做法是语言兼容的,而不是原生 EVM 兼容的。 对于基于堆栈的模型来说,证明起来更难,并且很难直接支持原生工具链。
  • 第五,以太坊存储布局带来了巨大的开销。 以太坊存储布局高度依赖 Keccak 和一个巨大的 MPT[4],它们都不是 zk 友好的,并且具有巨大的证明开销。 例如,Keccak 哈希在电路中比 Poseidon 哈希大 1000 倍。 但是,如果用另一个哈希替换 Keccak,则会导致现有以太坊基础设施的一些兼容性问题。
  • 第六,基于机器的证明具有巨大的开销。 即使你可以正确处理所有上述问题,你仍然需要找到一种有效的方法将它们组合在一起以获得完整的 EVM 电路。 正如我在上一节中提到的,即使像 add 这样简单的操作码也可能导致整个 EVM 电路的开销。

为什么现在可以实现?

感谢研究人员在该领域取得的巨大进展,越来越多的效率问题在过去两年中得到解决,zkEVM 的证明成本最终是可行的!最大的技术改进来自以下几个方面:

  • 多项式承诺的使用。 在过去的几年中,大多数简洁的零知识证明协议都坚持使用 R1CS,PCP 查询以特定于应用程序的可信设置进行编码。 电路大小通常会膨胀,并且你无法进行许多自定义优化,因为每个约束的度数需要为 2(双线性配对 仅允许指数中的一个乘法)。 使用多项式承诺方案,你可以使用通用设置甚至透明设置将约束提升到任何程度。 这在后端的选择上提供了很大的灵活性。
  • 查找表参数和自定义 gadget 的出现。 另一种强大的优化来自查找表的使用。 该优化首先在 Arya 中提出,然后在 Plookup 中得到优化。 这可以为 zk 不友好的原语节省大量资金(即,按位运算,如 AND、XOR 等)。 自定义 gadget 允许你有效地执行高度约束。 TurboPlonkUltraPlonk 定义了优雅的程序语法,使使用查找表和定义自定义 gadget 更加容易。 这对于减少 EVM 电路的开销非常有帮助。
  • 递归证明越来越可行。 递归证明在过去具有巨大的开销,因为它依赖于特殊的配对友好的循环椭圆曲线(即 基于 MNT 曲线的构造)。 这带来了大量的计算开销。 然而,更多的技术使这成为可能,而不会牺牲效率。 例如,Halo 可以避免对配对友好曲线的需求,并使用特殊的内积参数来分摊递归的成本。 Aztec 表明你可以直接对现有协议进行证明聚合(查找表可以减少非原生域运算的开销,从而可以使验证电路更小)。 它可以大大提高支持的电路尺寸的可扩展性。
  • 硬件加速使证明更加高效。 据我们所知,我们已经为证明者制造了最快的 GPU 和 ASIC/FPGA 加速器。 我们的论文 描述了 ASIC 证明器已被今年最大的计算机会议 (ISCA) 接受。 GPU 证明器比 Filecoin 的实现 快大约 5 倍-10 倍。 这可以大大提高证明者的计算效率。

好的,它是如何工作的,以及如何构建它?

除了强大的直觉和技术改进之外,我们仍然需要更清楚地了解我们需要证明什么,并找出更具体的架构。 我们将在后续文章中介绍更多技术细节和比较。 在这里,我们描述了总体工作流程和一些关键思想。

开发者和用户的工作流程

对于开发者来说,他们可以使用任何 EVM 兼容的语言实现智能合约,并将编译后的字节码部署在 Scroll 上。 然后,用户可以发送交易以与那些已部署的智能合约进行交互。 用户和开发人员的体验将与 Layer 1 完全相同。 但是,Gas 费用显着降低,并且交易在 Scroll 上立即获得预确认(提款仅需几分钟即可完成)。

zkEVM 的工作流程

即使外部的工作流程保持不变,Layer 1 和 Layer 2 的底层处理过程也完全不同:

  • Layer 1 依赖于智能合约的重新执行。
  • Layer 2 依赖于 zkEVM 电路的有效性证明。

让我们更详细地解释一下 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 设计亮点

在设计 zkEVM 的架构时,我们需要逐个处理/解决上述三个方面。

  1. 我们需要为一些加密累加器设计一个电路。

这部分就像一个“可验证存储”,我们需要一些技术来证明我们正在正确读取。 可以使用密码累加器来有效地实现此目的。[6]

让我们以 Merkle 树为例。 已部署的字节码将作为 Merkle 树中的一个叶子存储。 然后,验证者可以使用简洁的证明来验证字节码是否已从给定地址正确加载(即,在电路中验证 Merkle 路径)。 对于以太坊存储,我们需要电路与 Merkle Patricia Trie 和 Keccak 哈希函数兼容。

  1. 我们需要设计一个电路来将字节码与实际执行跟踪链接起来。

将字节码移动到静态电路中的一个问题是条件操作码,如 jump(对应于智能合约中的循环、if else 语句)。 它可以跳到任何地方。 在使用特定输入运行字节码之前,无法确定目标。 这就是我们需要验证实际执行跟踪的原因。 执行跟踪可以被认为是“展开的字节码”,它将包括实际执行顺序中的操作码序列(即,如果你跳到另一个位置,则跟踪将包含目标操作码和位置)。

证明者将直接提供执行跟踪作为电路的 witness。 我们需要证明提供的执行跟踪确实是从具有特定输入的字节码“展开”的。 这个想法是强制程序计数器的值保持一致。 为了处理不确定的目标,想法 是让证明者提供所有内容。 然后,你可以使用查找参数有效地检查一致性(即,证明具有适当全局计数器的操作码包含在“总线”中)。

  1. 我们需要为每个操作码设计电路(证明每个操作码中的读取、写入和计算是正确的)。

这是最重要的部分——证明执行跟踪中的每个操作码都是正确且一致的。 如果你将所有内容直接放在一起,将会带来巨大的开销。 这里重要的优化思想是

  • 我们可以将读取/写入和计算分成两个证明。 一个会将所有操作码所需的元素提取到“总线”中。 另一个将证明对来自“总线”的元素执行的计算已正确执行。 这可以大大减少每个部分的开销(即,你无需在计算证明中考虑整个 EVM 存储)。 在更详细的规范中,第一个称为“状态证明”,第二个称为“EVM 证明”。 另一个观察结果是,“总线映射”可以通过查找参数有效地处理。
  • 我们可以为每个操作码设计更高程度的自定义约束(即,EVM 字可以通过将其切成几个块来有效解决)。 我们可以根据需要选择是否通过选择器多项式“打开”约束。 这可以避免每个步骤中整个 EVM 电路的开销。

此架构首先由以太坊基金会指定。 它仍处于早期阶段,并且正在积极开发中。 我们正在与他们密切合作,以找到实现 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: 关于我们

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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
yezhang
yezhang
江湖只有他的大名,没有他的介绍。