fix(co-sign): convert user-friendly threshold to tss-lib format
- Rename thresholdT/thresholdN to requiredSigners/totalParties in Create.tsx - Add parameter conversion in main.ts: threshold_t = requiredSigners - 1 - In tss-lib, threshold t means t+1 parties needed to sign - For 3-of-5: requiredSigners=3 → threshold_t=2 (t+1=3 signers) - externalCount = requiredSigners (user parties) - persistentCount = totalParties - requiredSigners (server parties) - Backward compatible with legacy thresholdT/thresholdN format BREAKING: Existing co-managed wallets need re-keygen with new params 🤖 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
ca69ebc839
commit
e81757ad83
|
|
@ -1226,16 +1226,54 @@ function setupIpcHandlers() {
|
||||||
return { success: false, error: '请先连接到消息路由器' };
|
return { success: false, error: '请先连接到消息路由器' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态计算 server-party 数量: persistent = n - t
|
// Support both old format (thresholdT, thresholdN) and new format (requiredSigners, totalParties)
|
||||||
// 例如: 2-of-3 -> persistent=1, 3-of-5 -> persistent=2, 4-of-7 -> persistent=3
|
// New format uses user-friendly "required signers" which needs conversion to tss-lib threshold
|
||||||
// 这样平台备份方数量等于"允许丢失的份额数",确保用户丢失密钥后仍可恢复
|
let thresholdT: number;
|
||||||
const persistentCount = params.thresholdN - params.thresholdT;
|
let thresholdN: number;
|
||||||
const externalCount = params.thresholdT; // 用户持有的份额数量
|
let externalCount: number;
|
||||||
|
let persistentCount: number;
|
||||||
|
|
||||||
|
if (params.requiredSigners !== undefined && params.totalParties !== undefined) {
|
||||||
|
// New format: Convert user-friendly parameters to tss-lib parameters
|
||||||
|
// In tss-lib, threshold_t means you need (t+1) parties to sign
|
||||||
|
// So if user wants 3 signers, threshold_t = 3 - 1 = 2
|
||||||
|
const requiredSigners = params.requiredSigners;
|
||||||
|
const totalParties = params.totalParties;
|
||||||
|
|
||||||
|
thresholdT = requiredSigners - 1; // tss-lib threshold
|
||||||
|
thresholdN = totalParties;
|
||||||
|
externalCount = requiredSigners; // user parties = required signers
|
||||||
|
persistentCount = totalParties - requiredSigners; // server backup parties
|
||||||
|
|
||||||
|
console.log('[CreateSession] Converting user-friendly params to tss-lib:', {
|
||||||
|
requiredSigners,
|
||||||
|
totalParties,
|
||||||
|
'tss-lib threshold_t': thresholdT,
|
||||||
|
'tss-lib threshold_n': thresholdN,
|
||||||
|
externalCount,
|
||||||
|
persistentCount,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Old format: Use thresholdT and thresholdN directly (backward compatible)
|
||||||
|
thresholdT = params.thresholdT;
|
||||||
|
thresholdN = params.thresholdN;
|
||||||
|
// 动态计算 server-party 数量: persistent = n - t
|
||||||
|
// 例如: 2-of-3 -> persistent=1, 3-of-5 -> persistent=2, 4-of-7 -> persistent=3
|
||||||
|
persistentCount = thresholdN - thresholdT;
|
||||||
|
externalCount = thresholdT;
|
||||||
|
|
||||||
|
console.log('[CreateSession] Using legacy params (backward compatible):', {
|
||||||
|
thresholdT,
|
||||||
|
thresholdN,
|
||||||
|
externalCount,
|
||||||
|
persistentCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const result = await accountClient?.createKeygenSession({
|
const result = await accountClient?.createKeygenSession({
|
||||||
wallet_name: params.walletName,
|
wallet_name: params.walletName,
|
||||||
threshold_t: params.thresholdT,
|
threshold_t: thresholdT,
|
||||||
threshold_n: params.thresholdN,
|
threshold_n: thresholdN,
|
||||||
initiator_party_id: partyId,
|
initiator_party_id: partyId,
|
||||||
initiator_name: params.initiatorName || '发起者',
|
initiator_name: params.initiatorName || '发起者',
|
||||||
persistent_count: persistentCount,
|
persistent_count: persistentCount,
|
||||||
|
|
@ -1271,8 +1309,8 @@ function setupIpcHandlers() {
|
||||||
partyIndex: joinResult.party_index,
|
partyIndex: joinResult.party_index,
|
||||||
participants: participants,
|
participants: participants,
|
||||||
threshold: {
|
threshold: {
|
||||||
t: params.thresholdT,
|
t: thresholdT,
|
||||||
n: params.thresholdN,
|
n: thresholdN,
|
||||||
},
|
},
|
||||||
walletName: params.walletName,
|
walletName: params.walletName,
|
||||||
encryptionPassword: '', // 不使用加密密码
|
encryptionPassword: '', // 不使用加密密码
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,11 @@ export default function Create() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [walletName, setWalletName] = useState('');
|
const [walletName, setWalletName] = useState('');
|
||||||
const [thresholdT, setThresholdT] = useState(3);
|
// requiredSigners: 用户选择的"需要几人签名"(用户直观理解)
|
||||||
const [thresholdN, setThresholdN] = useState(5);
|
// 例如:用户选择 3 表示需要 3 人签名
|
||||||
|
// 在 tss-lib 中,threshold_t = requiredSigners - 1(因为 tss-lib 需要 t+1 人签名)
|
||||||
|
const [requiredSigners, setRequiredSigners] = useState(3);
|
||||||
|
const [totalParties, setTotalParties] = useState(5);
|
||||||
const [participantName, setParticipantName] = useState('');
|
const [participantName, setParticipantName] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -30,15 +33,15 @@ export default function Create() {
|
||||||
setError('请输入您的名称');
|
setError('请输入您的名称');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thresholdT > thresholdN) {
|
if (requiredSigners > totalParties) {
|
||||||
setError('签名阈值不能大于参与方总数');
|
setError('签名人数不能大于参与方总数');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thresholdT < 1) {
|
if (requiredSigners < 2) {
|
||||||
setError('签名阈值至少为 1');
|
setError('签名人数至少为 2');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thresholdN < 2) {
|
if (totalParties < 2) {
|
||||||
setError('参与方总数至少为 2');
|
setError('参与方总数至少为 2');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -48,10 +51,17 @@ export default function Create() {
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Pass user-friendly parameters directly
|
||||||
|
// main.ts will convert to tss-lib parameters
|
||||||
|
console.log('[Create] Sending parameters:', {
|
||||||
|
requiredSigners,
|
||||||
|
totalParties,
|
||||||
|
});
|
||||||
|
|
||||||
const createResult = await window.electronAPI.grpc.createSession({
|
const createResult = await window.electronAPI.grpc.createSession({
|
||||||
walletName: walletName.trim(),
|
walletName: walletName.trim(),
|
||||||
thresholdT,
|
requiredSigners,
|
||||||
thresholdN,
|
totalParties,
|
||||||
initiatorName: participantName.trim(),
|
initiatorName: participantName.trim(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -122,23 +132,23 @@ export default function Create() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.inputGroup}>
|
<div className={styles.inputGroup}>
|
||||||
<label className={styles.label}>阈值设置 (T-of-N)</label>
|
<label className={styles.label}>签名设置</label>
|
||||||
<div className={styles.thresholdConfig}>
|
<div className={styles.thresholdConfig}>
|
||||||
<div className={styles.thresholdItem}>
|
<div className={styles.thresholdItem}>
|
||||||
<span className={styles.thresholdLabel}>签名阈值 (T)</span>
|
<span className={styles.thresholdLabel}>需要签名人数</span>
|
||||||
<div className={styles.numberInput}>
|
<div className={styles.numberInput}>
|
||||||
<button
|
<button
|
||||||
className={styles.numberButton}
|
className={styles.numberButton}
|
||||||
onClick={() => setThresholdT(Math.max(1, thresholdT - 1))}
|
onClick={() => setRequiredSigners(Math.max(2, requiredSigners - 1))}
|
||||||
disabled={thresholdT <= 1}
|
disabled={requiredSigners <= 2}
|
||||||
>
|
>
|
||||||
-
|
-
|
||||||
</button>
|
</button>
|
||||||
<span className={styles.numberValue}>{thresholdT}</span>
|
<span className={styles.numberValue}>{requiredSigners}</span>
|
||||||
<button
|
<button
|
||||||
className={styles.numberButton}
|
className={styles.numberButton}
|
||||||
onClick={() => setThresholdT(Math.min(thresholdN, thresholdT + 1))}
|
onClick={() => setRequiredSigners(Math.min(totalParties, requiredSigners + 1))}
|
||||||
disabled={thresholdT >= thresholdN}
|
disabled={requiredSigners >= totalParties}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -146,24 +156,24 @@ export default function Create() {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.thresholdDivider}>of</div>
|
<div className={styles.thresholdDivider}>of</div>
|
||||||
<div className={styles.thresholdItem}>
|
<div className={styles.thresholdItem}>
|
||||||
<span className={styles.thresholdLabel}>参与方总数 (N)</span>
|
<span className={styles.thresholdLabel}>参与方总数</span>
|
||||||
<div className={styles.numberInput}>
|
<div className={styles.numberInput}>
|
||||||
<button
|
<button
|
||||||
className={styles.numberButton}
|
className={styles.numberButton}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newN = Math.max(2, thresholdN - 1);
|
const newN = Math.max(3, totalParties - 1);
|
||||||
setThresholdN(newN);
|
setTotalParties(newN);
|
||||||
if (thresholdT > newN) setThresholdT(newN);
|
if (requiredSigners > newN) setRequiredSigners(newN);
|
||||||
}}
|
}}
|
||||||
disabled={thresholdN <= 2}
|
disabled={totalParties <= 3}
|
||||||
>
|
>
|
||||||
-
|
-
|
||||||
</button>
|
</button>
|
||||||
<span className={styles.numberValue}>{thresholdN}</span>
|
<span className={styles.numberValue}>{totalParties}</span>
|
||||||
<button
|
<button
|
||||||
className={styles.numberButton}
|
className={styles.numberButton}
|
||||||
onClick={() => setThresholdN(thresholdN + 1)}
|
onClick={() => setTotalParties(totalParties + 1)}
|
||||||
disabled={thresholdN >= 10}
|
disabled={totalParties >= 10}
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -171,7 +181,7 @@ export default function Create() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.hint}>
|
<p className={styles.hint}>
|
||||||
需要 {thresholdT} 个参与方共同签名才能执行交易 (其中 2 个由平台托管用于备份)
|
需要 {requiredSigners} 个参与方共同签名才能执行交易 (其中 {totalParties - requiredSigners} 个由平台托管用于备份)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,12 @@ interface Settings {
|
||||||
|
|
||||||
interface CreateSessionParams {
|
interface CreateSessionParams {
|
||||||
walletName: string;
|
walletName: string;
|
||||||
thresholdT: number;
|
// New format: user-friendly parameters (preferred)
|
||||||
thresholdN: number;
|
requiredSigners?: number; // Number of signers needed (e.g., 3 for "3-of-5")
|
||||||
|
totalParties?: number; // Total parties (e.g., 5 for "3-of-5")
|
||||||
|
// Legacy format: tss-lib parameters (backward compatible)
|
||||||
|
thresholdT?: number; // tss-lib threshold (requiredSigners - 1)
|
||||||
|
thresholdN?: number; // Same as totalParties
|
||||||
initiatorName: string;
|
initiatorName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue