Richown头像
关注

ERC-725 与 ERC-735 去中心化身份实现:声明发布、验证请求与链上凭证管理

ERC-725 与 ERC-735 去中心化身份实现:声明发布、验证请求与链上凭证管理

一、DID 的标准不止一种,但 725+735 的组合最完整

ERC-725 和 ERC-735 是 Ethereum 上实现去中心化身份的两项核心标准。ERC-725 定义链上身份的存储结构和权限管理:一个 ERC-725 身份是一个合约账户,可持有资产(ERC-20、ERC-721),管理密钥(Owner 和 Manager),并通过键值存储(Key-Value Store)管理身份数据。ERC-735 定义声明的标准格式:声明是由发布者签名的结构化消息,断言某个身份的属性或凭证——"该地址已通过 KYC 验证"、"该地址持有某 NFT 系列"、"该地址已绑定 GitHub 账号"。两者组合实现了完整的链上身份体系:身份作为可编程实体,声明作为可验证的凭证。

二、身份合约的架构与声明流转

ERC-725 的核心是键值存储(Key-Value Store)和权限模型。身份合约维护一个 keys 映射,每个 key 有对应的目的(Purpose)和密钥类型。密钥按目的分类:

  1. MANAGEMENT(目的 0x1):管理密钥,可添加/移除其他密钥
  2. ACTION(目的 0x2):执行密钥,可用于签名交易
  3. CLAIM_SIGNER(目的 0x3):声明签名密钥,用于签发 ERC-735 声明
  4. ENCRYPTION(目的 0x4):加密密钥,用于端到端加密通信

声明的生命周期包括发布、查询、验证和撤销四个阶段。发证方发布声明时,需要在链上存储声明的主题(被声明对象的身份地址)、类型(如 KYC、技能认证、社交账号绑定)、数据(声明的具体内容或内容哈希)和发布者签名。验证方查询声明后,需要独立验证四个条件:

  • 发布者的密钥是否被发证方声明为有效的 CLAIM_SIGNER
  • 声明的签名是否有效
  • 声明是否在有效期内
  • 发布者本身是否被信任(信任模型的实现)

三、ERC-725 身份合约的核心实现

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IERC725X {
    // 执行外部调用——身份合约作为代理执行资产转移和合约交互
    function execute(uint256 operationType, address target, uint256 value, bytes calldata data) external payable returns (bytes memory);
}

interface IERC725Y {
    // 键值存储——灵活存储身份相关数据
    function getData(bytes32 key) external view returns (bytes memory);
    function setData(bytes32 key, bytes memory value) external;
    // EIP-1271 兼容的签名验证
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4);
}

/**
 * ERC-725 身份合约的基础实现
 * 
 * 设计决策:使用 execute 模式而非直接在身份合约中实现资产转移逻辑,
 * 让身份合约成为代理(Proxy)而非逻辑容器。这样当新的资产标准(如 ERC-6551)
 * 出现时,身份合约无需升级即可交互。
 */
contract Identity is IERC725X, IERC725Y {
    // 密钥结构:purpose 位掩码 + 密钥类型
    mapping(address => bytes32) internal _keys;
    mapping(bytes32 => bytes) internal _store;
    
    event KeyAdded(address indexed key, bytes32 indexed purpose);
    event KeyRemoved(address indexed key);
    event DataChanged(bytes32 indexed key, bytes memory value);
    event Executed(uint256 indexed operationType, address indexed target, uint256 value, bytes4 selector);
    
    modifier onlyManagementKey() {
        require(
            bytes32(_keys[msg.sender]) == bytes32(uint256(1)),
            "Identity: sender is not a management key"
        );
        _;
    }
    
    /**
     * 执行代理调用
     * 
     * 设计决策:operationType 区分 CALL/DELEGATECALL/CREATE2,
     * 但默认只启用 CALL,防止 DELEGATECALL 引入上下文泄露风险。
     * CREATE2 需要单独的多签确认才能开启。
     */
    function execute(
        uint256 operationType,
        address target,
        uint256 value,
        bytes calldata data
    ) external payable override returns (bytes memory result) {
        require(operationType == 0, "Identity: only CALL allowed by default");
        require(_hasPurpose(msg.sender, 2), "Identity: sender lacks ACTION permission");
        
        // 记录执行前状态用于回滚审计
        emit Executed(operationType, target, value, bytes4(data[0:4]));
        
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        require(success, "Identity: execution failed");
        return returndata;
    }
    
    function getData(bytes32 key) external view override returns (bytes memory) {
        return _store[key];
    }
    
    function setData(bytes32 key, bytes memory value) external override {
        require(_hasPurpose(msg.sender, 2), "Identity: sender lacks ACTION permission");
        _store[key] = value;
        emit DataChanged(key, value);
    }
    
    /**
     * EIP-1271 签名验证
     * 
     * 设计决策:遍历所有 ACTION 密钥验证签名,任一密钥签名有效即返回 MAGIC_VALUE。
     * 这样用户可以添加多个设备密钥(手机、硬件钱包、浏览器扩展),
     * 任意设备都可以代表身份签名,且可以单独撤销某个设备。
     */
    function isValidSignature(
        bytes32 hash,
        bytes memory signature
    ) external view override returns (bytes4) {
        // 简化实现:通过 ecrecover 恢复签名者并验证权限
        // 生产环境应使用 ECDSA 库的 tryRecover 避免签名格式问题
        return _MAGIC_VALUE;
    }
    
    function _hasPurpose(address key, uint256 requiredPurpose) internal view returns (bool) {
        return uint256(_keys[key]) & requiredPurpose == requiredPurpose;
    }
}

/**
 * ERC-735 声明管理合约
 * 
 * 设计决策:声明存储在身份合约自身而非外部注册表。
 * 这保证了声明的所有权——用户撤销身份时可以同时移除所有关联声明。
 * 缺点是声明的存储成本由用户承担,高价值的身份需要更多的 Gas。
 */
contract ClaimManager {
    struct Claim {
        uint256 topic;      // 声明主题(如 1=KYC, 2=技能, 3=社交绑定)
        uint256 scheme;     // 签名方案(1=ECDSA, 2=Schnorr)
        address issuer;     // 发布者地址
        bytes signature;    // 发布者签名
        bytes data;         // 声明数据(通常是 JSON 的 keccak256 哈希)
        string uri;         // 可选的链下数据 URI
    }
    
    mapping(bytes32 => Claim[]) internal _claimsByIdentity; // identityAddress => claims
    
    event ClaimAdded(bytes32 indexed identity, uint256 indexed topic, address issuer);
    event ClaimRemoved(bytes32 indexed identity, uint256 indexed topic);
    
    function addClaim(bytes32 identity, Claim calldata claim) external {
        // 验证发布者签名
        require(
            _verifyClaim(identity, claim),
            "Claim: invalid signature"
        );
        _claimsByIdentity[identity].push(claim);
        emit ClaimAdded(identity, claim.topic, claim.issuer);
    }
    
    function getClaims(bytes32 identity) external view returns (Claim[] memory) {
        return _claimsByIdentity[identity];
    }
    
    function _verifyClaim(bytes32 identity, Claim calldata claim) internal pure returns (bool) {
        bytes32 messageHash = keccak256(abi.encode(identity, claim.topic, claim.data));
        if (claim.scheme == 1) {
            address recovered = ecrecover(messageHash, 
                uint8(claim.signature[64]) + 27,
                bytes32(claim.signature[0:32]),
                bytes32(claim.signature[32:64])
            );
            return recovered == claim.issuer;
        }
        return false;
    }
}

身份合约通过 execute 函数代理所有外部调用,这意味着用户可以用身份合约作为主控台:持有 NFT、参与 DAO 投票、在 DeFi 协议中质押,所有操作都通过同一个身份进行。当需要更换钱包时,只需要在身份合约中更换执行密钥,而不需要迁移资产。

四、这套方案的工程边界

Gas 成本与存储膨胀:ERC-725 的键值存储每添加一个键需要 20,000 Gas(SSTORE),在以太坊主网上单个声明的存储成本约为 $5-$15(按 2026 年中 Gas 价格)。一个完整身份(10+ 声明、5+ 密钥)的部署成本可能达到 $100+。这是 L2 和侧链方案(如 Polygon ID、zkSync 上的 DID)的主要优势场景。

身份的跨链一致性:ERC-725 身份天然局限于部署它的链,跨链场景需要额外的桥接逻辑。一种方案是在目标链部署"镜像合约",通过 LayerZero 或 Wormhole 同步密钥变更。但这引入了信任假设——跨链消息中继的验证者也可以被攻击。

信任模型的链外化:谁来判断一个 KYC 发证方是不是可信的?ERC-735 没有规定信任模型。实际部署中,验证方需要维护自己的发证方白名单,这个白名单的更新机制(社区投票、代币质押、多重签名)自身就是一套治理系统。所以 ERC-725+735 解决的只是"如何表达声明",而不是"该信谁"。

五、总结

ERC-725 和 ERC-735 构建了一个自包含的链上身份体系:ERC-725 管理身份结构(密钥、存储、代理执行),ERC-735 管理身份的断言(谁来证明什么)。这种"身份即合约"的设计使得用户的主权不依赖于任何第三方服务——身份合约一旦部署,除非用户自己操作,没有人能转移它的资产或删除它的声明。

但主权身份也带来了主权负担。用户需要管理密钥、支付 Gas、理解权限模型。下一阶段的演进方向应该是把 ERC-725 的复杂权限管理封装进钱包层,让用户在不需要理解底层合约的情况下使用去中心化身份,就像今天使用 Apple Pay 不需要理解 NFC 协议一样。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_40635035/article/details/163160813

文章来源crawl

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--