2024-04-18 23:17:45 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:electrum_adapter/electrum_adapter.dart';
|
2024-10-01 18:30:14 +00:00
|
|
|
|
|
|
|
import '../utilities/logger.dart';
|
2024-11-26 00:29:58 +00:00
|
|
|
import '../utilities/prefs.dart';
|
|
|
|
import '../utilities/tor_plain_net_option_enum.dart';
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
2024-04-18 23:17:45 +00:00
|
|
|
|
|
|
|
class ClientManager {
|
|
|
|
ClientManager._();
|
|
|
|
static final ClientManager sharedInstance = ClientManager._();
|
|
|
|
|
|
|
|
final Map<String, ElectrumClient> _map = {};
|
2024-11-26 00:29:58 +00:00
|
|
|
final Map<String, TorPlainNetworkOption> _mapNet = {};
|
2024-04-18 23:17:45 +00:00
|
|
|
final Map<String, int> _heights = {};
|
|
|
|
final Map<String, StreamSubscription<BlockHeader>> _subscriptions = {};
|
|
|
|
final Map<String, Completer<int>> _heightCompleters = {};
|
|
|
|
|
|
|
|
String _keyHelper(CryptoCurrency cryptoCurrency) {
|
|
|
|
return "${cryptoCurrency.runtimeType}_${cryptoCurrency.network.name}";
|
|
|
|
}
|
|
|
|
|
|
|
|
final Finalizer<ClientManager> _finalizer = Finalizer((manager) async {
|
|
|
|
await manager._kill();
|
|
|
|
});
|
|
|
|
|
|
|
|
ElectrumClient? getClient({
|
|
|
|
required CryptoCurrency cryptoCurrency,
|
2024-11-26 00:29:58 +00:00
|
|
|
required TorPlainNetworkOption netType,
|
|
|
|
}) {
|
|
|
|
final _key = _keyHelper(cryptoCurrency);
|
2024-04-18 23:17:45 +00:00
|
|
|
|
2024-11-26 00:29:58 +00:00
|
|
|
if (netType == _mapNet[_key]) {
|
|
|
|
return _map[_key];
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> addClient(
|
2024-04-18 23:17:45 +00:00
|
|
|
ElectrumClient client, {
|
|
|
|
required CryptoCurrency cryptoCurrency,
|
2024-11-26 00:29:58 +00:00
|
|
|
required TorPlainNetworkOption netType,
|
|
|
|
}) async {
|
2024-04-18 23:17:45 +00:00
|
|
|
final key = _keyHelper(cryptoCurrency);
|
|
|
|
if (_map[key] != null) {
|
2024-11-26 00:29:58 +00:00
|
|
|
if (_mapNet[key] == netType) {
|
|
|
|
throw Exception(
|
|
|
|
"ElectrumX Client for $key and $netType already exists.",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
await remove(cryptoCurrency: cryptoCurrency);
|
|
|
|
|
|
|
|
_map[key] = client;
|
|
|
|
_mapNet[key] = netType;
|
2024-04-18 23:17:45 +00:00
|
|
|
} else {
|
|
|
|
_map[key] = client;
|
2024-11-26 00:29:58 +00:00
|
|
|
_mapNet[key] = netType;
|
2024-04-18 23:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_heightCompleters[key] = Completer<int>();
|
2024-10-01 18:30:14 +00:00
|
|
|
_subscriptions[key] = client.subscribeHeaders().listen(
|
|
|
|
(event) {
|
|
|
|
_heights[key] = event.height;
|
|
|
|
|
|
|
|
if (!_heightCompleters[key]!.isCompleted) {
|
|
|
|
_heightCompleters[key]!.complete(event.height);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onError: (Object err, StackTrace s) => Logging.instance.log(
|
|
|
|
"ClientManager listen: $err\n$s",
|
|
|
|
level: LogLevel.Error,
|
|
|
|
),
|
|
|
|
);
|
2024-04-18 23:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> getChainHeightFor(CryptoCurrency cryptoCurrency) async {
|
|
|
|
final key = _keyHelper(cryptoCurrency);
|
|
|
|
|
|
|
|
if (_map[key] == null) {
|
|
|
|
throw Exception(
|
2024-04-25 21:54:40 +00:00
|
|
|
"No managed ElectrumClient for $key found.",
|
2024-04-18 23:17:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if (_heightCompleters[key] == null) {
|
|
|
|
throw Exception(
|
2024-04-25 21:54:40 +00:00
|
|
|
"No managed _heightCompleters for $key found.",
|
2024-04-18 23:17:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-11-26 00:29:58 +00:00
|
|
|
if (Prefs.instance.useTor) {
|
|
|
|
if (_mapNet[key]! == TorPlainNetworkOption.clear) {
|
|
|
|
throw Exception(
|
|
|
|
"Non-TOR only client for $key found.",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_mapNet[key]! == TorPlainNetworkOption.tor) {
|
|
|
|
throw Exception(
|
|
|
|
"TOR only client for $key found.",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-18 23:17:45 +00:00
|
|
|
return _heights[key] ?? await _heightCompleters[key]!.future;
|
|
|
|
}
|
|
|
|
|
2024-11-26 00:29:58 +00:00
|
|
|
Future<(ElectrumClient?, TorPlainNetworkOption?)> remove({
|
2024-04-18 23:17:45 +00:00
|
|
|
required CryptoCurrency cryptoCurrency,
|
|
|
|
}) async {
|
|
|
|
final key = _keyHelper(cryptoCurrency);
|
|
|
|
await _subscriptions[key]?.cancel();
|
|
|
|
_subscriptions.remove(key);
|
|
|
|
_heights.remove(key);
|
|
|
|
_heightCompleters.remove(key);
|
|
|
|
|
2024-11-26 00:29:58 +00:00
|
|
|
return (_map.remove(key), _mapNet.remove(key));
|
2024-04-18 23:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> closeAll() async {
|
|
|
|
await _kill();
|
|
|
|
_finalizer.detach(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _kill() async {
|
|
|
|
for (final sub in _subscriptions.values) {
|
|
|
|
await sub.cancel();
|
|
|
|
}
|
|
|
|
for (final client in _map.values) {
|
|
|
|
await client.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
_heightCompleters.clear();
|
|
|
|
_heights.clear();
|
|
|
|
_subscriptions.clear();
|
2024-11-26 00:29:58 +00:00
|
|
|
_mapNet.clear();
|
2024-04-18 23:17:45 +00:00
|
|
|
_map.clear();
|
|
|
|
}
|
|
|
|
}
|