EIP-1702: 通用账户版本控制方案
Authors | Wei Tang (@sorpaas) |
---|---|
Created | 2017-12-30 |
Discussion Link | https://github.com/sorpaas/EIPs/issues/2 |
Table of Contents
简述
为智能合约引入账户版本控制,以便更容易地升级 VM 或引入新的 VM。
摘要
本文定义了一种在保持现有账户完全功能的同时进行硬分叉的方法,允许同一区块中执行多个版本的虚拟机。当我们引入链上 WebAssembly 虚拟机时,这对于定义未来的账户状态结构也很有用。
动机
通过允许账户版本控制,我们可以为不同时间创建的合约执行不同的虚拟机。这允许实现突破性功能,同时确保现有合约按预期工作。
请注意,此规范可能不适用于所有硬分叉。过去,我们曾因网络攻击而进行紧急硬分叉。它们是否应保持现有账户兼容性应根据具体情况进行评估。如果攻击只能对某些特定合约执行一次,那么此处定义的方案可能仍然适用。否则,进行简单的紧急硬分叉可能仍然是一个好主意。
规范
账户状态
重新定义存储在世界状态 trie 中的账户状态,使其具有 5 个项目:nonce
、balance
、storageRoot
、codeHash
和 version
。新添加的字段 version
是一个 256 位 scalar。我们使用黄皮书中 “scalar” 的定义。请注意,这与 nonce
和 balance
的类型相同,并且等效于没有前导零且最大长度为 32 的 RLP 可变大小字节数组。
当 version
为零时,账户使用前 4 个项目进行 RLP 编码。当 version
不为零时,账户使用 5 个项目进行 RLP 编码。
账户版本还可以选择定义额外的账户状态 RLP 字段,其含义通过其 version
字段指定。在这些情况下,解析策略在 “账户状态 RLP 中的附加字段” 部分中定义。
合约执行
从状态中获取账户代码时,我们总是同时获取关联的 version 字段。我们在下面将其称为 code’s version。账户的代码始终在 code’s version 中执行。
特别是,这意味着对于 DELEGATECALL
和 CALLCODE
,执行调用帧的版本与委托/接收合约的版本相同。
合约部署
在以太坊中,合约具有部署方法,可以通过合约创建交易或通过另一个合约进行部署。如果我们将此部署方法视为合约的 parent,那么我们发现它们形成一个合约家族,其中 root 是一个合约创建交易。
我们让一个合约家族始终具有相同的 version
。也就是说,CREATE
和 CREATE2
始终部署具有与 code’s version 相同的 version
的合约。
换句话说,CREATE
和 CREATE2
将使用当前的 code’s version 执行初始化代码,并部署当前 code’s version 的合约。即使要部署的代码为空,这仍然成立。
验证
一个新的短语,validation 被添加到合约部署中(通过 CREATE
/ CREATE2
操作码,或通过合约创建交易)。当 version
为 0
时,该短语不执行任何操作并始终成功。未来的 VM 版本可以定义必须通过的额外验证。
如果验证短语失败,则部署不会继续并返回 out-of-gas。
合约创建交易
在硬分叉中定义 LATEST_VERSION
为最新支持的 VM 版本。合约创建交易始终在 LATEST_VERSION
中执行(这意味着 code’s version 是 LATEST_VERSION
),并部署 LATEST_VERSION
的合约。在执行合约创建交易之前,对合约创建代码运行 validation。如果未通过,则返回 out-of-gas。
预编译合约和外部拥有地址
预编译合约和外部拥有地址没有 version
。如果消息调用交易或 CALL
/ CALLCODE
/ STATICCALL
/ DELEGATECALL
触及新的外部拥有地址或不存在的预编译合约地址,则始终使用 version
字段为 0
创建它。
账户状态 RLP 中的附加字段
将来,我们可能需要将更多信息关联到账户中,并且我们已经有一些 EIP 定义了账户状态 RLP 中的新附加字段。在本节中,我们定义添加附加字段时的解析策略。
- 检查 RLP 列表长度,如果为 4,则将账户版本设置为
0
,并且不解析任何附加字段。 - 如果 RLP 列表长度大于 4,则将账户版本设置为位置
4
(从0
开始计数)的 scalar。- 检查版本规范中定义的附加字段数量
N
,如果 RLP 列表长度不等于5 + N
,则返回解析错误。 - 将 RLP 位置
5
到4 + N
解析为附加字段中指定的含义。
- 检查版本规范中定义的附加字段数量
扩展
关于上面的 “规范” 部分,我们已经定义了基本账户版本控制层。基本账户版本控制层本身已经很有用,可以处理大多数 EVM 改进。下面我们定义两个可以单独部署的规范,这提高了基本层账户版本控制的功能。
请注意,本节仅用于文档目的。当 “启用 EIP-1702” 时,除非还包含扩展规范,否则不应启用这些扩展。
用法模板
本节定义了其他 EIP 如何使用此账户版本控制规范。请注意,目前我们仅定义基本层的用法模板。
账户版本控制通常直接应用于硬分叉元信息提案 EIP。硬分叉中的 EIP 按虚拟机类型分组,例如 EVM 和 eWASM。对于每个 EIP,我们定义:
- Version: 一个小于
2^256
的非零 scalar,用于唯一标识此版本。请注意,它不需要是顺序的。 - Parent version: 所有新功能都来源于的基础。当父版本为
0
时,我们将基础定义为旧版 VM。请注意,一旦定义了除0
之外的版本,就必须冻结旧版 VM 的功能集。当定义一个全新的 VM(例如 eWASM)时,父版本不适用。 - Features: 在此版本上启用的所有其他功能。
如果元 EIP 包括提供额外账户状态 RLP 字段的 EIP,我们还定义:
- Account fields: 直至此元 EIP 结束的所有账户字段,不包括基本 5 个字段(
nonce
、balance
、storageRoot
、codeHash
和version
)。如果包含的特定于修改账户字段的 EIP 不修改 VM 执行逻辑,建议我们指定一个额外的版本,其执行逻辑与先前版本相同,但仅更改账户字段。
理由
这通过账户状态中的新 RLP 项引入了账户版本控制。上面的设计通过使合约家族始终具有相同的版本来获得账户版本控制。这样,只需要合约创建交易提供版本,并且对任何版本的代码格式都没有限制。如果我们想支持多个最新的 VM(例如,EVM 和 WebAssembly 一起运行),那么这将需要诸如 44-VERTXN 和 45-VEROP 之类的扩展。
或者,账户版本控制也可以通过以下方式完成:
- 26-VER 和 40-UNUSED: 这使得账户的版本控制完全取决于其代码头前缀。如果仅使用 26-VER,则无法证明任何代码是有效的,因为当前的 VM 允许将代码视为数据。这可以通过 40-UNUSED 来修复,但缺点是它可能向后不兼容。
- EIP-1891: 我们不是将 version 字段写入账户 RLP 状态,而是将其写入单独的合约中。这可以完成与此 EIP 相同的事情,并且可能会降低代码复杂性,但缺点是每次代码执行都需要额外的 trie 遍历,这会影响性能。
向后兼容性
账户版本控制是完全向后兼容的,并且不会更改当前合约的执行方式。
讨论
性能
目前,几乎所有完整节点实现都使用配置参数来决定要使用的虚拟机版本。切换虚拟机版本只是一个使用不同配置参数集更改指针的操作。因此,此方案对性能几乎没有影响。
WebAssembly
当我们部署链上 WebAssembly 虚拟机时,此方案也可能很有帮助。在这种情况下,WASM 合约和 EVM 合约可以共存,并且执行边界和交互模型如上所述被清晰地定义。
测试用例和实现
待添加。
参考
该规范的来源可以在 43-VER 找到。
版权
通过 CC0 放弃版权和相关权利。
Citation
Please cite this document as:
Wei Tang (@sorpaas), "EIP-1702: 通用账户版本控制方案 [DRAFT]," Ethereum Improvement Proposals, no. 1702, December 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1702.