# MPC Party Service API 文档 ## 概述 MPC Party Service 提供 RESTful API,用于参与 MPC 密钥生成、签名和密钥轮换操作。 **基础 URL**: `/api/v1/mpc-party` **认证方式**: Bearer Token (JWT) ## 认证 除了健康检查端点外,所有 API 都需要 JWT 认证: ```http Authorization: Bearer ``` JWT Payload 结构: ```json { "sub": "user-id", "type": "access", "partyId": "user123-server", "iat": 1699887766, "exp": 1699895766 } ``` ## API 端点 ### 健康检查 #### GET /health 检查服务健康状态。此端点不需要认证。 **请求**: ```http GET /api/v1/mpc-party/health ``` **响应** `200 OK`: ```json { "success": true, "data": { "status": "ok", "service": "mpc-party-service", "timestamp": "2024-01-15T10:30:00.000Z" } } ``` --- ### 密钥生成 (Keygen) #### POST /keygen/participate 参与 MPC 密钥生成会话(异步)。立即返回 202,后台异步执行 MPC 协议。 **请求**: ```http POST /api/v1/mpc-party/keygen/participate Content-Type: application/json Authorization: Bearer ``` **请求体**: ```json { "sessionId": "550e8400-e29b-41d4-a716-446655440000", "partyId": "user123-server", "joinToken": "join-token-abc123", "shareType": "wallet", "userId": "user-id-123" } ``` | 字段 | 类型 | 必填 | 描述 | |------|------|------|------| | sessionId | string (UUID) | 是 | 会话唯一标识 | | partyId | string | 是 | 参与方 ID,格式:`{identifier}-{type}` | | joinToken | string | 是 | 加入会话的令牌 | | shareType | enum | 是 | 分片类型:`wallet` 或 `custody` | | userId | string | 否 | 关联的用户 ID | **响应** `202 Accepted`: ```json { "success": true, "data": { "message": "Keygen participation started", "sessionId": "550e8400-e29b-41d4-a716-446655440000", "partyId": "user123-server" } } ``` **错误响应**: `400 Bad Request` - 参数验证失败: ```json { "success": false, "message": "Validation failed", "errors": [ { "field": "sessionId", "message": "sessionId must be a UUID" } ] } ``` `401 Unauthorized` - 认证失败: ```json { "success": false, "message": "缺少认证令牌" } ``` --- #### POST /keygen/participate-sync 参与 MPC 密钥生成会话(同步)。等待 MPC 协议完成后返回结果。 **请求**: 与异步端点相同 **响应** `200 OK`: ```json { "success": true, "data": { "shareId": "share_1699887766123_abc123xyz", "publicKey": "03a1b2c3d4e5f6...", "threshold": "2-of-3", "sessionId": "550e8400-e29b-41d4-a716-446655440000", "partyId": "user123-server" } } ``` --- ### 签名 (Signing) #### POST /signing/participate 参与 MPC 签名会话(异步)。 **请求**: ```http POST /api/v1/mpc-party/signing/participate Content-Type: application/json Authorization: Bearer ``` **请求体**: ```json { "sessionId": "660e8400-e29b-41d4-a716-446655440001", "partyId": "user123-server", "joinToken": "join-token-def456", "messageHash": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "publicKey": "03a1b2c3d4e5f6..." } ``` | 字段 | 类型 | 必填 | 描述 | |------|------|------|------| | sessionId | string (UUID) | 是 | 签名会话唯一标识 | | partyId | string | 是 | 参与方 ID | | joinToken | string | 是 | 加入会话的令牌 | | messageHash | string (hex, 64 chars) | 是 | 待签名的消息哈希 | | publicKey | string (hex) | 是 | 对应的公钥 | **响应** `202 Accepted`: ```json { "success": true, "data": { "message": "Signing participation started", "sessionId": "660e8400-e29b-41d4-a716-446655440001", "partyId": "user123-server" } } ``` **错误响应**: `400 Bad Request` - 消息哈希格式无效: ```json { "success": false, "message": "Validation failed", "errors": [ { "field": "messageHash", "message": "messageHash must be a 64-character hex string" } ] } ``` --- #### POST /signing/participate-sync 参与 MPC 签名会话(同步)。 **响应** `200 OK`: ```json { "success": true, "data": { "signature": "1122334455...", "r": "aabbccdd...", "s": "11223344...", "v": 27, "messageHash": "abcdef1234...", "publicKey": "03a1b2c3d4e5f6..." } } ``` --- ### 密钥轮换 (Key Rotation) #### POST /share/rotate 参与密钥轮换会话(异步)。更新密钥分片而不改变公钥。 **请求**: ```http POST /api/v1/mpc-party/share/rotate Content-Type: application/json Authorization: Bearer ``` **请求体**: ```json { "sessionId": "770e8400-e29b-41d4-a716-446655440002", "partyId": "user123-server", "joinToken": "join-token-ghi789", "publicKey": "03a1b2c3d4e5f6..." } ``` | 字段 | 类型 | 必填 | 描述 | |------|------|------|------| | sessionId | string (UUID) | 是 | 轮换会话唯一标识 | | partyId | string | 是 | 参与方 ID | | joinToken | string | 是 | 加入会话的令牌 | | publicKey | string (hex) | 是 | 要轮换的密钥公钥 | **响应** `202 Accepted`: ```json { "success": true, "data": { "message": "Share rotation started", "sessionId": "770e8400-e29b-41d4-a716-446655440002", "partyId": "user123-server" } } ``` --- ### 分片管理 (Share Management) #### GET /shares 列出分片,支持过滤和分页。 **请求**: ```http GET /api/v1/mpc-party/shares?partyId=user123-server&status=active&page=1&limit=10 Authorization: Bearer ``` **查询参数**: | 参数 | 类型 | 必填 | 默认值 | 描述 | |------|------|------|--------|------| | partyId | string | 否 | - | 按参与方 ID 过滤 | | status | enum | 否 | - | 按状态过滤:`active`, `rotated`, `revoked` | | shareType | enum | 否 | - | 按类型过滤:`wallet`, `custody` | | publicKey | string | 否 | - | 按公钥过滤 | | page | number | 否 | 1 | 页码(从 1 开始) | | limit | number | 否 | 20 | 每页数量(1-100) | **响应** `200 OK`: ```json { "success": true, "data": { "items": [ { "id": "share_1699887766123_abc123xyz", "partyId": "user123-server", "sessionId": "550e8400-e29b-41d4-a716-446655440000", "shareType": "wallet", "publicKey": "03a1b2c3d4e5f6...", "threshold": "2-of-3", "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "updatedAt": "2024-01-15T10:30:00.000Z" } ], "total": 1, "page": 1, "limit": 10, "totalPages": 1 } } ``` --- #### GET /shares/:shareId 获取单个分片的详细信息。 **请求**: ```http GET /api/v1/mpc-party/shares/share_1699887766123_abc123xyz Authorization: Bearer ``` **响应** `200 OK`: ```json { "success": true, "data": { "id": "share_1699887766123_abc123xyz", "partyId": "user123-server", "sessionId": "550e8400-e29b-41d4-a716-446655440000", "shareType": "wallet", "publicKey": "03a1b2c3d4e5f6...", "threshold": "2-of-3", "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "updatedAt": "2024-01-15T10:30:00.000Z", "lastUsedAt": "2024-01-15T12:00:00.000Z" } } ``` **错误响应**: `404 Not Found`: ```json { "success": false, "message": "Share not found" } ``` --- ## 错误处理 ### 错误响应格式 所有错误响应遵循统一格式: ```json { "success": false, "message": "错误描述", "errors": [ { "field": "字段名", "message": "具体错误信息" } ], "statusCode": 400, "timestamp": "2024-01-15T10:30:00.000Z", "path": "/api/v1/mpc-party/keygen/participate" } ``` ### HTTP 状态码 | 状态码 | 描述 | |--------|------| | 200 | 成功 | | 202 | 已接受(异步操作) | | 400 | 请求参数错误 | | 401 | 未认证 | | 403 | 无权限 | | 404 | 资源不存在 | | 500 | 服务器内部错误 | ### 业务错误码 | 错误码 | 描述 | |--------|------| | SHARE_NOT_FOUND | 分片不存在 | | SHARE_REVOKED | 分片已撤销 | | INVALID_SESSION | 无效的会话 | | SESSION_EXPIRED | 会话已过期 | | THRESHOLD_NOT_MET | 参与方数量未达到门限 | | ENCRYPTION_ERROR | 加密/解密错误 | | TSS_PROTOCOL_ERROR | TSS 协议执行错误 | --- ## 数据模型 ### ShareType 枚举 ```typescript enum ShareType { WALLET = 'wallet', // 用户钱包密钥分片 CUSTODY = 'custody' // 托管密钥分片 } ``` ### ShareStatus 枚举 ```typescript enum ShareStatus { ACTIVE = 'active', // 活跃状态 ROTATED = 'rotated', // 已轮换(旧分片) REVOKED = 'revoked' // 已撤销 } ``` ### Threshold 格式 门限以 `t-of-n` 格式表示: - `n`: 总分片数 - `t`: 签名所需最小分片数 示例:`2-of-3` 表示 3 个分片中需要 2 个才能签名。 --- ## Webhook 事件(通过 Kafka) 当关键操作完成时,服务会发布事件到 Kafka: ### ShareCreatedEvent ```json { "eventType": "share.created", "eventId": "evt_1699887766123_xyz", "occurredAt": "2024-01-15T10:30:00.000Z", "payload": { "shareId": "share_1699887766123_abc123xyz", "partyId": "user123-server", "sessionId": "550e8400-e29b-41d4-a716-446655440000", "shareType": "wallet", "publicKey": "03a1b2c3d4e5f6...", "threshold": "2-of-3" } } ``` ### ShareRotatedEvent ```json { "eventType": "share.rotated", "eventId": "evt_1699887766124_abc", "occurredAt": "2024-01-15T11:00:00.000Z", "payload": { "newShareId": "share_1699887766124_def456uvw", "oldShareId": "share_1699887766123_abc123xyz", "partyId": "user123-server", "sessionId": "770e8400-e29b-41d4-a716-446655440002" } } ``` ### ShareRevokedEvent ```json { "eventType": "share.revoked", "eventId": "evt_1699887766125_def", "occurredAt": "2024-01-15T12:00:00.000Z", "payload": { "shareId": "share_1699887766123_abc123xyz", "partyId": "user123-server", "reason": "Security audit requirement" } } ``` ### SigningCompletedEvent ```json { "eventType": "signing.completed", "eventId": "evt_1699887766126_ghi", "occurredAt": "2024-01-15T13:00:00.000Z", "payload": { "sessionId": "660e8400-e29b-41d4-a716-446655440001", "signature": "1122334455...", "publicKey": "03a1b2c3d4e5f6...", "messageHash": "abcdef1234..." } } ``` --- ## 速率限制 | 端点类型 | 限制 | |----------|------| | 健康检查 | 无限制 | | 查询端点 | 100 次/分钟 | | Keygen | 10 次/分钟 | | Signing | 60 次/分钟 | | Rotation | 5 次/分钟 | 超出限制时返回 `429 Too Many Requests`。 --- ## SDK 使用示例 ### TypeScript/JavaScript ```typescript import axios from 'axios'; const api = axios.create({ baseURL: 'https://api.example.com/api/v1/mpc-party', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); // 参与 Keygen async function participateInKeygen(params: { sessionId: string; partyId: string; joinToken: string; shareType: 'wallet' | 'custody'; }) { const response = await api.post('/keygen/participate', params); return response.data; } // 参与签名 async function participateInSigning(params: { sessionId: string; partyId: string; joinToken: string; messageHash: string; publicKey: string; }) { const response = await api.post('/signing/participate', params); return response.data; } // 列出分片 async function listShares(params?: { partyId?: string; status?: string; page?: number; limit?: number; }) { const response = await api.get('/shares', { params }); return response.data; } // 获取分片信息 async function getShareInfo(shareId: string) { const response = await api.get(`/shares/${shareId}`); return response.data; } ``` ### cURL 示例 ```bash # 健康检查 curl -X GET https://api.example.com/api/v1/mpc-party/health # 参与 Keygen curl -X POST https://api.example.com/api/v1/mpc-party/keygen/participate \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "sessionId": "550e8400-e29b-41d4-a716-446655440000", "partyId": "user123-server", "joinToken": "join-token-abc123", "shareType": "wallet", "userId": "user-id-123" }' # 列出分片 curl -X GET "https://api.example.com/api/v1/mpc-party/shares?partyId=user123-server&page=1&limit=10" \ -H "Authorization: Bearer " ``` --- ## Swagger 文档 在非生产环境,Swagger UI 可通过以下地址访问: ``` http://localhost:3006/api/docs ``` 提供交互式 API 文档和测试功能。