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
Serhii committed
Dec 25, 2024 at 21:27 UTC
b79ef988c80a6e7000b2d906b49791a69e6733f7
2 files changed
+64
-11
cw_bitcoin/lib/electrum.dart
+2
-2
@@ -235,7 +235,7 @@ class ElectrumClient {
235
return [];
236
});
237
238
- Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) async {
238
+ Future<List<Map<String, dynamic>>?> getListUnspent(String scriptHash) async {
239
final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]);
240
241
if (result is List) {
@@ -248,7 +248,7 @@ class ElectrumClient {
248
}).toList();
249
}
250
251
- return [];
251
+ return null;
252
}
253
254
Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>
cw_bitcoin/lib/electrum_wallet.dart
+62
-9
@@ -478,6 +478,7 @@ abstract class ElectrumWalletBase
478
if (alwaysScan == true) {
479
_setListeners(walletInfo.restoreHeight);
480
} else {
481
+ if (syncStatus is LostConnectionSyncStatus) return;
482
syncStatus = SyncedSyncStatus();
483
}
484
} catch (e, stacktrace) {
@@ -1361,6 +1362,10 @@ abstract class ElectrumWalletBase
1362
Future<void> updateAllUnspents() async {
1363
List<BitcoinUnspent> updatedUnspentCoins = [];
1364
1365
+ final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
1366
+ utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
1367
+ utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
1368
+
1369
if (hasSilentPaymentsScanning) {
1370
// Update unspents stored from scanned silent payment transactions
1371
transactionHistory.transactions.values.forEach((tx) {
@@ -1377,13 +1382,27 @@ abstract class ElectrumWalletBase
1382
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
1383
});
1384
1380
- await Future.wait(walletAddresses.allAddresses
1385
+ final addressFutures = walletAddresses.allAddresses
1386
.where((element) => element.type != SegwitAddresType.mweb)
1382
- .map((address) async {
1383
- updatedUnspentCoins.addAll(await fetchUnspent(address));
1384
- }));
1387
+ .map((address) => fetchUnspent(address))
1388
+ .toList();
1389
1386
- unspentCoins = updatedUnspentCoins;
1390
+ final results = await Future.wait(addressFutures);
1391
+ final failedCount = results.where((result) => result == null).length;
1392
+
1393
+ if (failedCount == 0) {
1394
+ for (final result in results) {
1395
+ updatedUnspentCoins.addAll(result!);
1396
+ }
1397
+ unspentCoins = updatedUnspentCoins;
1398
+ } else {
1399
+ unspentCoins = handleFailedUtxoFetch(
1400
+ failedCount: failedCount,
1401
+ previousUnspentCoins: previousUnspentCoins,
1402
+ updatedUnspentCoins: updatedUnspentCoins,
1403
+ results: results,
1404
+ );
1405
+ }
1406
1407
final currentWalletUnspentCoins =
1408
unspentCoinsInfo.values.where((element) => element.walletId == id);
@@ -1396,6 +1415,38 @@ abstract class ElectrumWalletBase
1415
await _refreshUnspentCoinsInfo();
1416
}
1417
1418
+ List<BitcoinUnspent> handleFailedUtxoFetch({
1419
+ required int failedCount,
1420
+ required List<BitcoinUnspent> previousUnspentCoins,
1421
+ required List<BitcoinUnspent> updatedUnspentCoins,
1422
+ required List<List<BitcoinUnspent>?> results,
1423
+ }) {
1424
+
1425
+ if (failedCount == results.length) {
1426
+ printV("All UTXOs failed to fetch, falling back to previous UTXOs");
1427
+ return previousUnspentCoins;
1428
+ }
1429
+
1430
+ final successfulUtxos = <BitcoinUnspent>[];
1431
+ for (final result in results) {
1432
+ if (result != null) {
1433
+ successfulUtxos.addAll(result);
1434
+ }
1435
+ }
1436
+
1437
+ if (failedCount > 0 && successfulUtxos.isEmpty) {
1438
+ printV("Some UTXOs failed, but no successful UTXOs, falling back to previous UTXOs");
1439
+ return previousUnspentCoins;
1440
+ }
1441
+
1442
+ if (failedCount > 0) {
1443
+ printV("Some UTXOs failed, updating with successful UTXOs");
1444
+ updatedUnspentCoins.addAll(successfulUtxos);
1445
+ }
1446
+
1447
+ return updatedUnspentCoins;
1448
+ }
1449
+
1450
Future<void> updateCoins(List<BitcoinUnspent> newUnspentCoins) async {
1451
if (newUnspentCoins.isEmpty) {
1452
return;
@@ -1427,15 +1478,17 @@ abstract class ElectrumWalletBase
1478
@action
1479
Future<void> updateUnspentsForAddress(BitcoinAddressRecord address) async {
1480
final newUnspentCoins = await fetchUnspent(address);
1430
- await updateCoins(newUnspentCoins);
1481
+ await updateCoins(newUnspentCoins ?? []);
1482
}
1483
1484
@action
1434
- Future<List<BitcoinUnspent>> fetchUnspent(BitcoinAddressRecord address) async {
1435
- List<Map<String, dynamic>> unspents = [];
1485
+ Future<List<BitcoinUnspent>?> fetchUnspent(BitcoinAddressRecord address) async {
1486
List<BitcoinUnspent> updatedUnspentCoins = [];
1487
1438
- unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
1488
+ final unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
1489
+
1490
+ // Failed to fetch unspents
1491
+ if (unspents == null) return null;
1492
1493
await Future.wait(unspents.map((unspent) async {
1494
try {