From b79ef988c80a6e7000b2d906b49791a69e6733f7 Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 25 Dec 2024 21:27:28 +0200 Subject: [PATCH] Cw 868 fix false synchronised status when the socket connection fails due to network issues (#1892) * prevent setting Synced status when the connection is lost * fallback for UTXO fetch failures * minor fix --- cw_bitcoin/lib/electrum.dart | 4 +- cw_bitcoin/lib/electrum_wallet.dart | 71 +++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 4fc4c1ad8..1f5c369e3 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -235,7 +235,7 @@ class ElectrumClient { return []; }); - Future>> getListUnspent(String scriptHash) async { + Future>?> getListUnspent(String scriptHash) async { final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]); if (result is List) { @@ -248,7 +248,7 @@ class ElectrumClient { }).toList(); } - return []; + return null; } Future>> getMempool(String scriptHash) => diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 64eafb021..eae830db1 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -478,6 +478,7 @@ abstract class ElectrumWalletBase if (alwaysScan == true) { _setListeners(walletInfo.restoreHeight); } else { + if (syncStatus is LostConnectionSyncStatus) return; syncStatus = SyncedSyncStatus(); } } catch (e, stacktrace) { @@ -1361,6 +1362,10 @@ abstract class ElectrumWalletBase Future updateAllUnspents() async { List updatedUnspentCoins = []; + final previousUnspentCoins = List.from(unspentCoins.where((utxo) => + utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb && + utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)); + if (hasSilentPaymentsScanning) { // Update unspents stored from scanned silent payment transactions transactionHistory.transactions.values.forEach((tx) { @@ -1377,13 +1382,27 @@ abstract class ElectrumWalletBase if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0; }); - await Future.wait(walletAddresses.allAddresses + final addressFutures = walletAddresses.allAddresses .where((element) => element.type != SegwitAddresType.mweb) - .map((address) async { - updatedUnspentCoins.addAll(await fetchUnspent(address)); - })); + .map((address) => fetchUnspent(address)) + .toList(); - unspentCoins = updatedUnspentCoins; + final results = await Future.wait(addressFutures); + final failedCount = results.where((result) => result == null).length; + + if (failedCount == 0) { + for (final result in results) { + updatedUnspentCoins.addAll(result!); + } + unspentCoins = updatedUnspentCoins; + } else { + unspentCoins = handleFailedUtxoFetch( + failedCount: failedCount, + previousUnspentCoins: previousUnspentCoins, + updatedUnspentCoins: updatedUnspentCoins, + results: results, + ); + } final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id); @@ -1396,6 +1415,38 @@ abstract class ElectrumWalletBase await _refreshUnspentCoinsInfo(); } + List handleFailedUtxoFetch({ + required int failedCount, + required List previousUnspentCoins, + required List updatedUnspentCoins, + required List?> results, + }) { + + if (failedCount == results.length) { + printV("All UTXOs failed to fetch, falling back to previous UTXOs"); + return previousUnspentCoins; + } + + final successfulUtxos = []; + for (final result in results) { + if (result != null) { + successfulUtxos.addAll(result); + } + } + + if (failedCount > 0 && successfulUtxos.isEmpty) { + printV("Some UTXOs failed, but no successful UTXOs, falling back to previous UTXOs"); + return previousUnspentCoins; + } + + if (failedCount > 0) { + printV("Some UTXOs failed, updating with successful UTXOs"); + updatedUnspentCoins.addAll(successfulUtxos); + } + + return updatedUnspentCoins; + } + Future updateCoins(List newUnspentCoins) async { if (newUnspentCoins.isEmpty) { return; @@ -1427,15 +1478,17 @@ abstract class ElectrumWalletBase @action Future updateUnspentsForAddress(BitcoinAddressRecord address) async { final newUnspentCoins = await fetchUnspent(address); - await updateCoins(newUnspentCoins); + await updateCoins(newUnspentCoins ?? []); } @action - Future> fetchUnspent(BitcoinAddressRecord address) async { - List> unspents = []; + Future?> fetchUnspent(BitcoinAddressRecord address) async { List updatedUnspentCoins = []; - unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + final unspents = await electrumClient.getListUnspent(address.getScriptHash(network)); + + // Failed to fetch unspents + if (unspents == null) return null; await Future.wait(unspents.map((unspent) async { try {