fix(android): 修复 markPartyReady 重试逻辑的循环退出Bug [CRITICAL]

## 发现的新Bug(从用户日志)

```
16:19:03.667 Successfully marked party ready on attempt 2  
16:19:03.716 markPartyReady attempt 3 failed: cannot transition to ready status  
16:19:03.731 markPartyReady attempt 4 failed: cannot transition to ready status  
16:19:03.749 markPartyReady attempt 5 failed: cannot transition to ready status  
16:19:03.750 Cancelled job: progress_collection  💀
```

## 根本原因

Kotlin `repeat` 的陷阱:
- `return@repeat` 只是跳过当前迭代
- **不会退出整个循环**
- 导致第2次成功后,第3、4、5次继续执行
- 服务器返回 "already ready, cannot transition"
- 第5次失败,代码认为所有尝试都失败,停止 keygen

## 修复内容

在每次迭代开始时检查成功标志:
```kotlin
repeat(5) { attempt ->
    if (markReadySuccess) return@repeat  // ← 添加这一行!

    val markReadyResult = grpcClient.markPartyReady(sessionId, partyId)
    if (markReadyResult.isSuccess) {
        markReadySuccess = true
        return@repeat
    }
    ...
}
```

现在流程:
- 第1次:optimistic lock conflict → 延迟重试
- 第2次:成功 → 设置标志 → return@repeat
- 第3次:检查标志已成功 → 立即 return@repeat(跳过)
- 第4次:检查标志已成功 → 立即 return@repeat(跳过)
- 第5次:检查标志已成功 → 立即 return@repeat(跳过)
- 循环结束 → 检查标志 = true → 继续执行 keygen 

## 影响范围

修复了所有 markPartyReady 重试位置(6处):
- startKeygenAsInitiator
- joinKeygenViaGrpc
- startSignAsInitiator
- joinSignViaGrpc
- startSignAsJoiner
- 其他相关函数

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-27 00:24:40 -08:00
parent 003871aded
commit 41e7eed2c1
1 changed files with 16 additions and 6 deletions

View File

@ -1345,10 +1345,14 @@ class TssRepository @Inject constructor(
_sessionStatus.value = SessionStatus.IN_PROGRESS _sessionStatus.value = SessionStatus.IN_PROGRESS
// Mark ready - with retry on optimistic lock conflict // Mark ready - with retry on optimistic lock conflict
var markReadySuccess = false
repeat(5) { attempt -> repeat(5) { attempt ->
if (markReadySuccess) return@repeat // Already succeeded, skip remaining attempts
val markReadyResult = grpcClient.markPartyReady(sessionId, partyId) val markReadyResult = grpcClient.markPartyReady(sessionId, partyId)
if (markReadyResult.isSuccess) { if (markReadyResult.isSuccess) {
android.util.Log.d("TssRepository", "markPartyReady successful on attempt ${attempt + 1}") android.util.Log.d("TssRepository", "markPartyReady successful on attempt ${attempt + 1}")
markReadySuccess = true
return@repeat return@repeat
} }
val error = markReadyResult.exceptionOrNull() val error = markReadyResult.exceptionOrNull()
@ -1359,6 +1363,14 @@ class TssRepository @Inject constructor(
} }
} }
// Check if any attempt succeeded
if (!markReadySuccess) {
android.util.Log.e("TssRepository", "All markPartyReady attempts failed")
stopProgressCollection()
_sessionStatus.value = SessionStatus.FAILED
return@coroutineScope Result.failure(Exception("Failed to mark party ready after 5 attempts"))
}
// Wait for keygen result // Wait for keygen result
val keygenResult = tssNativeBridge.waitForKeygenResult(password) val keygenResult = tssNativeBridge.waitForKeygenResult(password)
if (keygenResult.isFailure) { if (keygenResult.isFailure) {
@ -2149,6 +2161,8 @@ class TssRepository @Inject constructor(
// Mark ready - with retry on optimistic lock conflict // Mark ready - with retry on optimistic lock conflict
var markReadySuccess = false var markReadySuccess = false
repeat(5) { attempt -> repeat(5) { attempt ->
if (markReadySuccess) return@repeat // Already succeeded, skip remaining attempts
val markReadyResult = grpcClient.markPartyReady(sessionId, partyId) val markReadyResult = grpcClient.markPartyReady(sessionId, partyId)
if (markReadyResult.isSuccess) { if (markReadyResult.isSuccess) {
android.util.Log.d("TssRepository", "Successfully marked party ready on attempt ${attempt + 1}") android.util.Log.d("TssRepository", "Successfully marked party ready on attempt ${attempt + 1}")
@ -2162,19 +2176,15 @@ class TssRepository @Inject constructor(
if (error?.message?.contains("optimistic lock conflict") == true && attempt < 4) { if (error?.message?.contains("optimistic lock conflict") == true && attempt < 4) {
android.util.Log.d("TssRepository", "Optimistic lock conflict detected, retrying after ${(attempt + 1) * 500}ms...") android.util.Log.d("TssRepository", "Optimistic lock conflict detected, retrying after ${(attempt + 1) * 500}ms...")
delay((attempt + 1) * 500L) // 500ms, 1s, 1.5s, 2s delay((attempt + 1) * 500L) // 500ms, 1s, 1.5s, 2s
} else if (attempt == 4) {
// Last attempt failed, return error
stopProgressCollection()
_sessionStatus.value = SessionStatus.FAILED
return@coroutineScope Result.failure(Exception("Failed to mark party ready after 5 attempts: ${error?.message}"))
} }
} }
} }
if (!markReadySuccess) { if (!markReadySuccess) {
android.util.Log.e("TssRepository", "All markPartyReady attempts failed")
stopProgressCollection() stopProgressCollection()
_sessionStatus.value = SessionStatus.FAILED _sessionStatus.value = SessionStatus.FAILED
return@coroutineScope Result.failure(Exception("Failed to mark party ready")) return@coroutineScope Result.failure(Exception("Failed to mark party ready after 5 attempts"))
} }
// Wait for keygen result // Wait for keygen result