diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/MainActivity.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/MainActivity.kt index 2418939d..c4a4887b 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/MainActivity.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/MainActivity.kt @@ -77,6 +77,7 @@ fun TssPartyApp( val currentSessionId by viewModel.currentSessionId.collectAsState() val sessionParticipants by viewModel.sessionParticipants.collectAsState() val currentRound by viewModel.currentRound.collectAsState() + val totalRounds by viewModel.totalRounds.collectAsState() val publicKey by viewModel.publicKey.collectAsState() val hasEnteredSession by viewModel.hasEnteredSession.collectAsState() @@ -349,7 +350,7 @@ fun TssPartyApp( sessionStatus = sessionStatus, participants = signParticipants, currentRound = signCurrentRound, - totalRounds = 9, + totalRounds = if (totalRounds > 0) totalRounds else 9, // Default to sign rounds preparedTx = preparedTx, signSessionId = signSessionId, inviteCode = signInviteCode, @@ -435,7 +436,7 @@ fun TssPartyApp( hasEnteredSession = hasEnteredSession, participants = sessionParticipants, currentRound = currentRound, - totalRounds = 9, + totalRounds = if (totalRounds > 0) totalRounds else 4, // Default to keygen rounds publicKey = publicKey, countdownSeconds = uiState.countdownSeconds, onCreateSession = { name, t, n, participantName -> @@ -486,7 +487,7 @@ fun TssPartyApp( sessionInfo = screenSessionInfo, participants = joinKeygenParticipants, currentRound = joinKeygenRound, - totalRounds = 9, + totalRounds = if (totalRounds > 0) totalRounds else 4, // Default to keygen rounds publicKey = joinKeygenPublicKey, countdownSeconds = uiState.countdownSeconds, onValidateInviteCode = { inviteCode -> @@ -542,7 +543,7 @@ fun TssPartyApp( signSessionInfo = screenSignSessionInfo, participants = coSignParticipants, currentRound = coSignRound, - totalRounds = 9, + totalRounds = if (totalRounds > 0) totalRounds else 9, // Default to sign rounds signature = coSignSignature, countdownSeconds = uiState.countdownSeconds, onValidateInviteCode = { inviteCode -> diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt index cdd7fbf3..f10b032c 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt @@ -218,6 +218,9 @@ class MainViewModel @Inject constructor( private val _currentRound = MutableStateFlow(0) val currentRound: StateFlow = _currentRound.asStateFlow() + private val _totalRounds = MutableStateFlow(0) + val totalRounds: StateFlow = _totalRounds.asStateFlow() + private val _publicKey = MutableStateFlow(null) val publicKey: StateFlow = _publicKey.asStateFlow() @@ -299,8 +302,10 @@ class MainViewModel @Inject constructor( } // Setup progress callback for real-time round updates from native TSS bridge - repository.setProgressCallback { round, totalRounds -> - android.util.Log.d("MainViewModel", "Progress update: $round / $totalRounds") + repository.setProgressCallback { round, totalRoundsFromGo -> + android.util.Log.d("MainViewModel", "Progress update: $round / $totalRoundsFromGo") + // Update totalRounds from Go library (keygen=4, sign=9) + _totalRounds.value = totalRoundsFromGo // Update the appropriate round state based on which session type is active when { // Initiator keygen (CreateWallet) @@ -338,6 +343,8 @@ class MainViewModel @Inject constructor( val currentSessionId = _currentSessionId.value if (currentSessionId != null && event.sessionId == currentSessionId) { android.util.Log.d("MainViewModel", "Session started event for keygen initiator, triggering keygen") + // Initialize participant list with all N parties (keygen requires all parties) + _sessionParticipants.value = (1..event.thresholdN).map { "参与方 $it" } viewModelScope.launch { startKeygenAsInitiator( sessionId = currentSessionId, @@ -359,6 +366,8 @@ class MainViewModel @Inject constructor( val joinSignInfo = pendingJoinSignInfo if (joinSignInfo != null && event.sessionId == joinSignInfo.sessionId) { android.util.Log.d("MainViewModel", "Session started event for sign joiner, triggering sign") + // Initialize participant list with T parties (sign requires T parties) + _coSignParticipants.value = (1..event.thresholdT).map { "参与方 $it" } startSignAsJoiner() } @@ -367,6 +376,8 @@ class MainViewModel @Inject constructor( android.util.Log.d("MainViewModel", "Checking for sign initiator: signSessionId=$signSessionId, eventSessionId=${event.sessionId}") if (signSessionId != null && event.sessionId == signSessionId) { android.util.Log.d("MainViewModel", "Session started event for sign initiator, triggering sign") + // Initialize participant list with T parties (sign requires T parties) + _signParticipants.value = (1..event.thresholdT).map { "参与方 $it" } startSignAsInitiator(event.selectedParties) } else { android.util.Log.d("MainViewModel", "NOT triggering sign initiator: signSessionId=$signSessionId, pendingSignInitiatorInfo=${pendingSignInitiatorInfo?.sessionId}") @@ -515,6 +526,7 @@ class MainViewModel @Inject constructor( _currentSessionId.value = null _sessionParticipants.value = emptyList() _currentRound.value = 0 + _totalRounds.value = 0 _publicKey.value = null _createdInviteCode.value = null _hasEnteredSession.value = false @@ -659,7 +671,11 @@ class MainViewModel @Inject constructor( viewModelScope.launch { _uiState.update { it.copy(isLoading = true, error = null) } - android.util.Log.d("MainViewModel", "Starting keygen as joiner: sessionId=${joinInfo.sessionId}, partyIndex=${joinInfo.partyIndex}") + // Initialize participant list with all N parties (keygen requires all parties) + // This ensures UI shows correct participant count even if we missed some participant_joined events + _joinKeygenParticipants.value = (1..joinInfo.thresholdN).map { "参与方 $it" } + + android.util.Log.d("MainViewModel", "Starting keygen as joiner: sessionId=${joinInfo.sessionId}, partyIndex=${joinInfo.partyIndex}, thresholdN=${joinInfo.thresholdN}") val result = repository.executeKeygenAsJoiner( sessionId = joinInfo.sessionId, diff --git a/backend/mpc-system/services/service-party-android/tsslib/tsslib.go b/backend/mpc-system/services/service-party-android/tsslib/tsslib.go index abdc731c..ba462dfb 100644 --- a/backend/mpc-system/services/service-party-android/tsslib/tsslib.go +++ b/backend/mpc-system/services/service-party-android/tsslib/tsslib.go @@ -393,6 +393,17 @@ func SendIncomingMessage(fromPartyIndex int, isBroadcast bool, payloadBase64 str return fmt.Errorf("failed to parse message: %w", err) } + // Extract round from incoming message and update progress + // This ensures progress updates on both sending and receiving messages + totalRounds := 4 // GG20 keygen has 4 rounds + if !session.isKeygen { + totalRounds = 9 // GG20 signing has 9 rounds + } + currentRound := extractRoundFromMessageType(parsedMsg.Type()) + if currentRound > 0 { + session.callback.OnProgress(currentRound, totalRounds) + } + go func() { _, err := session.localParty.Update(parsedMsg) if err != nil {