fix(session-coordinator): 支持 co_managed_keygen 动态参与者加入
问题: 通过邀请码加入共管钱包会话时报 "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 <noreply@anthropic.com>
This commit is contained in:
parent
af08f0f9c6
commit
a5ab2e8350
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue