Fix Bitcoin transactions not showing (#978)

* handle multiple responses coming in a single event * Add timeout for getting transaction info, to allow other transactions to be returned in case of any failure or network issue * Handle other cases of receiving multiple messages in the same response

Omar Hatem committed Jul 13, 2023 at 21:46 UTC bc432b104228c76f785563a903775d72403c17d6
1 file changed +51 -37
cw_bitcoin/lib/electrum.dart
+51 -37
@@ -66,54 +66,68 @@ class ElectrumClient {
66 socket!.listen((Uint8List event) {
67 try {
68 final msg = utf8.decode(event.toList());
69 - final response =
70 - json.decode(msg) as Map<String, dynamic>;
71 - _handleResponse(response);
72 - } on FormatException catch (e) {
73 - final msg = e.message.toLowerCase();
74 -
75 - if (e.source is String) {
76 - unterminatedString += e.source as String;
77 - }
78 -
79 - if (msg.contains("not a subtype of type")) {
80 - unterminatedString += e.source as String;
81 - return;
82 - }
83 -
84 - if (isJSONStringCorrect(unterminatedString)) {
85 - final response =
86 - json.decode(unterminatedString) as Map<String, dynamic>;
87 - _handleResponse(response);
88 - unterminatedString = '';
89 - }
90 - } on TypeError catch (e) {
91 - if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
92 - return;
93 - }
94 -
95 - final source = utf8.decode(event.toList());
96 - unterminatedString += source;
97 -
98 - if (isJSONStringCorrect(unterminatedString)) {
99 - final response =
100 - json.decode(unterminatedString) as Map<String, dynamic>;
101 - _handleResponse(response);
102 - // unterminatedString = null;
103 - unterminatedString = '';
69 + final messagesList = msg.split("\n");
70 + for (var message in messagesList) {
71 + if (message.isEmpty) {
72 + continue;
73 + }
74 + _parseResponse(message);
75 }
76 } catch (e) {
77 print(e.toString());
78 }
79 }, onError: (Object error) {
80 print(error.toString());
81 + unterminatedString = '';
82 _setIsConnected(false);
83 }, onDone: () {
84 + unterminatedString = '';
85 _setIsConnected(false);
86 });
87 keepAlive();
88 }
89
90 + void _parseResponse(String message) {
91 + try {
92 + final response = json.decode(message) as Map<String, dynamic>;
93 + _handleResponse(response);
94 + } on FormatException catch (e) {
95 + final msg = e.message.toLowerCase();
96 +
97 + if (e.source is String) {
98 + unterminatedString += e.source as String;
99 + }
100 +
101 + if (msg.contains("not a subtype of type")) {
102 + unterminatedString += e.source as String;
103 + return;
104 + }
105 +
106 + if (isJSONStringCorrect(unterminatedString)) {
107 + final response =
108 + json.decode(unterminatedString) as Map<String, dynamic>;
109 + _handleResponse(response);
110 + unterminatedString = '';
111 + }
112 + } on TypeError catch (e) {
113 + if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
114 + return;
115 + }
116 +
117 + unterminatedString += message;
118 +
119 + if (isJSONStringCorrect(unterminatedString)) {
120 + final response =
121 + json.decode(unterminatedString) as Map<String, dynamic>;
122 + _handleResponse(response);
123 + // unterminatedString = null;
124 + unterminatedString = '';
125 + }
126 + } catch (e) {
127 + print(e.toString());
128 + }
129 + }
130 +
131 void keepAlive() {
132 _aliveTimer?.cancel();
133 _aliveTimer = Timer.periodic(aliveTimerDuration, (_) async => ping());
@@ -217,7 +231,7 @@ class ElectrumClient {
231
232 Future<Map<String, dynamic>> getTransactionRaw(
233 {required String hash}) async =>
220 - call(method: 'blockchain.transaction.get', params: [hash, true])
234 + callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
235 .then((dynamic result) {
236 if (result is Map<String, dynamic>) {
237 return result;
@@ -228,7 +242,7 @@ class ElectrumClient {
242
243 Future<String> getTransactionHex(
244 {required String hash}) async =>
231 - call(method: 'blockchain.transaction.get', params: [hash, false])
245 + callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
246 .then((dynamic result) {
247 if (result is String) {
248 return result;