mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 09:05:48 +00:00
fix: stop swallowed errors from silently killing balance/updater sync
StatemineAssetBalance and NativeAssetBalance's startSyncingBalance()
wrapped setup and subscription failures in runCatching{}/catch{} that
returned emptyFlow()/NoCause instead of propagating. This meant
FullSyncPaymentUpdater's retryWhen (added for the Tron fix) never
actually engaged for these asset types - a transient failure silently
and permanently stopped sync for that asset (HEZ, PEZ, USDT, DOT, ...)
with only a logcat line as evidence. Let the exceptions propagate so
the existing retry boundary can do its job.
ChainRegistry.currentChains ran registerChain()/unregisterChain() per
chain, unguarded, inside a plain (non-Supervisor) CoroutineScope and an
Eagerly-shared flow that never restarts once it dies. One bad chain row
could permanently kill sync for every chain. Isolated each chain's
register/unregister in its own runCatching, and switched the scope to
a SupervisorJob as defense in depth.
ChainUpdaterGroupUpdateSystem.runUpdaters() called getRuntime() and
built per-chain updater flows with no error boundary; callers merge
several chains' results together, so one chain's failure (e.g. a
disabled chain throwing DisabledChainException) could kill
governance/staking/crowdloan sync for every other chain in the group.
Wrapped the body in flow{} + catch to isolate failures per chain.
This commit is contained in:
+22
-26
@@ -131,6 +131,10 @@ class StatemineAssetBalance(
|
||||
)
|
||||
}
|
||||
|
||||
// Deliberately lets setup/subscription failures propagate as exceptions rather than swallowing them into
|
||||
// emptyFlow()/BalanceSyncUpdate.NoCause: the caller, FullSyncPaymentUpdater.syncAsset(), wraps this whole
|
||||
// call in a single retryWhen boundary that exists specifically to catch and retry failures like these. If
|
||||
// we swallow here, that boundary never triggers - the asset silently stops syncing instead of retrying.
|
||||
override suspend fun startSyncingBalance(
|
||||
chain: Chain,
|
||||
chainAsset: Chain.Asset,
|
||||
@@ -138,40 +142,32 @@ class StatemineAssetBalance(
|
||||
accountId: AccountId,
|
||||
subscriptionBuilder: SharedRequestsBuilder
|
||||
): Flow<BalanceSyncUpdate> {
|
||||
return runCatching {
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
val statemineType = chainAsset.requireStatemine()
|
||||
val encodableAssetId = statemineType.prepareIdForEncoding(runtime)
|
||||
val statemineType = chainAsset.requireStatemine()
|
||||
val encodableAssetId = statemineType.prepareIdForEncoding(runtime)
|
||||
|
||||
val module = runtime.metadata.statemineModule(statemineType)
|
||||
val module = runtime.metadata.statemineModule(statemineType)
|
||||
|
||||
val assetAccountStorage = module.storage("Account")
|
||||
val assetAccountKey = assetAccountStorage.storageKey(runtime, encodableAssetId, accountId)
|
||||
val assetAccountStorage = module.storage("Account")
|
||||
val assetAccountKey = assetAccountStorage.storageKey(runtime, encodableAssetId, accountId)
|
||||
|
||||
val assetDetailsFlow = statemineAssetsRepository.subscribeAndSyncAssetDetails(chain.id, statemineType, subscriptionBuilder)
|
||||
val assetDetailsFlow = statemineAssetsRepository.subscribeAndSyncAssetDetails(chain.id, statemineType, subscriptionBuilder)
|
||||
|
||||
combine(
|
||||
subscriptionBuilder.subscribe(assetAccountKey),
|
||||
assetDetailsFlow.map { it.status.transfersFrozen }
|
||||
) { balanceStorageChange, isAssetFrozen ->
|
||||
val assetAccountDecoded = assetAccountStorage.decodeValue(balanceStorageChange.value, runtime)
|
||||
val assetAccount = bindAssetAccountOrEmpty(assetAccountDecoded)
|
||||
return combine(
|
||||
subscriptionBuilder.subscribe(assetAccountKey),
|
||||
assetDetailsFlow.map { it.status.transfersFrozen }
|
||||
) { balanceStorageChange, isAssetFrozen ->
|
||||
val assetAccountDecoded = assetAccountStorage.decodeValue(balanceStorageChange.value, runtime)
|
||||
val assetAccount = bindAssetAccountOrEmpty(assetAccountDecoded)
|
||||
|
||||
val assetChanged = updateAssetBalance(metaAccount.id, chainAsset, isAssetFrozen, assetAccount)
|
||||
val assetChanged = updateAssetBalance(metaAccount.id, chainAsset, isAssetFrozen, assetAccount)
|
||||
|
||||
if (assetChanged) {
|
||||
BalanceSyncUpdate.CauseFetchable(balanceStorageChange.block)
|
||||
} else {
|
||||
BalanceSyncUpdate.NoCause
|
||||
}
|
||||
}.catch { error ->
|
||||
Log.e(LOG_TAG, "Balance sync failed for ${chainAsset.symbol} on ${chain.name}: ${error.message}")
|
||||
emit(BalanceSyncUpdate.NoCause)
|
||||
if (assetChanged) {
|
||||
BalanceSyncUpdate.CauseFetchable(balanceStorageChange.block)
|
||||
} else {
|
||||
BalanceSyncUpdate.NoCause
|
||||
}
|
||||
}.getOrElse { error ->
|
||||
Log.e(LOG_TAG, "Failed to start balance sync for ${chainAsset.symbol} on ${chain.name}: ${error.message}")
|
||||
emptyFlow()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-11
@@ -151,6 +151,9 @@ class NativeAssetBalance(
|
||||
}
|
||||
}
|
||||
|
||||
// Setup/subscription failures are allowed to propagate rather than being swallowed into emptyFlow()/NoCause:
|
||||
// the caller, FullSyncPaymentUpdater.syncAsset(), wraps this whole call in a single retryWhen boundary meant
|
||||
// to catch and retry exactly these failures. Swallowing here would make that retry boundary never trigger.
|
||||
override suspend fun startSyncingBalance(
|
||||
chain: Chain,
|
||||
chainAsset: Chain.Asset,
|
||||
@@ -160,13 +163,7 @@ class NativeAssetBalance(
|
||||
): Flow<BalanceSyncUpdate> {
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
val key = try {
|
||||
runtime.metadata.system().storage("Account").storageKey(runtime, accountId)
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "Failed to construct account storage key: ${e.message} in ${chain.name}")
|
||||
|
||||
return emptyFlow()
|
||||
}
|
||||
val key = runtime.metadata.system().storage("Account").storageKey(runtime, accountId)
|
||||
|
||||
return subscriptionBuilder.subscribe(key)
|
||||
.map { change ->
|
||||
@@ -179,10 +176,6 @@ class NativeAssetBalance(
|
||||
BalanceSyncUpdate.NoCause
|
||||
}
|
||||
}
|
||||
.catch { error ->
|
||||
Log.e(LOG_TAG, "Balance sync failed for ${chainAsset.symbol} on ${chain.name}: ${error.message}")
|
||||
emit(BalanceSyncUpdate.NoCause)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindBalanceHolds(dynamicInstance: Any?): List<BlockchainHold>? {
|
||||
|
||||
Reference in New Issue
Block a user