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:
parent
1bf7f57e12
commit
454e596d40
|
|
@ -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": []
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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 签名
|
||||
|
|
|
|||
Loading…
Reference in New Issue