fix(co-keygen): convert threshold at storage time to match tss-lib convention

User says "3-of-5" meaning 3 signers needed.
tss-lib threshold t means t+1 signers required.
Now we store t-1 at session creation (like persistent-only does).

Changes:
- co_managed_handler.go: tssThresholdT = req.ThresholdT - 1
- tss-party/main.go: remove -1 from sign (now consistent with keygen)

BREAKING: Existing co-managed wallets must be regenerated.
ROLLBACK: Revert this commit if signing still fails.

🤖 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-31 10:14:21 -08:00
parent b876c9dfba
commit 4dcc7d37ba
2 changed files with 16 additions and 7 deletions

View File

@ -123,10 +123,19 @@ func (h *CoManagedHTTPHandler) CreateSession(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// IMPORTANT: Convert user-friendly threshold to tss-lib format
// User says "3-of-5" meaning 3 signers needed
// tss-lib threshold t means t+1 signers needed
// So we store t-1: user's 3 becomes 2, tss-lib needs 2+1=3 signers
// This matches persistent-only behavior (see account.go:144)
// ROLLBACK: If this breaks, revert this change and the tss-party/main.go changes
tssThresholdT := req.ThresholdT - 1
logger.Info("Creating co-managed keygen session",
zap.String("wallet_name", req.WalletName),
zap.Int("threshold_n", req.ThresholdN),
zap.Int("threshold_t", req.ThresholdT),
zap.Int("threshold_t_user", req.ThresholdT),
zap.Int("threshold_t_tss", tssThresholdT),
zap.Int("persistent_count", persistentCount),
zap.Int("external_count", externalCount),
zap.String("initiator_party_id", req.InitiatorPartyID))
@ -136,7 +145,7 @@ func (h *CoManagedHTTPHandler) CreateSession(c *gin.Context) {
req.WalletName,
inviteCode,
int32(req.ThresholdN),
int32(req.ThresholdT),
int32(tssThresholdT),
int32(persistentCount),
int32(externalCount),
3600, // 1 hour expiry for session creation phase

View File

@ -597,15 +597,15 @@ func executeSign(
// Create peer context and parameters
// For signing, the first parameter to NewParameters must be the number of parties
// actually participating in the signing (len(sortedPartyIDs)), NOT the original keygen N.
// The threshold parameter is the minimum signers minus 1 (tss-lib convention: t means t+1 required)
// The threshold parameter must match what was used during keygen.
//
// For co-managed signing with 3-of-5:
// - thresholdN = 5 (original keygen parties) - NOT used here
// - thresholdT = 3 (signers needed)
// - thresholdN = 5 (original keygen parties) - NOT used here for party count
// - thresholdT = 3 (threshold from keygen, meaning 3+1=4 parties needed... but we have 3 signers)
// - len(sortedPartyIDs) = 3 (actual signing participants)
// - threshold param = thresholdT - 1 = 2 (tss-lib needs 2+1=3 signers)
// - threshold param = thresholdT (must match keygen threshold)
peerCtx := tss.NewPeerContext(sortedPartyIDs)
params := tss.NewParameters(tss.S256(), peerCtx, selfTSSID, len(sortedPartyIDs), thresholdT-1)
params := tss.NewParameters(tss.S256(), peerCtx, selfTSSID, len(sortedPartyIDs), thresholdT)
// Create channels
outCh := make(chan tss.Message, thresholdT*10)