25 KiB
25 KiB
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: 改变系统状态的操作
ParticipateInKeygenCommandParticipateInSigningCommandRotateShareCommand
-
Queries: 只读操作
GetShareInfoQueryListSharesQuery
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 实体
// 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 (值对象)
值对象是不可变的,通过值来识别:
// 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 (领域服务)
// 分片加密服务
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
// 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
应用服务协调命令和查询处理器:
@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
@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
// 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
@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 加密:
// 加密流程
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 实现:
// 接口定义
export interface TssProtocolDomainService {
runKeygen(params: KeygenParams): Promise<KeygenResult>;
runSigning(params: SigningParams): Promise<SigningResult>;
runRefresh(params: RefreshParams): Promise<RefreshResult>;
}
// 可替换实现
// - GG20 实现
// - FROST 实现
// - 其他 TSS 协议
3. 插件化设计
基础设施层的实现可以轻松替换:
- 数据库:Prisma 支持多种数据库
- 缓存:可替换为其他缓存方案
- 消息队列:可替换为 RabbitMQ 等
配置管理
// 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,支持结构化日志:
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. 健康检查
@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 原则,实现了:
- 高内聚低耦合:各层职责明确,依赖于抽象
- 可测试性:依赖注入使得单元测试和集成测试易于实现
- 可扩展性:插件化设计支持不同的 TSS 协议和基础设施
- 安全性:多层安全措施保护敏感的密钥分片
- 可观测性:完善的日志和健康检查支持运维监控