L2输出根提案规范

本文档介绍了L2输出根提案规范,这是Optimism Rollup中将L2状态同步到L1(结算层)的关键环节。Proposer的角色是构建并提交输出根到L1上的L2OutputOracle合约,以此作为桥梁的L2 状态视图,并可接受fault proof挑战。其中详细说明了L2输出承诺的构建方式和L2OutputOracle智能合约的接口定义与配置,以及安全方面的考虑,例如 L1 重组。

L2 输出根提案规范

<!-- 本文件中所有的词汇表引用。 -->

<!-- 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 -->

在处理一个或多个区块后,输出需要与结算层(L1)同步,以便对 L2 到 L1 的消息传递(例如提款)进行无需信任的执行。 这些输出提案充当桥对 L2 状态的视图。 被称为 "Proposers" 的参与者将输出根提交到结算层(L1),并且可以通过欺诈证明提出异议, 如果证明是错误的,则会损失一部分抵押金。 op-proposer 是 proposer 的一个实现。

注意:Optimism 上的欺诈证明目前尚未完全指定。虽然欺诈证明 构造和验证 已在 Cannon 中实现, 但欺诈证明博弈规范以及将输出根挑战者集成到 rollup-node 中是后续规范里程碑的一部分。

提出 L2 输出承诺

Proposer 的作用是构造和提交输出根,这些输出根是对 L2 状态的承诺, 提交到 L1 上的 L2OutputOracle 合约(结算层)。为此,proposer 定期 查询 rollup 节点 以获取从最新的 最终确定 的 L1 区块派生的最新输出根。然后,它获取输出根并 将其提交到结算层(L1)上的 L2OutputOracle 合约。

L2OutputOracle v1.0.0

输出提案的提交被授权给单个帐户。预计此 帐户会随着时间的推移继续提交输出提案,以确保用户的提款不会停止。

L2 输出 proposer 预计会以确定的 间隔提交输出根,该间隔基于 L2OutputOracle 中配置的 SUBMISSION_INTERVALSUBMISSION_INTERVAL 越大, 发送到 L2OutputOracle 合约的 L1 交易就越少,但是 L2 用户需要等待更长的时间才能将输出根包含在 L1(结算层)中, 该输出根包含他们从系统提款的意图。

诚实的 op-proposer 算法假定与 L2OutputOracle 合约建立连接,以了解 与必须提交的下一个输出提案相对应的 L2 区块号。它还假定与 op-node 建立连接,以便能够查询 optimism_syncStatus RPC 端点。

import time

while True:
    next_checkpoint_block = L2OutputOracle.nextBlockNumber()
    rollup_status = op_node_client.sync_status()
    if rollup_status.finalized_l2.number >= next_checkpoint_block:
        output = op_node_client.output_at_block(next_checkpoint_block)
        tx = send_transaction(output)
    time.sleep(poll_interval)

CHALLENGER 帐户可以通过调用 deleteL2Outputs() 函数 并指定要删除的第一个输出的索引来删除多个输出根,这也将删除所有后续输出。

L2OutputOracle v2.0.0

输出提案的提交是无需许可的,并且没有必须提交输出提案的间隔。预计用户将 "just in time" 地提出一个输出 提案,以方便他们自己的提款。必须在输出提案中放置一个保证金,以阻止恶意输出的提案。如果可以证明该输出是恶意的, 无论是通过欺诈证明还是通过证明证明,则可以削减保证金并将其用作 支付给支付 gas 费以删除恶意输出的用户的报酬。

op-proposer 仍然可以用于提交输出提案。op-proposer 的一个简单的实现 将按间隔提交输出提案。但是,这不是必需的,其他 proposer 实现可以在任何时候提交有效的输出。一个更理想的实现 将使用启发式方法,例如上次提交的时间或尚未包含在输出提案中的待处理提款数量。

下面描述了此 proposer 的一次迭代(将一个输出根发布到 L1):

sequenceDiagram
    participant L1
    participant Rollup Node
    participant Proposer

    L1->>L1: L1 区块已经最终确认
    L1->>Rollup Node: L1 区块已经最终确认
    Proposer->>Rollup Node: optimism_syncStatus
    Rollup Node->>Proposer: 同步状态 { 已经最终确认的 L1 区块号 }
    Proposer->>Rollup Node: optimism_outputAtBlock
    Rollup Node->>Proposer: 输出根
    Proposer->>L1: 查询 L2OutputOracle 以获取此输出根
    L1->>Proposer: 输出根或 nil
    Proposer->>Proposer: 如果当前输出已经提出则停止
    Proposer->>L1: L2OutputOracle.proposeOutputRoot

由于启用无需许可的输出提案时可能同时运行多个 proposer, 因此 op-proposer 将在发送提案交易之前检查是否已针对给定的 L2 区块 号发布了其输出根。当 Proposer 查询 L2OutputOracle 以获取输出根时,这在上面的序列图中显示。如果它收到一个等于从 rollup 节点收到的 输出根,它将不会在交易中将此输出根发送到 L2OutputOracle

另请注意,虽然 op-proposer 实现基于已最终确认的 或安全的 L2 区块提交输出,但其他 proposer 实现可能会提交与不安全(未最终确认的)L2 区块相对应的输出。这会带来风险,因为 batchers 可能会提交与已提供(意味着这些输出现在无效)的 输出不对应的 L2 区块。

版本 v2.0.0 包括对 L2OutputOracle ABI 的重大更改。

L2 输出承诺构造

output_root 是一个 32 字节的字符串,它基于版本化的方案派生:

output_root = keccak256(version_byte || payload)

其中:

  1. version_byte (bytes32) 一个简单的版本字符串,只要输出根的构造发生变化,就会递增。

  2. payload (bytes) 是一个任意长度的字节字符串。

在输出承诺构造的初始版本中,版本为 bytes32(0),有效负载定义为:

payload = state_root || withdrawal_storage_root || latest_block_hash

其中:

  1. latest_block_hash (bytes32) 是最新 L2 区块的区块哈希。

  2. state_root (bytes32) 是所有执行层帐户的 Merkle-Patricia-Trie (MPT) 根。 该值经常使用,因此更接近 L2 输出根,从而无需证明其 包含在 latest_block_hash 的前映像中。这降低了 Merkle 证明深度和在 L1 上访问 L2 状态根的成本。

  3. withdrawal_storage_root (bytes32) 提升了 Message Passer 合约 存储的 Merkle-Patricia-Trie (MPT) 根。而不是对状态根进行 MPT 证明的提款(首先证明 L2toL1MessagePasser 的存储根与状态根, 然后针对该存储根的提款),我们可以直接针对 L2toL1MessagePasser 的存储根进行证明, 从而降低了 L1 上提款的验证成本。

L2 输出 Oracle 智能合约

L2 区块以 L2_BLOCK_TIME(2 秒)的恒定速率产生。 每个 SUBMISSION_INTERVAL 必须将一个新的 L2 输出附加到链中一次,该间隔基于区块的数量。 确切的数字尚未确定,并且将取决于欺诈证明博弈的设计。

L2 输出 Oracle 合约实现以下接口:

/**
 * @notice 此合约中记录的第一个 L2 区块的编号。
 */
uint256 public startingBlockNumber;

/**
 * @notice 此合约中记录的第一个 L2 区块的时间戳。
 */
uint256 public startingTimestamp;

/**
 * @notice 接受 L2 outputRoot 和相应 L2 区块的时间戳。该
 * 时间戳必须等于 `nextTimestamp()` 返回的当前值才能被接受。
 * 此函数只能由 Proposer 调用。
 *
 * @param _l2Output 检查点区块的 L2 输出。
 * @param _l2BlockNumber 导致 _l2Output 的 L2 区块号。
 * @param _l1Blockhash 必须包含在当前链中的区块哈希。
 * @param _l1BlockNumber 具有指定区块哈希的区块号。
*/
  function proposeL2Output(
      bytes32 _l2Output,
      uint256 _l2BlockNumber,
      bytes32 _l1Blockhash,
      uint256 _l1BlockNumber
  )

/**
 * @notice 删除给定输出索引对应的提案之后和包括该提案的所有输出提案。
 *         只有挑战者地址可以删除输出。
 *
 * @param _l2OutputIndex 要删除的第一个 L2 输出的索引。此之后的所有输出
 *                       输出也将被删除。
 */
function deleteL2Outputs(uint256 _l2OutputIndex) external

/**
 * @notice 计算需要进行检查点设置的下一个 L2 区块的区块号。
 */
function nextBlockNumber() public view returns (uint256)

配置

startingBlockNumber 必须至少是第一个 Bedrock 区块的编号。 startingTimestamp 必须与起始区块的时间戳相同。

因此,提出的第一个 outputRoot 将位于高度 startingBlockNumber + SUBMISSION_INTERVAL

安全考虑

L1 重组

如果在生成和提交输出后 L1 发生重组,则 L2 状态和正确的输出可能会发生变化,从而导致错误的提案。这可以通过允许 proposer 在附加新输出时将 L1 区块号和哈希提交到输出 Oracle 来缓解。如果发生重组,区块哈希将与该编号的区块的哈希不匹配,并且调用将恢复。

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

0 条评论

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