本文档介绍了在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 -->
已存款的交易 与现有交易类型有以下显著区别:
我们定义一个新的 EIP-2718 兼容交易类型,前缀为 0x7E
,以表示存款交易。
一个存款具有以下字段(按照它们在此处出现的顺序进行 RLP 编码):
bytes32 sourceHash
:source-hash,唯一标识存款的来源。address from
:发送方账户的地址。address to
:接收方账户的地址,如果已存款的交易是合约创建,则为 null(零长度)地址。uint256 mint
:要在 L2 上铸造的 ETH 值。uint256 value
:要发送到接收方账户的 ETH 值。uint64 gas
:L2 交易的 gas 限制。bool isSystemTx
:如果为 true,则交易不与 L2 区块 gas 池交互。
false
)。bytes data
:调用数据。与 EIP-155 交易相比,此交易类型:
nonce
,因为它由 sourceHash
标识。
API 响应仍然包括 nonce
属性:
nonce
始终为 0
nonce
设置为相应交易收据的 depositNonce
属性。from
地址。
API 响应包含零签名的 v
、r
、s
值,以实现向后兼容性。sourceHash
、from
、mint
和 isSystemTx
属性。
API 响应将这些作为附加字段包含在内。我们选择 0x7E
是因为当前允许交易类型标识符高达 0x7F
。选择较高的标识符可以最大限度地降低标识符在未来被 L1 链上的另一种交易类型声明的风险。我们不选择 0x7F
本身,以防它被用于可变长度编码方案。
存款交易的 sourceHash
基于来源计算:
keccak256(bytes32(uint256(0)), keccak256(l1BlockHash, bytes32(uint256(l1LogIndex))))
。
其中 l1BlockHash
和 l1LogIndex
均指 L1 上存款日志事件的包含。
l1LogIndex
是区块日志事件组合列表中的存款事件日志的索引。keccak256(bytes32(uint256(1)), keccak256(l1BlockHash, bytes32(uint256(seqNumber))))
。
其中 l1BlockHash
指的是 info 属性被存入的 L1 区块哈希值。
并且 seqNumber = l2BlockNum - l2EpochStartBlockNum
,
其中 l2BlockNum
是在 L2 中包含存款交易的 L2 区块号,
并且 l2EpochStartBlockNum
是 epoch 中第一个 L2 区块的 L2 区块号。如果没有存款中的 sourceHash
,则两个不同的已存款交易可能具有完全相同的哈希值。
外部 keccak256
使用域对实际的唯一标识信息进行哈希处理,以避免不同类型的来源之间发生冲突。
我们不使用发送方的 nonce 来确保唯一性,因为这需要在区块推导期间从执行引擎额外读取 L2 EVM 状态。
虽然我们只定义了一种新的交易类型,但我们可以根据它们在 L2 区块中的位置来区分两种已存款的交易:
OptimismPortal
)。
用户已存款的交易仅存在于 L2 epoch 的第一个区块中。我们仅定义一种新的交易类型,以最大限度地减少对 L1 客户端软件的修改,并降低总体复杂性。
如上所述,已存款的交易类型不包括用于验证的签名。相反,授权由 L2 链推导 过程处理,该过程在正确应用时,只会推导出 from
地址由 L1 存款合约 的日志证明的交易。
为了执行已存款的交易:
首先,from
账户的余额 必须 增加 mint
的金额。
这是无条件的,并且不会因存款失败而回滚。
然后,根据交易的属性初始化已存款的交易的执行环境,其方式与 EIP-155 交易完全相同。
存款交易的处理方式与类型 -3 (EIP-1559) 交易完全相同,但以下情况除外:
nonce
字段:存款没有任何 nonce,它由其 sourceHash
唯一标识。from
是否为外部拥有帐户 (EOA):通过 L1 地址确保存款不是 EOA
掩蔽,这可能会在未来的 L1 合约部署中发生变化,例如启用类似帐户抽象的机制。isSystemTx
为 false:执行输出声明它使用 gasLimit
gas。isSystemTx
为 true:执行输出声明它使用 0
gas。0
这一事实)请注意,这包括与常规交易一样的合约部署行为,并且 gas 计量是相同的(除了上述与费用相关的更改之外),包括内部 gas 的计量。
EVM 执行发出的任何非 EVM 状态转换错误都将以特殊方式处理:
value
ETH 金额。from
的 nonce 递增 1,使错误等同于本机 EVM 故障。
请注意,之前的 nonce 递增可能发生在 EVM 处理期间,但会首先回滚。最后,在上述处理之后,执行后处理的运行方式相同:
即,gas 池和收据的处理方式与常规交易相同。
但是,从 Regolith 升级开始,存款交易的收据将扩展一个额外的 depositNonce
值,用于存储在 EVM 处理 之前 注册的 from
发送方的 nonce
值。
请注意,执行输出声明的已用 gas 量将从 gas 池中扣除, 但在 Regolith 升级之前的此执行输出值具有特殊的边缘情况。
应用程序开发人员请注意:由于 CALLER
和 ORIGIN
设置为 from
,因此
使用 tx.origin == msg.sender
检查的语义将无法确定在存款交易期间调用者是否为 EOA。相反,该检查只能用于
识别 L2 存款交易中的第一个调用。但是,此检查仍然满足常见情况,即开发人员使用此检查来确保 CALLER
无法
在调用之前和之后执行代码。
尽管缺少签名验证,但我们仍然会在执行存款交易时递增 from
帐户的 nonce。在仅限存款回滚的上下文中,对于交易排序或重放预防而言,这不是必需的
,但是它可以保持与在合约创建期间使用 nonce 的一致性。它还可以简化与下游的集成
工具(例如钱包和区块浏览器)。
交易收据使用 EIP-2718 中的标准键入。
存款交易收据类型等于常规收据,
但扩展了一个可选的 depositNonce
字段。
RLP 编码的共识强制字段为:
postStateOrStatus
(标准):这包含交易状态,请参见 EIP-658。cumulativeGasUsed
(标准):到目前为止,区块中使用的 gas,包括此交易。
CumulativeGasUsed
的差值。bloom
(标准):交易日志的布隆过滤器。logs
(标准):EVM 处理发出的日志事件。depositNonce
(唯一扩展):可选字段。存款交易保留执行期间使用的 nonce。
depositNonce
字段。depositNonce
字段。从 Regolith 开始,收据 API 响应利用收据更改来获得更准确的响应数据:
depositNonce
包含在 API 响应的收据 JSON 数据中to == null
时),depositNonce
有助于推导出正确的 contractAddress
元数据,
而不是假设 nonce 为零。cumulativeGasUsed
说明了实际的 gas 使用量,如 EVM 处理中所计量。L1 属性已存款的交易 是发送到 L1 属性预部署合约 的存款交易。
此交易 必须 具有以下值:
from
是 0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001
(L1 属性存款人账户的地址)to
是 0x4200000000000000000000000000000000000015
(L1 属性预部署合约的地址)。mint
是 0
value
是 0
gasLimit
设置为 150,000,000。isSystemTx
设置为 true
。data
是对 L1 属性预部署合约 的 ABI 编码调用
setL1BlockValues()
函数,其中包含与相应 L1 区块关联的正确值(参见
参考实现)。如果 Regolith 升级已激活,则某些字段将被覆盖:
gasLimit
设置为 1,000,000isSystemTx
设置为 false
此系统发起的 L1 属性交易不收取任何 ETH 用于其分配的 gasLimit
,
因为它实际上是状态转换处理的一部分。
L1 属性存款交易涉及两个特殊用途账户:
存款人账户是一个没有已知私钥的 EOA。它的地址是 0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001
。它的值由在 L1 属性已存款的交易执行期间由 CALLER
和 ORIGIN
操作码返回。
一个在地址 0x4200000000000000000000000000000000000015
上预部署的 L2 合约,它在存储中保存来自相应 L1 区块的某些区块变量,以便可以在后续的已存款交易执行期间访问它们。
预部署存储以下值:
number
(uint64
)timestamp
(uint64
)basefee
(uint256
)hash
(bytes32
)sequenceNumber
(uint64
):这等于相对于 epoch 开始的 L2 区块号,
即 L2 区块距离 L1 属性上次更改的 L2 区块高度,
并在新 epoch 开始时重置为 0。batcherHash
(bytes32
):当抢跑的批量提交者的版本控制承诺。overhead
(uint256
):应用于此 L2 区块中交易的 L1 成本计算的 L1 费用开销。scalar
(uint256
):应用于此 L2 区块中交易的 L1 成本计算的 L1 费用标量。该合约实现了一种授权方案,因此它仅接受来自 存款人账户 的状态更改调用。
该合约具有以下 Solidity 接口,并且可以根据 合约 ABI 规范与之交互。
L1 属性预部署合约的参考实现可以在 L1Block.sol 中找到。
在 packages/contracts
目录中运行 pnpm build
后,要添加到 genesis
文件的字节码将位于 /packages/contracts/artifacts/contracts/L2/L1Block.sol/L1Block.json
处的构建工件文件的 deployedBytecode
字段中。
用户已存款的交易 是由 L2 链推导 流程生成的已存款的交易。每个用户已存款的交易的内容
由 存款合约 在 L1 上发出的相应 TransactionDeposited
事件确定。
from
与发出的值保持不变(尽管它可能
已在 OptimismPortal
(存款 feed 合约)中转换为别名)。to
是任何 20 字节的地址(包括零地址)
isCreation
),此地址设置为 null
。mint
设置为发出的值。value
设置为发出的值。gaslimit
与发出的值保持不变。它必须至少为 21000。isCreation
设置为 true
,否则设置为 false
。data
与发出的值保持不变。根据 isCreation
的值,它被处理为调用数据或合约初始化代码。isSystemTx
由回滚节点为某些具有未计量执行的交易设置。
对于用户存款的交易,它为 false
存款合约已部署到 L1。已存款的交易来自由
存款合约发出的 TransactionDeposited
事件中的值。
存款合约负责维护保证 gas 市场, 收取存款以用于 L2 上的 gas,并确保单个 L1 区块中的保证 gas 总量不超过 L2 区块 gas 限制。
存款合约处理两种特殊情况:
isCreation
标志设置为 true
来指示。
如果 to
地址为非零,则合约将回滚。from
值将转换为其 L2
别名。如果调用者是一个合约,则将通过将
0x1111000000000000000000000000000000001111
添加到其中来转换地址。该数学运算是 unchecked
并且在
Solidity uint160
上完成,因此该值将溢出。这可以防止 L1 上的合约与 L2 上的合约具有相同地址但没有相同代码的攻击。我们可以安全地忽略此
,因为保证 EOA 具有相同的“代码”(即根本没有代码)。当 Sequencer 处于关闭状态时,这也使得用户可以与 L2 上的合约进行交互。
存款合约的参考实现可以在 OptimismPortal.sol 中找到。
- 原文链接: github.com/ethereum-opti...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!