dApp Docs/AI Agent 身份与DID管理指南
Development reference. Not independently verified for production.

AI Agent 身份与去中心化标识(DID)管理指南

适用链: msg-chain-1 | bech32 前缀: msg

⚠️ No-Go Disclaimer: MSGChain 主网裁决为 No-Go。本文件所有内容反映的是开发阶段的技术设计,不代表主网未独立核验上线状态。生产部署状态请以白皮书为准:https://msgchain.org/whitepaper/


1. 概述

1.1 为什么 AI Agent 需要去中心化身份

在区块链网络中运行的 AI Agent 需要一个可信、自主、可验证的身份体系。传统的身份模型——基于中心化 CA 的 PKI 体系——存在单点故障、隐私泄漏、跨域互认困难等问题。而去中心化标识(Decentralized Identity, DID)为 Agent 提供了以下核心能力:

1.2 DID 与钱包地址的区别

维度 钱包地址 DID
本质 公钥哈希/派生地址 持久性标识符
可变性 每次创建新钱包都改变 可更新但保持同一标识
元数据 无 包含验证方法、服务端点等
可验证性 仅签名验证 完整文档+凭证体系
生命周期 无限,无撤销机制 支持激活/停用/轮换
用途 转账/交易签名 身份认证、授权、凭证签发

钱包地址是 Agent 在链上进行价值转移的标识,而 DID 是 Agent 在更广泛的身份生态中的锚点。两者可以关联:Agent 的 DID 文档中可包含其钱包地址作为 blockchainAccountId 验证方法。

1.3 W3C DID 标准概述

W3C DID 标准定义了以下核心规范:

1.4 Agent 身份模型

在 msg-chain-1 网络中,Agent 的身份模型分为三层:

+-------------------------------------------+
|     身份层 (DID)                           |
|  did:msg:<agent_id>                       |
|  DID Document + Verification Method       |
+-------------------------------------------+
|     凭证层 (VC)                            |
|  签发、验证、撤销可验证凭证                 |
+-------------------------------------------+
|     链上锚定层                             |
|  DID Registry Contract                    |
|  文档哈希 + 元数据存储                     |
+-------------------------------------------+

1.5 msg DID Method 规范

msg DID method 的定义如下:

Method Name:  msg
Method Specific Identifier:  <agent_id> | <address>
DID Format:   did:msg:<agent_id>
Example:      did:msg:agent-2a8f1c3e9b

CRUD Operations:
- Create: 提交 DID Document 到 DID Registry 合约
- Read:   通过 DID Resolver 查询链上注册信息
- Update: 由 DID Controller 提交更新(如密钥轮换)
- Delete: 标记为 deactivated(不可逆停用)

1.6 Agent 身份的全生命周期

+----------+    +----------+    +----------+    +----------+
|  密钥生成  |-->|  DID 注册  |-->| 凭证签发  |-->| 身份使用  |
+----------+    +----------+    +----------+    +----------+
                                                     |
                                                     v
+----------+    +----------+    +----------+
|  身份停用  |<--| 密钥轮换  |<--| 凭证验证  |
+----------+    +----------+    +----------+

1.7 本指南涵盖的内容

本指南将完整覆盖以下内容:

  1. DID 文档结构与注册流程
  2. DID 锚定合约的设计与部署
  3. 可验证凭证的签发、验证与撤销
  4. 跨链身份互认与 IBC 集成
  5. 身份与 Agent 宪章绑定
  6. 隐私保护身份技术
  7. 完整的端到端示例
  8. 安全最佳实践与常见陷阱

2. DID 注册与管理

2.1 密钥对生成

Agent 身份的基础是公私钥对。在 msg-chain-1 上,我们推荐使用 Ed25519 曲线,因为它性能优异且广泛支持。

import hashlib
from typing import Optional
from nacl.bindings import crypto_sign_keypair, crypto_sign_seed_keypair


class AgentKeyPair:
    """Ed25519 key pair with utility methods for DID usage"""

    def __init__(self, seed: Optional[bytes] = None):
        if seed:
            if len(seed) != 32:
                raise ValueError("Seed must be exactly 32 bytes")
            self.private_key, self.public_key = crypto_sign_seed_keypair(seed)
        else:
            self.private_key, self.public_key = crypto_sign_keypair()

    @property
    def public_key_multibase(self) -> str:
        return "z" + self._b58encode(self.public_key)

    @property
    def did(self) -> str:
        agent_id = hashlib.sha256(self.public_key).hexdigest()[:24]
        return f"did:msg:{agent_id}"

    @property
    def msg_address(self) -> str:
        from bech32 import bech32_encode, convertbits
        sha = hashlib.sha256(self.public_key).digest()
        ripe = hashlib.new('ripemd160', sha).digest()
        five_bit = convertbits(ripe, 8, 5)
        return bech32_encode('msg', five_bit)

    @staticmethod
    def _b58encode(data: bytes) -> str:
        alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        n = int.from_bytes(data, 'big')
        chars = []
        while n > 0:
            n, r = divmod(n, 58)
            chars.append(alphabet[r])
        for byte in data:
            if byte == 0:
                chars.append(alphabet[0])
            else:
                break
        return ''.join(reversed(chars))

    @staticmethod
    def _b58decode(s: str) -> bytes:
        alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        n = 0
        for c in s:
            n = n * 58 + alphabet.index(c)
        result = n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\x00'
        return result

2.2 DID Document 构建

DID Document 是描述 Agent 身份的核心 JSON-LD 文档。它包含验证方法、服务端点以及与其他身份的关联信息。

import json
from typing import List, Optional
from enum import Enum


class VerificationMethodType(str, Enum):
    ED25519_2020 = "Ed25519VerificationKey2020"
    ED25519_2018 = "Ed25519VerificationKey2018"
    JSON_WEB_KEY = "JsonWebKey2020"
    MSG_CHAIN_ADDRESS = "MsgChainAddress2024"


class ServiceType(str, Enum):
    AGENT_MESSAGING = "AgentMessaging"
    DID_COMM = "DIDComm"
    LINKED_DOMAINS = "LinkedDomains"
    CREDENTIAL_REGISTRY = "CredentialRegistry"


class DIDDocument:
    """W3C DID Document implementation for msg-chain-1."""

    def __init__(self, did: str):
        self.did = did
        self.contexts = ["https://www.w3.org/ns/did/v1"]
        self.verification_methods: List[dict] = []
        self.authentication: List[str] = []
        self.assertion_method: List[str] = []
        self.key_agreement: List[str] = []
        self.capability_invocation: List[str] = []
        self.capability_delegation: List[str] = []
        self.services: List[dict] = []
        self.also_known_as: List[str] = []

    def add_ed25519_verification_method(self, id_suffix="#keys-1", public_key_multibase="", controller=None):
        vm_id = f"{self.did}{id_suffix}"
        method = {
            "id": vm_id,
            "type": VerificationMethodType.ED25519_2020.value,
            "controller": controller or self.did,
            "publicKeyMultibase": public_key_multibase,
        }
        self.verification_methods.append(method)
        return vm_id

    def add_msg_address_verification_method(self, msg_address, id_suffix="#blockchain-account"):
        vm_id = f"{self.did}{id_suffix}"
        method = {
            "id": vm_id,
            "type": VerificationMethodType.MSG_CHAIN_ADDRESS.value,
            "controller": self.did,
            "blockchainAccountId": f"cosmos:{msg_address}",
        }
        self.verification_methods.append(method)
        return vm_id

    def add_service_endpoint(self, service_type, endpoint_url, id_suffix=None, metadata=None):
        suffix = id_suffix or f"#{service_type.value.lower()}-endpoint"
        service_id = f"{self.did}{suffix}"
        service = {"id": service_id, "type": service_type.value, "serviceEndpoint": endpoint_url}
        if metadata:
            service["metadata"] = metadata
        self.services.append(service)
        return service_id

    def to_dict(self) -> dict:
        doc = {"@context": self.contexts, "id": self.did}
        if self.also_known_as: doc["alsoKnownAs"] = self.also_known_as
        if self.verification_methods: doc["verificationMethod"] = self.verification_methods
        if self.authentication: doc["authentication"] = self.authentication
        if self.assertion_method: doc["assertionMethod"] = self.assertion_method
        if self.key_agreement: doc["keyAgreement"] = self.key_agreement
        if self.capability_invocation: doc["capabilityInvocation"] = self.capability_invocation
        if self.capability_delegation: doc["capabilityDelegation"] = self.capability_delegation
        if self.services: doc["service"] = self.services
        return doc

    def to_json(self, pretty=False) -> str:
        return json.dumps(self.to_dict(), indent=2 if pretty else None, ensure_ascii=False)

    @staticmethod
    def from_json(json_str: str) -> "DIDDocument":
        data = json.loads(json_str)
        doc = DIDDocument(data["id"])
        doc.contexts = data.get("@context", [doc.contexts])
        if isinstance(doc.contexts, str): doc.contexts = [doc.contexts]
        for attr in ["verification_methods", "authentication", "assertion_method",
                     "key_agreement", "capability_invocation", "capability_delegation",
                     "services", "also_known_as"]:
            json_key = attr[0] + attr[1:].replace("_", "").capitalize() if attr[0] == "v" else ""
            # Simple mapping
        doc.verification_methods = data.get("verificationMethod", [])
        doc.authentication = data.get("authentication", [])
        doc.assertion_method = data.get("assertionMethod", [])
        doc.key_agreement = data.get("keyAgreement", [])
        doc.capability_invocation = data.get("capabilityInvocation", [])
        doc.capability_delegation = data.get("capabilityDelegation", [])
        doc.services = data.get("service", [])
        doc.also_known_as = data.get("alsoKnownAs", [])
        return doc


def build_standard_agent_document(key_pair, agent_name, endpoint_url, msg_address):
    """Build a standard DID document for a msg-chain-1 agent"""
    doc = DIDDocument(key_pair.did)
    vm_id = doc.add_ed25519_verification_method(
        id_suffix="#keys-1", public_key_multibase=key_pair.public_key_multibase)
    doc.add_msg_address_verification_method(msg_address)
    doc.authentication = [vm_id]
    doc.assertion_method = [vm_id]
    doc.capability_invocation = [vm_id]
    doc.capability_delegation = [vm_id]
    doc.add_service_endpoint(ServiceType.AGENT_MESSAGING, endpoint_url,
                             metadata={"agentName": agent_name, "version": "1.0.0"})
    doc.add_service_endpoint(
        ServiceType.CREDENTIAL_REGISTRY,
        f"https://agent.msgchain.org/{key_pair.did}/credentials",
        id_suffix="#credential-registry")
    return doc

2.3 AgentDIDManager 完整实现

import json, hashlib, logging
from typing import Optional, Any

logger = logging.getLogger(__name__)


class DIDRegistryConfig:
    """Configuration for DID Registry on msg-chain-1"""
    def __init__(self, chain_id="msg-chain-1", rpc_endpoint="https://rpc.msgchain.org",
                 registry_contract="", gas_price="1000000000attoMSG"):
        self.chain_id = chain_id
        self.rpc_endpoint = rpc_endpoint
        self.registry_contract = registry_contract
        self.gas_price = gas_price


class AgentDIDManager:
    """Complete DID lifecycle manager for AI Agents on msg-chain-1."""

    def __init__(self, agent_id: str, key_pair: AgentKeyPair, config=None):
        self.agent_id = agent_id
        self.key_pair = key_pair
        self.config = config or DIDRegistryConfig()
        self.did = f"did:msg:{agent_id}"
        self.did_document = self.build_did_document()
        self._client = None

    async def _get_client(self):
        from cosmos_sdk.client.lcd import CosmWasmClient
        if not self._client:
            self._client = await CosmWasmClient.connect(self.config.rpc_endpoint)
        return self._client

    def build_did_document(self) -> dict:
        doc = DIDDocument(self.did)
        vm_id = doc.add_ed25519_verification_method(
            id_suffix="#keys-1", public_key_multibase=self.key_pair.public_key_multibase)
        doc.add_msg_address_verification_method(self.key_pair.msg_address)
        doc.authentication = [vm_id]
        doc.assertion_method = [vm_id]
        doc.capability_invocation = [vm_id]
        doc.capability_delegation = [vm_id]
        doc.add_service_endpoint(ServiceType.AGENT_MESSAGING,
            f"https://agent.msgchain.org/{self.did}",
            metadata={"agentId": self.agent_id, "protocol": "DIDComm/v2"})
        doc.add_service_endpoint(ServiceType.CREDENTIAL_REGISTRY,
            f"https://agent.msgchain.org/{self.did}/vc", id_suffix="#vc-registry")
        return doc.to_dict()

    def compute_document_hash(self) -> str:
        doc_json = json.dumps(self.did_document, sort_keys=True, ensure_ascii=False)
        return hashlib.sha256(doc_json.encode()).hexdigest()

    async def register_did_on_chain(self, owner_address: str, signer: Any) -> dict:
        doc_hash = self.compute_document_hash()
        client = await self._get_client()
        register_msg = {"register_did": {
            "did": self.did, "document_hash": doc_hash,
            "document_uri": f"ipfs://{await self._pin_document()}",
            "controller": owner_address,
            "verification_methods": [vm["id"] for vm in self.did_document.get("verificationMethod", [])]
        }}
        tx = await client.execute_contract(
            sender=owner_address, contract_address=self.config.registry_contract,
            msg=register_msg, signer=signer, gas_adjustment=1.4)
        logger.info(f"DID registered: {self.did}, tx: {tx.txhash}")
        return {"tx_hash": tx.txhash, "did": self.did, "document_hash": doc_hash}

    async def _pin_document(self) -> str:
        import ipfshttpclient
        client = ipfshttpclient.connect()
        doc_json = json.dumps(self.did_document, ensure_ascii=False).encode()
        return client.add_bytes(doc_json)

    async def resolve_did(self, did=None):
        target_did = did or self.did
        client = await self._get_client()
        result = await client.query_contract(self.config.registry_contract,
                                            {"resolve_did": {"did": target_did}})
        if not result or not result.get("document_uri"):
            return None
        import ipfshttpclient
        doc_bytes = ipfshttpclient.connect().cat(result["document_uri"].replace("ipfs://", ""))
        return json.loads(doc_bytes)

    async def update_did_document(self, owner_address, signer, new_document=None):
        if new_document: self.did_document = new_document
        doc_hash = self.compute_document_hash()
        client = await self._get_client()
        tx = await client.execute_contract(
            sender=owner_address, contract_address=self.config.registry_contract,
            msg={"update_did": {"did": self.did, "document_hash": doc_hash,
                                 "document_uri": f"ipfs://{await self._pin_document()}"}},
            signer=signer, gas_adjustment=1.4)
        logger.info(f"DID updated: {self.did}")
        return {"tx_hash": tx.txhash, "document_hash": doc_hash}

    async def rotate_keys(self, owner_address, signer, new_key_pair):
        self.key_pair = new_key_pair
        self.did_document = self.build_did_document()
        return await self.update_did_document(owner_address, signer)

    async def deactivate_did(self, owner_address, signer, reason=''):
        client = await self._get_client()
        tx = await client.execute_contract(
            sender=owner_address, contract_address=self.config.registry_contract,
            msg={"deactivate_did": {"did": self.did, "reason": reason}},
            signer=signer, gas_adjustment=1.4)
        logger.warning(f"DID deactivated: {self.did}")
        return {"tx_hash": tx.txhash, "did": self.did, "status": "deactivated"}

    def export_did_document(self, filepath: str):
        with open(filepath, "w", encoding="utf-8") as f:
            json.dump(self.did_document, f, indent=2, ensure_ascii=False)

2.4 密钥轮换策略

class KeyRotationPolicy:
    """Defines the key rotation schedule and strategy for agents."""
    def __init__(self, rotation_interval_days=90, overlap_period_days=30, max_retired_keys=5):
        self.rotation_interval = rotation_interval_days
        self.overlap_period = overlap_period_days
        self.max_retired = max_retired_keys

    def should_rotate(self, key_age_days: int) -> bool:
        return key_age_days >= self.rotation_interval

    def prune_retired_keys(self, did_document: dict) -> dict:
        active_vm, retired_vm = [], []
        for vm in did_document.get("verificationMethod", []):
            (retired_vm if "retired" in vm.get("id", "") else active_vm).append(vm)
        retained = retired_vm[-self.max_retired:] if len(retired_vm) > self.max_retired else retired_vm
        did_document["verificationMethod"] = active_vm + retained
        return did_document

2.5 DID 文档示例

一个完整的 Agent DID 文档示例如下:

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d",
  "verificationMethod": [
    {
      "id": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d#keys-1",
      "type": "Ed25519VerificationKey2020",
      "controller": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d",
      "publicKeyMultibase": "z6Mkf5r7h7Z5d8f9g0a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
    },
    {
      "id": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d#blockchain-account",
      "type": "MsgChainAddress2024",
      "controller": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d",
      "blockchainAccountId": "cosmos:msg1q2w3e4r5t6y7u8i9o0p1a2s3d4f5g6h7j8k9l"
    }
  ],
  "authentication": ["did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d#keys-1"],
  "assertionMethod": ["did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d#keys-1"],
  "service": [
    {
      "id": "did:msg:agent-2a8f1c3e9b7d4f5a0c3b8e1d#agent-messaging-endpoint",
      "type": "AgentMessaging",
      "serviceEndpoint": "https://agent.msgchain.org/did:msg:agent-2a8f1c3e9b",
      "metadata": {"agentId": "agent-2a8f1c3e9b", "protocol": "DIDComm/v2"}
    }
  ],
  "created": "2025-06-15T08:30:00Z",
  "updated": "2025-09-15T10:00:00Z"
}

2.6 DID 解析器

import aiohttp


class DIDResolver:
    """Universal DID Resolver for msg-chain-1."""

    def __init__(self, registry_endpoint: str):
        self.registry_endpoint = registry_endpoint
        self.cache = {}

    async def resolve(self, did: str, use_cache: bool = True):
        if not did or not did.startswith("did:"):
            raise ValueError(f"Invalid DID format: {did}")
        if use_cache and did in self.cache:
            return self.cache[did]
        method = did.split(":")[1]
        resolvers = {"msg": self._resolve_msg, "key": self._resolve_key}
        resolver = resolvers.get(method)
        if not resolver:
            raise ValueError(f"Unsupported DID method: {method}")
        document = await resolver(did)
        if document and use_cache:
            self.cache[did] = document
        return document

    async def _resolve_msg(self, did: str):
        async with aiohttp.ClientSession() as session:
            async with session.post(f"{self.registry_endpoint}/query",
                                    json={"resolve_did": {"did": did}}) as resp:
                if resp.status != 200: return None
                result = await resp.json()
                if not result.get("document_uri"): return None
                cid = result["document_uri"].replace("ipfs://", "")
                async with session.get(f"https://ipfs.msgchain.org/ipfs/{cid}") as ipfs_resp:
                    return await ipfs_resp.json() if ipfs_resp.status == 200 else None

    async def _resolve_key(self, did: str):
        key_data = did.split(":")[2]
        return {"@context": "https://www.w3.org/ns/did/v1", "id": did,
                "verificationMethod": [{"id": f"{did}#keys-1",
                  "type": "Ed25519VerificationKey2020", "controller": did,
                  "publicKeyMultibase": key_data}],
                "authentication": [f"{did}#keys-1"]}

    async def resolve_verification_method(self, did: str, method_id: str):
        doc = await self.resolve(did)
        if not doc: return None
        for vm in doc.get("verificationMethod", []):
            if vm["id"] == method_id or vm["id"].endswith(method_id):
                return vm
        return None

    def clear_cache(self): self.cache.clear()


3. DID 锚定合约

3.1 合约架构

DID Registry 是部署在 msg-chain-1 上的 CosmWasm 智能合约,负责链上 DID 生命周期管理。该合约存储 DID 文档的哈希和元数据,但不存储完整的 DID 文档(出于成本考虑,完整文档存储在 IPFS 上)。

use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response,
    StdError, StdResult, Uint64};
use cw_storage_plus::{Item, Map};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct DIDEntry {
    pub did: String,
    pub document_hash: String,
    pub document_uri: String,
    pub controller: Addr,
    pub activated: bool,
    pub deactivated: bool,
    pub created_at: Uint64,
    pub updated_at: Uint64,
    pub deactivation_reason: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RevocationEntry {
    pub credential_id: String,
    pub issuer_did: String,
    pub revoked: bool,
    pub revoked_at: Option<Uint64>,
    pub reason: Option<String>,
}

pub const DID_REGISTRY: Map<&str, DIDEntry> = Map::new("did_registry");
pub const CONTROLLER_DIDS: Map<&Addr, Vec<String>> = Map::new("controller_dids");
pub const REVOCATION_REGISTRY: Map<&str, RevocationEntry> = Map::new("revocation_registry");
pub const TOTAL_DIDS: Item<Uint64> = Item::new("total_dids");

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct InstantiateMsg { pub admin: Option<String>, pub chain_id: String }

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    RegisterDID { did: String, document_hash: String, document_uri: String,
                  verification_methods: Vec<String> },
    UpdateDID { did: String, document_hash: String, document_uri: String },
    DeactivateDID { did: String, reason: Option<String> },
    RevokeCredential { credential_id: String, reason: Option<String> },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    ResolveDID { did: String },
    ListDIDsByController { controller: String, limit: Option<u32> },
    CheckRevocation { credential_id: String },
    GetStats {},
}

pub fn validate_did_format(did: &str) -> StdResult<()> {
    if !did.starts_with("did:msg:") {
        return Err(StdError::generic_err("Invalid DID prefix: must start with did:msg:"));
    }
    if did.split(':').count() != 3 {
        return Err(StdError::generic_err("Invalid DID format"));
    }
    Ok(())
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(deps: DepsMut, _env: Env, _info: MessageInfo, _msg: InstantiateMsg) -> StdResult<Response> {
    TOTAL_DIDS.save(deps.storage, &Uint64::new(0))?;
    Ok(Response::new().add_attribute("action", "instantiate"))
}

pub fn register_did(deps: DepsMut, env: Env, info: MessageInfo,
    did: String, document_hash: String, document_uri: String, _vm: Vec<String>) -> StdResult<Response> {
    validate_did_format(&did)?;
    if DID_REGISTRY.has(deps.storage, &did) {
        return Err(StdError::generic_err("DID already registered"));
    }
    let entry = DIDEntry {
        did: did.clone(), document_hash, document_uri,
        controller: info.sender.clone(), activated: true, deactivated: false,
        created_at: Uint64::new(env.block.height),
        updated_at: Uint64::new(env.block.height),
        deactivation_reason: None,
    };
    DID_REGISTRY.save(deps.storage, &did, &entry)?;
    let mut controlled = CONTROLLER_DIDS.may_load(deps.storage, &info.sender)?.unwrap_or_default();
    controlled.push(did.clone());
    CONTROLLER_DIDS.save(deps.storage, &info.sender, &controlled)?;
    let total = TOTAL_DIDS.load(deps.storage)?;
    TOTAL_DIDS.save(deps.storage, &(total + Uint64::new(1)))?;
    Ok(Response::new().add_attribute("action", "register_did").add_attribute("did", &did))
}

pub fn update_did(deps: DepsMut, env: Env, info: MessageInfo,
    did: String, document_hash: String, document_uri: String) -> StdResult<Response> {
    let mut entry = DID_REGISTRY.load(deps.storage, &did)
        .map_err(|_| StdError::generic_err("DID not found"))?;
    if entry.controller != info.sender {
        return Err(StdError::generic_err("Unauthorized"));
    }
    if entry.deactivated {
        return Err(StdError::generic_err("Cannot update deactivated DID"));
    }
    entry.document_hash = document_hash;
    entry.document_uri = document_uri;
    entry.updated_at = Uint64::new(env.block.height);
    DID_REGISTRY.save(deps.storage, &did, &entry)?;
    Ok(Response::new().add_attribute("action", "update_did").add_attribute("did", &did))
}

pub fn deactivate_did(deps: DepsMut, env: Env, info: MessageInfo,
    did: String, reason: Option<String>) -> StdResult<Response> {
    let mut entry = DID_REGISTRY.load(deps.storage, &did)
        .map_err(|_| StdError::generic_err("DID not found"))?;
    if entry.controller != info.sender {
        return Err(StdError::generic_err("Unauthorized"));
    }
    entry.deactivated = true; entry.activated = false;
    entry.updated_at = Uint64::new(env.block.height);
    entry.deactivation_reason = reason;
    DID_REGISTRY.save(deps.storage, &did, &entry)?;
    Ok(Response::new().add_attribute("action", "deactivate_did").add_attribute("did", &did))
}

pub fn revoke_credential(deps: DepsMut, env: Env, info: MessageInfo,
    credential_id: String, reason: Option<String>) -> StdResult<Response> {
    let entry = RevocationEntry {
        credential_id: credential_id.clone(), issuer_did: info.sender.to_string(),
        revoked: true, revoked_at: Some(Uint64::new(env.block.height)), reason,
    };
    REVOCATION_REGISTRY.save(deps.storage, &credential_id, &entry)?;
    Ok(Response::new().add_attribute("action", "revoke_credential").add_attribute("id", &credential_id))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RegisterDID { did, document_hash, document_uri, verification_methods } =>
            register_did(deps, env, info, did, document_hash, document_uri, verification_methods),
        ExecuteMsg::UpdateDID { did, document_hash, document_uri } =>
            update_did(deps, env, info, did, document_hash, document_uri),
        ExecuteMsg::DeactivateDID { did, reason } => deactivate_did(deps, env, info, did, reason),
        ExecuteMsg::RevokeCredential { credential_id, reason } =>
            revoke_credential(deps, env, info, credential_id, reason),
    }
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::ResolveDID { did } => {
            let entry = DID_REGISTRY.load(deps.storage, &did)?;
            cosmwasm_std::to_binary(&entry)
        }
        QueryMsg::ListDIDsByController { controller, limit } => {
            let addr = deps.api.addr_validate(&controller)?;
            let dids = CONTROLLER_DIDS.may_load(deps.storage, &addr)?.unwrap_or_default();
            if let Some(l) = limit {
                cosmwasm_std::to_binary(&dids.into_iter().take(l as usize).collect::<Vec<_>>())
            } else { cosmwasm_std::to_binary(&dids) }
        }
        QueryMsg::CheckRevocation { credential_id } => {
            let revoked = REVOCATION_REGISTRY.may_load(deps.storage, &credential_id)?
                .map(|e| e.revoked).unwrap_or(false);
            cosmwasm_std::to_binary(&revoked)
        }
        QueryMsg::GetStats {} => {
            let total = TOTAL_DIDS.load(deps.storage)?;
            cosmwasm_std::to_binary(&serde_json::json!({"total_dids": total}))
        }
    }
}

3.2 合约交互客户端

class DIDRegistryClient:
    """Client for interacting with the DID Registry contract."""

    def __init__(self, contract_address: str, rpc_endpoint: str):
        self.contract_address = contract_address
        self.rpc_endpoint = rpc_endpoint

    async def send_register(self, owner: str, did: str, doc_hash: str, doc_uri: str, signer) -> dict:
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect(self.rpc_endpoint)
        tx = await client.execute_contract(sender=owner,
            contract_address=self.contract_address,
            msg={"register_did": {"did": did, "document_hash": doc_hash,
                 "document_uri": doc_uri, "verification_methods": []}},
            signer=signer, gas_adjustment=1.4)
        return {"tx_hash": tx.txhash, "did": did}

    async def query_resolve(self, did: str) -> Optional[dict]:
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect(self.rpc_endpoint)
        return await client.query_contract(self.contract_address,
                                          {"resolve_did": {"did": did}})

    async def query_by_controller(self, controller: str):
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect(self.rpc_endpoint)
        return await client.query_contract(self.contract_address,
                                          {"list_dids_by_controller": {"controller": controller}})

    async def send_deactivate(self, owner: str, did: str, reason: str, signer) -> dict:
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect(self.rpc_endpoint)
        tx = await client.execute_contract(sender=owner,
            contract_address=self.contract_address,
            msg={"deactivate_did": {"did": did, "reason": reason}},
            signer=signer, gas_adjustment=1.4)
        return {"tx_hash": tx.txhash, "did": did, "status": "deactivated"}


4. 可验证凭证 (Verifiable Credentials)

4.1 VC 数据模型

可验证凭证(VC)是 W3C 标准化的数据模型,用于表示经过加密验证的声明。在 Agent 生态中,VC 用于证明 Agent 的权限、属性、资质和声誉。

+---------------------------------------------+
|         Verifiable Credential                |
+---------------------------------------------+
|  @context: [W3C base, custom schemas]        |
|  id: urn:uuid:<unique-id>                    |
|  type: [VerifiableCredential, <type>]        |
|  issuer: did:msg:<agent-id>                  |
|  issuanceDate: ISO 8601 timestamp            |
|  expirationDate: ISO 8601 timestamp (opt)    |
|  credentialSubject: {                        |
|    id: did:msg:<subject-id>,                |
|    <claims>                                  |
|  }                                           |
|  proof: {                                    |
|    type: Ed25519Signature2020,              |
|    verificationMethod: <vm-id>,             |
|    proofPurpose: assertionMethod,            |
|    proofValue: <multibase-signature>         |
|  }                                           |
+---------------------------------------------+

4.2 VerifiableCredential 完整实现

import json, hashlib, logging
from datetime import datetime, timezone
from typing import Optional, List, Any
from uuid import uuid4
from nacl.bindings import crypto_sign_detached, crypto_sign_verify_detached

logger = logging.getLogger(__name__)


class CredentialSchema:
    """Defines the schema for a verifiable credential type."""

    def __init__(self, schema_id: str, schema_type: str, properties: dict, required: List[str]):
        self.id = schema_id
        self.type = schema_type
        self.properties = properties
        self.required = required

    def validate_claims(self, claims: dict) -> bool:
        return all(f in claims for f in self.required)


AGENT_PERMISSION_SCHEMA = CredentialSchema(
    schema_id="https://schemas.msgchain.org/agent-permission/v1",
    schema_type="AgentPermission",
    properties={"agentId": "string", "permissionType": "string", "resource": "string",
                "grantedBy": "did", "allowedActions": "array"},
    required=["agentId", "permissionType", "resource", "grantedBy"])


class VerifiableCredential:
    """Complete Verifiable Credential implementation for msg-chain-1 agents."""

    def __init__(self, agent_did: str, key_pair: AgentKeyPair, resolver=None, revocation_contract=''):
        self.agent_did = agent_did
        self.key_pair = key_pair
        self.resolver = resolver
        self.revocation_contract = revocation_contract
        self.default_contexts = ["https://www.w3.org/2018/credentials/v1"]

    async def issue_credential(self, subject_did: str, credential_type: str, claims: dict,
                               schema=None, expiration_date=None) -> dict:
        """Issue a verifiable credential."""
        if schema and not schema.validate_claims(claims):
            raise ValueError("Claims failed schema validation")
        contexts = list(self.default_contexts)
        if schema: contexts.append(schema.id)
        vc = {"@context": contexts, "id": f"urn:uuid:{uuid4()}",
              "type": ["VerifiableCredential", credential_type],
              "issuer": self.agent_did,
              "issuanceDate": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")}
        if expiration_date: vc["expirationDate"] = expiration_date
        cs = {"id": subject_did}; cs.update(claims)
        vc["credentialSubject"] = cs
        vc["proof"] = self._generate_proof(vc)
        return vc

    def _generate_proof(self, vc_without_proof: dict) -> dict:
        vc_copy = dict(vc_without_proof)
        vc_copy.pop("proof", None)
        canonical = json.dumps(vc_copy, sort_keys=True, ensure_ascii=False).encode()
        proof_opts = {"type": "Ed25519Signature2020",
                      "created": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
                      "verificationMethod": f"{self.agent_did}#keys-1",
                      "proofPurpose": "assertionMethod"}
        sig_input = hashlib.sha256(json.dumps(proof_opts, sort_keys=True).encode() + canonical).digest()
        sig = crypto_sign_detached(sig_input, self.key_pair.private_key)
        proof_opts["proofValue"] = "z" + self._b58encode(sig)
        return proof_opts

    @staticmethod
    def _b58encode(data: bytes) -> str:
        alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        n, chars = int.from_bytes(data, 'big'), []
        while n > 0: n, r = divmod(n, 58); chars.append(alphabet[r])
        for b in data:
            if b == 0: chars.append(alphabet[0])
            else: break
        return ''.join(reversed(chars))

    @staticmethod
    def _b58decode(s: str) -> bytes:
        alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        n = 0
        for c in s: n = n * 58 + alphabet.index(c)
        return n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\x00'

    async def verify_credential(self, vc: dict, check_revocation=True, check_expiration=True) -> dict:
        """Verify a verifiable credential."""
        result = {"verified": False, "checks": {}, "errors": []}
        # Structure
        required = ["@context", "id", "type", "issuer", "issuanceDate", "credentialSubject", "proof"]
        missing = [f for f in required if f not in vc]
        if missing: result["errors"].extend(missing)
        if "VerifiableCredential" not in vc.get("type", []):
            result["errors"].append("type must include VerifiableCredential")
        # Expiration
        if check_expiration and vc.get("expirationDate"):
            try:
                exp = datetime.fromisoformat(vc["expirationDate"].replace("Z", "+00:00"))
                if datetime.now(timezone.utc) > exp:
                    result["errors"].append("Credential expired")
            except: result["errors"].append("Invalid expirationDate")
        # Proof
        proof = vc.get("proof", {})
        if not proof: result["errors"].append("No proof")
        elif proof.get("type") != "Ed25519Signature2020":
            result["errors"].append("Unsupported proof type")
        else:
            try:
                vc_copy = dict(vc); vc_copy.pop("proof", None)
                po = dict(proof); pv = po.pop("proofValue", "")
                canonical = json.dumps(vc_copy, sort_keys=True, ensure_ascii=False).encode()
                sig_input = hashlib.sha256(json.dumps(po, sort_keys=True).encode() + canonical).digest()
                crypto_sign_verify_detached(self._b58decode(pv[1:]), sig_input, self.key_pair.public_key)
            except Exception as e: result["errors"].append(f"Proof verification failed: {e}")
        result["verified"] = len(result["errors"]) == 0
        return result

    async def revoke_credential(self, vc_id: str, reason: str = '', owner: str = '', signer=None) -> dict:
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect('https://rpc.msgchain.org')
        tx = await client.execute_contract(sender=owner,
            contract_address=self.revocation_contract,
            msg={"revoke_credential": {"credential_id": vc_id, "reason": reason or "No reason"}},
            signer=signer, gas_adjustment=1.4)
        return {"tx_hash": tx.txhash, "credential_id": vc_id, "revoked": True}

4.3 Agent 权限凭证示例

async def create_agent_permission(vc_manager, target_agent_did, permissions, resources, ttl_days=30):
    from datetime import timedelta
    exp = (datetime.now(timezone.utc) + timedelta(days=ttl_days)).isoformat().replace('+00:00', 'Z')
    return await vc_manager.issue_credential(
        subject_did=target_agent_did,
        credential_type="AgentPermission",
        schema=AGENT_PERMISSION_SCHEMA,
        claims={"agentId": target_agent_did.split(":")[-1], "permissionType": "delegated",
                "resource": ",".join(resources), "grantedBy": vc_manager.agent_did,
                "expiresAt": exp, "allowedActions": permissions},
        expiration_date=exp)

async def verify_agent_permission(vc_manager, vc, required_action, required_resource):
    result = await vc_manager.verify_credential(vc)
    if not result["verified"]: return False
    subj = vc.get("credentialSubject", {})
    return (required_action in subj.get("allowedActions", []) and
            required_resource in subj.get("resource", ""))

4.4 可验证表达 (Verifiable Presentations)

class VerifiablePresentation:
    """Verifiable Presentation — a wrapper around VCs signed by the holder."""

    def __init__(self, holder_did: str, key_pair: AgentKeyPair):
        self.holder_did = holder_did
        self.key_pair = key_pair

    async def create_presentation(self, credentials: List[dict], challenge=None, domain=None):
        vp = {"@context": ["https://www.w3.org/2018/presentations/v1"],
              "type": ["VerifiablePresentation"], "holder": self.holder_did,
              "verifiableCredential": credentials}
        if challenge: vp["challenge"] = challenge
        if domain: vp["domain"] = domain
        vp["proof"] = self._sign_presentation(vp)
        return vp

    def _sign_presentation(self, vp: dict) -> dict:
        vp_copy, proof = dict(vp), {}
        vp_copy.pop("proof", None)
        canonical = json.dumps(vp_copy, sort_keys=True, ensure_ascii=False).encode()
        proof = {"type": "Ed25519Signature2020",
                 "created": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
                 "verificationMethod": f"{self.holder_did}#keys-1",
                 "proofPurpose": "authentication",
                 "challenge": vp.get("challenge", "")}
        sig_input = hashlib.sha256(json.dumps(proof, sort_keys=True).encode() + canonical).digest()
        proof["proofValue"] = "z" + VerifiableCredential._b58encode(
            crypto_sign_detached(sig_input, self.key_pair.private_key))
        return proof


5. 跨链身份互认

5.1 跨链 DID 解析

在 Agent 网络中,Agent 可能需要与不同区块链上的其他 Agent 或服务交互。跨链身份互认允许 Agent 在一个链上建立的 DID 和凭证被其他链信任。

import aiohttp, hashlib, json, time, logging
from typing import Optional, Dict, List

logger = logging.getLogger(__name__)


class IBCDIDResolver:
    """IBC-based cross-chain DID resolution."""

    def __init__(self, chain_id: str, ibc_connections: Dict[str, str],
                 registry_endpoints: Dict[str, str]):
        self.chain_id = chain_id
        self.ibc_connections = ibc_connections
        self.registry_endpoints = registry_endpoints

    async def resolve_did(self, did: str):
        method = did.split(":")[1] if ":" in did else ""
        resolvers = {"msg": self._resolve_local, "cosmos": self._resolve_ibc,
                     "ethr": self._resolve_ethereum, "key": self._resolve_key,
                     "web": self._resolve_web}
        resolver = resolvers.get(method)
        if not resolver: raise ValueError(f"Unsupported DID method: {method}")
        return await resolver(did)

    async def _resolve_local(self, did: str):
        from cosmos_sdk.client.lcd import CosmWasmClient
        client = await CosmWasmClient.connect('https://rpc.msgchain.org')
        result = await client.query_contract(self.registry_endpoints.get("msg", ""),
                                            {"resolve_did": {"did": did}})
        if not result or not result.get("document_uri"): return None
        import ipfshttpclient
        return json.loads(ipfshttpclient.connect().cat(
            result["document_uri"].replace("ipfs://", "")))

    async def _resolve_ibc(self, did: str):
        method = did.split(":")[1]
        channel = self.ibc_connections.get(method)
        if not channel: raise ValueError(f"No IBC connection for {method}")
        packet = {"type": "did_resolution", "did": did,
                  "requester_chain": self.chain_id, "timestamp": int(time.time())}
        async with aiohttp.ClientSession() as sess:
            async with sess.post('https://rpc.msgchain.org/ibc/query',
                json={"source_channel": channel, "packet_data": packet,
                      "timeout_seconds": 30}) as resp:
                return await resp.json() if resp.status == 200 else None

    async def _resolve_ethereum(self, did: str):
        async with aiohttp.ClientSession() as sess:
            async with sess.get(f"https://dev.uniresolver.io/1.0/identifiers/{did}") as resp:
                return await resp.json() if resp.status == 200 else None

    async def _resolve_key(self, did: str):
        k = did.split(":")[2]
        return {"@context": "https://www.w3.org/ns/did/v1", "id": did,
                "verificationMethod": [{"id": f"{did}#keys-1",
                  "type": "Ed25519VerificationKey2020", "controller": did,
                  "publicKeyMultibase": k}],
                "authentication": [f"{did}#keys-1"]}

    async def _resolve_web(self, did: str):
        parts = did.split(":"); domain = parts[2]
        path = parts[3] if len(parts) > 3 else '.well-known'
        async with aiohttp.ClientSession() as sess:
            async with sess.get(f"https://{domain}/{path}/did.json") as resp:
                return await resp.json() if resp.status == 200 else None

    async def verify_cross_chain_credential(self, vc: dict) -> dict:
        issuer = vc.get("issuer", "")
        if not issuer: return {"verified": False, "error": "No issuer"}
        doc = await self.resolve_did(issuer)
        if not doc: return {"verified": False, "error": f"Cannot resolve {issuer}"}
        return {"verified": True}

5.2 IBC 数据包协议

class IBCDIDPacket:
    """IBC packet protocol for cross-chain DID operations."""
    TYPE_DID_RESOLUTION = "did_resolution"
    TYPE_VC_VERIFICATION = "vc_verification"
    TYPE_REVOCATION_CHECK = "revocation_check"

    @staticmethod
    def create_resolution_packet(did, requester_chain, requester_did, nonce):
        return {"type": IBCDIDPacket.TYPE_DID_RESOLUTION, "did": did,
                "requester_chain": requester_chain, "requester_did": requester_did,
                "nonce": nonce,
                "timestamp": datetime.now(timezone.utc).isoformat() + "Z"}

    @staticmethod
    def create_response(request_packet, success, data=None, error=None):
        return {"type": f"{request_packet['type']}_response",
                "original_nonce": request_packet.get("nonce", ""),
                "success": success, "data": data, "error": error}

5.3 通用解析器

class UniversalDIDResolver:
    """Universal DID Resolver following W3C DID Resolution spec."""
    def __init__(self): self.resolvers = {}
    def register_resolver(self, method, resolver): self.resolvers[method] = resolver

    async def resolve(self, did, resolution_options=None):
        method = did.split(":")[1] if ":" in did else ""
        resolver = self.resolvers.get(method)
        if not resolver:
            return {"didResolutionMetadata": {"error": "methodNotSupported"},
                    "didDocument": None, "didDocumentMetadata": {}}
        try:
            doc = await resolver.resolve(did)
            if doc:
                return {"@context": "https://w3id.org/did-resolution/v1",
                        "didDocument": doc,
                        "didResolutionMetadata": {"contentType": "application/did+ld+json"},
                        "didDocumentMetadata": {}}
        except Exception as e:
            return {"didResolutionMetadata": {"error": "resolutionFailed", "errorMessage": str(e)},
                    "didDocument": None, "didDocumentMetadata": {}}
        return {"didResolutionMetadata": {"error": "notFound"}, "didDocument": None,
                "didDocumentMetadata": {}}


6. 身份与宪章绑定

6.1 Agent 宪章模型

Agent 宪章(Constitution)是 Agent 行为的根本规则——它定义了 Agent 的目标、权限边界、治理规则和伦理约束。将宪章与 Agent 的 DID 绑定,确保 Agent 的身份与其行为规则不可分割地关联。

import json, hashlib
from datetime import datetime, timezone
from typing import Optional, List
from uuid import uuid4


class AgentConstitution:
    """Agent Constitution — foundational rules governing an AI Agent."""

    def __init__(self, agent_did, name, purpose, principles, permissions, governance, jurisdiction='msg-chain-1'):
        self.agent_did = agent_did
        self.name = name
        self.purpose = purpose
        self.principles = principles
        self.permissions = permissions
        self.governance = governance
        self.jurisdiction = jurisdiction
        self.created_at = datetime.now(timezone.utc).isoformat() + 'Z'
        self.amended_at = None
        self.amendment_history = []

    def to_dict(self) -> dict:
        return {"agentDID": self.agent_did, "name": self.name, "purpose": self.purpose,
                "principles": self.principles, "permissions": self.permissions,
                "governance": self.governance, "jurisdiction": self.jurisdiction,
                "createdAt": self.created_at, "amendedAt": self.amended_at,
                "amendmentHistory": self.amendment_history}

    def compute_hash(self) -> str:
        canon = json.dumps(self.to_dict(), sort_keys=True, ensure_ascii=False)
        return hashlib.sha256(canon.encode()).hexdigest()

    def propose_amendment(self, amendment, proposed_by, reason):
        proposal = {"id": str(uuid4()), "amendment": amendment, "proposedBy": proposed_by,
                    "reason": reason, "status": "proposed",
                    "proposedAt": datetime.now(timezone.utc).isoformat() + "Z"}
        self.amendment_history.append(proposal)
        return proposal

    @classmethod
    def create_default(cls, agent_did, agent_name):
        return cls(agent_did=agent_did, name=agent_name,
            purpose=f"AI Agent {agent_name} operating on msg-chain-1",
            principles=[{"principle": "Beneficence", "rule": "Act in best interest of principal"},
                        {"principle": "Non-maleficence", "rule": "Not cause harm"},
                        {"principle": "Autonomy", "rule": "Respect autonomy"},
                        {"principle": "Transparency", "rule": "Decisions are auditable"}],
            permissions=[{"action": "sign_transaction", "resource": "funds", "constraint": "max <= 1000 MSG"},
                         {"action": "issue_credential", "resource": "identity", "constraint": "only_agent_did"}],
            governance={"model": "principal_controlled", "overseerDID": agent_did,
                       "amendmentProcess": "multi_sig", "requiredApprovals": 2},
            jurisdiction="msg-chain-1")

6.2 ConstitutionIdentity 绑定实现

class ConstitutionIdentity:
    """Bind an Agent's Constitution to its DID via VC."""

    def __init__(self, agent_did, agent_key_pair, resolver, vc_manager):
        self.agent_did = agent_did
        self.key_pair = agent_key_pair
        self.resolver = resolver
        self.vc_manager = vc_manager
        self.binding_vc = None

    async def bind_constitution(self, constitution: AgentConstitution) -> dict:
        constitution_hash = constitution.compute_hash()
        vc = await self.vc_manager.issue_credential(
            subject_did=self.agent_did, credential_type="ConstitutionBinding",
            claims={"constitutionHash": constitution_hash, "bindingType": "immutable",
                    "jurisdiction": "msg-chain-1",
                    "governanceModel": constitution.governance.get("model", ""),
                    "boundAt": datetime.now(timezone.utc).isoformat() + "Z"})
        self.binding_vc = vc
        return vc

    async def verify_constitution_binding(self, vc: dict, expected_hash: str) -> bool:
        result = await self.vc_manager.verify_credential(vc)
        if not result["verified"]: return False
        return vc.get("credentialSubject", {}).get("constitutionHash", "") == expected_hash

    async def prove_constitution_compliance(self, action, resource, constitution: AgentConstitution) -> dict:
        for perm in constitution.permissions:
            if perm["action"] == action and perm["resource"] == resource:
                return await self.vc_manager.issue_credential(
                    subject_did=self.agent_did, credential_type="ConstitutionComplianceProof",
                    claims={"action": action, "resource": resource,
                            "constitutionHash": constitution.compute_hash(),
                            "compliant": True,
                            "provedAt": datetime.now(timezone.utc).isoformat() + "Z"})
        raise ValueError(f"Action {action} on {resource} not permitted by constitution")

6.3 宪章治理流程

宪章治理流程:

  1. 创建: Agent 创建初始宪章 (AgentConstitution.create_default())
  2. 哈希: 计算宪章内容哈希 (constitution.compute_hash())
  3. 绑定: 签发 ConstitutionBinding VC 并存储在链上
  4. 提案: 利益相关方提出宪章修正案 (propose_amendment())
  5. 投票: 治理模型规定的投票流程(例如多签批准)
  6. 应用: 批准后更新宪章并签发新的绑定 VC
  7. 审计: 任何第三方可通过验证绑定 VC 来确认宪章的完整性

7. 隐私保护身份

7.1 零知识身份验证

零知识证明(ZKP)允许 Agent 在不泄露具体信息的前提下证明其身份属性。例如,Agent 可以证明其年龄超过某个阈值而不透露具体出生日期,或者证明其拥有某个凭证而不透露凭证的全部内容。

class ZKIdentityProof:
    """Zero-knowledge identity verification for agents."""

    def __init__(self, agent_did: str):
        self.agent_did = agent_did

    async def create_age_proof(self, birth_date: str, min_age: int = 18) -> dict:
        from datetime import date
        birth = date.fromisoformat(birth_date)
        today = date.today()
        age = today.year - birth.year - ((today.month, today.day) < (birth.month, birth.day))
        # In production: use circom + snarkjs for actual ZK proof
        return {"type": "ZKAgeProof",
                "circuit": "https://schemas.msgchain.org/circuits/age-check/v1",
                "publicInputs": {"minAge": min_age, "subjectDID": self.agent_did,
                                  "meetsRequirement": age >= min_age},
                "proof": {"pi_a": ["0x1234..."], "pi_b": [["0x9abc..."]],
                          "pi_c": ["0x3333..."], "protocol": "groth16", "curve": "bn128"}}

    async def verify_proof(self, proof: dict) -> bool:
        if proof.get("type") != "ZKAgeProof": return False
        return proof.get("publicInputs", {}).get("meetsRequirement", False)

7.2 选择性披露

选择性披露(Selective Disclosure)允许 Agent 在出示凭证时只展示必要的字段,而不暴露整个凭证内容。BBS+ 签名方案是实现选择性披露的主流选择。

class SelectiveDisclosureCredential:
    """Selective disclosure support for VCs."""

    def __init__(self, vc_manager: VerifiableCredential):
        self.vc_manager = vc_manager

    async def create_disclosed_presentation(self, vc: dict, disclose_fields: List[str]) -> dict:
        full_subject = vc.get('credentialSubject', {})
        disclosed = {'id': full_subject.get('id', '')}
        for f in disclose_fields:
            if f in full_subject: disclosed[f] = full_subject[f]
        disclosed_vc = dict(vc)
        disclosed_vc['credentialSubject'] = disclosed
        disclosed_vc.pop('proof', None)
        return await VerifiablePresentation(
            holder_did=self.vc_manager.agent_did,
            key_pair=self.vc_manager.key_pair
        ).create_presentation(credentials=[disclosed_vc], challenge=str(uuid4()))

    async def verify_disclosed_presentation(self, vp: dict, disclosed_fields: List[str]) -> bool:
        for vc in vp.get('verifiableCredential', []):
            subject = vc.get('credentialSubject', {})
            unexpected = [k for k in subject if k != 'id' and k not in disclosed_fields]
            if unexpected: return False
        return True

7.3 匿名 Agent 交互

class AnonymousAgentInteraction:
    """Anonymous interaction between agents using ephemeral DIDs."""

    def __init__(self, resolver: DIDResolver):
        self.resolver = resolver

    async def create_ephemeral_did(self) -> AgentKeyPair:
        key_pair = AgentKeyPair()
        return key_pair

    async def anonymous_auth(self, ephemeral_key: AgentKeyPair, target_service_did: str) -> dict:
        target_doc = await self.resolver.resolve(target_service_did)
        if not target_doc: raise ValueError(f"Cannot resolve {target_service_did}")
        return {"@context": ["https://www.w3.org/2018/credentials/v1"],
                "id": f"urn:uuid:{uuid4()}",
                "type": ["VerifiableCredential", "AnonymousAuth"],
                "issuer": ephemeral_key.did,
                "issuanceDate": datetime.now(timezone.utc).isoformat() + "Z",
                "credentialSubject": {"id": target_service_did, "purpose": "anonymous_access"}}


8. 完整示例

下面展示一个完整的端到端流程:从生成 Agent 身份、注册 DID、签发凭证、验证凭证到跨链交互。

import asyncio, json


async def demo_agent_identity_lifecycle():
    """Demonstrate the complete Agent DID lifecycle."""
    print("=" * 60)
    print("MSG Chain AI Agent DID & VC Lifecycle Demo")
    print("=" * 60)

    # Step 1: Generate keys
    print("\n[1] Generating Agent Key Pair...")
    agent_key = AgentKeyPair()
    print(f"  DID:      {agent_key.did}")
    print(f"  Address:  {agent_key.msg_address}")
    print(f"  PubKey:   {agent_key.public_key_multibase[:20]}...")

    # Step 2: Build DID document
    print("\n[2] Building DID Document...")
    doc = build_standard_agent_document(
        key_pair=agent_key, agent_name="DemoAgent-1",
        endpoint_url="https://agent.msgchain.org/demo-agent",
        msg_address=agent_key.msg_address)
    did_doc = doc.to_dict()
    print(f"  Document built: {len(json.dumps(did_doc))} chars")

    # Step 3: Initialize DID Manager
    print("\n[3] Initializing DID Manager...")
    config = DIDRegistryConfig(chain_id="msg-chain-1",
        rpc_endpoint="https://rpc.msgchain.org",
        registry_contract="msg1registrycontractaddress...")
    manager = AgentDIDManager(agent_id="demo-agent-001", key_pair=agent_key, config=config)
    print(f"  Manager ready for DID: {manager.did}")

    # Step 4: Register DID on chain (simulated)
    print("\n[4] Registering DID on msg-chain-1...")
    doc_hash = manager.compute_document_hash()
    print(f"  Document Hash: {doc_hash}")
    # In production: await manager.register_did_on_chain(owner, signer)
    print("  [SIMULATED] DID registered successfully")

    # Step 5: Issue Verifiable Credentials
    print("\n[5] Issuing AgentPermission Credential...")
    vc_manager = VerifiableCredential(agent_did=agent_key.did, key_pair=agent_key,
        resolver=DIDResolver(registry_endpoint='https://rpc.msgchain.org'))
    vc = await vc_manager.issue_credential(
        subject_did="did:msg:target-agent-002",
        credential_type="AgentPermission",
        claims={"agentId": "target-agent-002", "permissionType": "delegated",
                "resource": "data_oracle", "grantedBy": agent_key.did,
                "allowedActions": ["read", "query"]})
    print(f"  VC ID:    {vc['id'][:30]}...")
    print(f"  Issuer:   {vc['issuer']}")
    print(f"  Subject:  {vc['credentialSubject']['id']}")

    # Step 6: Verify Credential
    print("\n[6] Verifying Credential...")
    vc_result = await vc_manager.verify_credential(vc)
    print(f"  Verified: {vc_result['verified']}")
    if vc_result['errors']:
        for e in vc_result['errors']: print(f"    Error: {e}")

    # Step 7: Create Verifiable Presentation
    print("\n[7] Creating Verifiable Presentation...")
    vp_manager = VerifiablePresentation(holder_did=agent_key.did, key_pair=agent_key)
    vp = await vp_manager.create_presentation(credentials=[vc], challenge='abc123')
    print(f"  VP Holder: {vp.get('holder')}")
    print(f"  VP Type:   {vp.get('type')}")

    # Step 8: Bind Constitution
    print("\n[8] Binding Constitution to Agent Identity...")
    constitution = AgentConstitution.create_default(agent_did=agent_key.did, agent_name="DemoAgent-1")
    print(f"  Constitution Hash: {constitution.compute_hash()}")
    identity = ConstitutionIdentity(agent_did=agent_key.did, agent_key_pair=agent_key,
        resolver=DIDResolver(registry_endpoint=''), vc_manager=vc_manager)
    binding_vc = await identity.bind_constitution(constitution)
    print(f"  Binding VC ID: {binding_vc['id'][:30]}...")

    # Step 9: Cross-chain resolution (simulated)
    print("\n[9] Cross-Chain DID Resolution...")
    ibc_resolver = IBCDIDResolver(chain_id="msg-chain-1",
        ibc_connections={"cosmos": "channel-1", "ethr": "channel-2"},
        registry_endpoints={"msg": "msg1registry..."})
    ethr_doc = await ibc_resolver.resolve_did("did:ethr:0x123456789abcdef")
    print(f"  Cross-chain resolution: {ethr_doc is not None}")

    # Step 10: Key export (backup)
    print("\n[10] Exporting Encrypted Key Pair (Backup)...")
    manager.export_did_document('/tmp/demo_did_document.json')
    print("  DID document exported to /tmp/demo_did_document.json")

    print("\n" + "=" * 60)
    print("Demo completed successfully!")
    print("=" * 60)


if __name__ == '__main__':
    asyncio.run(demo_agent_identity_lifecycle())


9. 安全最佳实践

9.1 密钥管理

9.2 凭证安全

9.3 智能合约安全

9.4 常见陷阱

陷阱 后果 预防
私钥硬编码 密钥泄露,身份被盗 使用环境变量或密钥管理服务
不设置过期时间 凭证永久有效 始终设置合理的 expirationDate
DID 文档过大 Gas 消耗过高 文档上 IPFS,链上只存哈希
不使用挑战值 重放攻击 在 VP 中加入随机 challenge
忽略撤销检查 接受已撤销的凭证 验证时查询链上撤销注册表
跨链无超时 IBC 查询阻塞 设置合理的超时时间
单密钥无备份 密钥丢失即身份丢失 多重备份 + 多签

附录: msg-chain-1 DID 快速参考

CLI 命令

# Register a DID
msgnoded tx wasm execute msg1registrycontract... \\
  '{"register_did":{"did":"did:msg:my-agent","document_hash":"abc...","document_uri":"ipfs://..."}}' \\
  --from my-key --chain-id msg-chain-1 --gas auto

# Resolve a DID
msgnoded query wasm contract-state smart msg1registrycontract... \\
  '{"resolve_did":{"did":"did:msg:my-agent"}}'

# Deactivate a DID
msgnoded tx wasm execute msg1registrycontract... \\
  '{"deactivate_did":{"did":"did:msg:my-agent","reason":"retired"}}' \\
  --from my-key --chain-id msg-chain-1 --gas auto

关键合约地址 (Mainnet)

DID Registry:     msg1d4r3gz8xgk7q5qfq3w8z7h6g5f4e3d2c1b0a9
Revocation Reg:   msg1r3v0c8x7g6f5e4d3c2b1a0z9y8x7w6v5u4t3s
Universal Res:    msg1u5n4i3v2e1r0s9a8l7d6e5f4c3b2a1z0y9x8w

相关资源