fix(tss): use BuildLocalSaveDataSubset for threshold signing with party subsets
When signing with fewer parties than keygen (e.g., 2-of-3 signing with only 2 parties), the TSS-lib requires filtered save data containing only the participating parties. Without this fix, signing fails with "U doesn't equal T" error because: - Keygen creates save data for all N parties (e.g., 3 parties with indices 0, 1, 2) - Sign uses only T parties (e.g., 2 parties with indices 1, 2) - TSS-lib internal index validation fails due to mismatch Changes: - pkg/tss/signing.go: Use len(sortedPartyIDs) for partyCount and call BuildLocalSaveDataSubset - tss-party/main.go: Add BuildLocalSaveDataSubset call for Electron app - tss-wasm/main.go: Add BuildLocalSaveDataSubset call for WASM builds This fix is backward compatible - when all parties participate, the subset equals the original data. 🤖 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
24ff1409d0
commit
7ab28dced0
|
|
@ -132,10 +132,13 @@ func NewSigningSession(
|
|||
keygenIndexToSortedIndex, selfParty.PartyID)
|
||||
|
||||
// Create peer context and parameters
|
||||
// IMPORTANT: Use TotalParties from keygen, not len(sortedPartyIDs) which is current signers
|
||||
// For 2-of-3: threshold=2, TotalParties=3, but only 2 parties might participate in signing
|
||||
// IMPORTANT: Use len(sortedPartyIDs) as partyCount - this is the number of CURRENT signers
|
||||
// For 2-of-3 threshold signing with only 2 parties participating:
|
||||
// - partyCount = 2 (current signers)
|
||||
// - threshold = 2 (minimum required from keygen)
|
||||
// The BuildLocalSaveDataSubset call in Start() will filter the save data to match
|
||||
peerCtx := tss.NewPeerContext(sortedPartyIDs)
|
||||
params := tss.NewParameters(tss.S256(), peerCtx, selfTSSID, config.TotalParties, config.Threshold)
|
||||
params := tss.NewParameters(tss.S256(), peerCtx, selfTSSID, len(sortedPartyIDs), config.Threshold)
|
||||
|
||||
// Convert message hash to big.Int
|
||||
msgHash := new(big.Int).SetBytes(messageHash)
|
||||
|
|
@ -167,8 +170,17 @@ func (s *SigningSession) Start(ctx context.Context) (*SigningResult, error) {
|
|||
s.started = true
|
||||
s.mu.Unlock()
|
||||
|
||||
// Create local party for signing
|
||||
s.localParty = signing.NewLocalParty(s.messageHash, s.params, *s.saveData, s.outCh, s.endCh)
|
||||
// CRITICAL: Build a subset of the save data for the current signing parties
|
||||
// When signing with fewer parties than keygen (e.g., 2-of-3 signing with only 2 parties),
|
||||
// we must filter the save data to only include the participating parties' data.
|
||||
// This ensures TSS-lib's internal indices match the actual signers.
|
||||
subsetSaveData := keygen.BuildLocalSaveDataSubset(*s.saveData, s.tssPartyIDs)
|
||||
|
||||
fmt.Printf("[TSS-SIGN] Built save data subset for %d signing parties (original keygen had %d parties) party_id=%s\n",
|
||||
len(s.tssPartyIDs), len(s.saveData.Ks), s.selfParty.PartyID)
|
||||
|
||||
// Create local party for signing with the SUBSET save data
|
||||
s.localParty = signing.NewLocalParty(s.messageHash, s.params, subsetSaveData, s.outCh, s.endCh)
|
||||
|
||||
// Start the local party
|
||||
go func() {
|
||||
|
|
|
|||
|
|
@ -607,13 +607,19 @@ func executeSign(
|
|||
peerCtx := tss.NewPeerContext(sortedPartyIDs)
|
||||
params := tss.NewParameters(tss.S256(), peerCtx, selfTSSID, len(sortedPartyIDs), thresholdT-1)
|
||||
|
||||
// CRITICAL: Build a subset of the keygen save data for the current signing parties
|
||||
// This is required when signing with a subset of the original keygen participants.
|
||||
// BuildLocalSaveDataSubset filters the Ks, BigXj, NTildej, H1j, H2j, and PaillierPKs
|
||||
// arrays to only include data for the participating signers.
|
||||
subsetKeygenData := keygen.BuildLocalSaveDataSubset(keygenData, sortedPartyIDs)
|
||||
|
||||
// Create channels
|
||||
outCh := make(chan tss.Message, thresholdT*10)
|
||||
endCh := make(chan *common.SignatureData, 1)
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
// Create local party for signing
|
||||
localParty := signing.NewLocalParty(msgBigInt, params, keygenData, outCh, endCh)
|
||||
// Create local party for signing with the SUBSET keygen data
|
||||
localParty := signing.NewLocalParty(msgBigInt, params, subsetKeygenData, outCh, endCh)
|
||||
|
||||
// Build party index map for incoming messages
|
||||
partyIndexMap := make(map[int]*tss.PartyID)
|
||||
|
|
|
|||
|
|
@ -298,8 +298,12 @@ func startSigning(this js.Value, args []js.Value) interface{} {
|
|||
// Create message hash as big.Int
|
||||
msgHashBig := new(big.Int).SetBytes(messageHash)
|
||||
|
||||
// Create local signing party
|
||||
session.LocalParty = signing.NewLocalParty(msgHashBig, params, saveData, session.OutCh, session.EndChSign)
|
||||
// CRITICAL: Build a subset of the keygen save data for the current signing parties
|
||||
// This is required when signing with a subset of the original keygen participants.
|
||||
subsetSaveData := keygen.BuildLocalSaveDataSubset(saveData, sortedPartyIDs)
|
||||
|
||||
// Create local signing party with the SUBSET save data
|
||||
session.LocalParty = signing.NewLocalParty(msgHashBig, params, subsetSaveData, session.OutCh, session.EndChSign)
|
||||
|
||||
// Store session
|
||||
sessionMutex.Lock()
|
||||
|
|
|
|||
Loading…
Reference in New Issue