Alert Source Discuss
Standards Track: ERC

ERC-6066: NFT 的签名验证方法

一种在签名实体是 ERC-721 或 ERC-1155 NFT 时验证签名的方法

Authors Jack Boyuan Xu (@boyuanx)
Created 2022-11-29
Requires EIP-165, EIP-721, EIP-1155, EIP-1271, EIP-5750

摘要

虽然Externally Owned Accounts 可以使用 ecrecover() 验证签名消息,并且智能合约可以使用 ERC-1271 中概述的规范验证签名,但目前还没有标准方法来创建或验证 NFT 产生的签名。我们提出了一种标准方法,供任何人验证 NFT 产生的签名是否有效。这可以通过最初在 ERC-1271 中找到的修改后的签名验证函数来实现:isValidSignature(tokenId, hash, data)

动机

近年来,Non-Fungible Token 标准的交易量达数十亿 ETH,已爆炸式流行起来。尽管链上拥有独特的代币化项目具有深远的影响,但 NFT 主要用于以头像或个人资料图片的形式表示艺术品。虽然这对于 ERC-721ERC-1155 代币标准来说肯定不是一个微不足道的用例,但我们认为可以做更多的事情来帮助社区发现 NFT 的替代用途。

NFT 的替代用例之一是使用它们来表示组织中的办公室。在这种情况下,将签名与可转让的 NFT 而不是 EOA 或智能合约联系起来至关重要。假设存在一个 DAO,它使用 NFT 作为徽章,代表某些行政办公室(即 CEO、COO、CFO 等),并且每季度进行一次民主选举,可能会取代当前占据这些办公室的人员。如果现任 COO 之前签署了协议或授权了某些行动,那么一旦他们被另一个 EOA 取代成为新当选的 COO,他们过去的签名将保留在曾经担任 COO 的 EOA 中,而不是 COO 的办公室本身。虽然整个 DAO 的多重签名钱包是缓解此问题的一种方法,但通常有助于在更复杂的级别上生成签名,以便建立和维护详细的职责分离。任命智能合约而不是 EOA 作为 COO 也是可行的,但这带来的复杂性是不必要的。如果 DAO 使用 ENS 来建立其组织层级结构,那么此提案将允许包装的 ENS 子域名(即 NFT)生成签名。

规范

本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、“MAY”和“OPTIONAL”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。

pragma solidity ^0.8.0;

interface IERC6066 {
    /**
     * @dev 如果提供的签名对于提供的 tokenId 和哈希有效,则必须返回
     * @param tokenId   签名 NFT 的 Token ID
     * @param hash      要签名的数据的哈希
     * @param data      可能有助于验证的可选任意数据
     *
     * 当函数通过时,必须返回 bytes4 魔术值 0x12edb34f。
     * 必须不修改状态(对于 solc < 0.5 使用 STATICCALL,对于 solc > 0.5 使用 view 修饰符)
     * 必须允许外部调用
     *
     */
    function isValidSignature(
        uint256 tokenId,
        bytes32 hash,
        bytes calldata data
    ) external view returns (bytes4 magicValue);
}

isValidSignature 可以调用任意方法来验证给定的签名。

希望使其代币持有者能够使用其 NFT 签署消息的 ERC-721ERC-1155 兼容合约可以实现此功能。如果签名者是 NFT 的持有者(ERC-721ERC-1155),则希望支持合约签名的兼容调用者必须调用此方法。

原理

我们有意决定不在本提案中包含签名生成标准,因为它会限制这种机制的灵活性,正如 ERC-1271 不强制执行智能合约的签名标准一样。我们还决定参考 Gnosis Safe 的合约签名方法,因为它既简单又被证明是足够的。如果签名验证需要额外数据,则 bytes calldata data 参数被认为是可选的,也为了面向未来,使此 EIP 符合 ERC-5750

向后兼容性

此 EIP 与之前关于签名验证的工作不兼容,因为它不验证任何加密生成的签名。相反,签名只是一个指示同意的布尔标志。这与 Gnosis Safe 的合约签名实现一致。

参考实现

符合 ERC-6066ERC-721 兼容合约的示例实现,具有自定义签名函数:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./interfaces/IERC6066.sol";

contract ERC6066Reference is ERC721, IERC6066 {
    // type(IERC6066).interfaceId
    bytes4 public constant MAGICVALUE = 0x12edb34f;
    bytes4 public constant BADVALUE = 0xffffffff;

    mapping(uint256 => mapping(bytes32 => bool)) internal _signatures;

    error ENotTokenOwner();

    /**
     * @dev 检查发送者是否拥有 ID 为 tokenId 的 NFT
     * @param tokenId   签名 NFT 的 Token ID
     */
    modifier onlyTokenOwner(uint256 tokenId) {
        if (ownerOf(tokenId) != _msgSender()) revert ENotTokenOwner();
        _;
    }

    constructor(string memory name_, string memory symbol_)
        ERC721(name_, symbol_)
    {}

    /**
     * @dev 如果给定发送者拥有所述 NFT,则应该使用 tokenId 的 NFT 签署提供的哈希
     * @param tokenId   签名 NFT 的 Token ID
     * @param hash      要签名的数据的哈希
     */
    function sign(uint256 tokenId, bytes32 hash)
        external
        onlyTokenOwner(tokenId)
    {
        _signatures[tokenId][hash] = true;
    }

    /**
     * @dev 如果提供的签名对于提供的 tokenId、哈希以及可选的数据有效,则必须返回
     */
    function isValidSignature(uint256 tokenId, bytes32 hash, bytes calldata data)
        external
        view
        override
        returns (bytes4 magicValue)
    {
        // 在此示例中未使用 data 参数
        return _signatures[tokenId][hash] ? MAGICVALUE : BADVALUE;
    }

    /**
     * @dev ERC-165 支持
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override returns (bool) {
        return
            interfaceId == type(IERC6066).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}

安全注意事项

基于合约的签名的可撤销性质延续到此 EIP。开发人员和用户都应考虑到这一点。

版权

Copyright 和相关权利已通过 CC0 放弃。

Citation

Please cite this document as:

Jack Boyuan Xu (@boyuanx), "ERC-6066: NFT 的签名验证方法," Ethereum Improvement Proposals, no. 6066, November 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6066.