connection reliability updates, update kotlin code to match swift code, minor electrum error handling

This commit is contained in:
Matthew Fosse 2024-07-29 13:09:53 -07:00
parent 13aeaf2c01
commit 56ea67666d
6 changed files with 107 additions and 60 deletions

View file

@ -356,14 +356,19 @@ class ElectrumClient {
BehaviorSubject<Map<String, dynamic>>? tipListener;
int? currentTip;
Future<int?> getCurrentBlockChainTip() async {
final method = 'blockchain.headers.subscribe';
final cb = (result) => currentTip = result['height'] as int;
if (tipListener == null) {
tipListener = subscribe(id: method, method: method);
tipListener?.listen(cb);
callWithTimeout(method: method).then(cb);
try {
final method = 'blockchain.headers.subscribe';
final cb = (result) => currentTip = result['height'] as int;
if (tipListener == null) {
tipListener = subscribe(id: method, method: method);
tipListener?.listen(cb);
callWithTimeout(method: method).then(cb);
}
return currentTip;
} catch (_) {
// our websocket connection was likely terminated ungracefully :/
return null;
}
return currentTip;
}
BehaviorSubject<Object>? chainTipSubscribe() {

View file

@ -32,6 +32,11 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
server = server ?: Mwebd.newServer("", dataDir, "")
port = port ?: server?.start(0)
result.success(port)
} else if (call.method == "stop") {
server?.stop()
server = null
port = null
result.success(null)
} else {
result.notImplemented()
}
@ -41,5 +46,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
channel.setMethodCallHandler(null)
server?.stop()
server = null
port = null
}
}

View file

@ -11,6 +11,7 @@ public class CwMwebPlugin: NSObject, FlutterPlugin {
private static var server: MwebdServer?
private static var port: Int = 0
private static var dataDir: String?
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
@ -18,54 +19,53 @@ public class CwMwebPlugin: NSObject, FlutterPlugin {
result("iOS " + UIDevice.current.systemVersion)
case "start":
let args = call.arguments as? [String: String]
// print("args: \(args)")
let dataDir = args?["dataDir"]
var error: NSError?
if dataDir == "stop" && CwMwebPlugin.server != nil {
print("Stopping server")
CwMwebPlugin.server?.stop()
CwMwebPlugin.server = nil
result(0)
return
}
if CwMwebPlugin.server == nil {
CwMwebPlugin.server = MwebdNewServer("", dataDir, "", &error)
if let server = CwMwebPlugin.server {
do {
print("starting server \(CwMwebPlugin.port)")
try server.start(0, ret0_: &CwMwebPlugin.port)
result(CwMwebPlugin.port)
} catch let startError as NSError {
print("Server Start Error: \(startError.localizedDescription)")
result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil))
}
} else if let error = error {
print("Server Creation Error: \(error.localizedDescription)")
result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil))
} else {
print("Unknown Error: Failed to create server")
result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil))
}
} else {
print("Server already running on port: \(CwMwebPlugin.port)")
// result(FlutterError(code: "Server Already Running", message: "The server is already running", details: nil))
result(CwMwebPlugin.port)
}
// result(0)
default:
result(FlutterMethodNotImplemented)
}
CwMwebPlugin.dataDir = dataDir
startServer(result: result)
case "stop":
stopServer()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
deinit {
print("Stopping and cleaning up server")
// Perform cleanup tasks
CwMwebPlugin.server?.stop()
CwMwebPlugin.server = nil
}
}
private func startServer(result: @escaping FlutterResult) {
if CwMwebPlugin.server == nil {
var error: NSError?
CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &error)
if let server = CwMwebPlugin.server {
do {
print("Starting server...")
try server.start(0, ret0_: &CwMwebPlugin.port)
print("Server started successfully on port: \(CwMwebPlugin.port)")
result(CwMwebPlugin.port)
} catch let startError as NSError {
print("Server Start Error: \(startError.localizedDescription)")
result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil))
}
} else if let error = error {
print("Server Creation Error: \(error.localizedDescription)")
result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil))
} else {
print("Unknown Error: Failed to create server")
result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil))
}
} else {
print("Server already running on port: \(CwMwebPlugin.port)")
result(CwMwebPlugin.port)
}
}
private func stopServer() {
print("Stopping server")
CwMwebPlugin.server?.stop()
CwMwebPlugin.server = nil
CwMwebPlugin.port = 0
}
deinit {
stopServer()
}
}

View file

@ -6,22 +6,48 @@ import 'mwebd.pbgrpc.dart';
class CwMweb {
static RpcClient? _rpcClient;
static ClientChannel? _clientChannel;
static int? _port;
static Future<RpcClient> stub() async {
static Future<void> _initializeClient() async {
final appDir = await getApplicationSupportDirectory();
int port = await CwMwebPlatform.instance.start(appDir.path) ?? 0;
_port = await CwMwebPlatform.instance.start(appDir.path);
if (_port == null || _port == 0) {
throw Exception("Failed to start server");
}
print("Attempting to connect to server on port: $_port");
_clientChannel = ClientChannel('127.0.0.1',
port: port,
port: _port!,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure(),
keepAlive: ClientKeepAliveOptions(permitWithoutCalls: true),
));
_rpcClient = RpcClient(_clientChannel!);
return _rpcClient!;
}
static Future<RpcClient> stub({int maxRetries = 3}) async {
for (int i = 0; i < maxRetries; i++) {
try {
if (_rpcClient == null) {
await _initializeClient();
}
final status = await _rpcClient!
.status(StatusRequest(), options: CallOptions(timeout: const Duration(seconds: 3)));
if (status.blockTime == 0) {
throw Exception("blockTime shouldn't be 0! (this connection is likely broken)");
}
return _rpcClient!;
} catch (e) {
print("Attempt $i failed: $e");
await stop(); // call stop so we create a new instance before retrying
await Future.delayed(const Duration(seconds: 2)); // wait before retrying
}
}
throw Exception("Failed to connect after $maxRetries attempts");
}
static Future<void> stop() async {
await CwMwebPlatform.instance.start("stop");
await CwMwebPlatform.instance.stop();
await cleanup();
}
@ -29,5 +55,6 @@ class CwMweb {
await _clientChannel?.terminate();
_rpcClient = null;
_clientChannel = null;
_port = null;
}
}

View file

@ -14,4 +14,9 @@ class MethodChannelCwMweb extends CwMwebPlatform {
final result = await methodChannel.invokeMethod<int>('start', {'dataDir': dataDir});
return result;
}
@override
Future<void> stop() async {
await methodChannel.invokeMethod<void>('stop');
}
}

View file

@ -26,4 +26,8 @@ abstract class CwMwebPlatform extends PlatformInterface {
Future<int?> start(String dataDir) {
throw UnimplementedError('start() has not been implemented.');
}
Future<void> stop() {
throw UnimplementedError('stop() has not been implemented.');
}
}