本文档描述了Optimism Rollup中L2输出根提案的规范。为了实现L2到L1消息传递的信任执行,需要将L2的状态同步到结算层L1。Proposer构建并提交L2状态的承诺(输出根)到L1上的L2OutputOracle合约。本文档详细介绍了L2输出承诺的构造方式,L2OutputOracle合约的接口,以及在面对L1重组时的安全考虑。
<!-- 本文件中所有的术语表引用。 -->
<!-- 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 状态的承诺,
提交到 L1 上的 L2OutputOracle
合约(结算层)。为此,提议者定期
查询 rollup 节点,以获取从最新的
最终确定 的 L1 区块派生的最新输出根。然后,它获取输出根并
将其提交到结算层(L1)上的 L2OutputOracle
合约。
输出提议的提交被授权给单个帐户。预计该 帐户会随着时间的推移继续提交输出提议,以确保用户的提款不会停止。
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()
函数
并指定要删除的第一个输出的索引来删除多个输出根,这也将删除所有后续输出。
output_root
是一个 32 字节的字符串,它是基于版本化的方案派生的:
output_root = keccak256(version_byte || payload)
其中:
version_byte
(bytes32
) 是一个简单的版本字符串,每当输出根的构造
发生更改时,该字符串都会递增。
payload
(bytes
) 是一个任意长度的字节字符串。
在输出承诺构造的初始版本中,版本是 bytes32(0)
,有效负载定义
为:
payload = state_root || withdrawal_storage_root || latest_block_hash
其中:
latest_block_hash
(bytes32
) 是最新 L2 区块的区块哈希。
state_root
(bytes32
) 是所有执行层帐户的 Merkle-Patricia-Trie (MPT) 根。
此值经常使用,因此更靠近 L2 输出根,这消除了证明其
包含在 latest_block_hash
的原像中的需要。这降低了在 L1 上访问
L2 状态根的 merkle 证明深度和成本。
withdrawal_storage_root
(bytes32
) 提升了 消息
传递合约 存储的 Merkle-Patricia-Trie (MPT) 根。
我们需要针对状态根对提款进行 MPT 证明(首先证明 L2toL1MessagePasser 的存储根与状态根,
然后针对该存储根进行提款),我们可以直接针对 L2toL1MessagePasser 的存储根进行证明,
从而降低 L1 上提款的验证成本。
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 在生成并提交输出后发生重组,则 L2 状态和正确的输出可能会发生变化, 从而导致错误的提议。通过允许提议者在附加新输出时向输出 Oracle 提交 L1 区块编号和哈希来缓解这种情况;如果发生重组,区块哈希 将与该编号的区块的哈希不匹配,并且调用将恢复。
- 原文链接: github.com/ethereum-opti...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!