Alert Source Discuss
Standards Track: ERC

ERC-1167: 最小代理合约

Authors Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman)
Created 2018-06-22
Requires EIP-211

简单总结

为了以简单且廉价的方式克隆合约功能,且保证其不可变性,本标准规定了一个最小字节码实现,将所有调用委托给一个已知的、固定的地址。

摘要

通过标准化一个已知的最小字节码重定向实现,本标准允许用户和第三方工具(例如 Etherscan)(a) 简单地发现合约将始终以已知方式重定向,并且 (b) 依赖于目标合约的代码行为作为重定向合约的行为。具体来说,工具可以询问重定向地址的字节码,以确定将要运行的代码的位置——并且可以依赖于关于该代码的表示(已验证的源代码、第三方审计等)。此实现将所有调用和 100% 的 gas 转发到实现合约,然后将返回值传递回调用者。如果实现回退,则回退以及有效负载数据(用于带有消息的回退)将一起传递回去。

动机

本标准支持以下用例:期望克隆精确的合约功能,且副作用(例如,内存槽践踏)最少,并以低 gas 成本部署重复代理。

规范

标准克隆合约的确切字节码如下:363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3,其中索引 10 - 29(包括)处的字节将替换为主功能合约的 20 字节地址。

可以在 optionality/clone-factory github 仓库中找到此规范的参考实现。

理由

这项工作的目标如下:

  • 廉价部署(克隆部署的 gas 成本低)
  • 支持在创建交易中初始化克隆(通过工厂合约模型)
  • 简单的克隆字节码,以鼓励直接的字节码询问(请参阅 clone-factory 项目中的 CloneProbe.sol)
  • 可靠的、锁定的行为 - 这不是为了处理可升级性而设计的,也不应该这样做,因为我们寻求的表示形式更强。
  • 较小的运营开销 - 每次调用增加一次调用成本
  • 处理回退消息的错误返回冒泡

向后兼容性

不存在向后兼容性问题。可能有些系统正在使用早期版本的代理合约字节码。它们将不符合此标准。

测试用例

测试用例包括:

  • 不带参数的调用
  • 带参数的调用
  • 带固定长度返回值的调用
  • 带可变长度返回值的调用
  • 带回退的调用(确认回退的有效负载已传输)

这些案例的测试包含在参考实现项目中。

实现

部署字节码不包含在本规范中。代理合约参考实现中定义了一种方法。

标准代理

标准部署的代理合约代码的反汇编(来自 r2 并经过编辑以包含堆栈可视化)

|           0x00000000      36             calldatasize          cds
|           0x00000001      3d             returndatasize        0 cds
|           0x00000002      3d             returndatasize        0 0 cds
|           0x00000003      37             calldatacopy          
|           0x00000004      3d             returndatasize        0
|           0x00000005      3d             returndatasize        0 0 
|           0x00000006      3d             returndatasize        0 0 0
|           0x00000007      36             calldatasize          cds 0 0 0
|           0x00000008      3d             returndatasize        0 cds 0 0 0
|           0x00000009      73bebebebebe.  push20 0xbebebebe     0xbebe 0 cds 0 0 0
|           0x0000001e      5a             gas                   gas 0xbebe 0 cds 0 0 0
|           0x0000001f      f4             delegatecall          suc 0
|           0x00000020      3d             returndatasize        rds suc 0
|           0x00000021      82             dup3                  0 rds suc 0
|           0x00000022      80             dup1                  0 0 rds suc 0
|           0x00000023      3e             returndatacopy        suc 0
|           0x00000024      90             swap1                 0 suc
|           0x00000025      3d             returndatasize        rds 0 suc
|           0x00000026      91             swap2                 suc 0 rds
|           0x00000027      602b           push1 0x2b            0x2b suc 0 rds
|       ,=< 0x00000029      57             jumpi                 0 rds
|       |   0x0000002a      fd             revert
|       `-> 0x0000002b      5b             jumpdest              0 rds
\           0x0000002c      f3             return

注意:为了尽可能降低 gas 成本,上述字节码依赖于 EIP-211 规范,即 returndatasize 在调用帧内的任何调用之前返回零。returndatasizedup* 少使用 1 个 gas。

靓号地址优化

通过将主合约安装到具有前导零字节的靓号合约部署地址,可以进一步优化代理部署。通过生成一个在其地址中包含 Z 个前导 0 字节的主合约靓号地址,您可以缩短代理字节码,方法是将 push20 操作码替换为 pushN(其中 N 为 20 - Z),后跟 N 个非零地址字节。在这种情况下,回退跳转地址会减小 Z。这是一个 Z = 4 的示例:

|           0x00000000      36             calldatasize          cds
|           0x00000001      3d             returndatasize        0 cds
|           0x00000002      3d             returndatasize        0 0 cds
|           0x00000003      37             calldatacopy          
|           0x00000004      3d             returndatasize        0
|           0x00000005      3d             returndatasize        0 0 
|           0x00000006      3d             returndatasize        0 0 0
|           0x00000007      36             calldatasize          cds 0 0 0
|           0x00000008      3d             returndatasize        0 cds 0 0 0
|           0x00000009      6fbebebebebe.  push16 0xbebebebe     0xbebe 0 cds 0 0 0
|           0x0000001a      5a             gas                   gas 0xbebe 0 cds 0 0 0
|           0x0000001b      f4             delegatecall          suc 0
|           0x0000001c      3d             returndatasize        rds suc 0
|           0x0000001d      82             dup3                  0 rds suc 0
|           0x0000001e      80             dup1                  0 0 rds suc 0
|           0x0000001f      3e             returndatacopy        suc 0
|           0x00000020      90             swap1                 0 suc
|           0x00000021      3d             returndatasize        rds 0 suc
|           0x00000022      91             swap2                 suc 0 rds
|           0x00000023      6027           push1 0x27            0x27 suc 0 rds
|       ,=< 0x00000025      57             jumpi                 0 rds
|       |   0x00000026      fd             revert
|       `-> 0x00000027      5b             jumpdest              0 rds
\           0x00000028      f3             return

这节省了 4 个字节的代理合约大小(每次部署都节省),并且对运行时 gas 成本没有影响。

版权

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

Citation

Please cite this document as:

Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman), "ERC-1167: 最小代理合约," Ethereum Improvement Proposals, no. 1167, June 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1167.