rwadurian/backend/services/mpc-service/docs/ARCHITECTURE.md

600 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. **可观测性**:完善的日志和健康检查支持运维监控