欺诈证明

本文档深入探讨了 Fault Proof(也称为欺诈证明或交互式游戏)的组成部分:program,vm,以及交互式的争议解决游戏。

故障证明

<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> 目录

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

概述

故障证明,也称为欺诈证明或交互式游戏,由 3 个组件组成:

  • Program:给定对所有 Rollup 输入(L1 数据)和争议的承诺,以无状态方式验证争议。
  • VM:给定一个无状态程序及其输入,跟踪任何指令步骤,并在 L1 上证明它。
  • Interactive Dispute Game:将争议分割为单个指令,并使用 VM 解决基本情况。

这 3 个组件中的每一个都可能有不同的实现,这些实现可以组合成不同的证明栈, 并在解决争议时有助于证明多样性。

程序的“无状态执行”及其单个指令指的是通过使用 Pre-image Oracle 验证输入来重现 完全相同的计算。

程序和 VM 架构图

预映像 Oracle

预映像 Oracle 是 Program(担任 Client 角色)和 VM(担任 Server 角色)之间 唯一的通信形式。

程序使用预映像 Oracle 查询用户可以获得的任何输入数据:

通信通过简单的请求-响应线路协议进行, 见 Pre-image communcation

预映像 Key 类型

预映像由一个bytes32类型前缀的 Key 标识:

  • 第一个字节标识请求的类型。
  • 剩下的 31 个字节标识预映像 Key。
类型 0:Zero Key

零前缀是非法的。这确保了所有预映像 Key 都是非零的, 从而实现了存储查找优化,并避免了 EVM 中无效的零 Key 带来的简单错误。

类型 1:Local Key

特定于争议的信息:Key 的其余部分可以是索引、字符串、哈希等。 只有管理此争议实例的合约才能提供此 Key 的值: 它是本地化的,并且依赖于上下文。

这种类型的 Key 用于程序启动引导,以按索引或名称标识初始输入参数。

类型 2:Global keccak256 Key

这种类型的 Key 使用全局预映像存储合约,并且完全独立于上下文且无需许可。 也就是说,每个 Key 必须具有单个唯一的值,而与链历史或时间无关。

使用全局存储可以减少重复的预映像注册工作, 并避免每个争议不必要的合约重新部署。

此全局存储合约应该是不可升级的。

由于keccak256是一个安全的 32 字节哈希输入,因此第一个字节被覆盖为2以派生 Key, 同时保持 Key 的其余部分“可读”(与原始哈希匹配)。

类型 3:Global generic Key

保留。此方案允许无限的应用层预映像类型,而无需重新部署故障证明 VM。

这是一个全局 Key 存储的通用版本:key = 0x03 ++ keccak256(x, sender)[1:],其中:

  • x是一个bytes32,它可以是任意长度的密码安全承诺类型的哈希。
  • sender是一个bytes32,用于标识预映像插入者的地址(左侧填充至 32 字节)

此全局存储合约应该是不可升级的。

全局合约是无需许可的:用户可以围绕验证预映像的外部合约进行标准化 (即,对于特定类型的预映像,始终信任特定的sender)。 外部合约在将预映像插入全局存储之前验证预映像,以供所有 故障证明 VM 使用,而无需更改 VM 或全局存储合约。

如果预期预映像验证的实现会发生变化, 用户可以围绕可升级的外部预映像合约进行标准化。

存储更新函数是update(x bytes32, offset uint64, span uint8, value bytes32)

  • xbytes32 x,用于计算预映像key
  • 每次只能插入预映像的一部分,从offset开始,最多(包括)32 字节span
  • 预映像可能具有未定义的长度(例如,流),我们只需要知道value有多少字节可用。
  • Key 和 offset 将被哈希在一起,以唯一地存储valuespan,以供以后提供预映像。

这使故障证明程序能够采用任何新的预映像方案,而无需更新 VM 或重新部署合约。

用户有责任通过此 Key 方案索引特殊的预映像值, 因为在不知道所述承诺或值的情况下,无法将其还原为原始承诺。

类型 4-128:保留范围

范围开始和结束都包括在内。

此范围的 Key 类型保留供核心协议将来使用。 例如,版本更改、合约迁移、链数据、其他核心功能等。

128专门(二进制的1000 0000)保留用于 Key 类型长度扩展 (将内容部分减少到30或更少的 Key 字节),如果需要的话。

类型 129-255:应用使用

此范围的 Key 类型可供故障证明协议的分支或自定义版本使用。

启动引导

初始输入是确定性的,但不一定是单数或全局性的: 可能同时存在多个不同的争议,每个争议都有其自己有争议的主张和 L1 上下文。

为了启动引导,程序使用预映像 Key 类型1从 VM 请求初始输入。

VM 知道外部上下文,并根据 Key 的类型映射所请求的预映像 Key,即 类型1的本地查找,类型2的全局查找,以及可选地支持其他 Key 类型。

提示

客户端和服务器之间还有一种可选的通信形式:预映像提示。 提示是可选的,并且在 L1 VM 实现中是一个空操作

提示本身会产生非常低的链上成本:提示可以是单个write系统调用, 这是即时的,因为写入作为提示的内存实际上不需要作为链上证明的一部分加载。

提示允许程序在链下生成证明时, 指示 VM 它对哪些数据感兴趣。

VM 可以选择随时执行所请求的提示:无论是本地执行(对于标准请求), 还是通过将提示重定向到 VM 程序可能附带的工具,以模块化的形式执行。

提示不必直接执行:它们可能首先只是被记录下来以显示程序的意图, 并且最新的提示可能会被缓冲以进行延迟执行,或者在只读模式下(例如在链上)完全删除。

当预映像 Oracle 提供请求时,并且无法从现有预映像集合中提供请求 (例如,本地预映像缓存),则 VM 可以执行提示以检索缺少的预映像。 程序有责任为每个预映像请求提供足够的提示。 某些提示可能必须重复:VM 仅需要在处理缺少的预映像时执行最后的提示。

请注意,提示可能会产生多个预映像: 例如,带有事务列表的以太坊区块的提示可能会为标头、 每个事务以及构成事务列表 Merkle Patricia Trie 的中间 Merkle 节点准备预映像。

提示是通过阻塞式双向流上的请求-确认线路协议实现的:

&lt;request> := &lt;length prefix> &lt;hint bytes>

&lt;repsonse> := &lt;ack>

&lt;length prefix> := big-endian uint32  # &lt;hint bytes> 的长度
&lt;hint bytes> := 字节序列
&lt;ack> := 1 字节零值

ack 通知客户端提示已处理。服务器可以异步响应提示和预映像(见下文) 请求,因为它们位于单独的流上。为了避免请求尚未获取的预映像, 客户端应仅在观察到提示确认后才请求预映像。

预映像通讯

预映像通过阻塞式双向流上的最小线路协议进行通信。 可以用阻塞式读/写系统调用来实现此协议。

&lt;request> := &lt;bytes32>  # 类型前缀的预映像 Key

&lt;response> := &lt;length prefix> &lt;pre-image bytes>

&lt;length prefix> := big-endian uint64  # &lt;pre-image bytes> 的长度,注意:uint64

这里的&lt;length prefix>可能任意高: 如果已读取预映像的所需部分,客户端可以随时停止读取。

在客户端写入新的&lt;request>字节后,服务器应准备好通过read调用从offset == 0开始响应 预映像。

即使完整的预映像已准备就绪,服务器也可能会人为地限制read结果,使其一次只返回少量字节: 这是预期的常规 IO 协议, 客户端只需一次继续读取少量结果, 直到读取 0 个字节为止,这表示 EOF。 这使服务器可以一次最多提供 32 个字节,或者使读取与 VM 内存结构对齐, 以限制每个系统调用指令更改的 VM 状态量, 从而使每个指令的证明大小保持在一定范围内。

故障证明程序

故障证明程序将 L2 Rollup 的状态转换输出的验证定义为 L1 数据的纯函数。

op-program是该程序的参考实现,基于op-nodeop-geth实现。

该程序包括:

  • 序言:加载输入,给定最小启动引导,以及可能的测试覆盖。
  • 主要内容:处理 L2 状态转换,即从 L1 输入导出状态更改。
  • 结语:检查状态更改以验证声明。

序言

该程序通过两个主要输入来引导:

  • l1_head:L1 区块哈希,将被视为 L1 链的顶端, 验证所有先前的 L1 历史记录。
  • dispute:要验证的声明的身份。

启动引导通过向程序主机发送特殊的输入请求来进行。

此外,还有隐含输入,这些输入从上述主要输入派生而来, 但是可以为了测试目的而覆盖:

  • l2_head:L2 区块哈希,将被视为先前商定的 L2 链的顶端, 验证所有先前的 L2 历史记录。
  • 链配置:链配置可以被烘焙到程序中, 或者从 L1 上已识别的dispute的属性确定。
    • l1_chain_config:L1 链的链配置(也称为l1_genesis.json
    • l2_chain_config:L2 链的链配置(也称为l2_genesis.json
    • rollup_config:Rollup 节点使用的 Rollup 配置(也称为rollup.json

隐含输入依赖于 L1 自省,以通过 dispute game interface加载dispute的属性,在 L1 历史记录中直至指定的l1_headdispute可以是声明本身,也可以是指向 L1 中特定先前声明的数据的指针, 具体取决于争议游戏接口。

隐含输入在实际的核心状态转换函数执行之前在“序言”中加载。 在测试期间,可以使用加载覆盖的简化序言。

注意:由于争议游戏接口正在积极更改,因此目前仅支持测试序言。

主要内容

为了验证有关 L2 状态的声明,该程序首先重现 L2 状态,方法是将 L1 数据应用于先前商定的 L2 历史记录。

此过程也称为 L2 派生过程, 并与 Rollup 节点执行引擎 中的处理相匹配。

不同之处在于,不是从 RPC 检索输入并将状态更改应用于磁盘, 而是通过 预映像 Oracle 加载输入,并且更改累积在内存中。

派生使用两个数据源执行:

  • 到只读 L1 链的接口,由预映像 Oracle 提供支持:
    • l1_head确定了对可用 L1 数据的视图:没有可用的后续 L1 数据。
    • 链的实现遍历从l1_head向下到服务的按 num 查询的标头链。
    • l1_head是不安全的 L1 头部,安全头部和finalized头部。
  • 到 L2 引擎 API 的接口
    • 先前的 L2 链历史记录由预映像 Oracle 提供支持,类似于 L1 链:
    • 初始l2_head确定了对初始可用 L2 历史记录的视图:没有可用的后续 L2 数据。
    • 链的实现遍历从l2_head向下到服务的按数字查询的头链。
    • l2_head是初始的 L2 不安全头部,安全头部和finalized头部。
    • 新的 L2 链历史记录累积在内存中。
    • 尽管如果内存有限,可以使用预映像 Oracle 按哈希值检索数据, 但是程序应首选将新创建的链数据保留在内存中,以最大程度地减少预映像 Oracle 访问。
    • 随着派生的进行,L2 不安全头部,安全头部和finalized L2 头部可能会发生变化。
    • L2 状态包括内存中更改的差异, 以及可以通过只读 L2 历史记录视图访问的任何未更改的状态节点。

有关这些数据源的预映像 Oracle 支持的规范,请参见 预映像路由

使用这些数据源,将处理派生管道,直到我们达到以下两种情况之一:

  • EOF:当我们用完 L1 数据时,L2 链将不会再更改,并且可以开始结语。
  • 渴望的结语条件:根据要验证的声明的类型, 如果 L2 结果是不可逆转的(即,没有后续的 L1 输入可以覆盖它), 则当结果准备就绪时,处理可能会提前结束。 例如,在特定的 L2 区块断言状态,而不是在 L2 链的最顶端。

尾声

虽然主要内容已经产生了有争议的 L2 状态, 但尾声总结了这对有争议的声明意味着什么。

该程序产生二进制输出以验证声明,使用标准的单字节 Unix 退出代码:

  • 0表示成功,即声明是正确的。
  • 非零代码表示失败,即声明不正确。
    • 应该首选1来标识不正确的声明。
    • 其他非零退出代码可能表示运行时失败, 例如,程序代码中的错误可能会以某种panic或意外错误的形式解决。 在这种情况下,应首选安全性而不是活跃性,并且claim将失败。

为了断言存在争议的声明,像主要内容一样,尾声 可以反思 L1 和 L2 链数据并进一步对其进行后处理, 然后使用最终退出代码对声明进行陈述。

可以通过首先生成输出根,然后比较它来驳斥有争议的输出根:

  1. 从 L2 链视图检索输出属性:状态根,区块哈希,提款存储根。
  2. 计算输出根,如 proposer 应计算它
  3. 如果输出根与claim匹配,则以代码 0 退出。否则,以代码 1 退出。

注意:争议游戏界面正在积极更改,可能需要其他声明断言。 输出根结语可能会被替换或扩展为常规 L2 消息证明。

预映像提示路由

故障证明程序实现了 VM 使用的提示处理, 以及 VM 环境之外的任何程序测试。 可以通过 CLI 或替代性进程间 API 公开此功能。

以下路由中的每个&lt;blockhash>实例都带有0x前缀,小写,十六进制编码。

l1-header &lt;blockhash>

请求主机准备区块&lt;blockhash>的 L1 区块头 RLP 预映像。

l1-transactions &lt;blockhash>

请求主机准备带有&lt;blockhash>的 L1 区块的交易列表: 准备其中每个交易的 RLP 预映像,包括交易列表 MPT 节点。

l1-receipts &lt;blockhash>

请求主机准备带有&lt;blockhash>的 L1 区块的收据列表: 准备其中每个交易的 RLP 预映像,包括收据列表 MPT 节点。

l2-header &lt;blockhash>

请求主机准备区块&lt;blockhash>的 L2 区块头 RLP 预映像。

l2-transactions &lt;blockhash>

请求主机准备带有&lt;blockhash>的 L2 区块的交易列表: 准备其中每个交易的 RLP 预映像,包括交易列表 MPT 节点。

l2-code &lt;codehash>

请求主机准备带有给定&lt;codehash>的 L2 智能合约代码。

l2-state-node &lt;nodehash>

请求主机准备带有给定&lt;nodehash>的 L2 MPT 节点预映像。

故障证明 VM

故障证明 VM 实现:

  • 一个智能合约,用于验证单个执行跟踪步骤,例如单个 MIPS 指令。
  • 一个 CLI 命令,用于生成单个执行跟踪步骤的证明。
  • 一个 CLI 命令,用于计算步骤 N 的 VM 状态根

故障证明 VM 依赖于故障证明程序来提供一个接口, 用于根据提示获取任何缺少的预映像。

VM 模拟程序,如为 VM 目标架构准备的那样, 并根据通过 VM CLI 请求生成状态根或指令证明数据。

有关更多用法信息,请参阅故障证明 VM 的文档。

故障证明 VM:

  • Cannon:大端 32 位 MIPS 证明,由 OP Labs 提供,正在积极开发中。
  • Asterisc:小端 64 位 RISC-V 证明,由@protolambda提供,正在积极开发中。

故障证明交互式争议游戏

交互式争议游戏允许参与者通过链上质询-响应游戏解决争议, 该游戏将 VM 的执行跟踪分成两半,并以证明 VM 跟踪步骤的基本情况为界。

该游戏是多人游戏:当参与者绑定时,不同的非对齐参与者可能会参与。

响应时间的分配基于树的分支中的剩余时间和与声明的对齐方式。 分配的响应时间受到争议游戏窗口的限制, 以及债券不足时基于 L1 费用变化的任何额外时间。

注意:定时的,绑定的,二分争议游戏正在开发中。 另请参见 fault dispute-game specs 了解故障争议游戏系统规范, 以及 dispute-game-interface specs 了解争议游戏接口规范。

  • 原文链接: github.com/ethereum-opti...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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