CW-193 Fixes for fetching of electrum transactions.

M committed Oct 17, 2022 at 16:55 UTC 902935bc16ecd11ed99b18a1ad2d6ca23c3b342c
3 files changed +56 -32
cw_bitcoin/lib/electrum.dart
+16 -15
@@ -65,8 +65,9 @@ class ElectrumClient {
65
66 socket!.listen((Uint8List event) {
67 try {
68 + final msg = utf8.decode(event.toList());
69 final response =
69 - json.decode(utf8.decode(event.toList())) as Map<String, dynamic>;
70 + json.decode(msg) as Map<String, dynamic>;
71 _handleResponse(response);
72 } on FormatException catch (e) {
73 final msg = e.message.toLowerCase();
@@ -136,14 +137,14 @@ class ElectrumClient {
137 return [];
138 });
139
139 - Future<Map<String, Object>> getBalance(String scriptHash) =>
140 + Future<Map<String, dynamic>> getBalance(String scriptHash) =>
141 call(method: 'blockchain.scripthash.get_balance', params: [scriptHash])
142 .then((dynamic result) {
142 - if (result is Map<String, Object>) {
143 + if (result is Map<String, dynamic>) {
144 return result;
145 }
146
146 - return <String, Object>{};
147 + return <String, dynamic>{};
148 });
149
150 Future<List<Map<String, dynamic>>> getHistory(String scriptHash) =>
@@ -151,11 +152,11 @@ class ElectrumClient {
152 .then((dynamic result) {
153 if (result is List) {
154 return result.map((dynamic val) {
154 - if (val is Map<String, Object>) {
155 + if (val is Map<String, dynamic>) {
156 return val;
157 }
158
158 - return <String, Object>{};
159 + return <String, dynamic>{};
160 }).toList();
161 }
162
@@ -170,12 +171,12 @@ class ElectrumClient {
171 .then((dynamic result) {
172 if (result is List) {
173 return result.map((dynamic val) {
173 - if (val is Map<String, Object>) {
174 + if (val is Map<String, dynamic>) {
175 val['address'] = address;
176 return val;
177 }
178
178 - return <String, Object>{};
179 + return <String, dynamic>{};
180 }).toList();
181 }
182
@@ -187,11 +188,11 @@ class ElectrumClient {
188 .then((dynamic result) {
189 if (result is List) {
190 return result.map((dynamic val) {
190 - if (val is Map<String, Object>) {
191 + if (val is Map<String, dynamic>) {
192 return val;
193 }
194
194 - return <String, Object>{};
195 + return <String, dynamic>{};
196 }).toList();
197 }
198
@@ -203,26 +204,26 @@ class ElectrumClient {
204 .then((dynamic result) {
205 if (result is List) {
206 return result.map((dynamic val) {
206 - if (val is Map<String, Object>) {
207 + if (val is Map<String, dynamic>) {
208 return val;
209 }
210
210 - return <String, Object>{};
211 + return <String, dynamic>{};
212 }).toList();
213 }
214
215 return [];
216 });
217
217 - Future<Map<String, Object>> getTransactionRaw(
218 + Future<Map<String, dynamic>> getTransactionRaw(
219 {required String hash}) async =>
220 call(method: 'blockchain.transaction.get', params: [hash, true])
221 .then((dynamic result) {
221 - if (result is Map<String, Object>) {
222 + if (result is Map<String, dynamic>) {
223 return result;
224 }
225
225 - return <String, Object>{};
226 + return <String, dynamic>{};
227 });
228
229 Future<String> getTransactionHex(
cw_bitcoin/lib/electrum_wallet.dart
+26 -7
@@ -455,7 +455,13 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
455 .addresses.map((address) => electrumClient
456 .getListUnspentWithAddress(address.address, networkType)
457 .then((unspent) => unspent
458 - .map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))));
458 + .map((unspent) {
459 + try {
460 + return BitcoinUnspent.fromJSON(address, unspent);
461 + } catch(_) {
462 + return null;
463 + }
464 + }).whereNotNull())));
465 unspentCoins = unspent.expand((e) => e).toList();
466
467 if (unspentCoinsInfo.isEmpty) {
@@ -542,8 +548,9 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
548 confirmations: confirmations);
549 }
550
545 - Future<ElectrumTransactionInfo> fetchTransactionInfo(
551 + Future<ElectrumTransactionInfo?> fetchTransactionInfo(
552 {required String hash, required int height}) async {
553 + try {
554 final tx = await getTransactionExpanded(hash: hash, height: height);
555 final addresses = walletAddresses.addresses.map((addr) => addr.address).toSet();
556 return ElectrumTransactionInfo.fromElectrumBundle(
@@ -552,6 +559,9 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
559 networkType,
560 addresses: addresses,
561 height: height);
562 + } catch(_) {
563 + return null;
564 + }
565 }
566
567 @override
@@ -578,12 +588,20 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
588 });
589 final historiesWithDetails = await Future.wait(
590 normalizedHistories
581 - .map((transaction) => fetchTransactionInfo(
582 - hash: transaction['tx_hash'] as String,
583 - height: transaction['height'] as int)));
584 -
591 + .map((transaction) {
592 + try {
593 + return fetchTransactionInfo(
594 + hash: transaction['tx_hash'] as String,
595 + height: transaction['height'] as int);
596 + } catch(_) {
597 + return Future.value(null);
598 + }
599 + }));
600 return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
601 <String, ElectrumTransactionInfo>{}, (acc, tx) {
602 + if (tx == null) {
603 + return acc;
604 + }
605 acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
606 return acc;
607 });
@@ -601,7 +619,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
619 walletAddresses.updateReceiveAddresses();
620 await transactionHistory.save();
621 _isTransactionUpdating = false;
604 - } catch (e) {
622 + } catch (e, stacktrace) {
623 + print(stacktrace);
624 print(e);
625 _isTransactionUpdating = false;
626 }
lib/reactions/on_wallet_sync_status_change.dart
+14 -10
@@ -21,18 +21,22 @@ void startWalletSyncStatusChangeReaction(
21 _onWalletSyncStatusChangeReaction?.reaction.dispose();
22 _onWalletSyncStatusChangeReaction =
23 reaction((_) => wallet.syncStatus, (SyncStatus status) async {
24 - if (status is ConnectedSyncStatus) {
25 - await wallet.startSync();
24 + try {
25 + if (status is ConnectedSyncStatus) {
26 + await wallet.startSync();
27
27 - if (wallet.type == WalletType.haven) {
28 - await updateHavenRate(fiatConversionStore);
28 + if (wallet.type == WalletType.haven) {
29 + await updateHavenRate(fiatConversionStore);
30 + }
31 }
30 - }
31 - if (status is SyncingSyncStatus) {
32 - await _wakeLock.enableWake();
33 - }
34 - if (status is SyncedSyncStatus || status is FailedSyncStatus) {
35 - await _wakeLock.disableWake();
32 + if (status is SyncingSyncStatus) {
33 + await _wakeLock.enableWake();
34 + }
35 + if (status is SyncedSyncStatus || status is FailedSyncStatus) {
36 + await _wakeLock.disableWake();
37 + }
38 + } catch(e) {
39 + print(e.toString());
40 }
41 });
42 }