fix(service-party-app): 动态计算 persistent_count 并修复 keygen 触发时机

1. 动态计算 server-party 数量: persistent = n - t
   - 2-of-3 -> persistent=1, external=2
   - 3-of-5 -> persistent=2, external=3
   - 4-of-7 -> persistent=3, external=4

2. 修复 5 分钟超时与 24 小时会话的冲突
   - 之前: joinSession 后立即启动 5 分钟轮询,导致超时失败
   - 现在: 等待 all_joined 事件后才启动 5 分钟倒计时
   - 用户可以在 24 小时内慢慢邀请其他参与者加入

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-29 21:28:23 -08:00
parent 66c3cec9a5
commit 1c66b55ea1
1 changed files with 43 additions and 20 deletions

View File

@ -125,8 +125,9 @@ function cleanExpiredEventCache() {
}
}
// 检查并触发 keygen100% 可靠方案:轮询等待所有参与者加入)
// 与 server-party 的 waitForAllParticipants 逻辑保持一致
// 检查并触发 keygen在收到 all_joined 事件后调用)
// 5 分钟超时是指:所有人加入后,启动 keygen 的超时时间
// 注意:此函数应该在收到 all_joined 事件后才调用,而不是 joinSession 后立即调用
async function checkAndTriggerKeygen(sessionId: string) {
console.log('[KEYGEN] checkAndTriggerKeygen called with sessionId:', sessionId);
@ -148,8 +149,8 @@ async function checkAndTriggerKeygen(sessionId: string) {
return;
}
const pollIntervalMs = 2000; // 2秒轮询间隔,与 server-party 保持一致
const maxWaitMs = 5 * 60 * 1000; // 5分钟超时
const pollIntervalMs = 2000; // 2秒轮询间隔
const maxWaitMs = 5 * 60 * 1000; // 5分钟超时(所有人加入后的启动超时)
const startTime = Date.now();
console.log('[KEYGEN] Starting to poll session status...');
@ -254,8 +255,16 @@ async function checkAndTriggerKeygen(sessionId: string) {
}
}
// 超时
debugLog.error('main', `Timeout waiting for all participants to join session ${sessionId}`);
// 超时 - 所有人已加入但 5 分钟内未能启动 keygen
debugLog.error('main', `Timeout: failed to start keygen within 5 minutes after all_joined for session ${sessionId}`);
// 通知前端超时
if (mainWindow) {
mainWindow.webContents.send(`session:events:${sessionId}`, {
type: 'keygen_start_timeout',
error: '启动密钥生成超时,请重试',
});
}
}
// 创建主窗口
@ -677,9 +686,18 @@ async function connectAndRegisterToMessageRouter() {
mainWindow?.webContents.send(`session:events:${event.session_id}`, eventData);
// 根据事件类型处理
if (event.event_type === 'session_started' || event.event_type === 'all_joined') {
// 当会话开始或所有参与方都加入时,检查是否需要触发 keygen
// Convert snake_case to camelCase for handleSessionStart
if (event.event_type === 'all_joined') {
// 收到 all_joined 事件表示所有参与方都已加入
// 此时启动 5 分钟倒计时,在此期间完成 keygen 启动
debugLog.info('main', `Received all_joined event for session ${event.session_id}, starting 5-minute keygen countdown`);
// 使用 setImmediate 确保不阻塞事件处理
setImmediate(() => {
checkAndTriggerKeygen(event.session_id);
});
} else if (event.event_type === 'session_started') {
// session_started 事件表示 keygen 可以开始了(所有人已准备就绪)
// 直接触发 keygen
await handleSessionStart({
eventType: event.event_type,
sessionId: event.session_id,
@ -773,11 +791,10 @@ function setupIpcHandlers() {
(tssHandler as { prepareForKeygen: (sessionId: string, partyId: string) => void }).prepareForKeygen(sessionId, partyId);
}
// 立即检查会话状态并触发 keygen不再延迟
// 使用 setImmediate 确保在当前事件循环结束后立即执行
setImmediate(() => {
checkAndTriggerKeygen(sessionId);
});
// 注意:不再在这里调用 checkAndTriggerKeygen
// 而是等待收到 all_joined 事件后再启动 5 分钟倒计时
// 这样用户可以在 24 小时内慢慢邀请其他参与者加入
debugLog.info('main', `Joined session ${sessionId}, waiting for all_joined event to start keygen`);
}
return { success: true, data: result };
} catch (error) {
@ -794,14 +811,20 @@ function setupIpcHandlers() {
return { success: false, error: '请先连接到消息路由器' };
}
// 动态计算 server-party 数量: persistent = n - t
// 例如: 2-of-3 -> persistent=1, 3-of-5 -> persistent=2, 4-of-7 -> persistent=3
// 这样平台备份方数量等于"允许丢失的份额数",确保用户丢失密钥后仍可恢复
const persistentCount = params.thresholdN - params.thresholdT;
const externalCount = params.thresholdT; // 用户持有的份额数量
const result = await accountClient?.createKeygenSession({
wallet_name: params.walletName,
threshold_t: params.thresholdT,
threshold_n: params.thresholdN,
initiator_party_id: partyId,
initiator_name: params.initiatorName || '发起者',
persistent_count: 0, // 服务端 party 数量,共管钱包模式下为 0
external_count: params.thresholdN, // 所有参与方都是外部 party
persistent_count: persistentCount,
external_count: externalCount,
expires_in_seconds: 86400, // 24 小时
});
@ -853,10 +876,10 @@ function setupIpcHandlers() {
(tssHandler as { prepareForKeygen: (sessionId: string, partyId: string) => void }).prepareForKeygen(result.session_id, partyId);
}
// 立即检查会话状态并触发 keygen不再延迟
setImmediate(() => {
checkAndTriggerKeygen(result.session_id);
});
// 注意:不再在这里调用 checkAndTriggerKeygen
// 而是等待收到 all_joined 事件后再启动 5 分钟倒计时
// 这样用户可以在 24 小时内慢慢邀请其他参与者加入
debugLog.info('main', `Initiator joined session ${result.session_id}, waiting for all_joined event to start keygen`);
} else {
console.warn('Initiator failed to join session');
}