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: '请先连接到消息路由器' };
|
||||
}
|
||||
|
||||
// 动态计算 server-party 数量: persistent = n - t
|
||||
// 例如: 2-of-3 -> persistent=1, 3-of-5 -> persistent=2, 4-of-7 -> persistent=3
|
||||
// 这样平台备份方数量等于"允许丢失的份额数",确保用户丢失密钥后仍可恢复
|
||||
const persistentCount = params.thresholdN - params.thresholdT;
|
||||
const externalCount = params.thresholdT; // 用户持有的份额数量
|
||||
// Support both old format (thresholdT, thresholdN) and new format (requiredSigners, totalParties)
|
||||
// New format uses user-friendly "required signers" which needs conversion to tss-lib threshold
|
||||
let thresholdT: number;
|
||||
let thresholdN: number;
|
||||
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({
|
||||
wallet_name: params.walletName,
|
||||
threshold_t: params.thresholdT,
|
||||
threshold_n: params.thresholdN,
|
||||
threshold_t: thresholdT,
|
||||
threshold_n: thresholdN,
|
||||
initiator_party_id: partyId,
|
||||
initiator_name: params.initiatorName || '发起者',
|
||||
persistent_count: persistentCount,
|
||||
|
|
@ -1271,8 +1309,8 @@ function setupIpcHandlers() {
|
|||
partyIndex: joinResult.party_index,
|
||||
participants: participants,
|
||||
threshold: {
|
||||
t: params.thresholdT,
|
||||
n: params.thresholdN,
|
||||
t: thresholdT,
|
||||
n: thresholdN,
|
||||
},
|
||||
walletName: params.walletName,
|
||||
encryptionPassword: '', // 不使用加密密码
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ export default function Create() {
|
|||
const navigate = useNavigate();
|
||||
|
||||
const [walletName, setWalletName] = useState('');
|
||||
const [thresholdT, setThresholdT] = useState(3);
|
||||
const [thresholdN, setThresholdN] = useState(5);
|
||||
// requiredSigners: 用户选择的"需要几人签名"(用户直观理解)
|
||||
// 例如:用户选择 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 [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -30,15 +33,15 @@ export default function Create() {
|
|||
setError('请输入您的名称');
|
||||
return;
|
||||
}
|
||||
if (thresholdT > thresholdN) {
|
||||
setError('签名阈值不能大于参与方总数');
|
||||
if (requiredSigners > totalParties) {
|
||||
setError('签名人数不能大于参与方总数');
|
||||
return;
|
||||
}
|
||||
if (thresholdT < 1) {
|
||||
setError('签名阈值至少为 1');
|
||||
if (requiredSigners < 2) {
|
||||
setError('签名人数至少为 2');
|
||||
return;
|
||||
}
|
||||
if (thresholdN < 2) {
|
||||
if (totalParties < 2) {
|
||||
setError('参与方总数至少为 2');
|
||||
return;
|
||||
}
|
||||
|
|
@ -48,10 +51,17 @@ export default function Create() {
|
|||
setError(null);
|
||||
|
||||
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({
|
||||
walletName: walletName.trim(),
|
||||
thresholdT,
|
||||
thresholdN,
|
||||
requiredSigners,
|
||||
totalParties,
|
||||
initiatorName: participantName.trim(),
|
||||
});
|
||||
|
||||
|
|
@ -122,23 +132,23 @@ export default function Create() {
|
|||
</div>
|
||||
|
||||
<div className={styles.inputGroup}>
|
||||
<label className={styles.label}>阈值设置 (T-of-N)</label>
|
||||
<label className={styles.label}>签名设置</label>
|
||||
<div className={styles.thresholdConfig}>
|
||||
<div className={styles.thresholdItem}>
|
||||
<span className={styles.thresholdLabel}>签名阈值 (T)</span>
|
||||
<span className={styles.thresholdLabel}>需要签名人数</span>
|
||||
<div className={styles.numberInput}>
|
||||
<button
|
||||
className={styles.numberButton}
|
||||
onClick={() => setThresholdT(Math.max(1, thresholdT - 1))}
|
||||
disabled={thresholdT <= 1}
|
||||
onClick={() => setRequiredSigners(Math.max(2, requiredSigners - 1))}
|
||||
disabled={requiredSigners <= 2}
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<span className={styles.numberValue}>{thresholdT}</span>
|
||||
<span className={styles.numberValue}>{requiredSigners}</span>
|
||||
<button
|
||||
className={styles.numberButton}
|
||||
onClick={() => setThresholdT(Math.min(thresholdN, thresholdT + 1))}
|
||||
disabled={thresholdT >= thresholdN}
|
||||
onClick={() => setRequiredSigners(Math.min(totalParties, requiredSigners + 1))}
|
||||
disabled={requiredSigners >= totalParties}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
|
|
@ -146,24 +156,24 @@ export default function Create() {
|
|||
</div>
|
||||
<div className={styles.thresholdDivider}>of</div>
|
||||
<div className={styles.thresholdItem}>
|
||||
<span className={styles.thresholdLabel}>参与方总数 (N)</span>
|
||||
<span className={styles.thresholdLabel}>参与方总数</span>
|
||||
<div className={styles.numberInput}>
|
||||
<button
|
||||
className={styles.numberButton}
|
||||
onClick={() => {
|
||||
const newN = Math.max(2, thresholdN - 1);
|
||||
setThresholdN(newN);
|
||||
if (thresholdT > newN) setThresholdT(newN);
|
||||
const newN = Math.max(3, totalParties - 1);
|
||||
setTotalParties(newN);
|
||||
if (requiredSigners > newN) setRequiredSigners(newN);
|
||||
}}
|
||||
disabled={thresholdN <= 2}
|
||||
disabled={totalParties <= 3}
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<span className={styles.numberValue}>{thresholdN}</span>
|
||||
<span className={styles.numberValue}>{totalParties}</span>
|
||||
<button
|
||||
className={styles.numberButton}
|
||||
onClick={() => setThresholdN(thresholdN + 1)}
|
||||
disabled={thresholdN >= 10}
|
||||
onClick={() => setTotalParties(totalParties + 1)}
|
||||
disabled={totalParties >= 10}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
|
|
@ -171,7 +181,7 @@ export default function Create() {
|
|||
</div>
|
||||
</div>
|
||||
<p className={styles.hint}>
|
||||
需要 {thresholdT} 个参与方共同签名才能执行交易 (其中 2 个由平台托管用于备份)
|
||||
需要 {requiredSigners} 个参与方共同签名才能执行交易 (其中 {totalParties - requiredSigners} 个由平台托管用于备份)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -112,8 +112,12 @@ interface Settings {
|
|||
|
||||
interface CreateSessionParams {
|
||||
walletName: string;
|
||||
thresholdT: number;
|
||||
thresholdN: number;
|
||||
// New format: user-friendly parameters (preferred)
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue