From a5ab2e83505d90b6e5f5a2d56f700a5cc43477e5 Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 29 Dec 2025 04:25:11 -0800 Subject: [PATCH] =?UTF-8?q?fix(session-coordinator):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20co=5Fmanaged=5Fkeygen=20=E5=8A=A8=E6=80=81=E5=8F=82=E4=B8=8E?= =?UTF-8?q?=E8=80=85=E5=8A=A0=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: 通过邀请码加入共管钱包会话时报 "party not invited" 错误 原因: 外部参与者不在 party pool 中,CreateSession 时无法预先选择 修复: - join_session.go: 对于 co_managed_keygen + wildcard token,允许动态添加参与者 - create_session.go: 新增 selectPartiesByCompositionForCoManaged,跳过 TemporaryCount 选择 - report_completion.go: 使用 IsKeygen() 方法,co_managed_keygen 完成后也创建账户记录 注意: 所有修改仅对 co_managed_keygen 类型生效,不影响现有 keygen/sign 流程 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../application/use_cases/create_session.go | 66 ++++++++++++++++++- .../application/use_cases/join_session.go | 53 +++++++++++++-- .../use_cases/report_completion.go | 4 +- 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go b/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go index cadb8bf4..b11d5c7f 100644 --- a/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go +++ b/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go @@ -160,8 +160,14 @@ func (uc *CreateSessionUseCase) Execute( // Check if party composition is specified if req.PartyComposition != nil { - // Select parties based on composition requirements - selectedParties, err = uc.selectPartiesByComposition(req.PartyComposition) + // For co_managed_keygen, TemporaryCount represents external participants + // who will join dynamically via invite code - don't select from pool + if sessionType == entities.SessionTypeCoManagedKeygen { + selectedParties, err = uc.selectPartiesByCompositionForCoManaged(req.PartyComposition) + } else { + // Select parties based on composition requirements + selectedParties, err = uc.selectPartiesByComposition(req.PartyComposition) + } if err != nil { logger.Warn("failed to select parties by composition, falling back to simple selection", zap.Error(err)) @@ -359,6 +365,62 @@ func (uc *CreateSessionUseCase) Execute( }, nil } +// selectPartiesByCompositionForCoManaged selects parties for co_managed_keygen sessions +// For co_managed_keygen, TemporaryCount represents external participants who will join +// dynamically via invite code - we don't select them from pool, only select persistent parties +func (uc *CreateSessionUseCase) selectPartiesByCompositionForCoManaged(composition *input.PartyComposition) ([]output.PartyEndpoint, error) { + if uc.partyPool == nil { + return nil, fmt.Errorf("party pool not configured") + } + + var allSelected []output.PartyEndpoint + + // Select persistent parties (server-side parties) + if composition.PersistentCount > 0 { + persistent, err := uc.partyPool.SelectPartiesWithFilter(output.PartySelectionFilter{ + Count: composition.PersistentCount, + Role: output.PartyRolePersistent, + }) + if err != nil { + return nil, fmt.Errorf("failed to select persistent parties: %w", err) + } + allSelected = append(allSelected, persistent...) + } + + // Select delegate parties if any + if composition.DelegateCount > 0 { + delegate, err := uc.partyPool.SelectPartiesWithFilter(output.PartySelectionFilter{ + Count: composition.DelegateCount, + Role: output.PartyRoleDelegate, + }) + if err != nil { + return nil, fmt.Errorf("failed to select delegate parties: %w", err) + } + allSelected = append(allSelected, delegate...) + } + + // NOTE: TemporaryCount is intentionally NOT selected from pool for co_managed_keygen + // External participants will join dynamically via invite code with wildcard token + if composition.TemporaryCount > 0 { + logger.Info("co_managed_keygen: TemporaryCount represents external participants, not selecting from pool", + zap.Int("temporary_count", composition.TemporaryCount), + zap.Int("persistent_selected", len(allSelected))) + } + + // Apply custom filters if provided + for _, filter := range composition.CustomFilters { + customParties, err := uc.partyPool.SelectPartiesWithFilter(filter) + if err != nil { + return nil, fmt.Errorf("failed to select parties with custom filter: %w", err) + } + allSelected = append(allSelected, customParties...) + } + + // For co_managed_keygen, it's OK to have no selected parties if all are external + // The session will use wildcard tokens for all participants + return allSelected, nil +} + // selectPartiesByComposition selects parties based on composition requirements func (uc *CreateSessionUseCase) selectPartiesByComposition(composition *input.PartyComposition) ([]output.PartyEndpoint, error) { if uc.partyPool == nil { diff --git a/backend/mpc-system/services/session-coordinator/application/use_cases/join_session.go b/backend/mpc-system/services/session-coordinator/application/use_cases/join_session.go index f32cd21c..2de4e416 100644 --- a/backend/mpc-system/services/session-coordinator/application/use_cases/join_session.go +++ b/backend/mpc-system/services/session-coordinator/application/use_cases/join_session.go @@ -81,14 +81,55 @@ func (uc *JoinSessionUseCase) Execute( } // 5. Get participant (must already exist from CreateSession) + // For co_managed_keygen with wildcard token, allow dynamic participant addition participant, err := session.GetParticipant(partyID) if err != nil { - // Participant doesn't exist - this party was not invited to this session - logger.Warn("party not found in session participants", - zap.String("session_id", session.ID.String()), - zap.String("party_id", inputData.PartyID), - zap.Int("existing_participant_count", len(session.Participants))) - return nil, entities.ErrPartyNotInvited + // Check if this is a co_managed_keygen session with wildcard token + // Wildcard tokens allow any party to join dynamically + if session.SessionType == entities.SessionTypeCoManagedKeygen && claims.PartyID == "*" { + // Dynamic participant addition for co_managed_keygen + if len(session.Participants) >= session.Threshold.N() { + logger.Warn("session is full, cannot add more participants", + zap.String("session_id", session.ID.String()), + zap.String("party_id", inputData.PartyID), + zap.Int("current_participants", len(session.Participants)), + zap.Int("threshold_n", session.Threshold.N())) + return nil, entities.ErrSessionFull + } + + // Create new participant with the next available index + newIndex := len(session.Participants) + participant, err = entities.NewParticipant(partyID, newIndex, inputData.DeviceInfo) + if err != nil { + logger.Error("failed to create new participant", + zap.String("session_id", session.ID.String()), + zap.String("party_id", inputData.PartyID), + zap.Error(err)) + return nil, err + } + + // Add participant to session + if err := session.AddParticipant(participant); err != nil { + logger.Error("failed to add participant to session", + zap.String("session_id", session.ID.String()), + zap.String("party_id", inputData.PartyID), + zap.Error(err)) + return nil, err + } + + logger.Info("dynamically added participant to co_managed_keygen session", + zap.String("session_id", session.ID.String()), + zap.String("party_id", inputData.PartyID), + zap.Int("party_index", newIndex), + zap.Int("total_participants", len(session.Participants))) + } else { + // Participant doesn't exist - this party was not invited to this session + logger.Warn("party not found in session participants", + zap.String("session_id", session.ID.String()), + zap.String("party_id", inputData.PartyID), + zap.Int("existing_participant_count", len(session.Participants))) + return nil, entities.ErrPartyNotInvited + } } logger.Debug("participant found in session", diff --git a/backend/mpc-system/services/session-coordinator/application/use_cases/report_completion.go b/backend/mpc-system/services/session-coordinator/application/use_cases/report_completion.go index c01f53b3..f845736d 100644 --- a/backend/mpc-system/services/session-coordinator/application/use_cases/report_completion.go +++ b/backend/mpc-system/services/session-coordinator/application/use_cases/report_completion.go @@ -191,8 +191,8 @@ func (uc *ReportCompletionUseCase) executeWithRetry( zap.Error(err)) } - // For keygen sessions, automatically create account record - if session.SessionType == entities.SessionTypeKeygen && uc.accountService != nil { + // For keygen sessions (including co_managed_keygen), automatically create account record + if session.SessionType.IsKeygen() && uc.accountService != nil { uc.createAccountFromKeygen(ctx, session) } }