diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 85e61de3..225f5629 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -564,7 +564,8 @@ "Bash(go run:*)", "Bash(pkill:*)", "Bash(timeout 120 go run:*)", - "Bash(timeout 60 go run:*)" + "Bash(timeout 60 go run:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(transfer\\): pass sessionId and participants to executeSign\n\n- Add signSessionId state to store the session ID from createSignSession\n- Add ParticipantInfo interface and participants field to ShareInfo\n- Build participants list from share data for executeSign call\n- Fix \"Party not found in participants list\" error\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/backend/mpc-system/services/service-party-app/electron/main.ts b/backend/mpc-system/services/service-party-app/electron/main.ts index 5ea93e96..c0f9e2ce 100644 --- a/backend/mpc-system/services/service-party-app/electron/main.ts +++ b/backend/mpc-system/services/service-party-app/electron/main.ts @@ -564,9 +564,10 @@ async function handleKeygenComplete(result: KeygenResult) { try { // 1. 保存 share 到本地数据库 const publicKeyHex = result.publicKey.toString('hex'); - // 转换 participants 格式:从 { partyId, partyIndex, name } 到 { partyId, name } + // 转换 participants 格式:保存 partyId, partyIndex, name const participantsForSave = activeKeygenSession.participants.map(p => ({ partyId: p.partyId, + partyIndex: p.partyIndex, name: p.name, })); const saved = database.saveShare({ @@ -1220,9 +1221,9 @@ function setupIpcHandlers() { // 解析 participants_json 获取参与方列表 const participants = JSON.parse(share.participants_json || '[]'); - const parties = participants.map((p: { partyId: string }, index: number) => ({ + const parties = participants.map((p: { partyId: string; partyIndex?: number }, index: number) => ({ party_id: p.partyId, - party_index: index, + party_index: p.partyIndex ?? index, // 优先使用保存的 partyIndex,否则使用数组索引 })); const result = await accountClient?.createSignSession({ diff --git a/backend/mpc-system/services/service-party-app/src/pages/Sign.tsx b/backend/mpc-system/services/service-party-app/src/pages/Sign.tsx index abcf5e3d..98720f35 100644 --- a/backend/mpc-system/services/service-party-app/src/pages/Sign.tsx +++ b/backend/mpc-system/services/service-party-app/src/pages/Sign.tsx @@ -2,25 +2,35 @@ import { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import styles from './Sign.module.css'; +interface ParticipantInfo { + partyId: string; + partyIndex?: number; + name?: string; +} + interface ShareItem { id: string; walletName: string; publicKey: string; + sessionId: string; threshold: { t: number; n: number }; createdAt: string; - metadata: { + participants?: ParticipantInfo[]; + metadata?: { participants: Array<{ partyId: string; name: string }>; }; } interface SigningSession { sessionId: string; + keygenSessionId?: string; walletName: string; messageHash: string; threshold: { t: number; n: number }; currentParticipants: number; initiator: string; joinToken?: string; + parties?: Array<{ party_id: string; party_index: number }>; } export default function Sign() { @@ -117,6 +127,10 @@ export default function Sign() { setError('请选择要使用的钱包份额'); return; } + if (!signingSession) { + setError('签名会话信息丢失'); + return; + } setIsLoading(true); setError(null); setStep('signing'); @@ -132,36 +146,48 @@ export default function Sign() { } // 加入签名会话 - const result = await window.electronAPI.grpc.joinSigningSession({ - sessionId: signingSession!.sessionId, + const joinResult = await window.electronAPI.grpc.joinSigningSession({ + sessionId: signingSession.sessionId, shareId: selectedShare.id, password: password, - joinToken: signingSession!.joinToken, + joinToken: signingSession.joinToken, }); - if (result.success) { - // 订阅签名进度 - window.electronAPI.grpc.subscribeSigningProgress( - signingSession!.sessionId, - (event) => { - if (event.type === 'progress') { - setProgress({ current: event.current || 0, total: event.total || 0 }); - } else if (event.type === 'completed') { - setSignature(event.signature || ''); - setStep('completed'); - setIsLoading(false); - } else if (event.type === 'failed') { - setError(event.error || '签名失败'); - setStep('failed'); - setIsLoading(false); - } - } - ); - } else { - setError(result.error || '加入签名会话失败'); + if (!joinResult.success) { + setError(joinResult.error || '加入签名会话失败'); setStep('join'); setIsLoading(false); + return; } + + // 构建 participants 列表 + // 优先使用签名会话返回的 parties(包含正确的 party_index) + const participants = signingSession.parties?.map(p => ({ + partyId: p.party_id, + partyIndex: p.party_index, + })) || (selectedShare.participants || []).map((p, index) => ({ + partyId: p.partyId, + partyIndex: p.partyIndex ?? index, + })); + + // 执行 TSS 签名 + const signResult = await window.electronAPI.grpc.executeSign({ + sessionId: signingSession.sessionId, + shareId: selectedShare.id, + password: password, + messageHash: signingSession.messageHash, + participants, + threshold: signingSession.threshold, + }); + + if (signResult.success && signResult.signature) { + setSignature(signResult.signature); + setStep('completed'); + } else { + setError(signResult.error || '签名失败'); + setStep('failed'); + } + setIsLoading(false); } catch (err) { setError('签名过程出错: ' + (err as Error).message); setStep('failed'); diff --git a/backend/mpc-system/services/service-party-app/src/pages/Transfer.tsx b/backend/mpc-system/services/service-party-app/src/pages/Transfer.tsx index dbc2a164..3c8568fd 100644 --- a/backend/mpc-system/services/service-party-app/src/pages/Transfer.tsx +++ b/backend/mpc-system/services/service-party-app/src/pages/Transfer.tsx @@ -18,6 +18,7 @@ import { deriveEvmAddress, formatAddress } from '../utils/address'; interface ParticipantInfo { partyId: string; + partyIndex?: number; name?: string; } @@ -233,7 +234,7 @@ export default function Transfer() { // 构建 participants 列表 const participants = (share.participants || []).map((p, index) => ({ partyId: p.partyId, - partyIndex: index, + partyIndex: p.partyIndex ?? index, // 优先使用保存的 partyIndex })); // 执行 TSS 签名