From ebbc483b358d85e769a351293800cf49d0cca003 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 31 Dec 2025 01:12:07 -0800 Subject: [PATCH] fix(co-sign): use keygen session participants with correct party_index for signing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fetch keygen session status from backend to get accurate party_index - Filter out co-managed-party-* (server persistent parties) from signing - Only temporary/external user parties participate in signing - For 3-of-5 wallet: 3 user parties sign, 2 co-managed parties are backup only 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .claude/settings.local.json | 24 ++++++++++++- .../service-party-app/electron/main.ts | 35 +++++++++++++++---- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 1da5e338..1e6cea49 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -483,7 +483,29 @@ "Bash(git cherry-pick:*)", "Bash(git stash:*)", "Bash(docker compose build:*)", - "Bash(git log:*)" + "Bash(git log:*)", + "Bash(git tag -a v0.3.0-pre-transfer -m \"$\\(cat <<''EOF''\nPre-transfer development checkpoint\n\nCompleted features:\n- Co-keygen: Multi-party key generation with TSS \\(GG20\\)\n- Service-party-app: Electron desktop application\n - Create shared wallet \\(keygen initiator\\)\n - Join wallet creation \\(keygen participant\\)\n - Wallet management \\(list, export, delete\\)\n - Kava network switch \\(mainnet/testnet\\)\n - EVM address derivation and balance display\n\nNot yet implemented:\n- Co-sign: Multi-party transaction signing\n- Transfer functionality\n\nThis tag marks the stable state before transfer feature development.\nEOF\n\\)\")", + "Bash(tasklist:*)", + "Bash(docker port:*)", + "Bash(docker rm:*)", + "Bash(netstat:*)", + "Bash(start \"\" \"C:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\mpc-system\\\\services\\\\service-party-app\\\\release\\\\win-unpacked\\\\榴莲皇后绿积分共管账户服务.exe\")", + "Bash(go test:*)", + "Bash(./tss-party.exe sign:*)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian log --oneline --all)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian diff --name-only HEAD~5..HEAD)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian log --all --oneline --grep=\"co-sign\\\\|co-managed\\\\|CoManaged\")", + "Bash(git -C /c/Users/dong/Desktop/rwadurian show e038f178 --stat)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian show e114723a --stat)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian show c457d158 -- backend/mpc-system/services/account/adapters/input/http/account_handler.go)", + "Bash(git -C /c/Users/dong/Desktop/rwadurian log --oneline -- backend/mpc-system/services/account/adapters/input/http/account_handler.go)", + "Bash(git rev-list:*)", + "Bash(dir /d \"C:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\")", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(service-party-app\\): implement co-sign multi-party signing\n\nAdd complete co-sign functionality for multi-party transaction signing:\n\nFrontend \\(React\\):\n- CoSignCreate.tsx: Create signing session with share selection\n- CoSignJoin.tsx: Join signing session via invite code\n- CoSignSession.tsx: Monitor signing progress and results\n- Add routes in App.tsx for new pages\n\nBackend \\(Electron\\):\n- main.ts: Add IPC handlers for co-sign operations\n- tss-handler.ts: Add participateSign\\(\\) for TSS signing\n- preload.ts: Expose cosign API to renderer\n- account-client.ts: Add sign session API types\n\nTSS Party \\(Go\\):\n- main.go: Implement ''sign'' command for GG20 signing protocol\n- integration_test.go: Add comprehensive tests for signing flow\n\nInfrastructure:\n- docker-compose.windows.yml: Expose gRPC port 50051\n\nThis is a pure additive change that does not affect existing\npersistent role keygen/sign functionality.\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(service-party-app\\): add transfer functionality with co-sign integration\n\nAdd complete KAVA transfer feature to the wallet home page:\n\nFrontend \\(React\\):\n- Home.tsx: Add transfer modal with address/amount input, transaction\n confirmation, and co-sign session initiation\n- Home.module.css: Transfer modal styles \\(form, confirm, error states\\)\n- CoSignSession.tsx: Add transaction broadcast after signing completion,\n with block explorer link\n\nUtils:\n- transaction.ts: EIP-1559 transaction building, RLP encoding, Keccak-256\n hashing, nonce/gas fetching, transaction broadcast via JSON-RPC\n\nFlow: Wallet -> Transfer Modal -> Prepare TX -> Confirm -> Co-Sign ->\n Sign Session -> Broadcast -> Block Explorer\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(powershell -Command:*)", + "Bash(powershell -Command \"\n$content = Get-Content ''main.ts'' -Raw\n\n# 修改 threshold 部分\n$old1 = @''\n threshold: {\n t: activeCoSignSession?.threshold?.t || 0,\n n: activeCoSignSession?.threshold?.n || 0,\n },\n''@\n\n$new1 = @''\n threshold: {\n // 优先使用 API 返回的阈值,回退到 activeCoSignSession\n t: result?.threshold_t || activeCoSignSession?.threshold?.t || 0,\n n: result?.threshold_n || activeCoSignSession?.threshold?.n || 0,\n },\n''@\n\n$content = $content.Replace\\($old1, $new1\\)\n\n# 修改 participants 部分\n$old2 = ''participants: result?.parties?.map\\(\\(p: { party_id: string; party_index: number }, idx: number\\) => \\({''\n$new2 = ''participants: \\(\\(result as { participants?: Array<{ party_id: string; party_index: number; status: string }> }\\)?.participants || []\\).map\\(\\(p, idx\\) => \\({''\n\n$content = $content.Replace\\($old2, $new2\\)\n\n# 修改 status 部分\n$old3 = \"\" status: ''ready'',\"\"\n$new3 = \"\" status: p.status || ''waiting'',\"\"\n\n$content = $content.Replace\\($old3, $new3\\)\n\n# 修改结尾部分\n$old4 = '' }\\)\\) || [],''\n$new4 = '' }\\)\\),''\n\n$content = $content.Replace\\($old4, $new4\\)\n\nSet-Content ''main.ts'' -Value $content -NoNewline\nWrite-Output ''Done''\n\")", + "Bash(node fix_main.js:*)" ], "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 27e2477c..4db29935 100644 --- a/backend/mpc-system/services/service-party-app/electron/main.ts +++ b/backend/mpc-system/services/service-party-app/electron/main.ts @@ -1566,19 +1566,40 @@ function setupIpcHandlers() { return { success: false, error: 'Share 不存在或密码错误' }; } - // 解析参与者信息 - const participants = JSON.parse(share.participants_json || '[]'); - const parties = participants.map((p: { partyId: string }, index: number) => ({ - party_id: p.partyId, - party_index: index, - })); + // 从后端获取 keygen 会话的参与者信息(包含正确的 party_index) + const keygenStatus = await accountClient?.getSessionStatus(share.session_id); + if (!keygenStatus?.participants || keygenStatus.participants.length === 0) { + return { success: false, error: '无法获取 keygen 会话的参与者信息' }; + } + + // 过滤掉 co-managed-party-*(服务器持久方),只保留 temporary/external 用户方 + // 只有用户方持有签名私钥份额,co-managed-party 是备份/恢复用的 + const signingParties = keygenStatus.participants + .filter(p => !p.party_id.startsWith('co-managed-party-')) + .map(p => ({ + party_id: p.party_id, + party_index: p.party_index, + })); + + console.log('[CO-SIGN] Signing parties (excluding co-managed-party):', { + total_keygen_participants: keygenStatus.participants.length, + signing_parties_count: signingParties.length, + signing_parties: signingParties.map(p => ({ id: p.party_id, index: p.party_index })), + }); + + if (signingParties.length < share.threshold_t) { + return { + success: false, + error: `签名参与方不足: 需要 ${share.threshold_t} 个,但只有 ${signingParties.length} 个用户方` + }; + } // 创建签名会话 const result = await accountClient?.createSignSession({ keygen_session_id: share.session_id, wallet_name: share.wallet_name, message_hash: params.messageHash, - parties: parties, + parties: signingParties, threshold_t: share.threshold_t, initiator_name: params.initiatorName || '发起者', });