stack_wallet/lib/electrumx_rpc/rpc.dart

129 lines
3.5 KiB
Dart
Raw Normal View History

2022-08-26 08:11:35 +00:00
import 'dart:async';
import 'dart:convert';
import 'dart:io';
2023-05-24 18:27:19 +00:00
import 'dart:typed_data';
2023-05-24 21:06:56 +00:00
import 'package:mutex/mutex.dart';
2022-08-26 08:11:35 +00:00
import 'package:stackwallet/utilities/logger.dart';
// hacky fix to receive large jsonrpc responses
class JsonRPC {
JsonRPC({
required this.host,
required this.port,
this.useSSL = false,
this.connectionTimeout = const Duration(seconds: 60),
});
2023-05-24 18:12:54 +00:00
final bool useSSL;
final String host;
final int port;
final Duration connectionTimeout;
2022-08-26 08:11:35 +00:00
Socket? socket;
2023-05-24 18:27:19 +00:00
StreamSubscription<Uint8List>? _subscription;
2023-05-24 21:06:56 +00:00
final m = Mutex();
2023-05-24 20:55:24 +00:00
void Function(List<int>)? _onData;
void Function(Object, StackTrace)? _onError;
2022-08-26 08:11:35 +00:00
Future<dynamic> request(String jsonRpcRequest) async {
final completer = Completer<dynamic>();
final List<int> responseData = [];
void dataHandler(List<int> data) {
responseData.addAll(data);
// 0x0A is newline
// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-basics.html
if (data.last == 0x0A) {
try {
final response = json.decode(String.fromCharCodes(responseData));
2023-05-24 20:55:36 +00:00
completer.complete(response); // TODO only complete on last chunk?
2022-08-26 08:11:35 +00:00
} catch (e, s) {
Logging.instance
.log("JsonRPC json.decode: $e\n$s", level: LogLevel.Error);
completer.completeError(e, s);
} finally {
2023-05-24 18:27:19 +00:00
Logging.instance.log(
"JsonRPC dataHandler: not destroying socket ${socket?.address}:${socket?.port}",
level: LogLevel.Info,
);
2023-05-24 18:15:19 +00:00
// socket?.destroy();
// TODO is this all we need to do?
2022-08-26 08:11:35 +00:00
}
}
}
_onData = dataHandler;
2022-08-26 08:11:35 +00:00
void errorHandler(Object error, StackTrace trace) {
Logging.instance
.log("JsonRPC errorHandler: $error\n$trace", level: LogLevel.Error);
completer.completeError(error, trace);
2023-05-24 18:27:19 +00:00
Logging.instance.log(
"JsonRPC errorHandler: not destroying socket ${socket?.address}:${socket?.port}",
level: LogLevel.Info,
);
2023-05-24 18:15:19 +00:00
// socket?.destroy();
// TODO do we need to recreate the socket?
2022-08-26 08:11:35 +00:00
}
_onError = errorHandler;
2022-08-26 08:11:35 +00:00
void doneHandler() {
2023-05-24 18:27:19 +00:00
Logging.instance.log(
"JsonRPC doneHandler: not destroying socket ${socket?.address}:${socket?.port}",
level: LogLevel.Info,
);
2023-05-24 18:15:19 +00:00
// socket?.destroy();
2023-05-24 21:06:56 +00:00
m.release();
2023-05-24 18:15:19 +00:00
// TODO is this all we need?
2022-08-26 08:11:35 +00:00
}
2023-05-24 18:15:10 +00:00
if (socket != null) {
// TODO check if the socket is valid, alive, connected, etc
2023-05-24 21:06:56 +00:00
} else {
Logging.instance.log(
"JsonRPC request: opening socket $host:$port",
level: LogLevel.Info,
);
2023-05-24 18:15:10 +00:00
}
2023-05-24 20:55:24 +00:00
// Do we need to check the subscription, too?
2023-05-24 21:06:56 +00:00
await m.acquire();
2023-05-24 18:15:10 +00:00
2022-08-26 08:11:35 +00:00
if (useSSL) {
2023-05-24 18:14:56 +00:00
socket ??= await SecureSocket.connect(host, port,
2023-05-24 20:55:36 +00:00
timeout: connectionTimeout, onBadCertificate: (_) => true); // TODO do not automatically trust bad certificates
2023-05-24 18:27:19 +00:00
_subscription ??= socket!.listen(
_onData,
onError: _onError,
2023-05-24 18:27:19 +00:00
onDone: doneHandler,
cancelOnError: true,
);
2022-08-26 08:11:35 +00:00
} else {
2023-05-24 18:27:19 +00:00
socket ??= await Socket.connect(
host,
port,
timeout: connectionTimeout,
);
_subscription ??= socket!.listen(
_onData,
onError: _onError,
2023-05-24 18:27:19 +00:00
onDone: doneHandler,
cancelOnError: true,
);
2022-08-26 08:11:35 +00:00
}
2023-05-24 20:56:08 +00:00
socket!.write('$jsonRpcRequest\r\n');
2022-08-26 08:11:35 +00:00
2023-05-24 20:55:55 +00:00
Logging.instance.log(
2023-05-24 21:06:56 +00:00
"JsonRPC request: wrote request $jsonRpcRequest to socket ${socket?.address}:${socket?.port}",
2023-05-24 20:55:55 +00:00
level: LogLevel.Info,
);
2022-08-26 08:11:35 +00:00
return completer.future;
}
}