Alert Source Discuss
🚧 Stagnant Standards Track: Core

EIP-2997: IMPERSONATECALL 操作码

Authors Sergio Demian Lerner (@SergioDemianLerner)
Created 2020-09-24
Discussion Link https://ethresear.ch/t/impersonatecall-opcode/8020

摘要

添加一个新的操作码 IMPERSONATECALL,位于 0xf6,它在概念上类似于 CALL (0xF1),但它模拟一个发送者,即,被调用者看到的发送者与实际调用者不同。模拟的发送者地址由实际调用者地址和一个 salt 派生而来。

动机

该提案实现了原生的多用户钱包(服务于多个用户的钱包),这些钱包可以被基于 EIP-712 的消息控制,因此可以实现元交易。多用户钱包还可以将转账操作聚合成类似于 rollups 的批次,但保持与普通链上交易相同的地址空间,因此发送者的钱包不需要升级来支持向多用户钱包的用户发送以太币或 tokens。 此外,许多时候赞助公司希望为其所有用户部署非托管智能钱包。赞助商不想预先支付每个用户合约的部署成本。反事实合约创建实现了这一点,但它迫使赞助商在用户第一次想从他/她的账户转出以太币或 tokens 时创建智能钱包(或它的代理合约)。这个提案避免了这笔额外的成本,每个用户至少需要 42000 gas。

规范

IMPERSONATECALL: 0xf6,接受 7 个操作数:

  • gas: 代码可以使用的 gas 量,以便执行;
  • to: 要执行的代码的目标地址;
  • in_offset: 输入在内存中的偏移量;
  • in_size: 输入的大小,以字节为单位;
  • ret_offset: 输出在内存中的偏移量;
  • ret_size: 输出的暂存空间的大小。
  • salt 是一个 32 字节的值(一个栈项)。

模拟发送者的计算

模拟的发送者地址计算为 keccak256( 0xff ++ address ++ salt ++ zeros32)[12:]

  • 0xff 是一个单字节,
  • address 总是 20 字节,表示实际调用者合约的地址。
  • salt 总是 32 字节。

  • 字段 zeros32 对应于 32 个零字节。

这个方案模拟了 CREATE2 地址的推导,但实际上不可能与 CREATE2 地址空间发生冲突。

注意事项

  • 在 gas 消耗方面,该操作码的行为与 CALL 完全相同。
  • 在被调用上下文中,CALLER (0x33) 返回模拟的地址。
  • 如果调用中的价值转移为非零,则价值从模拟账户转移,而不是从实际调用者转移。这可以用于从模拟账户转移以太币。

理由

即使 IMPERSONATECALL 需要哈希 3 个字,这意味着额外的 180 gas 成本,我们认为考虑哈希的好处并不能弥补增加实现的复杂性。

我们使用 zeros32 字段来将地址推导建立在与 CREATE2 大小相似的预图像中,并重用现有的地址推导函数。我们还避免担心 EOA 推导(65 字节预图像)、CREATE 推导(23 到 27 字节预图像,对于 32 位 nonce)和 CREATE2 推导(85 字节预图像)之间的地址冲突。

一种选择是省略 zeros32 字段:IMPERSONATECALL 地址的 Keccak 预图像的长度为 53 字节,这不会产生地址冲突。

虽然相同的功能可以在预编译合约中提供,但我们认为使用新的操作码是一种更简洁的解决方案。

澄清

  • 这个 EIP 使得地址冲突成为可能,但实际上是不可能的。

  • 如果一个合约已经存在于一个模拟地址中,IMPERSONATECALL 的执行方式相同,并且现有的代码将不会被执行。应该注意的是 SELFDESTRUCT (0xff) 不能使用 IMPERSONATECALL 直接执行,因为没有操作码在模拟账户的上下文中执行。

向后兼容性

操作码编号 0xf6 当前未使用,并导致 out-of-gas (OOG) 异常。Solidity 使用 INVALID (0xfe) 操作码(EIP-1803 称为 ABORT)来引发 OOG 异常,因此 0xf6 操作码不会出现在正常的 Solidity 程序中。已经建议程序员不要在用 EVM 汇编编写的合约中包含此操作码。因此,它不会造成任何向后兼容性风险。

测试用例

我们展示了 4 个模拟地址推导的例子:

例子 0

  • 地址 0x0000000000000000000000000000000000000000
  • salt 0x0000000000000000000000000000000000000000000000000000000000000000
  • 结果: 0xFFC4F52F884A02BCD5716744CD622127366F2EDF

例子 1

  • 地址 0xdeadbeef00000000000000000000000000000000
  • salt 0x0000000000000000000000000000000000000000000000000000000000000000
  • 结果: 0x85F15E045E1244AC03289B48448249DC0A34AA30

例子 2

  • 地址 0xdeadbeef00000000000000000000000000000000
  • salt 0x000000000000000000000000feed000000000000000000000000000000000000
  • 结果: 0x2DB27D1D6BE32C9ABFA484BA3D591101881D4B9F

例子 3

  • 地址 0x00000000000000000000000000000000deadbeef
  • salt 0x00000000000000000000000000000000000000000000000000000000cafebabe
  • 结果: 0x5004E448F43EFE3C7BF32F94B83B843D03901457

安全考虑

地址推导方案防止与另一个已部署的合约或外部拥有的账户发生地址冲突,因为模拟的发送者地址是从实际调用者地址和一个 salt 推导出来的。

版权

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

Citation

Please cite this document as:

Sergio Demian Lerner (@SergioDemianLerner), "EIP-2997: IMPERSONATECALL 操作码 [DRAFT]," Ethereum Improvement Proposals, no. 2997, September 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2997.