L2 输出根提案规范

本文档描述了Optimism Rollup中L2输出根提案的规范。为了实现L2到L1消息传递的信任执行,需要将L2的状态同步到结算层L1。Proposer构建并提交L2状态的承诺(输出根)到L1上的L2OutputOracle合约。本文档详细介绍了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 状态的视图。 被称为“提议者”的角色将输出根提交到结算层(L1),并且可以使用错误证明进行质疑, 如果证明是错误的,则会损失保证金。op-proposer 是提议者的一个实现。

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

提出 L2 输出承诺

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

L2OutputOracle v1.0.0

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

L2 输出提议者 预计会根据 L2OutputOracle 中配置的 SUBMISSION_INTERVAL,按确定的 时间间隔提交输出根。SUBMISSION_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() 函数 并指定要删除的第一个输出的索引来删除多个输出根,这也将删除所有后续输出。

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 的原像中的需要。这降低了在 L1 上访问 L2 状态根的 merkle 证明深度和成本。

  3. withdrawal_storage_root (bytes32) 提升了 消息 传递合约 存储的 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 状态和正确的输出可能会发生变化, 从而导致错误的提议。通过允许提议者在附加新输出时向输出 Oracle 提交 L1 区块编号和哈希来缓解这种情况;如果发生重组,区块哈希 将与该编号的区块的哈希不匹配,并且调用将恢复。

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

0 条评论

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