从零到英雄:OP Stack 故障证明系列 2
本文是Tokamak Network发布的OP Stack Fault Proof系列文章的第二篇,主要介绍了Optimism的FPVM——Cannon的故障证明系统。Cannon通过链下执行和链上验证来确保Layer2状态转换的完整性,文章详细讲解了Cannon的组件及其相互作用,包括MIPS.sol、MIPSEVM、PreimageOracle等,并阐述了它们在争议解决中的作用。
Cannon 的故障证明系统概述

来源:Oplabs 链接
这篇文章是“OP Stack 完整版故障证明系列”的第二期,该系列由 Tokamak Network 计划撰写 3 篇文章。在本文中,我们概述了 Cannon(Optimism 的 FPVM),它通过链下执行和链上验证来确保 Layer 2 状态转换的完整性。MIPS.sol、MIPSEVM 等关键组件协同工作,以实现高效的争议解决。
? 鉴于该系列的相互关联性,我们建议按顺序阅读这些文章,以便获得连贯的理解。
系列 1. Cannon 的故障证明系统: 在本文中,我们介绍了 Optimism 的故障证明程序,并解释了其多重证明架构如何增强 Layer 2 的安全性和可靠性。该程序集成了强大的故障证明机制,以确保准确的状态转换和高效的争议解决。[ 链接 ]
系列 3. 故障争议游戏 (FDG) / 二分游戏: 在本文中,我们探讨了 Optimism 的故障证明系统如何使用链上 (
MIPS.sol) 和链下 (MIPSEVM) 组件来解决 Layer 2 状态争议。我们涵盖了提议者和挑战者的角色、PreimageOracle的数据管理以及确保完整性和安全性的激励结构。此外,我们还解释了二分游戏,它将争议分解为单个指令以进行精确验证。[ 链接 ]
整体故障证明系统

图:整体故障证明系统流程图
此流程图概述了在 Optimism 网络中解决 Layer 2 区块状态转换争议的机制和流程。该过程的核心组件包括 Cannon 和 Op-challenger 之间的交互、二分游戏、通过 MIPS.sol 进行链上验证、通过 Op-program 进行执行跟踪管理以及见证证明的生成。以下是每个步骤的详细说明:
1. 对特定 L2 区块状态转换存在分歧:Cannon 与 Op-challenger 合作处理 MIPS 指令,生成状态见证哈希,以解决对特定 L2 区块状态转换的分歧。
2. 二分游戏:Op-challenger 利用生成的哈希来查明单个 MIPS 指令,即分歧的根源。然后,Cannon 生成一个见证证明(链下),其中包含在链上执行 MIPS 指令所需的所有数据(前像 + 状态信息)。
? 如果链下生成的见证证明被篡改了怎么办?
▶ 在 MIPS.sol 中争议 MIPS 指令的链上执行是确定性的,确保相同的输入和状态始终产生相同的输出。如果链下证明被篡改,链上执行将通过与预期结果不匹配来暴露差异。
3. 链上验证:有争议的 MIPS 指令在 MIPS.sol 中进行链上执行,以最终确定正确的后状态,从而解决故障争议。由 Cannon 链下生成的见证证明包含此链上执行所需的所有数据。该证明确保了链上验证过程中过程的准确性和完整性。如果指令引用了哈希值,Cannon 会通过 Op-preimage 从 PreimageOracle 检索所需的前像,以在链上验证该指令。
4. 使用 OP-Program 进行执行跟踪:从 Op-program 编译的 ELF 二进制文件被加载并在 Cannon 中执行。Op-program 确保获取所有必要的数据以导出 L2 状态,从而保证相同的输入导致一致的输出和执行跟踪。
5. 见证证明生成:争议游戏中的参与者可以运行 Op-program 以生成相同的见证证明,用于要在链上执行的 MIPS 指令。双方独立验证有争议的 MIPS 指令的正确性。如果需要前像,witness.go 会将相关的前像密钥和偏移量传递给 Op-challenger,使其可以链上发布到 PreimageOracle.sol 。
? 见证证明生成过程由位于 cannon/cmd 中的顶级 witness.go 文件启动,而 cannon/mipsevm 中的 witness.go 文件定义了包含特定 MIPS 指令所有必要信息的结构体。如果 MIPS 指令需要前像,则会将其包含在此过程中,以确保准确且完整的验证。
witness.go将相关的前像密钥和偏移量传递给Op-challenger。Op-challenger将此信息链上发布到PreimageOracle.sol。
? 此过程会在 MIPSEVM 执行状态更改之前生成有关要在链上执行的 MIPS 指令的所有相关信息。通过确保在发生这些状态更改之前生成并传递所有必要的信息,该过程可以保证见证证明的准确性和完整性。这可以防止证明编码不正确的后状态,并确保其捕获正确的先验状态以进行正确的争议解决。如果在链下运行指令后生成见证证明,它将编码后状态而不是必要的先验状态。
详细解释 Cannon 组件

图:Cannon 组件流程图
1. Cannon Runner
- 文件:
run.go、instrumented.go - 功能: 启动并管理
Cannon中的执行过程。此组件处理顶级执行逻辑,逐步执行 MIPS 指令,并根据用户配置确定其他操作,例如日志记录、停止、拍摄快照或生成见证证据。 - 交互: 这种交互确保 Cannon Runner 具有高效执行管理所需的准确状态和内存详细信息。
2. 内存和状态管理
- 文件:
memory.go、page.go、state.go - 功能:管理内存分配和 MIPS 虚拟机的状态。这包括以优化 Merkle 根计算的方式存储内存节点和页面、处理 MIPS 指令中字的大端字节顺序,以及维护执行状态、内存 Merkle 根和可选的前像提示。
- 交互:此组件通过提供当前状态和内存详细信息与 Cannon Runner 和
MIPSEVM进行交互。它确保 Cannon Runner 和MIPSEVM都具有一致且最新的内存和状态信息以供其操作。
3. MIPSEVM
- 文件:
mips.go - 功能:此组件执行 MIPS 指令并与
PreimageOracle(链下)交互,以检索验证哈希值所需的 preimage 数据。它确保链下 MIPS 执行产生与链上执行相同的结果,从而处理内存访问和状态更新。此外,它还跟踪需要内存证明的指令的内存访问。 - 交互:此组件与内存和状态管理交互以获取当前状态和内存详细信息,并与
PreimageOracle(链下)交互以检索所需的前像。
4. 见证证明生成
- 文件:
witness.go - 功能:生成在故障争议游戏期间争议解决所需的见证证明。此过程涉及创建
MIPS.sol在链上运行相同指令并导出用于解决争议的后状态所需的必要信息。它还包括将相关的前像密钥和偏移量传递给Op-challenger以便链上发布到PreimageOracle.sol。 - 交互:见证证明生成与 Cannon Runner 交互以生成争议解决所需的见证证明。
6. ELF 加载器(执行跟踪过程)
- 文件:
load_elf.go、patch.go、metadata.go - 功能:一旦二分游戏的执行跟踪部分开始,ELF 加载器就会加载并修补包含在
Cannon中编译为 MIPS 指令的Op-program的 ELF 文件。要在Cannon中运行Op-program,需要一个二进制加载器,它由load_elf.go、patch.go 和 metadata.go组成。 load_elf.go:此文件解析顶级参数,读取、加载和修补 ELF 二进制文件,以便Cannon可以运行它。patch.go:此文件解析 ELF 文件的标头以确定存在哪些程序及其内存位置,实例化MIPSEVM的执行状态,将每个程序加载到内存中的预期位置,并设置 PC、NextPC、Heap (0x20000000)、Stack (0x7fffd000) 和堆栈指针之上的 Go 运行时参数的初始值。metadata.go:此文件解析存储在 ELF 文件中的所有符号,有助于识别哪些 ELF 符号存在及其内存位置,这对于函数和其他功能非常重要。
? ELF 加载器的目的是准备 MIPS 程序以通过加载 ELF 文件、设置初始状态、修补函数和解析符号来执行。它确保挑战者和验证者都从二分游戏中相同的正确状态开始,从而能够精确地识别和演示 MIPS 程序执行中的任何差异。这种准确的设置对于挑战者在发现故障时计算和证明正确的值至关重要。
5. PreimageOracle 服务器

图:PreimageOracle 链上/链下流程图。
功能: PreimageOracle 服务器(链下)通过提供 MIPS 指令执行期间验证哈希值所需的必要前像数据来支持链下执行环境。该服务器通过访问正确的前像数据来确保链下执行可以产生与链上执行相同的结果。
链下计算:
MIPSEVM链下执行 MIPS 指令,与PreimageOracle服务器交互以检索必要的前像。这允许快速高效的处理。PreimageOracle 服务器存储和管理验证链下执行期间使用的哈希值所需的前像。
链上验证:
- 当需要验证或质疑状态转换或执行结果时,链上
PreimageOracle会提供前像,以确保链上MIPS.sol合约可以准确地重新执行该指令并验证结果。 - 这可确保链上状态与链下执行保持一致,从而为争议解决提供值得信赖的环境。
MIPS.sol 及其作用的详细说明
MIPS.sol 智能合约是一个链上虚拟机 (VM),它执行 32 位大端 MIPS III 指令集。它与链下对应物 MIPSEVM(用 Go 编写)一起工作,作为 Cannon FPVM 系统的关键组件。该系统旨在链上执行 MIPS 指令,以解决 Optimism 的 Layer 2 区块链中的争议。MIPS.sol 的无状态性质及其与其他合约的交互确保了高效且可重复使用的争议解决流程。
? “无状态”一词表示 MIPS.sol 只有一个不可变的状态变量: PreimageOracle.sol 合约的地址。它依赖于外部输入和前像数据,确保每次执行都是独立的、高效的且易于验证的,从而通过最大限度地减少对内部状态的依赖来增强安全性和鲁棒性。这种设计允许它在多个争议游戏中使用而无需重新部署。
MIPS.sol 的关键功能和作用
1. 与其他合约的交互:
- FaultDisputeGame.sol:
MIPS.sol主要由处理活跃争议的此合约调用。 - PreimageOracle.sol: 此合约存储前像,前像是争议解决过程中使用的数据快照。
2. 在争议解决中的作用:
- 当争议游戏达到状态转换树中的特定点(称为叶节点)时,会使用
MIPS.sol。此点表示需要链上执行的单个 MIPS 指令,以解决有争议的状态。 - 该合约使用前像(先前商定的 L2 状态)和当前指令状态来计算真实的后状态。这有助于通过将计算出的后状态与有争议的后状态进行比较来解决争议。
3. MIPS 指令的执行:
- 打包的 VM 执行状态: 合约需要详细的状态信息(例如,内存根、程序计数器、通用寄存器)才能执行 MIPS 指令。
- 内存证明: 为了验证内存读取和写入,
MIPS.sol使用二进制 Merkle 树结构,使用内存证明确保数据完整性。 - 状态哈希: 执行指令后,合约会返回一个状态哈希,该哈希表示新的 VM 状态,其中包含 VM 的状态以指示有效性。
MIPS.sol 函数:

图:MIPS.sol (来源: github 链接)
function oracle(): 检索 PreimageOracle 地址。

图:MIPS.sol (来源: github 链接)
function outputState(): 此函数会将 MIPS 状态复制到空闲内存位置,记录状态以进行调试,根据退出代码确定 VM 状态,计算状态的 keccak256 哈希,并返回带有设置的状态字节的哈希。
此函数管理并记录执行 MIPS 指令后虚拟机的状态。这是一个更详细的解释:
- 复制 MIPS 状态: 该函数将 MIPS 虚拟机的当前状态复制到空闲内存位置。此步骤确保状态被保留并且可以在后续操作中被引用或使用。
- 记录状态以进行调试: 记录状态,提供了一种跟踪执行并识别任何问题或不一致性的方法。此日志记录对于调试和验证 VM 是否按预期运行至关重要。
- 确定 VM 状态: 该函数检查 VM 的退出代码以确定其当前状态。退出代码指示 VM 是否已成功执行指令、遇到错误或达到特定条件(例如,程序终止)。
- 计算 keccak256 哈希: 该函数计算 VM 状态的 keccak256 哈希,以确保其完整性和唯一性,为状态验证生成唯一标识符。它还可以用于验证状态是否已被篡改。
- 返回带有状态字节的哈希: 该函数返回带有附加状态字节的哈希,从而为进一步处理、验证或存储提供 VM 状态的安全简洁的表示。

图:MIPS.sol (来源: github 链接)
function handleSyscall(): MIPS.sol 中的此函数用于管理 MIPS 虚拟机中的系统级操作。这是一个详细的解释:
- 处理系统调用: 该函数根据系统调用号识别系统调用类型,后者请求服务,例如 I/O 操作、内存管理和进程控制。
- 更新 VM 状态: 它执行指定的操作并更新 VM 状态,确保正确处理系统级任务。
- 处理的系统调用类型:
- 内存分配: 分配内存并更新内存管理结构。
- 程序终止: 终止程序并更新 VM 状态。
- 读取和写入: 管理 I/O 操作、从输入读取和写入输出。
- 文件描述符控制: 控制文件描述符以管理文件操作。
- 返回更新的状态哈希: 处理系统调用后,它会计算并返回更新状态的哈希,提供一个唯一的标识符,以确保安全地记录和验证更改。

图:MIPS.sol (来源: github 链接)
function handleBranch(): 此函数对于管理 MIPS VM 中的条件执行至关重要。它会根据分支条件的评估来更新程序计数器,从而确保程序执行正确的指令序列。
- 分支指令的执行: 该函数处理分支指令,以根据特定条件更改执行流。
- 更新程序计数 (PC): 它根据分支条件更新 PC,跳转到目标地址或移动到下一条指令。
参数:
- _opcode:分支指令的操作码。
- _insn:要执行的完整指令。
- _rtReg:用于分支条件的寄存器索引。
- _rs:要比较的寄存器的值。

图:MIPS.sol (来源: github 链接)
function HandleJump(): 此函数对于管理 MIPS 虚拟机中的跳转指令至关重要。
- 更新程序计数器 (PC): 它将程序计数器 (PC) 更新为
_dest参数指定的新目标地址。这会将执行流更改为程序的其他部分。 - 处理链接寄存器 (_linkReg): 如果
_linkReg参数指定了链接寄存器,则该函数会将延迟Slot之后指令的地址存储在此寄存器中。这通常用于跳转和链接指令(例如,JAL),其中需要在函数调用返回时保存返回地址以供以后使用。 - 管理跳转延迟Slot: 它确保正确处理跳转延迟Slot,这是 MIPS 体系结构的一个特性。延迟Slot是紧跟在跳转指令之后的指令,该指令在跳转生效之前执行。
handleJump函数确保正确管理此Slot,从而保持预期的执行顺序。 - 返回更新的状态哈希: 最后,该函数计算更新 VM 状态的哈希并返回它。此哈希为新状态提供了一个唯一的标识符,确保跳转操作的效果被安全地记录并可以验证。

图:MIPS.sol (来源: github 链接)
function handleHiLo(): 此函数处理 MIPS 指令,这些指令涉及 HI 和 LO 寄存器,用于执行各种操作,例如将值移入/移出 HI/LO、乘法和除法。在执行所需操作后,它会更新 VM 状态并返回哈希状态。
- 处理 HI 和 LO 寄存器指令: HI 和 LO 寄存器是特殊用途的寄存器,用于某些 MIPS 指令中,特别是对于生成的值太大而无法放入单个寄存器的操作。涉及这些寄存器的常见操作包括乘法和除法。
- 执行操作: 基于函数代码,
handleHiLo函数执行必要的操作。
例如:
- 乘法: 将 _rs 和 _rt 相乘的结果通常在 HI 和 LO 寄存器之间分开。
- 除法: 将 _rs 除以 _rt 的商和余数分别存储在 LO 和 HI 寄存器中。
- 移动操作: 可以将值移入或移出 HI 和 LO 寄存器。
参数:
- _func:指令的函数代码(确定特定操作)。
- _rs:该值表示操作中涉及的源寄存器的值
- _rt:该值表示操作中涉及的源寄存器的值
- _storeReg:应该存储结果的寄存器(如果适用)。

图:MIPS.sol (来源: github 链接)
function step(): MIPS.sol 中 step 函数的目的是管理 MIPS 虚拟机中各个指令的执行,确保正确处理程序的每个步骤。它获取并解码指令,执行指定的操作,相应地更新 VM 状态,并返回更新状态的哈希,以确保 VM 在每个步骤的执行的完整性和可验证性。
获取指令: 该函数首先从程序内存中获取当前指令,该内存基于程序计数器 (PC)。
解码指令: 获取指令后,该函数对其进行解码以确定其类型和需要执行的特定操作。指令类型可以是跳转、分支、算术逻辑单元 (ALU) 操作、内存访问或其他类型的指令。
执行操作: 基于解码的指令,该函数执行适当的操作。这包括:
- 分支指令: 评估一个条件并可能更改 PC。
- 跳转指令: 将 PC 更新为新地址。
- ALU 操作: 对寄存器执行算术或逻辑操作。
- 内存访问: 从内存位置读取或写入。
- 这些函数确保正确处理有条件和无条件的操作。
更新 VM 状态: 执行指令后,该函数会更新 VM 状态以反映指令所做的更改。这包括更新 PC、寄存器、内存以及 VM 状态的任何其他相关部分。
返回哈希状态: 最后,该函数计算更新 VM 状态的哈希并返回它。此哈希充当新状态的唯一标识符,确保安全地记录指令的效果并且可以验证。
结论
Cannon的设计通过将严格的链下处理与可靠的链上验证相结合,确保了 Optimism 生态系统中争议的准确高效解决。这种模块化和可扩展的方法为未来的增强奠定了基础,包括支持各种故障证明机制。_
参考
文档
- https://specs.optimism.io/fault-proof/index.html#pre-image-oracle
- https://docs.optimism.io/stack/protocol/fault-proofs/cannon
- https://docs.optimism.io/stack/protocol/fault-proofs/fp-components
Youtube
Git-hub
- 原文链接: medium.com/tokamak-netwo...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~