fix(service-party-android): 修复导入钱包签名时 'party not registered' 错误
导入的钱包份额携带原始 keygen partyId,与设备自身 partyId 不同。 签名时用原始 partyId 订阅 session events,但该 ID 未在 message-router 注册,导致服务端返回 FAILED_PRECONDITION。 修复:签名前将导入份额的 partyId 也注册到 message-router(带 3 次重试), 注册失败则中断签名流程并提示用户;断线重连时自动恢复双 partyId 注册; 签名结束后清理额外注册。 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fe6c1b3fce
commit
1f434f32fb
|
|
@ -97,6 +97,11 @@ class GrpcClient @Inject constructor() {
|
||||||
private var registeredPartyId: String? = null
|
private var registeredPartyId: String? = null
|
||||||
private var registeredPartyRole: String? = null
|
private var registeredPartyRole: String? = null
|
||||||
|
|
||||||
|
// Additional signing party registration (for imported/restored shares)
|
||||||
|
// When signing with a restored wallet, the signing partyId differs from the device partyId
|
||||||
|
// and must also be registered with the message-router
|
||||||
|
private var registeredSigningPartyId: String? = null
|
||||||
|
|
||||||
// Heartbeat state
|
// Heartbeat state
|
||||||
private var heartbeatJob: Job? = null
|
private var heartbeatJob: Job? = null
|
||||||
private val heartbeatFailCount = AtomicInteger(0)
|
private val heartbeatFailCount = AtomicInteger(0)
|
||||||
|
|
@ -460,6 +465,17 @@ class GrpcClient @Inject constructor() {
|
||||||
Log.e(TAG, "Re-registration failed: ${e.message}")
|
Log.e(TAG, "Re-registration failed: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also re-register the signing partyId if active (for imported/restored shares)
|
||||||
|
val signingId = registeredSigningPartyId
|
||||||
|
if (signingId != null && signingId != partyId) {
|
||||||
|
Log.d(TAG, "Re-registering signing party: $signingId")
|
||||||
|
try {
|
||||||
|
registerPartyInternal(signingId, "temporary", "1.0.0")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Re-registration of signing party failed: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -651,6 +667,29 @@ class GrpcClient @Inject constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an additional signing partyId with the message-router.
|
||||||
|
* Used when signing with imported/restored shares where the signing partyId
|
||||||
|
* differs from the device's own partyId. Does not overwrite the device registration.
|
||||||
|
*/
|
||||||
|
suspend fun registerSigningParty(signingPartyId: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
||||||
|
if (signingPartyId == registeredPartyId) {
|
||||||
|
// Same as device partyId, already registered
|
||||||
|
return@withContext Result.success(true)
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Registering signing partyId: $signingPartyId (device partyId: $registeredPartyId)")
|
||||||
|
registeredSigningPartyId = signingPartyId
|
||||||
|
registerPartyInternal(signingPartyId, "temporary", "1.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the additional signing party registration.
|
||||||
|
* Called when signing completes or fails.
|
||||||
|
*/
|
||||||
|
fun clearSigningPartyRegistration() {
|
||||||
|
registeredSigningPartyId = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join a session
|
* Join a session
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -388,12 +388,34 @@ class TssRepository @Inject constructor(
|
||||||
* CRITICAL: For signing with restored wallets, this should be the original
|
* CRITICAL: For signing with restored wallets, this should be the original
|
||||||
* partyId from keygen (shareEntity.partyId).
|
* partyId from keygen (shareEntity.partyId).
|
||||||
*/
|
*/
|
||||||
private fun ensureSessionEventSubscriptionActive(signingPartyId: String? = null) {
|
private suspend fun ensureSessionEventSubscriptionActive(signingPartyId: String? = null) {
|
||||||
// Check if the session event job is still active
|
// Check if the session event job is still active
|
||||||
val isActive = sessionEventJob?.isActive == true
|
val isActive = sessionEventJob?.isActive == true
|
||||||
val effectivePartyId = signingPartyId ?: currentSessionEventPartyId ?: partyId
|
val effectivePartyId = signingPartyId ?: currentSessionEventPartyId ?: partyId
|
||||||
android.util.Log.d("TssRepository", "Checking session event subscription: isActive=$isActive, effectivePartyId=$effectivePartyId")
|
android.util.Log.d("TssRepository", "Checking session event subscription: isActive=$isActive, effectivePartyId=$effectivePartyId")
|
||||||
|
|
||||||
|
// If subscribing with a different partyId (imported/restored share),
|
||||||
|
// register it with the message-router first to avoid "party not registered" error.
|
||||||
|
// Retry up to 3 times to handle transient network issues.
|
||||||
|
if (effectivePartyId != partyId) {
|
||||||
|
var registered = false
|
||||||
|
for (attempt in 1..3) {
|
||||||
|
android.util.Log.d("TssRepository", "Registering signing partyId before subscribing: $effectivePartyId (attempt $attempt/3)")
|
||||||
|
val regResult = grpcClient.registerSigningParty(effectivePartyId)
|
||||||
|
if (regResult.isSuccess) {
|
||||||
|
registered = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
android.util.Log.e("TssRepository", "Failed to register signing partyId (attempt $attempt/3): ${regResult.exceptionOrNull()?.message}")
|
||||||
|
if (attempt < 3) {
|
||||||
|
kotlinx.coroutines.delay(1000L * attempt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!registered) {
|
||||||
|
throw Exception("无法注册签名方 $effectivePartyId,请检查网络连接后重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
android.util.Log.w("TssRepository", "Session event subscription is not active, restarting...")
|
android.util.Log.w("TssRepository", "Session event subscription is not active, restarting...")
|
||||||
startSessionEventSubscription(signingPartyId)
|
startSessionEventSubscription(signingPartyId)
|
||||||
|
|
@ -1427,6 +1449,7 @@ class TssRepository @Inject constructor(
|
||||||
pendingSessionId = null // Clear pending session ID on completion
|
pendingSessionId = null // Clear pending session ID on completion
|
||||||
messageCollectionJob?.cancel()
|
messageCollectionJob?.cancel()
|
||||||
currentSigningPartyId = null // Clear after signing completes
|
currentSigningPartyId = null // Clear after signing completes
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
|
|
||||||
android.util.Log.d("TssRepository", "Sign as joiner completed: signature=${result.signature.take(20)}...")
|
android.util.Log.d("TssRepository", "Sign as joiner completed: signature=${result.signature.take(20)}...")
|
||||||
|
|
||||||
|
|
@ -1438,6 +1461,7 @@ class TssRepository @Inject constructor(
|
||||||
_sessionStatus.value = SessionStatus.FAILED
|
_sessionStatus.value = SessionStatus.FAILED
|
||||||
pendingSessionId = null // Clear pending session ID on failure
|
pendingSessionId = null // Clear pending session ID on failure
|
||||||
currentSigningPartyId = null // Clear on failure too
|
currentSigningPartyId = null // Clear on failure too
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1734,6 +1758,7 @@ class TssRepository @Inject constructor(
|
||||||
_sessionStatus.value = SessionStatus.COMPLETED
|
_sessionStatus.value = SessionStatus.COMPLETED
|
||||||
messageCollectionJob?.cancel()
|
messageCollectionJob?.cancel()
|
||||||
currentSigningPartyId = null // Clear after signing completes
|
currentSigningPartyId = null // Clear after signing completes
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
|
|
||||||
Result.success(result)
|
Result.success(result)
|
||||||
|
|
||||||
|
|
@ -1741,6 +1766,7 @@ class TssRepository @Inject constructor(
|
||||||
android.util.Log.e("TssRepository", "Join sign session failed", e)
|
android.util.Log.e("TssRepository", "Join sign session failed", e)
|
||||||
_sessionStatus.value = SessionStatus.FAILED
|
_sessionStatus.value = SessionStatus.FAILED
|
||||||
currentSigningPartyId = null // Clear on failure too
|
currentSigningPartyId = null // Clear on failure too
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2764,10 +2790,12 @@ class TssRepository @Inject constructor(
|
||||||
_sessionStatus.value = SessionStatus.COMPLETED
|
_sessionStatus.value = SessionStatus.COMPLETED
|
||||||
messageCollectionJob?.cancel()
|
messageCollectionJob?.cancel()
|
||||||
currentSigningPartyId = null // Clear after signing completes
|
currentSigningPartyId = null // Clear after signing completes
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
|
|
||||||
Result.success(result)
|
Result.success(result)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_sessionStatus.value = SessionStatus.FAILED
|
_sessionStatus.value = SessionStatus.FAILED
|
||||||
|
grpcClient.clearSigningPartyRegistration()
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue