EIP3670: EOF 代码验证

  • ethereum
  • 发布于 2025-02-27 17:25
  • 阅读 35

本文介绍了在合约创建时对EOF格式(EIP-3540)合约代码进行验证的新特性,以确保代码的正确性,拒绝包含不完整PUSH数据或未定义指令的合约。该项更改旨在将代码有效性纳入共识,同时提供向前兼容性,允许未来新指令的定义,并简化EVM的执行路径。

摘要

在合约创建时引入代码验证,以支持EOF格式(EIP-3540)的合约。拒绝包含被截断的 PUSH 数据或未定义指令的合约。遗留字节码(即不符合EOF格式的代码)不受此更改影响。

动机

当前现有合约不需要正确性的验证,EVM实现可以决定如何处理截断的字节码或未定义指令。此更改旨在将代码有效性纳入共识,使有关字节码的推理变得更加简单。此外,EVM实现可能需要更少的路径来决定哪个指令在当前执行上下文中是有效的。

如果希望在不提升EOF版本的情况下引入新指令,已经部署的未定义指令可能会破坏此类合约,因为某些指令的行为可能会改变。拒绝部署未定义指令允许以提升或不提升EOF版本的方式引入新指令。

EOF1 向前兼容性

EOF1格式提供以下向前兼容性特性:

  1. 可以为之前未分配的操作码定义新指令。这些指令可以具有立即数值。
  2. 强制性的EOF部分可以变为可选。
  3. 可以引入新的可选EOF部分。它们可以相对于以前定义的部分以任何顺序排列。

规范

此功能在启用EIP-3540的同一区块中引入,因此每个符合EOF1的字节码必须根据这些规则进行验证。

  1. 之前已弃用的指令 CALLCODE (0xf2) 和 SELFDESTRUCT (0xff),以及在EIP-3540中弃用的指令,被认为无效,其操作码未定义。(注意 还有更多已在EOF中弃用和拒绝的指令,具体由单独的EIP规定)
  2. 在合约创建时对EOF容器的每个代码部分执行 代码验证。如果以下任何检查失败,则代码无效。对于每个指令:
    1. 检查操作码是否已定义。INVALID (0xfe) 被视为已定义。
    2. 检查所有指令的立即字节是否都存在于代码中(代码不能在指令中间结束)。

理由

立即数据

允许 PUSH 指令的隐式零立即数据会为EVM实现带来低效率,而没有任何实际用例(EVM无法观察到代码末尾的 PUSH 指令的值)。此EIP要求所有立即字节必须明确定义在代码中。

拒绝弃用指令

弃用的指令 CALLCODE (0xf2) 和 SELFDESTRUCT (0xff) 已从 valid_opcodes 列表中删除,以防止将来使用。

BLOCKHASH 指令

BLOCKHASH 指令被EIP-2935中引入的系统合约良好替代。然而,尽管引入了替代方案,该操作码仍未被弃用。此操作码将在EOF中保持有效,以避免与遗留字节码区别。

向后兼容性

此更改对向后兼容性没有风险,因为它与EIP-3540同时引入。验证不涵盖遗留字节码(即不符合EOF格式的代码)。

测试用例

合约创建

每个案例应通过提交EOF容器进行EOF合约创建测试(具体规范在另一个EIP中)。每个案例应测试在不同索引处放置的代码部分。

有效代码

  • 包含 INVALID 的EOF代码
  • 数据部分包含未定义指令字节的EOF代码

无效代码

  • 包含未定义指令的EOF代码
  • 以不完整 PUSH 指令结束的EOF代码

参考实现

## 以下范围是由上海执行规范指定的。
## 注意:range(s, e) 不包括 e,因此 +1
shanghai_opcodes = [
    *range(0x00, 0x0b + 1),
    *range(0x10, 0x1d + 1),
    0x20,
    *range(0x30, 0x3f + 1),
    *range(0x40, 0x48 + 1),
    *range(0x50, 0x5b + 1),
    0x5f,
    *range(0x60, 0x6f + 1),
    *range(0x70, 0x7f + 1),
    *range(0x80, 0x8f + 1),
    *range(0x90, 0x9f + 1),
    *range(0xa0, 0xa4 + 1),
    # 注意:0xfe 被视为已分配。
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xfa, 0xfd, 0xfe, 0xff
]

## 删除在此和EIP-3540中被弃用和拒绝的操作码
rejected_in_eof = [
    0x38, 0x39, 0x3b, 0x3c, 0x3f, 0x5a, 0xf1, 0xf2, 0xf4, 0xfa, 0xff
]
valid_opcodes = [op for op in shanghai_opcodes not in rejected_in_eof]

immediate_sizes = 256 * [0]
immediate_sizes[0x60:0x7f + 1] = range(1, 32 + 1)  # PUSH1..PUSH32

## 在无效代码上引发 ValidationException
def validate_instructions(code: bytes):
    # 注意,EOF1已经通过代码部分要求进行了断言
    assert len(code) > 0

    pos = 0
    while pos < len(code):
        # 确保操作码有效
        opcode = code[pos]
        if opcode not in valid_opcodes:
            raise ValidationException("未定义的操作码")

        # 跳过立即数据
        pos += 1 + immediate_sizes[opcode]

    # 确保最后一条指令的立即数不超过代码末尾
    if pos != len(code):
        raise ValidationException("截断的立即数")

安全考虑

参见 EIP-3540 的安全考虑

版权

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

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

0 条评论

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