From 0bd764e1d1f807af8682b14b7227ca49be0ce6d5 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 1 Jan 2026 21:45:18 -0800 Subject: [PATCH] fix(android): ensure session event subscription active before creating sign session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ensureSessionEventSubscriptionActive() call at the start of createSignSession() to prevent race condition where session_started event arrives before subscription is ready. Also add debug logging for _signSessionId and pendingSignInitiatorInfo in event callback to help diagnose sign initiator event matching issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../tssparty/data/repository/TssRepository.kt | 15 +++++++++++++++ .../presentation/viewmodel/MainViewModel.kt | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt index 3a15aac2..46fe98a0 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt @@ -1936,6 +1936,11 @@ class TssRepository @Inject constructor( ): Result { return withContext(Dispatchers.IO) { try { + // CRITICAL: Ensure session event subscription is active BEFORE creating sign session + // This prevents race condition where session_started event arrives before subscription is ready + android.util.Log.d("TssRepository", "[CO-SIGN] Ensuring session event subscription is active before creating sign session") + ensureSessionEventSubscriptionActive() + // Get share record val shareEntity = shareRecordDao.getShareById(shareId) ?: return@withContext Result.failure(Exception("Share not found")) @@ -2145,8 +2150,15 @@ class TssRepository @Inject constructor( ?: return@withContext Result.failure(Exception("Share not found")) android.util.Log.d("TssRepository", "[CO-SIGN] startSigning: participants=${session.participants.size}") + android.util.Log.d("TssRepository", "[CO-SIGN] startSigning: sessionId=$sessionId, partyId=$partyId, partyIndex=${shareEntity.partyIndex}") + android.util.Log.d("TssRepository", "[CO-SIGN] startSigning: thresholdT=${session.thresholdT}, thresholdN=${shareEntity.thresholdN}") + android.util.Log.d("TssRepository", "[CO-SIGN] startSigning: messageHash=${session.messageHash?.take(20)}...") + session.participants.forEachIndexed { idx, p -> + android.util.Log.d("TssRepository", "[CO-SIGN] startSigning: participant[$idx] = ${p.partyId.take(12)}..., index=${p.partyIndex}") + } // Start TSS sign + android.util.Log.d("TssRepository", "[CO-SIGN] Calling tssNativeBridge.startSign...") val startResult = tssNativeBridge.startSign( sessionId = sessionId, partyId = partyId, @@ -2158,10 +2170,13 @@ class TssRepository @Inject constructor( shareData = shareEntity.encryptedShare, password = password ) + android.util.Log.d("TssRepository", "[CO-SIGN] tssNativeBridge.startSign returned: isSuccess=${startResult.isSuccess}") if (startResult.isFailure) { + android.util.Log.e("TssRepository", "[CO-SIGN] startSign FAILED: ${startResult.exceptionOrNull()?.message}") return@withContext Result.failure(startResult.exceptionOrNull()!!) } + android.util.Log.d("TssRepository", "[CO-SIGN] startSign succeeded, starting progress collection...") // Start collecting progress from native bridge startProgressCollection() 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 32cac7ff..458f4bfa 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 @@ -329,6 +329,8 @@ class MainViewModel @Inject constructor( android.util.Log.d("MainViewModel", " _currentSessionId: ${_currentSessionId.value}") android.util.Log.d("MainViewModel", " pendingJoinKeygenInfo?.sessionId: ${pendingJoinKeygenInfo?.sessionId}") android.util.Log.d("MainViewModel", " pendingJoinSignInfo?.sessionId: ${pendingJoinSignInfo?.sessionId}") + android.util.Log.d("MainViewModel", " _signSessionId: ${_signSessionId.value}") + android.util.Log.d("MainViewModel", " pendingSignInitiatorInfo?.sessionId: ${pendingSignInitiatorInfo?.sessionId}") when (event.eventType) { "session_started" -> { @@ -362,9 +364,12 @@ class MainViewModel @Inject constructor( // Check if this is for sign initiator (TransferScreen - 发起签名) val signSessionId = _signSessionId.value + 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") startSignAsInitiator(event.selectedParties) + } else { + android.util.Log.d("MainViewModel", "NOT triggering sign initiator: signSessionId=$signSessionId, pendingSignInitiatorInfo=${pendingSignInitiatorInfo?.sessionId}") } } "party_joined", "participant_joined" -> { @@ -1218,22 +1223,30 @@ class MainViewModel @Inject constructor( * Start the TSS signing process */ private fun startSigningProcess(sessionId: String, shareId: Long, password: String) { + android.util.Log.d("MainViewModel", "[SIGN] startSigningProcess called: sessionId=$sessionId, shareId=$shareId") viewModelScope.launch { + android.util.Log.d("MainViewModel", "[SIGN] Calling repository.startSigning...") val startResult = repository.startSigning(sessionId, shareId, password) + android.util.Log.d("MainViewModel", "[SIGN] repository.startSigning returned: isSuccess=${startResult.isSuccess}") if (startResult.isFailure) { + android.util.Log.e("MainViewModel", "[SIGN] startSigning FAILED: ${startResult.exceptionOrNull()?.message}") _uiState.update { it.copy(error = startResult.exceptionOrNull()?.message) } return@launch } // Wait for signature + android.util.Log.d("MainViewModel", "[SIGN] startSigning succeeded, calling waitForSignature...") val signResult = repository.waitForSignature() + android.util.Log.d("MainViewModel", "[SIGN] waitForSignature returned: isSuccess=${signResult.isSuccess}") signResult.fold( onSuccess = { result -> + android.util.Log.d("MainViewModel", "[SIGN] Signature received: ${result.signature.take(20)}...") _signature.value = result.signature }, onFailure = { e -> + android.util.Log.e("MainViewModel", "[SIGN] waitForSignature FAILED: ${e.message}") _uiState.update { it.copy(error = e.message) } } )