本文介绍了一种为以太坊虚拟机(EVM)设计的可扩展和版本化的容器格式(EOF),通过在部署时进行校验,提供了代码与数据的分离,有助于将来新功能的引入与旧功能的废弃。EOF格式的设计可以减少运行时的负担,提升合约的管理效率。并且,该格式包括了一些新特性,如静态跳转和多字节操作码的支持。
我们引入了一种可扩展和版本化的 EVM 容器格式,在部署时进行一次性验证。这里描述的版本带来了代码和数据分离的实际好处,允许将来轻松引入各种更改。这个变化依赖于 EIP-3541 中引入的保留字节。
总之,EOF 字节码具有以下布局:
magic, version, (section_kind, section_size_or_sizes)+, 0, <section contents>
当前链上部署的 EVM 字节码没有预定义的结构。代码通常在客户端中进行验证,在运行时每次执行之前都会进行 JUMPDEST
分析。这不仅带来了开销,还给引入新特性或弃用现有特性带来了挑战。
在合约创建过程中验证代码允许在帐户中不增加额外版本字段的情况下进行代码版本控制。版本控制是引入或弃用功能的有用工具,尤其适用于较大的更改(例如控制流的重大更改或诸如帐户抽象等功能)。
此 EIP 中描述的格式引入了一个简单且可扩展的容器,对客户端和语言所需的更改进行了最小化,并引入了验证。
它提供的第一个具体特性是代码和数据的分离。此分离对于链上代码验证器(如层二扩容工具(如 Optimism)所使用的那些)尤其有利,因为它们可以区分代码和数据(这还包括部署代码和构造函数参数)。目前,它们 a) 需要在合约部署之前进行更改; b) 实施脆弱的方法;或 c) 实施昂贵且有限制的跳转分析。代码和数据的分离可以简化使用并为此类用例实现显著的 gas 节省。此外,各种(静态)分析工具也可以受益,尽管链下工具已经能够处理现有代码,因此影响较小。
一份非详尽的可以受益于此格式的建议更改清单:
JUMPDEST
表格(以避免在执行时进行分析)和/或完全移除 JUMPDEST
。文档中的关键字 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, 和 “OPTIONAL” 应按 RFC 2119 和 RFC 8174 中的描述进行解释。
为了确保状态中的每个 EOF 格式合约都是有效的,我们需要防止已经部署(且未验证)的合约被识别为这种格式。这是通过选择一个 magic 字节序列来实现的,该序列在任何已部署的合约中都不存在。
如果代码以 MAGIC
开头,则被认为是 EOF 格式;否则被视为 legacy 代码。为清楚起见,MAGIC
及其版本号 n 被表示为 EOFn 前缀,例如 EOF1 前缀。
EOF 格式的合约是使用在单独 EIP 中引入的新指令创建的。
操作码 0xEF
目前是未定义的指令,因此:它不弹出任何堆栈项目,也不推送任何堆栈项目,并且在执行时会导致异常中止。这意味着以此指令开头的上一个 initcode 或已部署的 legacy code 将继续导致执行中止。
除非另有说明,所有整数以大端字节顺序编码。
我们引入 代码验证 用于新合约创建。为此,我们定义了一种称为 EVM 对象格式(EOF)的格式,包含一个版本指示符,以及与给定版本关联的有效性规则集。
legacy 代码不会受到 EOF 代码验证的影响。
代码验证在合约创建期间执行,并将在单独的 EIP 中详细说明。EOF 格式本身及其正式验证将在以下部分中描述。
EOF 容器是一种二进制格式,能够提供 EOF 版本号和 EOF 部分列表。
容器以 EOF 前缀开始:
描述 | 长度 | 值 | |
---|---|---|---|
magic | 2 字节 | 0xEF00 | |
version | 1 字节 | 0x01–0xFF | EOF 版本号 |
EOF 前缀后至少有一个节头。每个节头包含两个字段,section_kind
和 section_size
或 section_size_list
,具体取决于种类。当允许多个此类部分时,section_size_list
是一个大小值列表,以项数计数后跟随项。
描述 | 长度 | 值 | |
---|---|---|---|
section_kind | 1 字节 | 0x01–0xFF | uint8 |
section_size | 2 字节 | 0x0000–0xFFFF | uint16 |
section_size_list | 动态 | n/a | uint16, uint16+ |
节头的列表以 section headers terminator byte 0x00
结束。正文内容紧接其后。
version
必须不为 0
。section_kind
必须不为 0
。值 0
保留用于 section headers terminator byte。EOF 版本 1 由多个 EIP 组成,包括本 EIP。此规范中的某些值仅进行了简要讨论。要理解 EOF 的完整范围,需要深入审查每个 EIP。
EOF 版本 1 容器由 header
和 body
组成。
container := header, body
header :=
magic, version,
kind_type, type_size,
kind_code, num_code_sections, code_size+,
[kind_container, num_container_sections, container_size+,]
kind_data, data_size,
terminator
body := types_section, code_section+, container_section*, data_section
types_section := (inputs, outputs, max_stack_height)+
注: ,
是一个连接运算符,+
应理解为 “一个或多个” 前一项,`应理解为 “零个或多个” 前一项,
[item]` 应理解为一个可选项。*
名称 | 长度 | 值 | 描述 |
---|---|---|---|
magic | 2 字节 | 0xEF00 | |
version | 1 字节 | 0x01 | EOF 版本 |
kind_type | 1 字节 | 0x01 | 类型部分的种类标记 |
type_size | 2 字节 | 0x0004-0x1000 | 表示类型部分内容长度的 16 位无符号大端整型,每个代码部分 4 字节 |
kind_code | 1 字节 | 0x02 | 代码大小部分的种类标记 |
num_code_sections | 2 字节 | 0x0001-0x0400 | 表示代码部分数量的 16 位无符号大端整型 |
code_size | 2 字节 | 0x0001-0xFFFF | 表示代码部分内容长度的 16 位无符号大端整型 |
kind_container | 1 字节 | 0x03 | 容器大小部分的种类标记 |
num_container_sections | 2 字节 | 0x0001-0x0100 | 表示容器部分数量的 16 位无符号大端整型 |
container_size | 2 字节 | 0x0001-0xFFFF | 表示容器部分内容长度的 16 位无符号大端整型 |
kind_data | 1 字节 | 0x04 | 数据大小部分的种类标记 |
data_size | 2 字节 | 0x0000-0xFFFF | 表示数据部分内容长度的 16 位无符号大端整型 (*) |
terminator | 1 字节 | 0x00 | 标记头部的结束 |
(*) 对于尚未部署的容器,这可以大于实际内容长度。
名称 | 长度 | 值 | 描述 |
---|---|---|---|
types_section | 可变 | n/a | 存储代码节元数据 |
inputs | 1 字节 | 0x00-0x7F | 代码节消耗的堆栈元素数量 |
outputs | 1 字节 | 0x00-0x7F | 代码节返回的堆栈元素数量 |
max_stack_height | 2 字节 | 0x0000-0x03FF | 代码节在操作数栈上放置的最大元素数量 |
code_section | 可变 | n/a | 任意字节码 |
container_section | 可变 | n/a | 任意的 EOF 格式容器 |
data_section | 可变 | n/a | 任意字节序列 |
注:outputs
的特殊值 0x80
被指定为表示非返回函数,如单独 EIP 中所定义。
对容器格式施加以下有效性约束:
types_size
必须能被 4
整除。types_size / 4
。data_size
。MAX_INITCODE_SIZE
(如 EIP-3860 中定义)。对于一个 EOF 合约:
CODESIZE
, CODECOPY
, EXTCODESIZE
, EXTCODECOPY
, EXTCODEHASH
, GAS
被 EOF 合约的验证拒绝,没有替代品。CALL
, DELEGATECALL
, STATICCALL
被 EOF 合约的验证拒绝,替代指令将单独在 EIP 中引入。DELEGATECALL
(或 EOF 的任何替代指令)被禁止,并且应以调用深度检查失败的相同方式失败。我们允许生态 Legacy 到 EOF 路径,以便现有代理合约能够使用 EOF 升级。对于 legacy 合约:
EXTCODECOPY
的目标帐户是 EOF 合约,则它将从 EF00
复制最多 2 字节,作为那将是代码的样子。EXTCODEHASH
的目标帐户是 EOF 合约,则它将返回 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5
(EF00
的哈希值,作为那将是代码的样子)。EXTCODESIZE
的目标帐户是 EOF 合约,则将返回 2。注:与 legacy 目标一样,上述 EXTCODECOPY
, EXTCODEHASH
和 EXTCODESIZE
的行为不适用于正在创建中的 EOF 合约,即那些报告的结果与无代码的帐户相同。
EVM 和/或帐户版本控制在过去几年中被多次讨论。此提案旨在从中学习。 请参阅“Ethereum account versioning”在 Ethereum Magicians Fellowship 论坛上,作为良好的起点。
该规范引入创建时间验证,这意味着:
JUMPDEST
映射,消除执行前所需隐式 JUMPDEST
分析的需求。JUMPDEST
指令的需求。替代方案是对 EOF 进行执行时间验证。这在每次执行合约时都会进行,然而客户端可能能够缓存验证结果。这种 替代 方法具有以下特性:
0xEF
字节或 EOF 前缀 开头的数据合约可以部署。然而,这是一项可疑的好处。第一个字节 0xEF
由 EIP-3541 保留用于此目的。
第二个字节 0x00
被选择以避免与在 Mainnet 上部署的三个合约发生冲突:
0xca7bf67ab492b49806e24b6e2e4ec105183caa01
: EFF09f918bf09f9fa9
0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec
: EF
0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0
: EF
在其伦敦分叉区块时,公共测试网络(Goerli,Ropsten,Rinkeby,Kovan 和 Sepolia)中不存在以 0xEF
字节开头的合约。
注: 这个 EIP 必须不在包含以 MAGIC
开头的字节码且不有效 EOF 的链上启用。
版本号 0 不会在 EOF 中使用,因此我们可以将 legacy 代码称为 EOF0。另外,实现可能使用 API,其中 0 版本号表示 legacy 代码。
我们考虑了各部分的问题:
section_header, section_data, section_header, section_data, ...
)。它们对于受到编辑(添加/删除部分)的格式很方便。但对于 EVM 来说这不是一个有用的特性。一个适用于我们情况的微小好处是,它们不需要特定的 “头终止符”。另一方面,它们似乎不太适合代码分块/Merkle化,因为最好将所有节头放在同一个块中。number_of_sections
或 total_size_of_headers
。这两个都会引发一个问题,即这些字段能容纳多大的值。使用终止字节似乎避免了选择过小大小的问题,而没有明显的缺点,因此这是一条已采取的路径。参见 EIP-7480 中的 Lack of EXTDATACOPY 部分。
DELEGATECALL
EOF1 合约当前合约可以以三种不同的方式自毁(通过 SELFDESTRUCT
直接,自通过 CALLCODE
和通过 DELEGATECALL
间接)。 EIP-3670 禁用前两种可能性,然而第三种可能性仍然存在。允许 EOF1 合约只 DELEGATECALL
其他 EOF1 合约,允许以下强有力的声明:EOF1 合约永远无法被销毁。基于 SELFDESTRUCT
的攻击对 EOF1 合约完全消失。这包括被销毁的库合约(例如 Parity Multisig)。
对 EOF 容器实施 EOF 验证时间限制提供了一个参考限制,即 EVM 实现应该能够在验证和处理容器时处理容器的大小。MAX_INITCODE_SIZE
被选定为 EOF1,因为它是合约创建当前允许的大小。
鉴于这一限制的主要原因之一是为了避免对 JUMPDEST
分析的攻击向量,而 EOF 去掉了 JUMPDEST
分析的需求,并引入了用于部署时分析的成本结构,在未来此限制可以增加甚至取消。
这是一个破坏性更改,因为任何以 0xEF
开头的代码在此之前无法部署(并且如果执行将导致异常中止),但现在这种代码的某些子集可以成功部署和执行。
选择 MAGIC
确保链上现有的合约不受新规则的影响。
预计将来的 EOF 扩展,验证将具有线性的计算和空间复杂性。 我们认为验证成本得到了足够的覆盖。
版权和相关权利通过 CC0 放弃。
- 原文链接: github.com/ethereum/EIPs...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!