From e51edc2ce4dcf3fbfa74131b02558eaaea305fc7 Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 3 Dec 2025 21:21:22 -0800 Subject: [PATCH] =?UTF-8?q?fix(mpc-service):=20=E4=BF=AE=E5=A4=8D=20MPC=20?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E6=B5=81=E7=A8=8B=EF=BC=8C=E5=85=88=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E4=BC=9A=E8=AF=9D=E5=86=8D=E5=8A=A0=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:mpc-service 尝试用 identity-service 生成的 SHA256 哈希作为 joinToken 加入会话,但 session-coordinator 期望的是由它自己 CreateSession 接口生成的 JWT token。 修复: - coordinator-client.ts: 添加 createSession() 方法 - participate-keygen.handler.ts: 先创建会话获取 JWT,再加入 - participate-signing.handler.ts: 同上 - rotate-share.handler.ts: 同上(使用 keygen 类型) 流程变更: 1. CreateSession -> 获取 sessionId + JWT joinToken 2. JoinSession 使用 JWT token 加入会话 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../participate-keygen.handler.ts | 28 +++++++-- .../participate-signing.handler.ts | 30 ++++++++-- .../rotate-share/rotate-share.handler.ts | 28 +++++++-- .../external/mpc-system/coordinator-client.ts | 59 ++++++++++++++++++- 4 files changed, 132 insertions(+), 13 deletions(-) diff --git a/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts b/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts index e2ea5611..6840830f 100644 --- a/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts +++ b/backend/services/mpc-service/src/application/commands/participate-keygen/participate-keygen.handler.ts @@ -163,11 +163,31 @@ export class ParticipateInKeygenHandler { private async joinSession(command: ParticipateInKeygenCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + // The session-coordinator expects us to create a session first + this.logger.log('Creating MPC session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'keygen', + thresholdN: 3, // Default 2-of-3 MPC + thresholdT: 2, + createdBy: command.partyId, + expiresIn: 600, // 10 minutes }); + this.logger.log(`Session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, // Use the JWT from createSession + }); + + // Return session info with the original session ID for consistency + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + }; } catch (error) { throw new ApplicationError( `Failed to join session: ${error.message}`, diff --git a/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts b/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts index b3898790..f8b07a38 100644 --- a/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts +++ b/backend/services/mpc-service/src/application/commands/participate-signing/participate-signing.handler.ts @@ -167,11 +167,33 @@ export class ParticipateInSigningHandler { private async joinSession(command: ParticipateInSigningCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + this.logger.log('Creating MPC signing session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'sign', + thresholdN: 3, // Default 2-of-3 MPC + thresholdT: 2, + createdBy: command.partyId, + messageHash: command.messageHash, + expiresIn: 300, // 5 minutes for signing }); + this.logger.log(`Signing session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, + }); + + // Return session info with correct IDs and public key from command + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + publicKey: command.publicKey, // Preserve public key from command + messageHash: command.messageHash, + }; } catch (error) { throw new ApplicationError( `Failed to join signing session: ${error.message}`, diff --git a/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts b/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts index 7d0244e4..a0f0fe02 100644 --- a/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts +++ b/backend/services/mpc-service/src/application/commands/rotate-share/rotate-share.handler.ts @@ -174,11 +174,31 @@ export class RotateShareHandler { private async joinSession(command: RotateShareCommand): Promise { try { - return await this.coordinatorClient.joinSession({ - sessionId: command.sessionId, - partyId: command.partyId, - joinToken: command.joinToken, + // First, create the session via coordinator to get a valid JWT token + // Key refresh uses 'keygen' session type (coordinator doesn't have 'refresh' type) + this.logger.log('Creating MPC refresh session via coordinator...'); + const createResponse = await this.coordinatorClient.createSession({ + sessionType: 'keygen', // Use keygen type for key refresh + thresholdN: 3, + thresholdT: 2, + createdBy: command.partyId, + expiresIn: 600, // 10 minutes }); + this.logger.log(`Refresh session created: ${createResponse.sessionId}, now joining...`); + + // Now join using the valid JWT token from the coordinator + const sessionInfo = await this.coordinatorClient.joinSession({ + sessionId: createResponse.sessionId, + partyId: command.partyId, + joinToken: createResponse.joinToken, + }); + + return { + ...sessionInfo, + sessionId: createResponse.sessionId, + joinToken: createResponse.joinToken, + publicKey: command.publicKey, + }; } catch (error) { throw new ApplicationError( `Failed to join rotation session: ${error.message}`, diff --git a/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts b/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts index 95670ac3..15c93e3b 100644 --- a/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts +++ b/backend/services/mpc-service/src/infrastructure/external/mpc-system/coordinator-client.ts @@ -2,12 +2,32 @@ * MPC Coordinator Client * * Client for communicating with the external MPC Session Coordinator. + * + * Flow: + * 1. CreateSession - Creates a new MPC session and returns a JWT joinToken + * 2. JoinSession - Parties join using the JWT token */ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import axios, { AxiosInstance, AxiosError } from 'axios'; +export interface CreateSessionRequest { + sessionType: 'keygen' | 'sign'; + thresholdN: number; + thresholdT: number; + createdBy: string; + messageHash?: string; + expiresIn?: number; // seconds +} + +export interface CreateSessionResponse { + sessionId: string; + joinToken: string; // JWT token for joining + status: string; + expiresAt?: number; +} + export interface JoinSessionRequest { sessionId: string; partyId: string; @@ -22,6 +42,7 @@ export interface SessionInfo { participants: Array<{ partyId: string; partyIndex: number }>; publicKey?: string; messageHash?: string; + joinToken?: string; // JWT token for this session } export interface ReportCompletionRequest { @@ -89,7 +110,42 @@ export class MPCCoordinatorClient implements OnModuleInit { } /** - * Join an MPC session + * Create an MPC session + * + * This must be called first to get a valid JWT joinToken + */ + async createSession(request: CreateSessionRequest): Promise { + this.logger.log(`Creating session: type=${request.sessionType}, ${request.thresholdT}-of-${request.thresholdN}`); + + try { + const response = await this.client.post('/api/v1/sessions', { + sessionType: request.sessionType, + thresholdN: request.thresholdN, + thresholdT: request.thresholdT, + createdBy: request.createdBy, + messageHash: request.messageHash, + expiresIn: request.expiresIn || 600, // Default 10 minutes + }); + + this.logger.log(`Session created: ${response.data.sessionId}`); + + return { + sessionId: response.data.sessionId, + joinToken: response.data.joinToken, + status: response.data.status, + expiresAt: response.data.expiresAt, + }; + } catch (error) { + const message = this.getErrorMessage(error); + this.logger.error(`Failed to create session: ${message}`); + throw new Error(`Failed to create MPC session: ${message}`); + } + } + + /** + * Join an MPC session using a JWT token + * + * The joinToken must be obtained from createSession() */ async joinSession(request: JoinSessionRequest): Promise { this.logger.log(`Joining session: ${request.sessionId}`); @@ -114,6 +170,7 @@ export class MPCCoordinatorClient implements OnModuleInit { })) || [], publicKey: undefined, messageHash: undefined, + joinToken: request.joinToken, }; } catch (error) { const message = this.getErrorMessage(error);