Alert Source Discuss
🛑 Withdrawn Standards Track: Core

EIP-6913: SETCODE 指令

用于原地替换代码的新指令

Authors William Morriss (@wjmelements)
Created 2023-04-20
Discussion Link https://ethereum-magicians.org/t/eip-6913-setcode-instruction/13898

摘要

引入 SETCODE (0xfc) 指令,该指令从内存中替换正在执行的帐户的代码。

动机

许多合约都是可升级的,以便于改进或推迟决策,而无需迁移到新地址。 目前,合约通过以下几种方式实现这一点:

最古老的方法是使用 CALL。 这种方法的局限性在于,内部状态必须可由所有未来的实现修改。

其次,DELEGATECALL 可以代理实现。 一些代理是最小化的,而另一些则分支到许多单独的实现帐户。 此方法还可以绕过帐户代码大小限制。

第三种方法是使用 SELFDESTRUCTCREATE2 原地替换代码。 此方法通过消除调用外部合约的需要而改进了先前的方法。 此方法的一个限制是任何内部状态都会被 SELFDESTRUCT 删除。 另一个限制是 SELFDESTRUCT 不会删除代码,直到交易结束,从而牺牲了可用性,直到 CREATE2 可以完成升级。

鉴于即将弃用 SELFDESTRUCTSETCODE 引入了一种原地替换代码的更好方法。

规范

当在只读执行范围内(例如由 STATICCALL 创建的递归类型),SETCODE 会导致异常中止。

当当前执行的代码不等于正在执行的帐户的代码时,例如在 DELEGATECALLCREATE 内部发生时,SETCODE 会导致异常中止。

否则,SETCODE 从堆栈中消耗两个字:偏移量和长度。 这些指定了包含新代码的内存范围。 将对 CREATECREATE2 的结果执行的任何验证都会立即发生,可能会因异常中止而导致失败。 现在,EXTCODESIZEEXTCODECOPY 操作查询更新后的代码,并且诸如 DELEGATECALLCALLCODECALLSTATICCALL 之类的消息调用现在执行更新后的代码。 任何已经执行替换代码的执行范围(包括 SETCODE 的执行范围)将继续执行先前的代码。 在此类范围内,CODESIZECODECOPY 继续查询正在执行的代码。

SSTORE 类似,如果当前范围或任何父范围恢复或中止,则此帐户修改将被恢复。

SELFDESTRUCT 不同,SETCODE 不会清除帐户余额、nonce 或存储。

Gas

此操作的 gas 成本是 GselfdestructGcodedeposit 和新代码中的字节数的乘积之和。

理由

CODECOPYCODESIZEEXTCODESIZEEXTCODECOPY 的行为与 DELEGATECALLCREATE 的行为相匹配,在这些情况下,执行代码也可能与执行帐户的代码不同。

SETCODE 的 gas 成本与 CREATE 相当,但不包括 Gcreate,因为没有创建执行上下文,也没有创建任何新帐户。 其他帐户修改成本在执行 gas 之外计算。

SELFDESTRUCT 不同,在 SETCODE 之后,执行会正常进行,以便允许验证和返回数据。 更新后验证可以使用 REVERT 或使用递归 SETCODE 撤消 SETCODE 操作,但 REVERT 使用的 gas 更少。

阻止在大多数 DELEGATECALL 中使用 SETCODE 允许静态分析轻松识别可变代码。 可以安全地假定不包含 SETCODE 操作的帐户代码是不可变的。 在非恢复上下文中观察到的不可变代码将保持不可变,从而允许链上静态分析以实现不变性。

向后兼容性

唯一先前的更改代码的操作是 SELFDESTRUCT。 由于通过 SELFDESTRUCT 进行的代码修改会推迟到交易结束,因此它与 SETCODE 的交互是明确定义的。

测试用例

CodeStart CallData CodeResult Gas
365f5f37365ffc00 365f5f37365ffc00 365f5f37365ffc00 6613
365f5f37365ffc00 00 00 5213
365f5f37365ffc00     5013
365f5f37365ffc595ffd 365f5f37365ffc00 365f5f37365ffc595ffd 6617
365f5f37365ffcfe 365f5f37365ffc00 365f5f37365ffcfe all

安全考虑

SETCODE 相关的风险同样适用于其他升级模式。

大多数合约不应被替换,也不应是可升级的。 任何升级机制都可能导致永久性故障。 升级的可能性会使这种风险长期存在。

对升级操作的访问应受到限制。 切勿匆忙或在疲倦时执行升级。 应在尽可能接近生产的条件下测试升级;差异是出现意外结果的根源。 如果可能,多个工程师应预览并独立验证待处理的升级程序。

区块浏览器、钱包和其他界面应标记可升级的代码。 客户端软件应警告不要批准可升级帐户的 ERC-20ERC-721 代币。

版权

CC0 下放弃版权及相关权利。

Citation

Please cite this document as:

William Morriss (@wjmelements), "EIP-6913: SETCODE 指令 [DRAFT]," Ethereum Improvement Proposals, no. 6913, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6913.