# DID

Simple Profile Did文档解析.

## Install

```shell
yarn add @jccdex/did @jccdex/vc-vocabularies @jccdex/ipfs-rpc-client
```

### VC 签发与验签（0.3.0+）

业务词汇表在 `@jccdex/vc-vocabularies`；`@jccdex/did` 只提供类型无关的 `issueVC` / `verifyVC`。

```javascript
import { issueVC, verifyVC, EthrDidResolver } from "@jccdex/did";
import { buildNftOwnershipDescriptor } from "@jccdex/vc-vocabularies";
import { IpfsClient } from "@jccdex/ipfs-rpc-client";

// baseURL 为 ipfs-service 网关地址；请替换为实际部署地址
const client = new IpfsClient({ baseURL: "https://ipfs-service.example.com" });
const resolver = new EthrDidResolver(client);
const did = "did:ethr:0x...";

const descriptor = buildNftOwnershipDescriptor(
  "ccdao",
  { id: did, chainId: 1, contractAddress: "0x...", tokenId: "1", owner: did, status: "Active", standard: "ERC-721" },
  { id: `${did}#vc-1`, expirationDate: "2099-01-01T00:00:00.000Z" }
);

const vcJSON = await issueVC(descriptor, {
  sign: async (data) => window.ccdao.request({ method: "did_issueCredential", params: [data] }),
  keyDoc: { address: "0x...", did, id: `${did}#key-1` }
});

const { verified } = await verifyVC(vcJSON, { resolver });
```

### Usage in DAPP（DID 文档）

```javascript
import { 
  EthrDid, 
  EthrDidPublish, 
  EthrDidResolver, 
  EthrDidDocument, 
  SwtcDid, 
  SwtcDidPublish,
  SwtcDidResolver, 
  SwtcDidDocument,
  DidService,
  issueVC,
  verifyVC,
} from "@jccdex/did";
import { buildNftOwnershipDescriptor } from "@jccdex/vc-vocabularies";
import { IpfsClient } from "@jccdex/ipfs-rpc-client";

const ethereum = window.ethereum;

// CCDAOConnector授权以太坊地址
const account = "";
// CCDAOConnector授权swtc地址
const swtcAccount = "";

const client = new IpfsClient({
  baseURL: "https://ipfs-service.example.com",
  sign: async (data, address) => {
    const sign = await ethereum.request({
      method: 'ipfs_personalSign',
      params: [data, address],
    });
    return sign;
  },
  getPublicKey: async (address) => {
    const pub = await ethereum.request({
      method: 'ipfs_getPublicKey',
      params: [address]
    });
    return pub;
  }
});

// 创建did
const did = EthrDid.fromIdentifier(account);

const id = `${did.toString()}#key-1`;

const vcJSON = await issueVC(
  buildNftOwnershipDescriptor(
    "ccdao",
    {
      id: did.toString(),
      contractAddress: "0xED5AF388653567Af2F388E6224dC7C4b3241C544",
      chainId: 1,
      tokenId: "443",
      owner: "0xAWalletAddress",
      status: "Active",
      standard: "ERC-721"
    },
    {
      id: `${did.toString()}#nft-0xED5AF388653567Af2F388E6224dC7C4b3241C544-443-${did.toString()}`,
      expirationDate: "2099-01-01T00:00:00.000Z"
    }
  ),
  {
    sign: async (data) => ethereum.request({ method: "did_issueCredential", params: [data] }),
    keyDoc: { address: account, did: did.toString(), id }
  }
);

const resolver = new EthrDidResolver(client);
const { verified } = await verifyVC(vcJSON, { resolver });


// 创建simple profile DID文档
const didDoc = new EthrDidDocument(did.toString());
const profile = DidService.generateProfile({
    id: did.toString() + "#profile",
    nickname: "张三",
    // preferredAvatar 为 NFTOwnership VC 的 VCID（头像凭证 id），不是图片 URL
    preferredAvatar: vcJSON.id
});

// 现在ipfs service的ipns固定值
const ipns = "ipns://k2k4r8ntjlp1cmgped39eq1fi4yze6fsr8og1kcmjhamgs3ubwkfldei"

// 获取did文档cid, 如果是原始文件, 则不用获取, 为空字符串.
const { cid } = await resolver.stat(did.toString());

const ipfsStorage = DidService.generateIpfsStorage({
    id: did.toString() + "#ipfs-storage",
    ipns,
    previousCid: cid
});

// 从插件获取base58公钥及加密类型
const { publicKeyBase58, type } = await ethereum.request({
    method: 'did_getBase58PublicKey',
    params: [account],
});

didDoc.setVersion("1.0.0")
    .addAuthentication(id)
    .addAssertionMethod(id)
    .addVerificationMethod({
        id,
        type,
        controller: did.toString(),
        publicKeyBase58
    })
    .addService(profile)
    .addService(ipfsStorage)
    .addCredential(vcJSON);
    .setUpdated();

// 将did doc上传到ipfs service
const didPublisher = new EthrDidPublish(client);
const res = await didPublisher.upload(did.toString(), didDoc.toJSON(), account);
console.log("did publish result: ", res);

// 创建swtc did
const swtcDid = SwtcDid.fromIdentifier(swtcAccount);

const swtcId = `${swtcDid.toString()}#key-1`;

const swtcVcJSON = await issueVC(
  buildNftOwnershipDescriptor(
    "jdid",
    {
      id: swtcDid.toString(),
      chainId: 315,
      tokenName: "Golden Sands",
      tokenId: "2",
      owner: "swtcWalletAddress",
      status: "Active"
    },
    {
      id: "did:swtc:jHdWAmh8AAjhjqG7zEDA5RBgAnQHyd2g5m#nft-CrossChainDAONFT-j9pmACHpAV72ngFoSTNshhVtfhgGdQrXpc-43726F737320436861696E2044414F2000000000000000000000000000000002-did:swtc:jHdWAmh8AAjhjqG7zEDA5RBgAnQHyd2g5m",
      expirationDate: "2099-01-01T00:00:00.000Z"
    }
  ),
  {
    sign: async (data) => ethereum.request({ method: "did_issueCredential", params: [data] }),
    keyDoc: { address: swtcAccount, did: swtcDid.toString(), id: swtcId }
  }
);

const swtcResolver = new SwtcDidResolver(client);
const swtcVerified = await verifyVC(swtcVcJSON, { resolver: swtcResolver });

// 创建swtc simple profile DID文档
const swtcDidDoc = new SwtcDidDocument(swtcDid.toString());
const swtcProfile = DidService.generateProfile({
    id: swtcDid.toString() + "#profile",
    nickname: "李四",
    preferredAvatar: swtcVcJSON.id
});


// 获取swtc did文档cid, 如果是原始文件, 则不用获取, 为空字符串.
const { cid: swtcCid } = await swtcResolver.stat(swtcDid.toString());

const swtcIpfsStorage = DidService.generateIpfsStorage({
    id: swtcDid.toString() + "#ipfs-storage",
    ipns,
    previousCid: swtcCid
});


// 从插件获取base58公钥及加密类型
const { publicKeyBase58, type  } = await ethereum.request({
    method: 'did_getBase58PublicKey',
    params: [swtcAccount],
});

swtcDidDoc.setVersion("1.0.0")
    .addAuthentication(swtcId)
    .addAssertionMethod(swtcId)
    .addVerificationMethod({
        id: swtcId,
        type,
        controller: swtcDid.toString(),
        publicKeyBase58
    })
    .addService(swtcProfile)
    .addService(swtcIpfsStorage)
    .addCredential(swtcVcJSON)
    .setUpdated();

// 将swtc did doc上传到ipfs service
const swtcDidPublisher = new SwtcDidPublish(client);
const swtcRes = await swtcDidPublisher.upload(swtcDid.toString(), swtcDidDoc.toJSON(), swtcAccount);
console.log("swtc did publish result: ", swtcRes);


```

综合 README.md 和目录结构分析，frontend/did 这个 DAPP 主要实现了如下功能，并采用了相关 DID 生态开发包：

---

## 主要功能实现

1. **多链 DID 支持**
   - 支持以太坊（EthrDid）和 SWTC（SwtcDid）两种 DID 标准，能为不同链的账户生成 DID 标识和文档。

2. **DID 文档的创建、扩展与发布**
   - 可根据账户生成 DID 文档（DID Document），并支持添加 profile（昵称、头像）、ipfsStorage、service、credential 等扩展字段。
   - 支持将 DID 文档通过 IPFS 客户端上传到后端 ipfs-service，实现去中心化存储。

3. **可验证凭证（VC）管理**
   - 支持类型无关的 VC 签发/验签（`issueVC` / `verifyVC`），业务词汇表由 `@jccdex/vc-vocabularies` 提供。
   - VC 支持多链（以太坊、SWTC）和多类型扩展。

4. **DID 文档与 VC 的解析和验证**
   - 通过 EthrDidResolver、SwtcDidResolver 结合 IPFS 客户端，支持 DID 文档和 VC 的解析、CID 查询、签名验证等。

5. **与钱包/插件集成**
   - 通过 window.ethereum.request 与钱包或浏览器插件交互，实现链上签名、公钥获取、凭证签发等操作，支持 DApp 场景下的用户身份和凭证管理。

6. **IPFS 存储与版本管理**
   - 支持通过 ipfs-rpc-client 与后端 ipfs-service 通信，实现 DID 文档和凭证的去中心化存储、读取、CID 查询、IPNS 版本管理等。

---

## 主要依赖的开发包

- **@jccdex/did**
  - 提供 DID 标识、文档、凭证、解析、发布、服务扩展等全套能力，支持多链和多类型扩展。
  - 主要类和方法：EthrDid、SwtcDid、EthrDidPublish、SwtcDidPublish、EthrDidResolver、SwtcDidResolver、issueVC、verifyVC、DidService 等。

- **@jccdex/ipfs-rpc-client**
  - 提供与后端 ipfs-service 的 HTTP 通信能力，实现 IPFS 文件的上传、下载、CID 查询、IPNS 管理等。

---

## 代码结构与典型流程

- 代码分为 lib/（核心库）、src/（业务逻辑）、test/（测试）等目录，核心 DID 相关逻辑在 lib/ 和 src/ 下。
- 典型流程：  
  1. 通过账户生成 DID → 构建 DID 文档 → 生成/签名 VC → 通过 IPFS 客户端上传 DID 文档 → 解析/验证 DID 文档和 VC → 支持 profile、service、ipfsStorage 等扩展 → 与钱包插件集成实现链上签名和公钥获取。

---

## 特色与优势

- 支持多链 DID 和 NFT VC，适合多链身份和资产场景。
- DID 文档和凭证均可去中心化存储，支持版本管理和扩展。
- 与钱包深度集成，适合 DApp 场景。
- 代码结构清晰，扩展性强，便于二次开发。

---

## DID / VC 兼容性与职责边界

关于 `@jccdex/did` 协议层工作范围、应用端（`did_DApp`）如何应对 VC 类型与字段演进、以及 SDK 后续改造方向，见仓库根目录文档：[DID / VC 兼容性说明](../../README.md#did--vc-兼容性说明)。

---
