存款交易

本文档介绍了在L1上发起并在L2上执行的Deposit交易类型,包括其结构、在L1上的发起方式以及在L2上的验证和授权条件,以及两种类型的 Deposit 交易:L1 属性 Deposit 交易和用户 Deposit 交易。还详细说明了 L1 属性预部署合约和用户 Deposit 交易的 Deposit 合约。

存款

<!-- 本文件中所有的术语表参考。 -->

已存款的交易,也称为存款,是在 L1 上发起并在 L2 上执行的交易。本文档概述了一种新的交易类型用于存款。它还描述了如何在 L1 上发起存款,以及 L2 上的授权和验证条件。

词汇提示已存款的交易 特指 L2 交易,而 存款 可以指交易的各个阶段(例如,在 L1 上存款时)。

<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- 不要编辑此部分,请重新运行 doctoc 以更新 --> 目录

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

已存款的交易类型

已存款的交易 与现有交易类型有以下显著区别:

  1. 它们源自 Layer 1 区块,并且必须作为协议的一部分包含在内。
  2. 它们不包括签名验证(有关原因,请参见用户已存款的交易)。
  3. 它们在 L1 上购买其 L2 gas,因此,L2 gas 不可退还。

我们定义一个新的 EIP-2718 兼容交易类型,前缀为 0x7E,以表示存款交易。

一个存款具有以下字段(按照它们在此处出现的顺序进行 RLP 编码):

  • bytes32 sourceHashsource-hash,唯一标识存款的来源。
  • address from:发送方账户的地址。
  • address to:接收方账户的地址,如果已存款的交易是合约创建,则为 null(零长度)地址。
  • uint256 mint:要在 L2 上铸造的 ETH 值。
  • uint256 value:要发送到接收方账户的 ETH 值。
  • uint64 gas:L2 交易的 gas 限制。
  • bool isSystemTx:如果为 true,则交易不与 L2 区块 gas 池交互。
    • 注意:从 Regolith 升级开始,布尔值被禁用(强制为 false)。
  • bytes data:调用数据。

EIP-155 交易相比,此交易类型:

  • 不包括 nonce,因为它由 sourceHash 标识。 API 响应仍然包括 nonce 属性:
    • 在 Regolith 之前:nonce 始终为 0
    • 使用 Regolith:nonce 设置为相应交易收据的 depositNonce 属性。
  • 不包括签名信息,并显式指定 from 地址。 API 响应包含零签名的 vrs 值,以实现向后兼容性。
  • 包括新的 sourceHashfrommintisSystemTx 属性。 API 响应将这些作为附加字段包含在内。

我们选择 0x7E 是因为当前允许交易类型标识符高达 0x7F。选择较高的标识符可以最大限度地降低标识符在未来被 L1 链上的另一种交易类型声明的风险。我们不选择 0x7F 本身,以防它被用于可变长度编码方案。

源哈希计算

存款交易的 sourceHash 基于来源计算:

  • 用户已存款: keccak256(bytes32(uint256(0)), keccak256(l1BlockHash, bytes32(uint256(l1LogIndex))))。 其中 l1BlockHashl1LogIndex 均指 L1 上存款日志事件的包含。 l1LogIndex 是区块日志事件组合列表中的存款事件日志的索引。
  • L1 属性已存款: keccak256(bytes32(uint256(1)), keccak256(l1BlockHash, bytes32(uint256(seqNumber))))。 其中 l1BlockHash 指的是 info 属性被存入的 L1 区块哈希值。 并且 seqNumber = l2BlockNum - l2EpochStartBlockNum, 其中 l2BlockNum 是在 L2 中包含存款交易的 L2 区块号, 并且 l2EpochStartBlockNumepoch 中第一个 L2 区块的 L2 区块号。

如果没有存款中的 sourceHash,则两个不同的已存款交易可能具有完全相同的哈希值。

外部 keccak256 使用域对实际的唯一标识信息进行哈希处理,以避免不同类型的来源之间发生冲突。

我们不使用发送方的 nonce 来确保唯一性,因为这需要在区块推导期间从执行引擎额外读取 L2 EVM 状态。

已存款的交易种类

虽然我们只定义了一种新的交易类型,但我们可以根据它们在 L2 区块中的位置来区分两种已存款的交易:

  1. 第一个交易 必须 是一个 L1 属性已存款的交易,后跟
  2. 零个或多个 用户已存款的交易 数组,这些交易已提交到 L1 上的存款 feed 合约(称为 OptimismPortal)。 用户已存款的交易仅存在于 L2 epoch 的第一个区块中。

我们仅定义一种新的交易类型,以最大限度地减少对 L1 客户端软件的修改,并降低总体复杂性。

已存款的交易的验证和授权

如上所述,已存款的交易类型不包括用于验证的签名。相反,授权由 L2 链推导 过程处理,该过程在正确应用时,只会推导出 from 地址由 L1 存款合约 的日志证明的交易。

执行

为了执行已存款的交易:

首先,from 账户的余额 必须 增加 mint 的金额。 这是无条件的,并且不会因存款失败而回滚。

然后,根据交易的属性初始化已存款的交易的执行环境,其方式与 EIP-155 交易完全相同。

存款交易的处理方式与类型 -3 (EIP-1559) 交易完全相同,但以下情况除外:

  • 不验证费用字段:存款没有任何费用,因为它在 L1 上支付 gas 费用。
  • 不验证 nonce 字段:存款没有任何 nonce,它由其 sourceHash 唯一标识。
  • 不处理访问列表:存款没有访问列表,因此将其视为访问列表为空进行处理。
  • 不检查 from 是否为外部拥有帐户 (EOA):通过 L1 地址确保存款不是 EOA 掩蔽,这可能会在未来的 L1 合约部署中发生变化,例如启用类似帐户抽象的机制。
  • 在 Regolith 升级之前:
    • 执行输出声明了非标准的 gas 使用量:
    • 如果 isSystemTxfalse:执行输出声明它使用 gasLimit gas
    • 如果 isSystemTxtrue:执行输出声明它使用 0 gas
  • 不会将 gas 作为 ETH 退还。(要么不退还,要么利用存款的 gas 价格为 0 这一事实)
  • 不收取交易优先级费用。不向区块费用接收者付款。
  • 不收取 L1 成本费用,因为存款来自 L1,因此不必作为数据提交回 L1。
  • 不收取基本费用。基本费用总额的核算不会改变。

请注意,这包括与常规交易一样的合约部署行为,并且 gas 计量是相同的(除了上述与费用相关的更改之外),包括内部 gas 的计量。

EVM 执行发出的任何非 EVM 状态转换错误都将以特殊方式处理:

  • 它被转换为 EVM 错误: 即,存款将始终被包括在内,但如果它遇到非 EVM 状态转换错误,则其收据将表明失败 例如,由于帐户余额不足而无法转移指定的 value ETH 金额。
  • 世界状态将回滚到 EVM 处理开始时,即存款的铸造部分之后。
  • 世界状态中 fromnonce 递增 1,使错误等同于本机 EVM 故障。 请注意,之前的 nonce 递增可能发生在 EVM 处理期间,但会首先回滚。

最后,在上述处理之后,执行后处理的运行方式相同: 即,gas 池和收据的处理方式与常规交易相同。 但是,从 Regolith 升级开始,存款交易的收据将扩展一个额外的 depositNonce 值,用于存储在 EVM 处理 之前 注册的 from 发送方的 nonce 值。

请注意,执行输出声明的已用 gas 量将从 gas 池中扣除, 但在 Regolith 升级之前的此执行输出值具有特殊的边缘情况。

应用程序开发人员请注意:由于 CALLERORIGIN 设置为 from,因此 使用 tx.origin == msg.sender 检查的语义将无法确定在存款交易期间调用者是否为 EOA。相反,该检查只能用于 识别 L2 存款交易中的第一个调用。但是,此检查仍然满足常见情况,即开发人员使用此检查来确保 CALLER 无法 在调用之前和之后执行代码。

Nonce 处理

尽管缺少签名验证,但我们仍然会在执行存款交易时递增 from 帐户的 nonce。在仅限存款回滚的上下文中,对于交易排序或重放预防而言,这不是必需的 ,但是它可以保持与在合约创建期间使用 nonce 的一致性。它还可以简化与下游的集成 工具(例如钱包和区块浏览器)。

存款收据

交易收据使用 EIP-2718 中的标准键入。 存款交易收据类型等于常规收据, 但扩展了一个可选的 depositNonce 字段。

RLP 编码的共识强制字段为:

  • postStateOrStatus(标准):这包含交易状态,请参见 EIP-658
  • cumulativeGasUsed(标准):到目前为止,区块中使用的 gas,包括此交易。
    • 实际使用的 gas 来自与先前交易的 CumulativeGasUsed 的差值。
    • 从 Regolith 开始,这说明了存款的实际 gas 使用量,如常规交易一样。
  • bloom(标准):交易日志的布隆过滤器。
  • logs(标准):EVM 处理发出的日志事件。
  • depositNonce(唯一扩展):可选字段。存款交易保留执行期间使用的 nonce
    • 在 Regolith 之前,必须始终省略此 depositNonce 字段。
    • 使用 Regolith,必须始终包括此 depositNonce 字段。

从 Regolith 开始,收据 API 响应利用收据更改来获得更准确的响应数据:

  • depositNonce 包含在 API 响应的收据 JSON 数据中
  • 对于合约部署(当 to == null 时),depositNonce 有助于推导出正确的 contractAddress 元数据, 而不是假设 nonce 为零。
  • cumulativeGasUsed 说明了实际的 gas 使用量,如 EVM 处理中所计量。

L1 属性已存款的交易

L1 属性已存款的交易 是发送到 L1 属性预部署合约 的存款交易。

此交易 必须 具有以下值:

  1. from0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001L1 属性存款人账户的地址)
  2. to0x4200000000000000000000000000000000000015L1 属性预部署合约的地址)。
  3. mint0
  4. value0
  5. gasLimit 设置为 150,000,000。
  6. isSystemTx 设置为 true
  7. data 是对 L1 属性预部署合约ABI 编码调用 setL1BlockValues() 函数,其中包含与相应 L1 区块关联的正确值(参见 参考实现)。

如果 Regolith 升级已激活,则某些字段将被覆盖:

  1. gasLimit 设置为 1,000,000
  2. isSystemTx 设置为 false

此系统发起的 L1 属性交易不收取任何 ETH 用于其分配的 gasLimit, 因为它实际上是状态转换处理的一部分。

L2 上的特殊账户

L1 属性存款交易涉及两个特殊用途账户:

  1. L1 属性存款人账户
  2. L1 属性预部署合约

L1 属性存款人账户

存款人账户是一个没有已知私钥的 EOA。它的地址是 0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001。它的值由在 L1 属性已存款的交易执行期间由 CALLERORIGIN 操作码返回。

L1 属性预部署合约

一个在地址 0x4200000000000000000000000000000000000015 上预部署的 L2 合约,它在存储中保存来自相应 L1 区块的某些区块变量,以便可以在后续的已存款交易执行期间访问它们。

预部署存储以下值:

  • L1 区块属性:
    • number (uint64)
    • timestamp (uint64)
    • basefee (uint256)
    • hash (bytes32)
  • sequenceNumber (uint64):这等于相对于 epoch 开始的 L2 区块号, 即 L2 区块距离 L1 属性上次更改的 L2 区块高度, 并在新 epoch 开始时重置为 0。
  • 与 L1 区块绑定的系统可配置项,请参见系统配置规范
    • batcherHash (bytes32):当抢跑的批量提交者的版本控制承诺。
    • overhead (uint256):应用于此 L2 区块中交易的 L1 成本计算的 L1 费用开销。
    • scalar (uint256):应用于此 L2 区块中交易的 L1 成本计算的 L1 费用标量。

该合约实现了一种授权方案,因此它仅接受来自 存款人账户 的状态更改调用。

该合约具有以下 Solidity 接口,并且可以根据 合约 ABI 规范与之交互。

L1 属性预部署合约:参考实现

L1 属性预部署合约的参考实现可以在 L1Block.sol 中找到。

packages/contracts 目录中运行 pnpm build 后,要添加到 genesis 文件的字节码将位于 /packages/contracts/artifacts/contracts/L2/L1Block.sol/L1Block.json 处的构建工件文件的 deployedBytecode 字段中。

用户已存款的交易

用户已存款的交易 是由 L2 链推导 流程生成的已存款的交易。每个用户已存款的交易的内容 由 存款合约 在 L1 上发出的相应 TransactionDeposited 事件确定。

  1. from 与发出的值保持不变(尽管它可能 已在 OptimismPortal(存款 feed 合约)中转换为别名)。
  2. to 是任何 20 字节的地址(包括零地址)
    • 在合约创建的情况下(参见 isCreation),此地址设置为 null
  3. mint 设置为发出的值。
  4. value 设置为发出的值。
  5. gaslimit 与发出的值保持不变。它必须至少为 21000
  6. 如果交易是合约创建,则 isCreation 设置为 true,否则设置为 false
  7. data 与发出的值保持不变。根据 isCreation 的值,它被处理为调用数据或合约初始化代码。
  8. isSystemTx 由回滚节点为某些具有未计量执行的交易设置。 对于用户存款的交易,它为 false

存款合约

存款合约已部署到 L1。已存款的交易来自由 存款合约发出的 TransactionDeposited 事件中的值。

存款合约负责维护保证 gas 市场, 收取存款以用于 L2 上的 gas,并确保单个 L1 区块中的保证 gas 总量不超过 L2 区块 gas 限制。

存款合约处理两种特殊情况:

  1. 合约创建存款,通过将 isCreation 标志设置为 true 来指示。 如果 to 地址为非零,则合约将回滚。
  2. 来自合约账户的调用,在这种情况下,from 值将转换为其 L2 别名
地址别名

如果调用者是一个合约,则将通过将 0x1111000000000000000000000000000000001111 添加到其中来转换地址。该数学运算是 unchecked 并且在 Solidity uint160 上完成,因此该值将溢出。这可以防止 L1 上的合约与 L2 上的合约具有相同地址但没有相同代码的攻击。我们可以安全地忽略此 ,因为保证 EOA 具有相同的“代码”(即根本没有代码)。当 Sequencer 处于关闭状态时,这也使得用户可以与 L2 上的合约进行交互。

存款合约实现:Optimism Portal

存款合约的参考实现可以在 OptimismPortal.sol 中找到。

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

0 条评论

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