Alert Source Discuss
⚠️ Review Standards Track: Core

EIP-7873: EOF - TXCREATE 和 InitcodeTransaction 类型

向 EOF 添加 `TXCREATE` 指令以及配套的交易类型,允许从交易数据创建 EOF 合约

Authors Piotr Dobaczewski (@pdobacz), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Danno Ferrin (@shemnon)
Created 2025-01-31
Requires EIP-3540, EIP-3670, EIP-3860, EIP-7620

摘要

EVM Object Format (EOF) 移除了使用创建交易(带有空的 to 字段),CREATECREATE2 指令创建合约的可能性。我们引入一个新的指令:TXCREATE,以及一个新的交易类型 (InitcodeTransaction),以提供一种使用交易数据中的 EOF 容器创建合约的方法。

动机

此 EIP 使用来自 EIP-3540 的术语,该 EIP 引入了 EOF 格式。

创建交易和创建指令 CREATECREATE2 是传统 EVM 提供的部署新代码的方式,但根据移除代码可观察性的要求,它们不允许部署 EOF 代码。为了允许外部拥有账户 (EOA) 部署 EOF 合约,必须有一种方法可以使用交易数据中传递的字节码来创建 EOF 合约。

此外,此 EIP 中引入的新指令和交易类型使合约能够使用来自交易数据的 initcode 创建其他合约,这在传统 EVM 中是通过组合 CREATECREATE2 并从 calldata 加载 initcode 来实现的。

该机制补充了来自 EIP-7620EOFCREATERETURNCODE 指令,因此传统 EVM 中可用的所有合约创建用例都为 EOF 启用。

由于 TXCREATE 不限于 EOF 容器,它也服务于将 EOF 合约引导到状态的目的。

规范

本文档中的关键词“必须”,“不得”,“必需”,“应”,“不应”,“推荐”,“不推荐”,“可以”和“可选”应解释为 RFC 2119 和 RFC 8174 中所述。

参数

常量
INITCODE_TX_TYPE Bytes1(0x06)
MAX_INITCODE_COUNT 256
TX_CREATE_COST Ethereum Execution Layer Specs 中定义为 32000
STACK_DEPTH_LIMIT Ethereum Execution Layer Specs 中定义为 1024
GAS_CODE_DEPOSIT Ethereum Execution Layer Specs 中定义为 200
TX_DATA_COST_PER_ZERO Ethereum Execution Layer Specs 中定义为 4
TX_DATA_COST_PER_NON_ZERO Ethereum Execution Layer Specs 中定义为 16
MAX_CODE_SIZE EIP-170 中定义为 24576
MAX_INITCODE_SIZE EIP-3860 中定义为 2 * MAX_CODE_SIZE

交易类型

引入新的交易 InitcodeTransaction(类型 INITCODE_TX_TYPE),它通过添加一个新字段 initcodes: List[ByteList[MAX_INITCODE_SIZE], MAX_INITCODE_COUNT] 扩展了 EIP-1559(类型 2)交易。

initcodes 只能通过 TXCREATE 指令(见下文)访问,因此 InitcodeTransactions 旨在发送到包括 TXCREATE 在其执行中的合约。

Gas 消耗计划

initcodes 项目数据消耗与 calldata 相同的 gas:InitcodeTransaction 的交易 gas 被扩展为包括 initcodes 中的 tokens 以及 calldata 中的 tokens。使用 EIP-7623 中的约定,交易 gas 的计算方式如下:

STANDARD_TOKEN_COST = 4
TOTAL_COST_FLOOR_PER_TOKEN = 10

tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4
tokens_in_initcodes = 0
for initcode in initcodes:
    tokens_in_initcodes += zero_bytes_in_initcode + nonzero_bytes_in_initcode * 4
tx.gasUsed = (
    21000
    +
    max(
        STANDARD_TOKEN_COST * (tokens_in_calldata + tokens_in_initcodes)
        + execution_gas_used,
        TOTAL_COST_FLOOR_PER_TOKEN * (tokens_in_calldata + tokens_in_initcodes)
    )
)

交易验证

  • 如果 initcodes 中有零个条目,或者条目多于 MAX_INITCODE_COUNT,则 InitcodeTransaction 无效。
  • 如果 initcodes 中的任何条目的长度为零,或者任何条目超过 MAX_INITCODE_SIZE,则 InitcodeTransaction 无效。
  • 如果 tonil,则 InitcodeTransaction 无效。

在交易验证规则下,不对 initcodes 进行验证以使其符合 EOF 规范。仅在通过 TXCREATE 访问时才对其进行验证。这样可以避免潜在的 mempool DoS 攻击。如果在 InitcodeTransaction 的执行期间未调用任何 TXCREATE 指令,则该交易仍然有效。

其他支持合约创建的创建交易(特别是类型 0“Frontier”,类型 1“AccessList”,类型 2“FeeMarket”交易,其中 to 字段为空)将不会尝试解析其 input 字段中的 EOF 容器,并将代码作为非 EOF 代码执行。这将导致立即执行未定义的 0xEF 指令并停止。

RLP 和签名

给定 EIP-2718 中的定义,InitcodeTransactionTransactionPayload 是以下内容的 RLP 序列化:

[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, initcodes, y_parity, r, s]

TransactionTypeINITCODE_TX_TYPE,签名值 y_parityrs 是通过对以下摘要构造 secp256k1 签名来计算的:

keccak256(INITCODE_TX_TYPE || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, initcodes]))

此交易的 EIP-2718 ReceiptPayloadrlp([status, cumulative_transaction_gas_used, logs_bloom, logs])

执行语义

只要没有明确列出,EOF 合约创建规则以及 TXCREATE 指令应与 CREATE2 指令的规则相同或类似。这包括但不限于:

  • 关于 accessed_addresses 的行为和地址冲突 (EIP-684EIP-2929)
  • TXCREATE initcode 创建的 EVM 执行帧 - 内存,帐户上下文等。
  • 新创建合约的帐户的 nonce 递增 EIP-161
  • 用于创建捐赠 (value 参数) 的余额检查和转移

在激活 EIP-3540 的相同区块号上引入一个新指令:TXCREATE (0xed)。

TXCREATE

  • 扣除 TX_CREATE_COST gas
  • 如果当前帧处于 static-mode,则异常失败并停止。
  • 从操作数堆栈中弹出 tx_initcode_hashsaltinput_offsetinput_sizevalue
  • 使用 [input_offset, input_size] 执行(并收取费用)内存扩展
  • 从哈希到 tx_initcode_hash 的交易 initcodes 数组中加载 initcode EOF 容器
    • 如果交易中不存在这样的 initcode,或者从 TransactionType 不是 INITCODE_TX_TYPE 的交易调用,则失败(在堆栈上返回 0)
      • 不更新调用者的 nonce,并且不消耗 initcode 执行的 gas。
    • initcontainer 为该 EOF 容器,initcontainer_size 为其字节长度
  • 检查当前调用深度是否低于 STACK_DEPTH_LIMIT,并且调用者余额是否足以转移 value
    • 如果失败,则在堆栈上返回 0,则不更新调用者的 nonce,并且不消耗 initcode 执行的 gas。
  • 验证 initcode 容器及其所有子容器(递归地)
    • 与一般验证不同,此外还要求 initcontainer 在标头中声明的 data_size 等于实际的 data_section 大小。
    • 验证包括检查 initcontainer 是否不包含 RETURNSTOP
  • 如果容器无效,则失败(在堆栈上返回 0)
    • 不更新调用者的 nonce,并且不消耗 initcode 执行的 gas。
  • 调用方的内存切片 [input_offset:input_size] 用作 calldata
  • 执行容器并扣除执行 gas。EIP-150 中的 63/64 规则适用。
  • 递增 sender 帐户的 nonce
  • new_address 计算为 keccak256(0xff || sender32 || salt)[12:],其中 sender32 是发送者地址,左侧用零填充为 32 字节
  • 成功执行 initcode 会导致将 0 推送到堆栈上
    • 如果执行 REVERT,则可以填充 returndata
    • sender 的 nonce 保持更新
  • 成功执行以 initcode 执行 RETURNCODE{deploy_container_index}(aux_data_offset, aux_data_size) 指令结束(请参阅 EIP-7620)。之后:
    • 在从中执行 RETURNCODE 的容器中加载 deploy_container_index 处的部署 EOF 子容器
    • 将数据段与 (aux_data_offset, aux_data_offset + aux_data_size) 内存段连接起来,并更新标头中的数据大小
    • 如果更新后的部署容器大小超过 MAX_CODE_SIZE,则指令异常中止
    • state[new_address].code 设置为更新后的部署容器
    • new_address 推送到堆栈上
  • 扣除 GAS_CODE_DEPOSIT * deployed_code_size gas

请注意,期望实现缓存当前交易执行期间的容器验证结果,因此每个容器验证的成本已充分包含在 InitcodeTransaction 固有成本(initcodes 费用)中。

原理

TXCREATE 失败模式

TXCREATE 在 initcontainer 不存在以及 EOF 验证不成功的情况下有两种“轻量级”失败模式。考虑了一种替代设计,其中这两种情况都导致“硬”失败(消耗所有可用的 gas)。我们决定采用更精细和更宽容的失败模式,以便使产生的 gas 成本与 EVM 执行的实际工作对齐。

允许在传统 EVM 中使用 TXCREATE

EOF 合约创建需要一种特殊的可能性,即在传统代码中调用 EOF 操作码 - TXCREATE,因为否则传统合约和创建交易都无法部署 EOF 代码来引导。替代方法是继续使用传统创建机制,要么仍然依赖于从内存中获取 initcode 并且不满足代码非可观察性的总体要求,要么滥用传统创建交易机制,要么引入一个预部署的创建者合约到状态中。

这也使得 EIP-7698 (EOF - Creation transaction) 不再是将 EOF 合约部署到链上的基本要求。该 EIP 可以从 EOFv1 中删除并撤回。

新的地址哈希方案

TXCREATE 使用方案 new_address = keccak256(0xff || sender32 || salt)[12:],与 EOFCREATE 指令相同。是否将 initcontainer 哈希包含到 salt 中的决定留给 TXCREATE 调用者。有关详细原理,请参阅 EIP-7620

EOF 创建交易与部署模式

依赖 EOF 创建交易作为替代解决方案使得智能合约钱包无法部署任意 EOF 合约(只有 EOA 可以)。同时,这是一种当前传统创建规则允许的用例,这要归功于 CREATECREATE2 指令。一种解决方法是首先将这些任意 EOF 合约“上传”到工厂合约,然后使用 EXTDELEGATECALL-EOFCREATE 序列进行部署,这非常昂贵,因为它需要将部署的合约放在链上两次。因此,此 EIP 中提出的方法与账户抽象 (AA) 路线图更加兼容,在 AA 路线图中,智能合约钱包应与 EOA 具有同等的功能。

最重要的是,依赖于基于 nonce 的哈希方案来获取新创建合约的地址,就像 EOF 创建交易的情况一样,将阻止 EOF 合约以确定性的跨链地址进行反事实部署。TXCREATE 指令的引入开箱即用地支持这一点。可以编写 ERC 来提供 toehold 合约,这些合约将满足部署模式,例如无盐部署和将发送者的地址作为 salt 的一部分进行哈希处理。

在现有交易类型中处理带有 0xEF00 前缀的代码。

三种现有的交易类型(类型 0 “Frontier”,类型 1 “AccessList”,类型 2 “FeeMarket”)在某些配置中接受代码作为其输入数据的一部分。此代码可以以 EF 开头,因为它是一个 initcode,而不是已部署的合约。

处理这些交易中潜在 EOF 容器的一种可能方法是,如果他们试图将 EOF 容器作为 initcode 执行,则使它们无效。具体来说,如果输入数据以 0xef00 开头,并且 to 字段设置为 nil,表示合约创建交易,则该交易无效。这将使包含这些交易的区块无效,而在包含此 EIP 的分叉之前,它们将是有效的。

区块构建者会看到一个影响,因为他们需要确保无效的交易不包含在区块中。对此有先例,因为 EIP-7623 为交易中的 gas 限制建立了一个新的下限,在其采用之前,该限制较低。但是,区块构建者对 gas 限制和交易进行了现有的检查,并且该更改正在调整公式和常量。

如果 EIP-7702 委托指定显示为合约创建交易中的代码,我们也应该考虑对其进行处理。该交易有效,作为普通字节码执行,并且执行的第一个操作是 0xEF,这是一个无效的操作码,会导致整个交易中止。

让类型 0、1 和 2 合约创建交易的 输入数据 作为普通 EVM 代码执行,而不管魔术 0xEF00 字节如何, 都能 保持客户端代码中的一致行为,并防止区块构建者必须更新其旧交易类型的逻辑。

向后兼容性

此更改不会对向后兼容性构成风险,因为它是在与 EIP-3540 同步引入的。尽管为传统字节码(未进行 EOF 格式化的代码)引入了新指令,但有意义的合约不太可能无意中以正式有效的操作数执行 0xed 指令,并无意中导致它运行 EOF initcode(这也需要使用 InitcodeTransaction,否则 initcode 查找将失败)。

TXCREATE 指令引入传统 EVM 不会影响 JUMPDEST 分析,因为该指令没有直接参数。

在此更改激活之前,新类型的交易无效。

合约创建选项不会为传统字节码更改,包括当 calldata 中遇到可能看起来像 EOF 容器的代码时,现有 to: nil 交易的行为方式。

安全注意事项

需要讨论。

版权

通过 CC0 放弃版权和相关权利。

Citation

Please cite this document as:

Piotr Dobaczewski (@pdobacz), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Danno Ferrin (@shemnon), "EIP-7873: EOF - TXCREATE 和 InitcodeTransaction 类型 [DRAFT]," Ethereum Improvement Proposals, no. 7873, January 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7873.