622 lines
13 KiB
Markdown
622 lines
13 KiB
Markdown
# MPC Party Service API 文档
|
||
|
||
## 概述
|
||
|
||
MPC Party Service 提供 RESTful API,用于参与 MPC 密钥生成、签名和密钥轮换操作。
|
||
|
||
**基础 URL**: `/api/v1/mpc-party`
|
||
|
||
**认证方式**: Bearer Token (JWT)
|
||
|
||
## 认证
|
||
|
||
除了健康检查端点外,所有 API 都需要 JWT 认证:
|
||
|
||
```http
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
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 <token>
|
||
```
|
||
|
||
**请求体**:
|
||
```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 <token>
|
||
```
|
||
|
||
**请求体**:
|
||
```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 <token>
|
||
```
|
||
|
||
**请求体**:
|
||
```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 <token>
|
||
```
|
||
|
||
**查询参数**:
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|
||
|------|------|------|--------|------|
|
||
| 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 <token>
|
||
```
|
||
|
||
**响应** `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 <token>" \
|
||
-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 <token>"
|
||
```
|
||
|
||
---
|
||
|
||
## Swagger 文档
|
||
|
||
在非生产环境,Swagger UI 可通过以下地址访问:
|
||
|
||
```
|
||
http://localhost:3006/api/docs
|
||
```
|
||
|
||
提供交互式 API 文档和测试功能。
|