fix(sign): complete sign flow with executeSign call and partyIndex fix

Issues fixed:
1. participants_json now saves partyIndex (not just partyId and name)
2. createSignSession uses saved partyIndex instead of array index
3. Transfer.tsx uses saved partyIndex for executeSign
4. Sign.tsx now calls executeSign after joining session (was missing!)
5. Sign.tsx interfaces updated with proper types (sessionId, participants, parties)

The sign flow was broken because:
- partyIndex was not being saved during keygen completion
- Sign.tsx only joined session but never called executeSign
- This caused "Party not found in participants list" error

🤖 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-30 07:05:07 -08:00
parent 1bf7f57e12
commit 454e596d40
4 changed files with 58 additions and 29 deletions

View File

@ -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 <noreply@anthropic.com>\nEOF\n\\)\")"
],
"deny": [],
"ask": []

View File

@ -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({

View File

@ -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');

View File

@ -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 签名