Alert Source Discuss
Standards Track: ERC

ERC-5750: 用于方法行为的通用可扩展性

指定动态大小字节的最后一个参数,用于方法的行为扩展。

Authors Zainan Victor Zhou (@xinbenlv)
Created 2022-10-04
Requires EIP-165

摘要

本 EIP 标准化了将非结构化调用数据传递给函数的方式,以实现未来的可扩展性。

动机

在方法中包含额外数据的目的是允许对现有方法接口进行进一步的扩展。

使方法可扩展是很有用的。任何符合此 EIP 的方法,例如重载的 transfervote,都可以使用字符串原因作为额外数据。 已经导出符合此 EIP 的方法的现有 EIP 可以扩展其行为,例如使用额外数据来证明认可、作为盐、作为 nonce 或作为 reveal/commit 方案的承诺。 最后,可以将数据传递给回调。

实现现有函数的可扩展性有两种方法。每种方法都带来了一系列挑战:

  1. 添加一个新方法
  • 方法名称是什么?
  • 参数是什么?
  • 给定的方法签名支持多少个用例?
  • 这是否支持链下签名?
  1. 使用一个或多个现有参数,或添加一个或多个新参数
  • 应该重新利用现有参数,还是应该添加更多参数?
  • 应该使用多少个参数?
  • 它们的大小和类型是什么?

标准化方法的扩展方式有助于回答这些问题。

最后,本 EIP 旨在实现最大的向后和向前兼容性。 许多 EIP 已经部分支持此 EIP,例如 EIP-721EIP-1155。 此 EIP 支持多种用例,从 commit-reveal 方案 (EIP-5732),到添加与方法调用一起使用的数字签名。 其他实现者和 EIP 应该能够依赖于此 EIP 提供的兼容性,以便所有兼容的方法接口都有资格获得未来新的行为。

规范

本文档中使用的关键词“必须”,“禁止”,“需要”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。

在本 EIP 中使用时,术语 bytes 必须解释为 Solidity 数据类型中动态大小的字节数组。

  1. 与许多在 contract 级别上兼容的其他 ERC 不同,此 ERC 的规范在 method 级别上指定兼容性。

  2. 任何以 bytes 作为最后一个参数的方法都是 eligible 方法。它看起来像这样 function methodName(type1 value1, type2 value2, ... bytes data)

  3. 一个 compliant 方法 必须是一个 eligible 方法并且也必须指定该方法参数中的最后一个 bytes 字段用于行为扩展。

  4. 如果一个 eligible 方法有一个重载的兄弟方法, 具有完全相同的方法名称和完全相同的前导参数, 除了没有最后一个 bytes 参数,当最后一个 bytes 是一个空数组时, compliant 方法的行为必须与其重载的兄弟方法相同 。

兼容和不兼容的方法示例

  1. 这是一个 Foo 合同中的兼容方法 methodName1
contract Foo {
  // @dev 此方法允许通过 `_data` 字段进行扩展行为;
  function methodName1(uint256 _param1, address _param2, bytes calldata _data);
  function firstNonRelatedMethod(uint256 someValue);
  function secondNonRelatedMethod(uint256 someValue);
}
  1. 这是 Bar 合同中的一个兼容方法 methodName2,它是另一个 methodName2 的重载方法。
contract Foo {
  // @dev 这是 `methodName2(uint256 _param1, address _param2, bytes calldata _data);`  的兄弟方法
  function methodName2(uint256 _param1, address _param2);

  // @dev 此方法允许通过 `_data` 字段进行扩展行为;
  //      当传入一个空的 `_data` 数组,这个方法的行为
  //      必须与
  //      它的重载兄弟方法 `methodName2(uint256 _param1, address _param2);` 完全相同.
  function methodName2(uint256 _param1, address _param2, bytes calldata _data);

  function firstNonRelatedMethod(uint256 someValue);
  function secondNonRelatedMethod(uint256 someValue);
}
  1. 这是一个不兼容的方法 methodName1,因为它不允许扩展行为
contract Foo {
  // @dev 此方法**不允许**通过 `_data` 字段进行扩展行为;
  function methodName1(uint256 _param1, address _param2, bytes calldata _data);
  function firstNonRelatedMethod(uint256 someValue);
  function secondNonRelatedMethod(uint256 someValue);
}
  1. 这是一个不兼容的方法 methodName2(uint256 _param1, address _param2, bytes calldata _data); 因为它表现不同 与其重载的兄弟方法 methodName2(uint256 _param1, address _param2);_data 是一个空数组时。
contract Foo {
  // @dev 这是 `methodName2(uint256 _param1, address _param2, bytes calldata _data);`  的兄弟方法
  function methodName2(uint256 _param1, address _param2);

  // @dev 此方法允许通过 `_data` 字段进行扩展行为;
  //      当传入一个空的 `_data` 数组,这个方法的行为
  //      *不同于*
  //      它的重载兄弟方法 `methodName2(uint256 _param1, address _param2);`
  function methodName2(uint256 _param1, address _param2, bytes calldata _data);

  function firstNonRelatedMethod(uint256 someValue);
  function secondNonRelatedMethod(uint256 someValue);
}

理由

  1. 通过启用任意类型的有效负载,使用动态大小的 bytes 类型可以实现最大的灵活性。
  2. 将字节指定为最后一个参数使此 EIP 与 solidity 的 calldata 布局兼容。

向后兼容性

许多现有的 EIP 已经具有符合其规范的方法。 所有符合这些 EIP 的合约都完全或部分符合此 EIP。

这是一个不完整的列表:

  • EIP-721 中,以下方法已经兼容:
    • function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable; 已经兼容
  • EIP-1155 中,以下方法已经兼容
    • function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
    • function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
  • EIP-777 中,以下方法已经兼容
    • function burn(uint256 amount, bytes calldata data) external;
    • function send(address to, uint256 amount, bytes calldata data) external;

但是,并非所有以 bytes 作为最后一个参数的函数都兼容。 在没有重载的情况下,以下函数是不兼容的,因为它们的最后一个参数参与了功能:

  • EIP-2535 中,以下方法不兼容:
    • function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
    • 以下两种方式之一可以用来创建兼容性。
      1. 必须创建一个重载:function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata, bytes calldata _data) external;,它在原始方法的所有参数之后添加一个新的 _data
      2. 必须放宽对 bytes memory _calldata 的使用,以允许扩展行为。
  • EIP-1271 中,以下方法不兼容:
    • function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue);
    • 以下两种方式之一可以用来创建兼容性:
      1. 必须创建一个新的重载:function isValidSignature(bytes32 _hash, bytes memory _signature, bytes calldata _data) public view returns (bytes4 magicValue);,它在原始方法的所有参数之后添加一个新的 _data
      2. 必须放宽对 bytes memory _signature 的使用,以允许扩展行为。

安全考虑

  1. 如果使用额外数据进行扩展行为,例如为链上验证提供签名,或在 commit-reveal 方案中提供承诺,则应遵循针对这些特定扩展行为的最佳实践。
  2. 兼容的合约还必须考虑到,当提交到 mempool 或包含在区块中时,数据参数将被公开,因此必须考虑重放和交易排序攻击的风险。 切勿在数据参数中包含未加密的个人身份信息。

版权

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

Citation

Please cite this document as:

Zainan Victor Zhou (@xinbenlv), "ERC-5750: 用于方法行为的通用可扩展性," Ethereum Improvement Proposals, no. 5750, October 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5750.