fix(service-party-app): checkAndTriggerKeygen 改为轮询等待
问题: - 原来 checkAndTriggerKeygen 只检查一次 - 如果首次检查时会话状态还不是 in_progress,就直接返回 - 导致 external party 永远不触发 keygen 修复: - 改为与 server-party 的 waitForAllParticipants 一致的轮询逻辑 - 2 秒轮询间隔,最多等待 5 分钟 - 持续检查直到所有参与者加入且状态正确 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dfead071ab
commit
bd6537a2cb
|
|
@ -123,7 +123,8 @@ function cleanExpiredEventCache() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查并触发 keygen(100% 可靠方案:不依赖事件,直接检查状态)
|
// 检查并触发 keygen(100% 可靠方案:轮询等待所有参与者加入)
|
||||||
|
// 与 server-party 的 waitForAllParticipants 逻辑保持一致
|
||||||
async function checkAndTriggerKeygen(sessionId: string) {
|
async function checkAndTriggerKeygen(sessionId: string) {
|
||||||
if (!activeKeygenSession || activeKeygenSession.sessionId !== sessionId) {
|
if (!activeKeygenSession || activeKeygenSession.sessionId !== sessionId) {
|
||||||
console.log('No matching active keygen session for', sessionId);
|
console.log('No matching active keygen session for', sessionId);
|
||||||
|
|
@ -136,80 +137,98 @@ async function checkAndTriggerKeygen(sessionId: string) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 Account 服务获取最新会话状态
|
const pollIntervalMs = 2000; // 2秒轮询间隔,与 server-party 保持一致
|
||||||
try {
|
const maxWaitMs = 5 * 60 * 1000; // 5分钟超时
|
||||||
const status = await accountClient?.getSessionStatus(sessionId);
|
const startTime = Date.now();
|
||||||
if (!status) {
|
|
||||||
debugLog.warn('main', 'Failed to get session status');
|
debugLog.info('main', `Starting to poll session status for ${sessionId}`);
|
||||||
|
|
||||||
|
while (Date.now() - startTime < maxWaitMs) {
|
||||||
|
// 检查会话是否仍然有效
|
||||||
|
if (!activeKeygenSession || activeKeygenSession.sessionId !== sessionId) {
|
||||||
|
debugLog.warn('main', 'Active keygen session changed, stopping poll');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog.info('main', `Session ${sessionId} status: ${status.status} (${status.completed_parties}/${status.total_parties} parties)`);
|
// 如果 TSS 已经在运行(可能被其他事件触发),停止轮询
|
||||||
|
if (tssHandler?.getIsRunning()) {
|
||||||
// 对于 co_managed_keygen,必须等待所有 N 个参与者加入后才能开始
|
debugLog.info('main', 'TSS started running, stopping poll');
|
||||||
// 这与 server-party 的 waitForAllParticipants 逻辑一致
|
|
||||||
const expectedN = status.total_parties || activeKeygenSession.threshold.n;
|
|
||||||
const currentParticipants = status.participants?.length || 0;
|
|
||||||
|
|
||||||
if (currentParticipants < expectedN) {
|
|
||||||
debugLog.debug('main', `Waiting for all participants: ${currentParticipants}/${expectedN}`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果会话已经是 in_progress 或 all_joined,或者参与方已满,触发 keygen
|
try {
|
||||||
// 使用 status 字段判断是否可以开始
|
const status = await accountClient?.getSessionStatus(sessionId);
|
||||||
if (status.status === 'in_progress' ||
|
if (!status) {
|
||||||
status.status === 'all_joined' ||
|
debugLog.warn('main', 'Failed to get session status, will retry');
|
||||||
status.status === 'waiting_for_keygen') {
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
||||||
|
continue;
|
||||||
debugLog.info('main', `All ${expectedN} participants joined, triggering keygen...`);
|
|
||||||
|
|
||||||
// 使用后端返回的 participants 信息(包含正确的 party_index)
|
|
||||||
// 这解决了 co_managed_keygen 中 server-party 和 service-party-app 混合的问题
|
|
||||||
if (status.participants && status.participants.length > 0) {
|
|
||||||
const myPartyId = grpcClient?.getPartyId();
|
|
||||||
const updatedParticipants: Array<{ partyId: string; partyIndex: number; name: string }> = [];
|
|
||||||
|
|
||||||
for (const p of status.participants) {
|
|
||||||
// 查找已有的参与者名称
|
|
||||||
const existing = activeKeygenSession.participants.find(ep => ep.partyId === p.party_id);
|
|
||||||
updatedParticipants.push({
|
|
||||||
partyId: p.party_id,
|
|
||||||
partyIndex: p.party_index,
|
|
||||||
name: existing?.name || (p.party_id === myPartyId ? '我' : `参与方 ${p.party_index + 1}`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新参与者列表
|
|
||||||
activeKeygenSession.participants = updatedParticipants;
|
|
||||||
|
|
||||||
// 更新自己的 partyIndex
|
|
||||||
const myInfo = updatedParticipants.find(p => p.partyId === myPartyId);
|
|
||||||
if (myInfo) {
|
|
||||||
activeKeygenSession.partyIndex = myInfo.partyIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
debugLog.info('main', `Updated participants from server: ${JSON.stringify(updatedParticipants.map(p => ({
|
|
||||||
partyId: p.partyId.substring(0, 8) + '...',
|
|
||||||
partyIndex: p.partyIndex,
|
|
||||||
})))}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedParties = activeKeygenSession.participants.map(p => p.partyId);
|
const expectedN = status.total_parties || activeKeygenSession.threshold.n;
|
||||||
|
const currentParticipants = status.participants?.length || 0;
|
||||||
|
|
||||||
await handleSessionStart({
|
debugLog.debug('main', `Session ${sessionId} status: ${status.status}, participants: ${currentParticipants}/${expectedN}`);
|
||||||
eventType: 'session_started',
|
|
||||||
sessionId: sessionId,
|
// 检查是否满足启动条件
|
||||||
thresholdN: activeKeygenSession.threshold.n,
|
const hasAllParticipants = currentParticipants >= expectedN;
|
||||||
thresholdT: activeKeygenSession.threshold.t,
|
const statusReady = status.status === 'in_progress' ||
|
||||||
selectedParties: selectedParties,
|
status.status === 'all_joined' ||
|
||||||
});
|
status.status === 'waiting_for_keygen';
|
||||||
} else {
|
|
||||||
debugLog.debug('main', `Session not ready yet, status: ${status.status}`);
|
if (hasAllParticipants && statusReady) {
|
||||||
|
debugLog.info('main', `All ${expectedN} participants joined (status: ${status.status}), triggering keygen...`);
|
||||||
|
|
||||||
|
// 使用后端返回的 participants 信息(包含正确的 party_index)
|
||||||
|
if (status.participants && status.participants.length > 0) {
|
||||||
|
const myPartyId = grpcClient?.getPartyId();
|
||||||
|
const updatedParticipants: Array<{ partyId: string; partyIndex: number; name: string }> = [];
|
||||||
|
|
||||||
|
for (const p of status.participants) {
|
||||||
|
const existing = activeKeygenSession.participants.find(ep => ep.partyId === p.party_id);
|
||||||
|
updatedParticipants.push({
|
||||||
|
partyId: p.party_id,
|
||||||
|
partyIndex: p.party_index,
|
||||||
|
name: existing?.name || (p.party_id === myPartyId ? '我' : `参与方 ${p.party_index + 1}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
activeKeygenSession.participants = updatedParticipants;
|
||||||
|
|
||||||
|
const myInfo = updatedParticipants.find(p => p.partyId === myPartyId);
|
||||||
|
if (myInfo) {
|
||||||
|
activeKeygenSession.partyIndex = myInfo.partyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog.info('main', `Updated participants from server: ${JSON.stringify(updatedParticipants.map(p => ({
|
||||||
|
partyId: p.partyId.substring(0, 8) + '...',
|
||||||
|
partyIndex: p.partyIndex,
|
||||||
|
})))}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedParties = activeKeygenSession.participants.map(p => p.partyId);
|
||||||
|
|
||||||
|
await handleSessionStart({
|
||||||
|
eventType: 'session_started',
|
||||||
|
sessionId: sessionId,
|
||||||
|
thresholdN: activeKeygenSession.threshold.n,
|
||||||
|
thresholdT: activeKeygenSession.threshold.t,
|
||||||
|
selectedParties: selectedParties,
|
||||||
|
});
|
||||||
|
|
||||||
|
return; // 成功触发,退出循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待下次轮询
|
||||||
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
debugLog.error('main', `Failed to check session status: ${error}, will retry`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
debugLog.error('main', `Failed to check session status: ${error}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 超时
|
||||||
|
debugLog.error('main', `Timeout waiting for all participants to join session ${sessionId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建主窗口
|
// 创建主窗口
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue