fix(service-party-app): 修复 gRPC 响应字段名 snake_case 问题
问题:
- proto-loader 使用 keepCase: true,导致 gRPC 响应字段为 snake_case
- TypeScript 接口使用 camelCase,导致字段不匹配
- joinSession 响应的 session_info.threshold_t 和 threshold_n 无法读取
- 导致 activeKeygenSession.threshold 为 {t: 0, n: 0}
- TSS 进程收到错误的 threshold 参数导致 exit code 1
修复:
- grpc-client.ts 接口改为 snake_case 以匹配 proto 定义
- main.ts 更新为使用 snake_case 字段名
- SessionEvent 处理转换为 camelCase 再传递给 handleSessionStart
Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1b48c05aa7
commit
989364969d
|
|
@ -608,36 +608,44 @@ async function connectAndRegisterToMessageRouter() {
|
|||
});
|
||||
|
||||
// 监听会话事件并处理
|
||||
// Note: gRPC response uses snake_case field names due to keepCase: true in proto-loader
|
||||
grpcClient.on('sessionEvent', async (event: {
|
||||
eventId: string;
|
||||
eventType: string;
|
||||
sessionId: string;
|
||||
thresholdN: number;
|
||||
thresholdT: number;
|
||||
selectedParties: string[];
|
||||
joinTokens: Record<string, string>;
|
||||
messageHash?: Buffer;
|
||||
event_id: string;
|
||||
event_type: string;
|
||||
session_id: string;
|
||||
threshold_n: number;
|
||||
threshold_t: number;
|
||||
selected_parties: string[];
|
||||
join_tokens: Record<string, string>;
|
||||
message_hash?: Buffer;
|
||||
}) => {
|
||||
debugLog.info('grpc', `Received session event: ${event.eventType} for session ${event.sessionId}`);
|
||||
debugLog.info('grpc', `Received session event: ${event.event_type} for session ${event.session_id}`);
|
||||
|
||||
const eventData: SessionEventData = {
|
||||
type: event.eventType,
|
||||
sessionId: event.sessionId,
|
||||
thresholdN: event.thresholdN,
|
||||
thresholdT: event.thresholdT,
|
||||
selectedParties: event.selectedParties,
|
||||
type: event.event_type,
|
||||
sessionId: event.session_id,
|
||||
thresholdN: event.threshold_n,
|
||||
thresholdT: event.threshold_t,
|
||||
selectedParties: event.selected_parties,
|
||||
};
|
||||
|
||||
// 缓存事件(解决前端可能还未订阅的时序问题)
|
||||
cacheSessionEvent(event.sessionId, eventData);
|
||||
cacheSessionEvent(event.session_id, eventData);
|
||||
|
||||
// 转发事件到前端
|
||||
mainWindow?.webContents.send(`session:events:${event.sessionId}`, eventData);
|
||||
mainWindow?.webContents.send(`session:events:${event.session_id}`, eventData);
|
||||
|
||||
// 根据事件类型处理
|
||||
if (event.eventType === 'session_started' || event.eventType === 'all_joined') {
|
||||
if (event.event_type === 'session_started' || event.event_type === 'all_joined') {
|
||||
// 当会话开始或所有参与方都加入时,检查是否需要触发 keygen
|
||||
await handleSessionStart(event);
|
||||
// Convert snake_case to camelCase for handleSessionStart
|
||||
await handleSessionStart({
|
||||
eventType: event.event_type,
|
||||
sessionId: event.session_id,
|
||||
thresholdN: event.threshold_n,
|
||||
thresholdT: event.threshold_t,
|
||||
selectedParties: event.selected_parties,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
@ -680,16 +688,17 @@ function setupIpcHandlers() {
|
|||
const result = await grpcClient?.joinSession(sessionId, partyId, joinToken);
|
||||
if (result?.success) {
|
||||
// 设置活跃的 keygen 会话信息
|
||||
const participants: Array<{ partyId: string; partyIndex: number; name: string }> = result.otherParties?.map((p: { partyId: string; partyIndex: number }, idx: number) => ({
|
||||
partyId: p.partyId,
|
||||
partyIndex: p.partyIndex,
|
||||
// Note: gRPC response uses snake_case field names due to keepCase: true in proto-loader
|
||||
const participants: Array<{ partyId: string; partyIndex: number; name: string }> = result.other_parties?.map((p: { party_id: string; party_index: number }, idx: number) => ({
|
||||
partyId: p.party_id,
|
||||
partyIndex: p.party_index,
|
||||
name: `参与方 ${idx + 1}`, // 暂时使用默认名称
|
||||
})) || [];
|
||||
|
||||
// 添加自己到参与者列表
|
||||
participants.push({
|
||||
partyId: partyId,
|
||||
partyIndex: result.partyIndex,
|
||||
partyIndex: result.party_index,
|
||||
name: '我',
|
||||
});
|
||||
|
||||
|
|
@ -698,13 +707,13 @@ function setupIpcHandlers() {
|
|||
|
||||
activeKeygenSession = {
|
||||
sessionId: sessionId,
|
||||
partyIndex: result.partyIndex,
|
||||
partyIndex: result.party_index,
|
||||
participants: participants,
|
||||
threshold: {
|
||||
t: result.sessionInfo?.thresholdT || 0,
|
||||
n: result.sessionInfo?.thresholdN || 0,
|
||||
t: result.session_info?.threshold_t || 0,
|
||||
n: result.session_info?.threshold_n || 0,
|
||||
},
|
||||
walletName: walletName || result.sessionInfo?.sessionId || 'Shared Wallet',
|
||||
walletName: walletName || result.session_info?.session_id || 'Shared Wallet',
|
||||
encryptionPassword: '', // 不使用加密密码
|
||||
};
|
||||
|
||||
|
|
@ -768,18 +777,19 @@ function setupIpcHandlers() {
|
|||
|
||||
if (joinResult?.success) {
|
||||
// 设置活跃的 keygen 会话信息
|
||||
// Note: gRPC response uses snake_case field names due to keepCase: true in proto-loader
|
||||
const participants: Array<{ partyId: string; partyIndex: number; name: string }> = [];
|
||||
|
||||
// 添加发起方(自己)
|
||||
participants.push({
|
||||
partyId: partyId,
|
||||
partyIndex: joinResult.partyIndex,
|
||||
partyIndex: joinResult.party_index,
|
||||
name: params.initiatorName || '发起者',
|
||||
});
|
||||
|
||||
activeKeygenSession = {
|
||||
sessionId: result.session_id,
|
||||
partyIndex: joinResult.partyIndex,
|
||||
partyIndex: joinResult.party_index,
|
||||
participants: participants,
|
||||
threshold: {
|
||||
t: params.thresholdT,
|
||||
|
|
|
|||
|
|
@ -44,48 +44,65 @@ function loadProtoDefinition(): protoLoader.PackageDefinition {
|
|||
return packageDefinition;
|
||||
}
|
||||
|
||||
// Note: field names must match proto definitions with keepCase: true
|
||||
// Proto uses snake_case: session_id, session_type, threshold_n, threshold_t
|
||||
interface SessionInfo {
|
||||
sessionId: string;
|
||||
sessionType: string;
|
||||
thresholdN: number;
|
||||
thresholdT: number;
|
||||
messageHash?: Buffer;
|
||||
keygenSessionId?: string;
|
||||
session_id: string;
|
||||
session_type: string;
|
||||
threshold_n: number;
|
||||
threshold_t: number;
|
||||
message_hash?: Buffer;
|
||||
keygen_session_id?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
interface PartyInfo {
|
||||
partyId: string;
|
||||
partyIndex: number;
|
||||
party_id: string;
|
||||
party_index: number;
|
||||
}
|
||||
|
||||
interface JoinSessionResponse {
|
||||
success: boolean;
|
||||
sessionInfo?: SessionInfo;
|
||||
partyIndex: number;
|
||||
otherParties: PartyInfo[];
|
||||
session_info?: SessionInfo;
|
||||
party_index: number;
|
||||
other_parties: PartyInfo[];
|
||||
}
|
||||
|
||||
interface MPCMessage {
|
||||
messageId: string;
|
||||
sessionId: string;
|
||||
fromParty: string;
|
||||
isBroadcast: boolean;
|
||||
roundNumber: number;
|
||||
message_id: string;
|
||||
session_id: string;
|
||||
from_party: string;
|
||||
is_broadcast: boolean;
|
||||
round_number: number;
|
||||
payload: Buffer;
|
||||
createdAt: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface SessionEvent {
|
||||
eventId: string;
|
||||
eventType: string;
|
||||
sessionId: string;
|
||||
thresholdN: number;
|
||||
thresholdT: number;
|
||||
selectedParties: string[];
|
||||
joinTokens: Record<string, string>;
|
||||
messageHash?: Buffer;
|
||||
event_id: string;
|
||||
event_type: string;
|
||||
session_id: string;
|
||||
threshold_n: number;
|
||||
threshold_t: number;
|
||||
selected_parties: string[];
|
||||
join_tokens: Record<string, string>;
|
||||
message_hash?: Buffer;
|
||||
}
|
||||
|
||||
// Raw proto response (snake_case)
|
||||
interface RegisteredPartyProto {
|
||||
party_id: string;
|
||||
role: string;
|
||||
online: boolean;
|
||||
registered_at: string;
|
||||
last_seen_at: string;
|
||||
}
|
||||
|
||||
interface GetRegisteredPartiesResponse {
|
||||
parties: RegisteredPartyProto[];
|
||||
}
|
||||
|
||||
// Converted response (camelCase) - used by callers
|
||||
interface RegisteredParty {
|
||||
partyId: string;
|
||||
role: string;
|
||||
|
|
@ -94,10 +111,6 @@ interface RegisteredParty {
|
|||
lastSeenAt: string;
|
||||
}
|
||||
|
||||
interface GetRegisteredPartiesResponse {
|
||||
parties: RegisteredParty[];
|
||||
}
|
||||
|
||||
// 重连配置
|
||||
interface ReconnectConfig {
|
||||
maxRetries: number;
|
||||
|
|
|
|||
Loading…
Reference in New Issue