Skip to content

Latest commit

 

History

History
executable file
·
192 lines (89 loc) · 18.2 KB

1.zkEVM介绍.md

File metadata and controls

executable file
·
192 lines (89 loc) · 18.2 KB

致谢:感谢 Vitalik Buterin、Barry Whitehat、Chih-Cheng Liang、Kobi Gurkan 和 Georgios Konstantopoulos的检阅和有见地的评论。

*翻译:Junwei

TL;DR


我们相信zk-Rollup是圣杯——是Layer 2扩容中便宜且安全的最佳解决方案。然而,现有的 zk-Rollup 是针对特定应用程序的,在 zk-Rollup 中构建通用的可组合DApp和迁移现有应用程序非常地困难。我们引入 zkEVM,它可以生成 zk 证明,用于通用的 EVM 验证。这使我们能够构建一个完全兼容 EVM 的 zk-Rollup,任何现有的以太坊应用程序都可以轻松迁移至此。

在本文中,我们明确了zkEVM中的设计挑战以及为什么它现在是可行的。我们还给出了更具体的灵感方案,并描述了如何从头开始构建的顶层思想。

背景


zk-Rollup被公认为以太坊的最佳扩容解决方案。它与以太坊Layer 1一样安全,并且与所有其他Layer 2解决方案相比,拥有最短的最终确认时间(此处有详细比较)。

从中长期来看,随着ZK-SNARK技术的改进,ZK rollups将在所有方案中胜出。— Vitalik Buterin

zk-Rollup 的基本思想是将大量交易聚合到一个Rollup块中,并为该链下的块生成简洁的证明。然后Layer 1上的智能合约只需要验证证明并直接应用更新的状态,而无需重新执行那些交易。这可以帮助节省一个数量级的gas费用,因为验证证明比重新执行计算便宜得多。另一个节省来自数据压缩(即只保留最小的链上数据用于验证)

尽管zk-Rollup安全高效,但其应用目前仍仅限于支付和交易。难以构建通用的DApps,是由于以下两个原因。

  • 第一,如果你想在 zk-Rollup 中开发 DApps,你需要使用一种特殊的语言(即R1CS)编写你所有的智能合约逻辑。不仅所需语言的语法复杂,而且还需要极强的零知识证明专业知识。
  • 其次,当前的zk-Rollup不支持可组合性[1]。这意味着不同的zk-Rollup应用程序不能在Layer 2内相互交互。这极大地破坏了DeFi应用程序的可组合性。

简而言之,zk-Rollup目前对开发人员不友好,功能有限。这是我们要解决的最大问题。我们希望通过直接支持原生EVM验证来提供最佳的开发体验,并支持Layer 2内的可组合性,以便现有的以太坊应用程序可以简单地迁移到zk-Rollup上。

在zk-Rollup中构建通用 DApps


在zk-Rollup中有两种构建通用DApp的方案。

  • 一种是为不同的DApps构建专用电路(“ASIC”)。

  • 另一种是为智能合约执行构建一个通用的“EVM”电路。

“电路”是指零知识证明中使用的程序形式。例如,如果要证明hash(x) = y,则需要用电路形式重新编写哈希函数。电路形式只支持非常有限的表达式(即R1CS只支持加法和乘法)。因此,使用电路语言编写程序非常困难——必须使用加法和乘法构建所有程序逻辑(包括if else、loop等)。

第一种方案需要开发人员为不同的 DApp 设计专门的“ASIC”电路。这是使用零知识证明的最传统的方法。通过定制电路设计,每个DApp都会有更小的开销。然而,由于电路是“静态的”,它带来了可组合性的问题,并且由于需要强大的电路设计专业知识,因此开发人员的体验很糟糕[2]。

第二种方案不需要任何特殊的设计或开发人员的专业知识。这种基于机器的证明的顶层思想是任何程序最终都会在CPU上运行,因此我们只需要构建一个通用的CPU电路来验证低级的CPU操作。然后我们可以使用这个 CPU 电路来验证任何程序的执行。在我们的场景下,程序是智能合约,CPU是EVM。但是,由于开销较大,这种方法在过去几年中并没有受到广泛采纳。例如,即使你只想证明add这一步加法结果是正确的,仍然需要承担整个EVM电路的开销。如果执行过程中有数千个操作,那么证明者的EVM电路开销将是 1000 倍[3]。

最近,已经有很多研究在按照这两种方案优化 zk 证明,包括(i)提出新的 zk 友好的密码学原语,像Poseidon 哈希在电路中的效率是SHA256的100倍,(ii)提高效率的通用可验证虚拟机的开发,如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) 和Cario (starkware) 的架构在基于寄存器的模型中定义了自己的中间表示(IR,Intermediate Representation)/代数中间表示(AIR, Algebraic Intermediate Representation)。他们构建了一个专门的编译器,将智能合约代码编译成一新的zk友好IR。他们的方案是语言兼容而不是原生EVM兼容。基于堆栈的模型和直接支持原生链工具更难证明。
  • 第五,以太坊存储层带来巨大开销。 以太坊存储层高度依赖Keccak和巨大的MPT[4],它们都不是zk友好的,并且需要巨大的证明开销。例如,Keccak哈希比电路中的Poseidon哈希大1000 倍。但是,如果将 Keccak替换为另一个哈希算法,则会对现有的以太坊基础设施造成一些兼容性问题。
  • 第六,基于机器的证明需要巨大的开销。 即使能够妥善处理上述所有问题,仍然需要找到一种有效的方法将它们组合在一起以获得一个完整的EVM电路。正如我们上一节中所提到的,即使像add这样简单的操作码也需要整个 EVM 电路的开销。

为什么现在可行了?


感谢研究人员在这方面取得的巨大进步,近两年来越来越多的效率问题被解决,zkEVM的证明成本最终变得可行!最大的技术进步来自以下几个方面:

  • 多项式承诺的使用。 在过去的几年里,大多数简洁零知识证明协议都坚持使用 R1CS,将PCP查询编码在特定于应用程序的可信设置中。电路大小通常会爆炸,且不能进行许多自定义的优化,因为每个约束的项数需要为2(双线性配对只允许指数中的一次乘法)。使用多项式承诺方案,你可以通过通用设置甚至透明设置将约束提升到任何项数。这为后端的选择提供了极大的灵活性。

  • 查找表参数和自定义配置的出现。 另一个强大的优化来自查找表的使用。该优化首先在Arya中提出,然后Plookup中进一步升级。这可以为zk不友好的原语(即,AND、XOR 等按位运算)节省很多成本。自定义配置可以高效地进行高项数的约束。TurboPlonk和UltraPlonk定义了优雅的程序语法,以便更轻松地使用查找表和定制配置。这对于减少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的证明需要包含如下执行过程中所对应的方面

  • 字节码从持久存储中正确加载 (正在运行从给定地址加载的正确操作码)

  • 字节码中的操作码一致地一一执行 (它们的字节码按顺序执行,不会丢失或跳过任何操作码)

  • 每个操作码都正确执行 (每个操作码中的三个子步骤都正确执行,读/写+ 计算)

zkEVM 设计重点

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

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

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

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

  2. 我们需要设计一个电路来将字节码与真实的执行踪迹联系起来。

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

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

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

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

    • 我们可以将读/写和计算分成两个证明。一个将所有操作码所需的元素提取到“总线”中。另一个将证明对来自“总线”的元素执行的计算是正确的。这可以大大减少每个部分的开销(即不需要在计算证明中考虑整个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 Tech 是一家新兴的技术驱动型公司。我们的目标是构建一个与 EVM 兼容并具有强大的证明网络的zk-Rollup(请参阅此处的概述)。整个团队现在都专注于开发。我们正在积极招聘更多热情的开发人员,请通过[email protected]与我们联系。如果你对技术内容有任何疑问,请通过[email protected]与我联系。欢迎DM

脚注


[1]: Starkware claims to achieve composability a few days ago (reference here)

[2]: Circuit is fixed and static. For example, you can’t use variable upper bound loop when implementing a program as a circuit. The upper bound has to be fixed to its maximum value. It can’t deal with dynamic logic.

[3]: To make it more clear, We elaborate about the cost of EVM circuit here. As we described earlier, circuit is fixed and static. So, EVM circuit needs to contain all possible logic (10000x larger than pure add). That means even if you only want to prove for add, you still need to afford the overhead of all possible logics in the EVM circuit. It will 10000x amplify the cost. In the execution trace, you have a sequence of opcodes to prove and each opcode will have such a large overhead.

[4]: EVM itself is not tightly bound to the Merkle Patricia tree. MPT is just how Ethereum states are stored for now. A different one can easily be plugged in (i.e., the current proposal to replace MPT with Verkle trees).

[5]: This is a highly simplified abstraction. Technically, the list of “EVM state” is longer including PC, gas remaining, call stack (all of the above plus address and staticness per call in the stack), a set of logs, and transaction-scoped variables (warm storage slots, refunds, self-destructs). Composability can be supported directly with additional identifier for different call context.

[6]: We use accumulator for storage since the storage is huge. For memory and stack, one can use editable Plookup (“RAM” can be implemented efficiently in this way).

[7]: It’s non-trivial to add a complete recursive proof to the zkEVM circuit. The best way to do recursion is still using cyclic elliptic curves (i.e., Pasta curve). Need some “wrapping” process to make it verifiable on Ethereum Layer 1.