600 lines
26 KiB
Markdown
600 lines
26 KiB
Markdown
# MPC Party Service 架构文档
|
||
|
||
## 概述
|
||
|
||
MPC Party Service 是 RWA Durian 系统中的多方计算(Multi-Party Computation)服务端组件。该服务负责参与分布式密钥生成、签名和密钥轮换协议,安全地管理服务端的密钥分片。
|
||
|
||
## 架构设计原则
|
||
|
||
### 1. 六边形架构 (Hexagonal Architecture)
|
||
|
||
本服务采用六边形架构(又称端口与适配器架构),实现业务逻辑与外部依赖的解耦:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ API Layer (Driving Adapters) │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||
│ │ REST API │ │ gRPC API │ │ WebSocket │ │
|
||
│ │ Controllers │ │ (Future) │ │ Handlers │ │
|
||
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
||
└─────────┼────────────────┼────────────────┼─────────────────┘
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Application Layer │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ Command Handlers│ │ Query Handlers │ │
|
||
│ │ - Keygen │ │ - GetShareInfo │ │
|
||
│ │ - Signing │ │ - ListShares │ │
|
||
│ │ - Rotate │ │ │ │
|
||
│ └────────┬────────┘ └────────┬────────┘ │
|
||
│ │ │ │
|
||
│ ┌────────▼────────────────────▼────────┐ │
|
||
│ │ Application Services │ │
|
||
│ │ - MPCPartyApplicationService │ │
|
||
│ └────────────────┬─────────────────────┘ │
|
||
└───────────────────┼─────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Domain Layer │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||
│ │ Entities │ │ Value │ │ Domain │ │
|
||
│ │ - PartyShare│ │ Objects │ │ Services │ │
|
||
│ │ - Session │ │ - ShareId │ │ - TSS │ │
|
||
│ │ State │ │ - PartyId │ │ - Encryption│ │
|
||
│ │ │ │ - Threshold │ │ │ │
|
||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────┐ │
|
||
│ │ Domain Events │ │
|
||
│ │ - ShareCreatedEvent │ │
|
||
│ │ - ShareRotatedEvent │ │
|
||
│ │ - ShareRevokedEvent │ │
|
||
│ └─────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Infrastructure Layer (Driven Adapters) │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||
│ │ Prisma │ │ Redis │ │ Kafka │ │
|
||
│ │ Repository │ │ Cache │ │ Events │ │
|
||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||
│ │
|
||
│ ┌─────────────┐ ┌─────────────┐ │
|
||
│ │ Coordinator │ │ Message │ │
|
||
│ │ Client │ │ Router │ │
|
||
│ └─────────────┘ └─────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2. CQRS 模式 (Command Query Responsibility Segregation)
|
||
|
||
命令和查询分离,提高系统的可扩展性和可维护性:
|
||
|
||
- **Commands**: 改变系统状态的操作
|
||
- `ParticipateInKeygenCommand`
|
||
- `ParticipateInSigningCommand`
|
||
- `RotateShareCommand`
|
||
|
||
- **Queries**: 只读操作
|
||
- `GetShareInfoQuery`
|
||
- `ListSharesQuery`
|
||
|
||
### 3. 领域驱动设计 (DDD)
|
||
|
||
- **聚合根 (Aggregate Root)**: `PartyShare`
|
||
- **值对象 (Value Objects)**: `ShareId`, `PartyId`, `SessionId`, `Threshold`, `ShareData`, `PublicKey`, `Signature`, `MessageHash`
|
||
- **领域事件 (Domain Events)**: 用于解耦和异步处理
|
||
- **仓储接口 (Repository Interfaces)**: 定义在领域层,实现在基础设施层
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
src/
|
||
├── api/ # API 层 (Driving Adapters)
|
||
│ ├── controllers/ # REST 控制器
|
||
│ │ └── mpc-party.controller.ts
|
||
│ ├── dto/ # 数据传输对象
|
||
│ │ ├── request/ # 请求 DTO
|
||
│ │ └── response/ # 响应 DTO
|
||
│ └── api.module.ts
|
||
│
|
||
├── application/ # 应用层
|
||
│ ├── commands/ # 命令处理器
|
||
│ │ ├── participate-keygen/
|
||
│ │ ├── participate-signing/
|
||
│ │ └── rotate-share/
|
||
│ ├── queries/ # 查询处理器
|
||
│ │ ├── get-share-info/
|
||
│ │ └── list-shares/
|
||
│ ├── services/ # 应用服务
|
||
│ │ └── mpc-party-application.service.ts
|
||
│ └── application.module.ts
|
||
│
|
||
├── domain/ # 领域层
|
||
│ ├── entities/ # 实体
|
||
│ │ ├── party-share.entity.ts
|
||
│ │ └── session-state.entity.ts
|
||
│ ├── value-objects/ # 值对象
|
||
│ │ └── index.ts
|
||
│ ├── enums/ # 枚举
|
||
│ │ └── index.ts
|
||
│ ├── events/ # 领域事件
|
||
│ │ └── index.ts
|
||
│ ├── repositories/ # 仓储接口
|
||
│ │ ├── party-share.repository.interface.ts
|
||
│ │ └── session-state.repository.interface.ts
|
||
│ ├── services/ # 领域服务
|
||
│ │ ├── share-encryption.domain-service.ts
|
||
│ │ └── tss-protocol.domain-service.ts
|
||
│ └── domain.module.ts
|
||
│
|
||
├── infrastructure/ # 基础设施层 (Driven Adapters)
|
||
│ ├── persistence/ # 持久化
|
||
│ │ ├── prisma/ # Prisma ORM
|
||
│ │ ├── repositories/ # 仓储实现
|
||
│ │ └── mappers/ # 数据映射器
|
||
│ ├── redis/ # Redis 缓存与锁
|
||
│ │ ├── cache/
|
||
│ │ └── lock/
|
||
│ ├── messaging/ # 消息传递
|
||
│ │ └── kafka/
|
||
│ ├── external/ # 外部服务客户端
|
||
│ │ └── mpc-system/
|
||
│ └── infrastructure.module.ts
|
||
│
|
||
├── shared/ # 共享模块
|
||
│ ├── decorators/ # 装饰器
|
||
│ ├── filters/ # 异常过滤器
|
||
│ ├── guards/ # 守卫
|
||
│ └── interceptors/ # 拦截器
|
||
│
|
||
├── config/ # 配置
|
||
│ └── index.ts
|
||
│
|
||
├── app.module.ts # 根模块
|
||
└── main.ts # 入口文件
|
||
```
|
||
|
||
## 核心组件详解
|
||
|
||
### 1. Domain Layer (领域层)
|
||
|
||
#### PartyShare 实体
|
||
|
||
```typescript
|
||
// src/domain/entities/party-share.entity.ts
|
||
export class PartyShare {
|
||
private readonly _id: ShareId;
|
||
private readonly _partyId: PartyId;
|
||
private readonly _sessionId: SessionId;
|
||
private readonly _shareType: PartyShareType;
|
||
private readonly _shareData: ShareData;
|
||
private readonly _publicKey: PublicKey;
|
||
private readonly _threshold: Threshold;
|
||
private _status: PartyShareStatus;
|
||
private _lastUsedAt?: Date;
|
||
private readonly _domainEvents: DomainEvent[] = [];
|
||
|
||
// 工厂方法 - 创建新分片
|
||
static create(props: CreatePartyShareProps): PartyShare;
|
||
|
||
// 工厂方法 - 从持久化数据重建
|
||
static reconstruct(props: ReconstructPartyShareProps): PartyShare;
|
||
|
||
// 业务方法
|
||
markAsUsed(): void;
|
||
rotate(newShareData: ShareData, newSessionId: SessionId): PartyShare;
|
||
revoke(reason: string): void;
|
||
}
|
||
```
|
||
|
||
#### Value Objects (值对象)
|
||
|
||
值对象是不可变的,通过值来识别:
|
||
|
||
```typescript
|
||
// ShareId - 分片唯一标识
|
||
export class ShareId {
|
||
static create(value: string): ShareId;
|
||
static generate(): ShareId;
|
||
get value(): string;
|
||
equals(other: ShareId): boolean;
|
||
}
|
||
|
||
// Threshold - 门限配置
|
||
export class Threshold {
|
||
static create(n: number, t: number): Threshold;
|
||
get n(): number; // 总分片数
|
||
get t(): number; // 签名门限
|
||
canSign(availableParties: number): boolean;
|
||
}
|
||
|
||
// ShareData - 加密的分片数据
|
||
export class ShareData {
|
||
static create(encryptedData: Buffer, iv: Buffer, authTag: Buffer): ShareData;
|
||
toJSON(): ShareDataJson;
|
||
static fromJSON(json: ShareDataJson): ShareData;
|
||
}
|
||
```
|
||
|
||
#### Domain Services (领域服务)
|
||
|
||
```typescript
|
||
// 分片加密服务
|
||
export class ShareEncryptionDomainService {
|
||
encrypt(plaintext: Buffer, masterKey: Buffer): EncryptedData;
|
||
decrypt(encryptedData: EncryptedData, masterKey: Buffer): Buffer;
|
||
generateMasterKey(): Buffer;
|
||
deriveKeyFromPassword(password: string, salt: Buffer): Promise<Buffer>;
|
||
}
|
||
|
||
// TSS 协议服务接口
|
||
export interface TssProtocolDomainService {
|
||
runKeygen(params: KeygenParams): Promise<KeygenResult>;
|
||
runSigning(params: SigningParams): Promise<SigningResult>;
|
||
runRefresh(params: RefreshParams): Promise<RefreshResult>;
|
||
}
|
||
```
|
||
|
||
### 2. Application Layer (应用层)
|
||
|
||
#### Command Handlers
|
||
|
||
```typescript
|
||
// ParticipateInKeygenHandler
|
||
@CommandHandler(ParticipateInKeygenCommand)
|
||
export class ParticipateInKeygenHandler {
|
||
async execute(command: ParticipateInKeygenCommand): Promise<KeygenResultDto> {
|
||
// 1. 加入会话
|
||
const sessionInfo = await this.coordinatorClient.joinSession(/*...*/);
|
||
|
||
// 2. 运行 TSS Keygen 协议
|
||
const keygenResult = await this.tssProtocolService.runKeygen(/*...*/);
|
||
|
||
// 3. 加密并保存分片
|
||
const encryptedShare = this.encryptionService.encrypt(/*...*/);
|
||
const partyShare = PartyShare.create(/*...*/);
|
||
await this.partyShareRepository.save(partyShare);
|
||
|
||
// 4. 发布领域事件
|
||
await this.eventPublisher.publishAll(partyShare.domainEvents);
|
||
|
||
return result;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Application Service
|
||
|
||
应用服务协调命令和查询处理器:
|
||
|
||
```typescript
|
||
@Injectable()
|
||
export class MPCPartyApplicationService {
|
||
async participateInKeygen(params: ParticipateKeygenParams): Promise<KeygenResultDto>;
|
||
async participateInSigning(params: ParticipateSigningParams): Promise<SigningResultDto>;
|
||
async rotateShare(params: RotateShareParams): Promise<RotateResultDto>;
|
||
async getShareInfo(shareId: string): Promise<ShareInfoDto>;
|
||
async listShares(params: ListSharesParams): Promise<ListSharesResultDto>;
|
||
}
|
||
```
|
||
|
||
### 3. Infrastructure Layer (基础设施层)
|
||
|
||
#### Repository Implementation
|
||
|
||
```typescript
|
||
@Injectable()
|
||
export class PartyShareRepositoryImpl implements PartyShareRepository {
|
||
constructor(
|
||
private readonly prisma: PrismaService,
|
||
private readonly mapper: PartyShareMapper,
|
||
) {}
|
||
|
||
async save(share: PartyShare): Promise<void> {
|
||
const entity = this.mapper.toPersistence(share);
|
||
await this.prisma.partyShare.create({ data: entity });
|
||
}
|
||
|
||
async findById(id: ShareId): Promise<PartyShare | null> {
|
||
const entity = await this.prisma.partyShare.findUnique({
|
||
where: { id: id.value },
|
||
});
|
||
return entity ? this.mapper.toDomain(entity) : null;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### External Service Clients
|
||
|
||
```typescript
|
||
// MPC Coordinator Client - 与协调器通信
|
||
@Injectable()
|
||
export class MPCCoordinatorClient {
|
||
async joinSession(params: JoinSessionParams): Promise<SessionInfo>;
|
||
async reportCompletion(sessionId: string, status: string): Promise<void>;
|
||
}
|
||
|
||
// Message Router Client - P2P 消息传递
|
||
@Injectable()
|
||
export class MPCMessageRouterClient {
|
||
async subscribeMessages(sessionId: string): Promise<AsyncIterator<Message>>;
|
||
async sendMessage(message: Message): Promise<void>;
|
||
}
|
||
```
|
||
|
||
### 4. API Layer (API 层)
|
||
|
||
#### Controller
|
||
|
||
```typescript
|
||
@Controller('mpc-party')
|
||
@UseGuards(JwtAuthGuard)
|
||
export class MPCPartyController {
|
||
// 异步端点 - 立即返回 202
|
||
@Post('keygen/participate')
|
||
@HttpCode(HttpStatus.ACCEPTED)
|
||
async participateInKeygen(@Body() dto: ParticipateKeygenDto): Promise<KeygenAcceptedDto>;
|
||
|
||
// 同步端点 - 等待完成
|
||
@Post('keygen/participate-sync')
|
||
async participateInKeygenSync(@Body() dto: ParticipateKeygenDto): Promise<KeygenResultDto>;
|
||
|
||
// 查询端点
|
||
@Get('shares')
|
||
async listShares(@Query() query: ListSharesDto): Promise<ListSharesResponseDto>;
|
||
|
||
@Get('shares/:shareId')
|
||
async getShareInfo(@Param('shareId') shareId: string): Promise<ShareInfoResponseDto>;
|
||
|
||
// 公开端点
|
||
@Public()
|
||
@Get('health')
|
||
health(): HealthStatus;
|
||
}
|
||
```
|
||
|
||
## 数据流
|
||
|
||
### Keygen 流程
|
||
|
||
```
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Client │ │Controller│ │ Handler │ │TSS Service│
|
||
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │ │
|
||
│ POST /keygen │ │ │
|
||
│ /participate │ │ │
|
||
│───────────────>│ │ │
|
||
│ │ execute() │ │
|
||
│ │───────────────>│ │
|
||
│ │ │ joinSession() │
|
||
│ │ │───────────────>│ Coordinator
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ │ │ runKeygen() │
|
||
│ │ │───────────────>│
|
||
│ │ │ ...MPC... │
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ │ │ save(share) │
|
||
│ │ │───────────────>│ Repository
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ │ │ publish(event) │
|
||
│ │ │───────────────>│ Kafka
|
||
│ 202 Accepted │<───────────────│ │
|
||
│<───────────────│ │ │
|
||
│ │ │ │
|
||
```
|
||
|
||
### Signing 流程
|
||
|
||
```
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Client │ │Controller│ │ Handler │ │TSS Service│
|
||
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │ │
|
||
│ POST /signing │ │ │
|
||
│ /participate │ │ │
|
||
│───────────────>│ │ │
|
||
│ │ execute() │ │
|
||
│ │───────────────>│ │
|
||
│ │ │ findShare() │
|
||
│ │ │───────────────>│ Repository
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ │ │ decrypt() │
|
||
│ │ │───────────────>│ Encryption
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ │ │ runSigning() │
|
||
│ │ │───────────────>│
|
||
│ │ │ ...MPC... │
|
||
│ │ │<───────────────│
|
||
│ │ │ │
|
||
│ 202 Accepted │<───────────────│ signature │
|
||
│<───────────────│ │ │
|
||
```
|
||
|
||
## 安全设计
|
||
|
||
### 1. 分片加密
|
||
|
||
所有密钥分片在存储前使用 AES-256-GCM 加密:
|
||
|
||
```typescript
|
||
// 加密流程
|
||
const { encryptedData, iv, authTag } = encryptionService.encrypt(
|
||
shareData,
|
||
masterKey
|
||
);
|
||
|
||
// 存储加密后的数据
|
||
const shareData = ShareData.create(encryptedData, iv, authTag);
|
||
```
|
||
|
||
### 2. 访问控制
|
||
|
||
- JWT Token 验证
|
||
- Party ID 绑定
|
||
- 操作审计日志
|
||
|
||
### 3. 安全通信
|
||
|
||
- TLS 加密传输
|
||
- 消息签名验证
|
||
- 会话令牌认证
|
||
|
||
## 扩展性设计
|
||
|
||
### 1. 水平扩展
|
||
|
||
- 无状态服务设计
|
||
- Redis 分布式锁
|
||
- Kafka 事件驱动
|
||
|
||
### 2. 多协议支持
|
||
|
||
通过领域服务接口抽象,支持不同的 TSS 实现:
|
||
|
||
```typescript
|
||
// 接口定义
|
||
export interface TssProtocolDomainService {
|
||
runKeygen(params: KeygenParams): Promise<KeygenResult>;
|
||
runSigning(params: SigningParams): Promise<SigningResult>;
|
||
runRefresh(params: RefreshParams): Promise<RefreshResult>;
|
||
}
|
||
|
||
// 可替换实现
|
||
// - GG20 实现
|
||
// - FROST 实现
|
||
// - 其他 TSS 协议
|
||
```
|
||
|
||
### 3. 插件化设计
|
||
|
||
基础设施层的实现可以轻松替换:
|
||
|
||
- 数据库:Prisma 支持多种数据库
|
||
- 缓存:可替换为其他缓存方案
|
||
- 消息队列:可替换为 RabbitMQ 等
|
||
|
||
## 配置管理
|
||
|
||
```typescript
|
||
// src/config/index.ts
|
||
export const configurations = [
|
||
() => ({
|
||
port: parseInt(process.env.APP_PORT, 10) || 3006,
|
||
env: process.env.NODE_ENV || 'development',
|
||
apiPrefix: process.env.API_PREFIX || 'api/v1',
|
||
|
||
database: {
|
||
url: process.env.DATABASE_URL,
|
||
},
|
||
|
||
redis: {
|
||
host: process.env.REDIS_HOST,
|
||
port: parseInt(process.env.REDIS_PORT, 10) || 6379,
|
||
},
|
||
|
||
mpc: {
|
||
coordinatorUrl: process.env.MPC_COORDINATOR_URL,
|
||
messageRouterWsUrl: process.env.MPC_MESSAGE_ROUTER_WS_URL,
|
||
partyId: process.env.MPC_PARTY_ID,
|
||
},
|
||
|
||
security: {
|
||
jwtSecret: process.env.JWT_SECRET,
|
||
shareMasterKey: process.env.SHARE_MASTER_KEY,
|
||
},
|
||
}),
|
||
];
|
||
```
|
||
|
||
## 监控与可观测性
|
||
|
||
### 1. 日志
|
||
|
||
使用 NestJS Logger,支持结构化日志:
|
||
|
||
```typescript
|
||
private readonly logger = new Logger(MPCPartyController.name);
|
||
|
||
this.logger.log(`Keygen request: session=${sessionId}, party=${partyId}`);
|
||
this.logger.error(`Keygen failed: ${error.message}`, error.stack);
|
||
```
|
||
|
||
### 2. 健康检查
|
||
|
||
```typescript
|
||
@Get('health')
|
||
health() {
|
||
return {
|
||
status: 'ok',
|
||
timestamp: new Date().toISOString(),
|
||
service: 'mpc-party-service',
|
||
};
|
||
}
|
||
```
|
||
|
||
### 3. 指标(待实现)
|
||
|
||
- 请求延迟
|
||
- 错误率
|
||
- MPC 协议执行时间
|
||
- 分片操作统计
|
||
|
||
## 依赖关系图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ AppModule │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ imports: │
|
||
│ ├── ConfigModule (global) │
|
||
│ ├── JwtModule (global) │
|
||
│ ├── DomainModule │
|
||
│ ├── InfrastructureModule │
|
||
│ ├── ApplicationModule │
|
||
│ └── ApiModule │
|
||
│ │
|
||
│ providers: │
|
||
│ ├── GlobalExceptionFilter │
|
||
│ ├── TransformInterceptor │
|
||
│ └── JwtAuthGuard │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ DomainModule │ │Infrastructure │ │ApplicationModule│
|
||
│ │ │ Module │ │ │
|
||
│ - Encryption │◄─┤ - Prisma │◄─┤ - Handlers │
|
||
│ Service │ │ - Redis │ │ - AppService │
|
||
│ - TSS Service │ │ - Kafka │ │ │
|
||
│ (interface) │ │ - Clients │ │ │
|
||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ ApiModule │
|
||
│ │
|
||
│ - Controllers │
|
||
│ - DTOs │
|
||
└─────────────────┘
|
||
```
|
||
|
||
## 总结
|
||
|
||
MPC Party Service 采用清晰的分层架构,遵循 DDD 和 CQRS 原则,实现了:
|
||
|
||
1. **高内聚低耦合**:各层职责明确,依赖于抽象
|
||
2. **可测试性**:依赖注入使得单元测试和集成测试易于实现
|
||
3. **可扩展性**:插件化设计支持不同的 TSS 协议和基础设施
|
||
4. **安全性**:多层安全措施保护敏感的密钥分片
|
||
5. **可观测性**:完善的日志和健康检查支持运维监控
|