Alert Source Discuss
⚠️ Review Standards Track: Core

EIP-663: SWAPN、DUPN 和 EXCHANGE 指令

引入额外的指令来操作堆栈,允许访问更深层次的堆栈

Authors Alex Beregszaszi (@axic), Charles Cooper (@charles-cooper), Danno Ferrin (@shemnon)
Created 2017-07-03
Requires EIP-3540, EIP-5450

摘要

目前,SWAP*DUP* 指令仅限于 16 的堆栈深度。引入三个新指令 SWAPNDUPNEXCHANGE,取消此限制并允许访问更深层次的堆栈。

动机

虽然堆栈的深度为 1024 项,但只能轻松访问前 16 项。通过手动将其保存在内存中或通过编译器中的“堆栈到内存提升”,可以支持更多的局部变量。这可能会导致复杂且效率低下的代码。

此外,在 EVM 之上实现更高级别的构造(例如函数)将产生输入和输出参数列表以及返回的指令偏移量。

这些参数(或堆栈项)的数量很容易超过 16 个,因此需要编译器额外小心地布置它们,以便仍然可以访问所有这些参数。

最后,交换堆栈中第 1 个和第 N 个项目之外的项目对于编译器实现堆栈调度算法(堆栈机器的寄存器分配的类似物)非常重要,这些算法试图在给定一组变量和使用情况分析的情况下最小化堆栈流量。

引入 SWAPNDUPNEXCHANGE 将为编译器提供简化访问深层堆栈项的选项。

规范

我们引入了三个新指令:

  1. DUPN (0xe6)
  2. SWAPN (0xe7)
  3. EXCHANGE (0xe8)

如果代码是旧版字节码,则任何这些指令都会导致异常停止。 (注意:这意味着行为没有改变。

如果代码是有效的 EOF1,则适用以下规则:

  1. 这些指令后面跟一个 8 位立即数值,我们称之为 imm,其值可以为 0 到 255。
    1. 对于 DUPNSWAPN,我们引入变量 n,它等于 imm + 1
    2. 对于 EXCHANGE,我们引入变量 n,它等于 (imm >> 4) + 1,变量 m 等于 (imm & 0x0F) + 1(即,imm 的第一个和第二个半字节,转换为从一开始的索引)。
  2. 扩展代码验证以检查没有相对跳转指令(RJUMP / RJUMPI / RJUMPV)以 DUPNSWAPNEXCHANGE 的立即数值为目标。
  3. EIP-5450 的堆栈验证算法已扩展:
    1. DUPN 之前,如果当前堆栈高度小于 n,则代码无效。在 DUPN 之后,堆栈高度会增加。
    2. SWAPN 之前,如果当前堆栈高度小于 n + 1,则代码无效。在 SWAPN 之后,堆栈高度不变。
    3. EXCHANGE 之前,如果当前堆栈高度小于 n + m + 1,则代码无效。在 EXCHANGE 之后,堆栈高度不变。
  4. 执行规则:
    1. DUPN: 第 n 个堆栈项在堆栈顶部被复制。(注意:此处我们使用从 1 开始的索引。
    2. SWAPN: 第 n + 1 个堆栈项与堆栈顶部交换。
    3. EXCHANGE: 第 n + 1 个堆栈项与第 n + m + 1 个堆栈项交换。

所有三个指令的 Gas 成本都设置为 3。

理由

使用立即参数

允许动态选择要交换、复制或交换的参数可用于阻止对堆栈内容的静态分析。由于静态分析是安全审计员的重要工具,因此我们希望尽我们所能使他们的工作更轻松。因此,操作数需要一个本质上不是动态的立即参数。

仅限 EOF

由于此指令依赖于立即参数编码,因此它只能在 EOF 中启用。在旧版字节码中,该编码可能与 jumpdest 分析相矛盾。

立即参数的大小

对于 DUPNSWAPN,考虑到 16 位大小可以容纳 1024 个项目的完整堆栈空间,但是:

  1. 这将需要额外的限制/检查 (n < 1024)
  2. 256 深度是对当前 16 的重大改进,而额外字节的开销会使其用处降低

类似地,对于 EXCHANGE,所提出的方案允许寻址 32 个项目。

Gas 成本

这些操作的 Gas 成本与现有 DUP*SWAP* 指令的 Gas 成本相同,因为它们只是实现为指针交换。

EXCHANGE vs SWAPN

如前所述,EXCHANGE 对于实现堆栈调度算法的编译器非常重要。具体来说,如果计划在堆栈中更深处消耗堆栈项(例如,堆栈中的第 3 项需要移动到第 2 个位置才能被下一个操作消耗),那么当前需要三个指令, SWAP2 SWAP3 SWAP2。但是,在 EVM 实现中,该实现只是一个指针交换,因此可以在单个指令中实现,而无需额外的客户端运行时成本。

向后兼容性

这对向后兼容性没有影响,因为操作码以前没有分配,并且该功能仅在 EOF 中启用。

测试用例

给定 stack[] 是一个从 0 开始的数据结构,并且 nmimm 的定义符合规范:

  • 如果 stack_height < n,则 DUPN imm 验证失败。
  • 如果 stack_height < n + 1,则 SWAPN imm 验证失败。
  • 如果 stack_height < n + m + 1,则 EXCHANGE imm 验证失败。
  • DUPN imm 增加函数的最大堆栈高度。如果最大堆栈高度超过 1023 的限制,则验证失败。
  • 如果可用 Gas 小于 3,则 DUPN immSWAPN immEXCHANGE imm 在运行时失败。
  • DUPN imm 应该复制 stack[n - 1] 项并将其推送到堆栈
  • SWAPN imm 应该将 stack[n]stack[stack.top()] 交换
  • EXCHANGE imm 应该将 stack[n]stack[n + m] 交换。

安全注意事项

作者不知道此处引入的任何其他风险。 EVM 堆栈固定为 1024 个项目,并且大多数实现始终将其保存在内存中。此更改将增加可以通过单个指令访问的堆栈项的数量。

版权

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

Citation

Please cite this document as:

Alex Beregszaszi (@axic), Charles Cooper (@charles-cooper), Danno Ferrin (@shemnon), "EIP-663: SWAPN、DUPN 和 EXCHANGE 指令 [DRAFT]," Ethereum Improvement Proposals, no. 663, July 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-663.