From 99fa003b1295b236ea8b0c07b1bf69e2ba3e5544 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 31 Dec 2025 06:03:13 -0800 Subject: [PATCH] fix(co-sign): fix session start logic to check all registered participants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CanStart(): Check if all registered participants have joined, not based on T/N - AddParticipant(): Keep N as max limit (API handles T vs T+1 validation) - AllPartiesReady(): Check all registered participants, not based on T/N - This approach works for both co-managed (T parties) and persistent (T+1 parties) signing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../domain/entities/mpc_session.go | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/mpc-system/services/session-coordinator/domain/entities/mpc_session.go b/backend/mpc-system/services/session-coordinator/domain/entities/mpc_session.go index 9b0b06c7..b5f23c67 100644 --- a/backend/mpc-system/services/session-coordinator/domain/entities/mpc_session.go +++ b/backend/mpc-system/services/session-coordinator/domain/entities/mpc_session.go @@ -98,6 +98,9 @@ func NewMPCSession( // AddParticipant adds a participant to the session func (s *MPCSession) AddParticipant(p *Participant) error { + // For sign sessions, the max participant check is handled at the API level + // (co-managed uses T, persistent uses T+1) + // Here we just prevent exceeding N which is the absolute maximum if len(s.Participants) >= s.Threshold.N() { return ErrSessionFull } @@ -140,26 +143,22 @@ func (s *MPCSession) UpdateParticipantStatus(partyID value_objects.PartyID, stat // CanStart checks if all participants have joined and the session can start func (s *MPCSession) CanStart() bool { - // Determine required participant count based on session type - // For keygen: all N parties must participate - // For sign: only T parties participate - requiredCount := s.Threshold.N() - if s.SessionType == SessionTypeSign { - requiredCount = s.Threshold.T() - } - - if len(s.Participants) != requiredCount { + // Session can start when all registered participants have joined + // The number of participants was determined at session creation time: + // - For keygen: N parties are registered + // - For sign: T or T+1 parties are registered (depending on the signing flow) + // We don't need to check against T or N here - just verify all registered parties have joined + if len(s.Participants) == 0 { return false } - readyCount := 0 for _, p := range s.Participants { - // Accept participants in either joined or ready status - if p.IsJoined() || p.IsReady() { - readyCount++ + // All participants must be in joined or ready status + if !p.IsJoined() && !p.IsReady() { + return false } } - return readyCount == requiredCount + return true } // Start transitions the session to in_progress @@ -265,7 +264,9 @@ func (s *MPCSession) MarkPartyReady(partyID string) error { // AllPartiesReady checks if all participants are ready func (s *MPCSession) AllPartiesReady() bool { - if len(s.Participants) != s.Threshold.N() { + // Check that all registered participants are ready or completed + // The participant count was determined at session creation time + if len(s.Participants) == 0 { return false } for _, p := range s.Participants {