Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-5851: 链上可验证凭证

用于管理可验证声明和标识符作为 Soulbound Token 的合约接口。

Authors Yu Liu (@yuliu-debond), Junyi Zhong (@Jooeys)
Created 2022-10-18
Discussion Link https://ethereum-magicians.org/t/eip-5815-kyc-certification-issuer-and-verifier-standard/11513
Requires EIP-721, EIP-1155, EIP-1167, EIP-1967, EIP-3475

摘要

本提案介绍了一种证明特定地址满足某项声明的方法,以及一种使用链上元数据验证这些证明的方法。声明是对主体具有某些可能满足的属性(例如:age >= 18)的断言或陈述,并由发行者使用 Soundbound Token (SBT) 进行认证。

动机

可验证证明的链上发行对于以下用例至关重要:

  • 通过一人一票避免女巫攻击
  • 凭借凭证参与某些活动
  • 遵守政府金融法规等。

我们正在为去中心化身份 (DID) 发行者和验证者实体提出一个标准声明结构,以创建智能合约,以便提供链下验证过程的链上承诺,并且一旦给定的地址与给定的身份验证链下证明相关联,发行者就可以让其他验证者(即治理机构、金融机构、非营利组织、web3 相关合作机构)定义用户的持有权条件,以减少当前实现的技术障碍和开销。

本提案背后的动机是为验证者和发行者智能合约创建一个标准,以便以更有效的方式相互通信。这将降低 KYC 流程的成本,并提供链上 KYC 检查的可能性。通过为验证者和发行者之间的通信创建标准,它将创建一个生态系统,用户可以确信他们的数据是安全和私密的。最终,这将导致更高效的 KYC 流程,并有助于为用户创造一个更值得信赖的环境。它还将有助于确保所有验证者和发行者智能合约都与最新的 KYC 法规保持同步。

规范

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

定义

  • 零知识证明 (ZKP):一种密码学设备,可以使验证者确信断言是正确的,而无需泄露断言的所有输入。

  • Soulbound Token (SBT):一种用于定义用户身份的不可替代且不可转让的代币。

  • SBT 证书:一种 SBT,表示与 function standardClaim() 中定义的声明相对应的 ID 签名的所有权。

  • 可验证凭证 (VC):由发行者制作的声明集合。这些是显而易见的凭证,允许持有人证明他们拥有验证实体要求的某些特征(例如,护照验证,诸如钱包中代币价值之类的约束等)。

  • 声明:DID 持有者必须满足才能被验证的断言。

  • 持有者:存储声明的实体,例如数字身份提供商或 DID 注册表。持有者负责验证声明并提供声明的可验证证据。

  • 声明者:提出声明的一方,例如在身份验证过程中。

  • 发行者:从关于一个或多个主体的声明到持有者创建可验证凭证的实体。发行者的示例包括政府、公司、非营利组织、行业协会和个人。

  • 验证者:验证可验证凭证发行者提供的数据的实体,确定其准确性、来源、时效性和可信度。

元数据标准

声明必须以以下结构公开:

1. 元数据信息

每个声明要求都必须使用以下结构公开:

    /** 元数据
    * 
    * @param title 定义声明字段的名称
    * @_type 是数据的类型 (bool,string,address,bytes,..)
    * @param description 有关声明详细信息的其他信息。
     */
    struct Metadata {
        string title;
        string _type;
        string description;
    }

2. 值信息

以下结构将用于定义实际的声明信息,基于 Metadata 结构的描述,该结构与 EIP-3475Values 结构相同。

   struct Values{
       string stringValue;
       uint uintValue;
       address addressValue;
       bool boolValue;
  }

3. 声明结构

声明(例如 age >= 18,许可名单中的管辖权等)由以下 Claim 结构的一个或多个实例表示:

    /** 声明
    * 
    * 声明结构由持有人声明关联且验证者必须验证的条件和值组成。
    * @notice 下面给出的参数仅供参考,开发人员可以通过使用 TLV、编码为 base64 等方案来优化需要在链上表示的字段。
    * @dev 定义 SBT 证书的特定声明参数的结构
    * @notice 此结构用于验证过程,它包含元数据、逻辑和期望
    * @notice 逻辑可以表示用于定义不同操作的枚举格式,也可以是逻辑运算符(以基于 unicode 标准的 ASCII 图形形式存储)。例如:
("⊄" = U+2284, "⊂" = U+2282,  "<" = U+003C , "<=" = U + 2265,"==" = U + 003D, "!="U + 2260, ">=" = U + 2265,">" =  U + 2262).
    */
    struct Claim {
        Metadata metadata;
        string logic;
        Values expectation;
   
    }

可以使用的一些逻辑函数的描述如下:

符号 描述
不属于相应 Values 定义的值(或范围)集
参数属于 Values 定义的其中一个值的条件
< 参数大于 Values 定义的值的条件
== 参数严格等于 Values 结构定义的值的条件

声明示例

{
   "title":"age",
   "type":"unit",
   "description":"基于合法文件上的出生日期的年龄",
   "logic":">=",
   "value":"18"
}

定义为索引 1 编码的条件(即持有人必须等于或大于 18 岁)。

接口规范

验证者


    /// @notice getter 函数,用于验证地址 `claimer` 是否是 tokenId `SBTID` 定义的声明的持有人
    /// @dev 它必须定义条件运算符(下面解释的逻辑)以允许应用程序将其转换为代码逻辑
    /// @dev 此处给出的逻辑必须是条件运算符,必须是以下之一("⊄", "⊂", "<", "<=", "==", "!=", ">=", ">")
    /// @param claimer 是想要验证发行者发给它的 SBT 的 EOA 地址。
    /// @param SBTID 是用户作为声明人的 SBT 的 Id。
    /// @return 如果断言有效,则返回 true,否则返回 false
    /**
    例如,ifVerified(0xfoo, 1) => true 将意味着 0xfoo 是给定集合的 tokenId 定义的 SBT 身份代币的持有人。
    */
    function ifVerified(address claimer, uint256 SBTID) external view returns (bool);

发行者

  
    /// @notice getter 函数,用于获取给定身份持有者的链上身份验证逻辑。
    /// @dev 它不能为 address(0) 定义。
    /// @param SBTID 是用户作为声明人的 SBT 的 Id。
    /// @return 由管理员为给定 KYC 提供商定义的所有条件元数据的描述的结构数组。
    /**
    例如:standardClaim(1) --> {
    { "title":"age",
        "type": "uint",
        "description": "基于合法文件上的出生日期的年龄",
        },
       "logic": ">=",
    "value":"18"  
    }
    定义为身份索引 1 编码的条件,定义身份条件,即持有人必须等于或大于 18 岁。
    **/

    function standardClaim(uint256 SBTID) external view returns (Claim[] memory);

    /// @notice 用于设置由 SBTID 定义的给定身份代币的声明要求逻辑(由 Claims 元数据定义)详细信息的函数。
    /// @dev 它应该只由 admin 地址调用。
    /// @param SBTID 是管理员想要为其定义 Claims 的基于 SBT 的身份证书的 Id。
    /// @param `claims` 是由管理员定义的所有条件元数据的描述的结构数组。有关更多信息,请查看元数据部分。
    /**
    例如:changeStandardClaim(1, { "title":"age",
            "type": "uint",
            "description": "基于合法文件上的出生日期的年龄",
            },
        "logic": ">=",
        "value":"18"  
    });
    将对应于管理员需要根据 Claims 数组结构详细信息中描述的条件来调整 tokenId = 1 的身份验证 SBT 的标准声明的功能。
    **/

    function changeStandardClaim(uint256 SBTID, Claim[] memory _claims) external returns (bool);

    /// @notice 使用 ZKProof 协议根据给定的身份验证身份的函数
    /// @dev 它应该只由 admin 地址调用。
    /// @param SBTID 是管理员想要为其定义 Claims 的基于 SBT 的身份证书的 Id。
    /// @param claimer 是需要被证明为 tokenID 定义的 SBT 所有者的地址。
    /**
    例如:certify(0xA....., 10) 意味着管理员将 id 为 10 的 DID 徽章分配给 `0xA....` 钱包定义的地址。
    */
    function certify(address claimer, uint256 SBTID) external returns (bool);

    /// @notice 使用 ZKProof 协议根据给定的身份验证身份的函数
    /// @dev 它应该只由 admin 地址调用。
    /// @param SBTID 是管理员想要为其定义 Claims 的基于 SBT 的身份证书的 Id。
    /// @param claimer 是需要被证明为 tokenID 定义的 SBT 所有者的地址。
    /* 例如:revoke(0xfoo,1):表示 KYC 管理员撤销地址“0xfoo”的 SBT 证书编号 1。*/
    function revoke(address certifying, uint256 SBTID) external returns (bool);

事件

    /**
    * standardChanged
    * @notice 当管理员更改声明时,必须触发 standardChanged。
    * @dev 对于创建新的 SBTID,也必须触发 standardChanged。
    例如:emit StandardChanged(1, Claims(Metadata('age', 'uint', '基于合法文件上的出生日期的年龄' ), ">=", "18");
    当更改声明条件时发出,该条件允许证书持有人调用带有修改器的函数,声明持有人必须等于或大于 18 岁。
    */
    event StandardChanged(uint256 SBTID, Claim[] _claims);
    
    /**
    * certified
    * @notice 当将 SBT 证书授予认证地址时,必须触发 certified。
    例如:Certified(0xfoo,2); 意味着钱包持有人地址 `0xfoo` 经过认证可以持有 id 为 2 的证书,因此可以满足所需接口定义的所有条件。
    */
    event Certified(address claimer, uint256 SBTID);
    
    /**
    * revoked
    * @notice 撤销 SBT 证书时,必须触发 revoked。
    例如:Revoked( 0xfoo,1); 意味着实体用户 0xfoo 已被撤销为 SBT ID 1 定义的所有函数访问权限。
    */
    event Revoked(address claimer, uint256 SBTID);
}

原理

待定

向后兼容性

  • 对于保持先前发出的 SBT 的元数据结构及其 ID 和声明要求详细信息不变的合约,此 EIP 是向后兼容的。
    • 例如,如果 DeFI 提供商(使用修改器来验证所有者所需的 SBT 的所有权)希望管理员更改验证逻辑或删除某些声明结构,则先前的证书持有人将受到这些更改的影响。

测试用例

有关最小参考实现的测试用例,请参见 此处,以使用关于用户是否持有代币的交易验证。使用 Remix IDE 编译和测试合约。

参考实现

接口 分为两个单独的实现:

  • EIP-5851 验证者 是一个简单的修改器,需要由仅由 SBT 证书持有人调用的函数导入。然后,该修改器将调用发行者合约以验证声明人是否具有有问题的 SBT 证书。

  • EIP-5851 发行者 是一个身份证书示例,可以由 KYC 控制器合约分配。这是标准接口的完整实现。

安全考虑

  1. 在 SBT 上创建 KYC 的功能接口(即 changeStandardClaim()certify()revoke())的实现取决于管理员角色。因此,开发人员必须确保管理员角色的安全性,并将该角色轮换到 KYC 证明服务提供商和使用此证明服务的 DeFI 协议信任的实体。

版权

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

Citation

Please cite this document as:

Yu Liu (@yuliu-debond), Junyi Zhong (@Jooeys), "ERC-5851: 链上可验证凭证 [DRAFT]," Ethereum Improvement Proposals, no. 5851, October 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5851.