Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/db/db_version_migration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class DbVersionMigrator with WalletDB {
name: e.name,
id: e.id,
useSSL: e.useSSL,
torEnabled: e.torEnabled,
clearnetEnabled: e.clearnetEnabled,
),
)
.toList();
Expand All @@ -88,6 +90,8 @@ class DbVersionMigrator with WalletDB {
name: node.name,
id: node.id,
useSSL: node.useSSL,
torEnabled: node.torEnabled,
clearnetEnabled: node.clearnetEnabled,
),
prefs: prefs,
failovers: failovers,
Expand Down
51 changes: 44 additions & 7 deletions lib/electrumx_rpc/client_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import 'dart:async';
import 'package:electrum_adapter/electrum_adapter.dart';

import '../utilities/logger.dart';
import '../utilities/prefs.dart';
import '../utilities/tor_plain_net_option_enum.dart';
import '../wallets/crypto_currency/crypto_currency.dart';

class ClientManager {
ClientManager._();
static final ClientManager sharedInstance = ClientManager._();

final Map<String, ElectrumClient> _map = {};
final Map<String, TorPlainNetworkOption> _mapNet = {};
final Map<String, int> _heights = {};
final Map<String, StreamSubscription<BlockHeader>> _subscriptions = {};
final Map<String, Completer<int>> _heightCompleters = {};
Expand All @@ -24,18 +27,37 @@ class ClientManager {

ElectrumClient? getClient({
required CryptoCurrency cryptoCurrency,
}) =>
_map[_keyHelper(cryptoCurrency)];
required TorPlainNetworkOption netType,
}) {
final _key = _keyHelper(cryptoCurrency);

void addClient(
if (netType == _mapNet[_key]) {
return _map[_key];
} else {
return null;
}
}

Future<void> addClient(
ElectrumClient client, {
required CryptoCurrency cryptoCurrency,
}) {
required TorPlainNetworkOption netType,
}) async {
final key = _keyHelper(cryptoCurrency);
if (_map[key] != null) {
throw Exception("ElectrumX Client for $key already exists.");
if (_mapNet[key] == netType) {
throw Exception(
"ElectrumX Client for $key and $netType already exists.",
);
}

await remove(cryptoCurrency: cryptoCurrency);

_map[key] = client;
_mapNet[key] = netType;
} else {
_map[key] = client;
_mapNet[key] = netType;
}

_heightCompleters[key] = Completer<int>();
Expand Down Expand Up @@ -68,10 +90,24 @@ class ClientManager {
);
}

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.",
);
}
}

return _heights[key] ?? await _heightCompleters[key]!.future;
}

Future<ElectrumClient?> remove({
Future<(ElectrumClient?, TorPlainNetworkOption?)> remove({
required CryptoCurrency cryptoCurrency,
}) async {
final key = _keyHelper(cryptoCurrency);
Expand All @@ -80,7 +116,7 @@ class ClientManager {
_heights.remove(key);
_heightCompleters.remove(key);

return _map.remove(key);
return (_map.remove(key), _mapNet.remove(key));
}

Future<void> closeAll() async {
Expand All @@ -99,6 +135,7 @@ class ClientManager {
_heightCompleters.clear();
_heights.clear();
_subscriptions.clear();
_mapNet.clear();
_map.clear();
}
}
46 changes: 43 additions & 3 deletions lib/electrumx_rpc/electrumx_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import '../utilities/amount/amount.dart';
import '../utilities/extensions/impl/string.dart';
import '../utilities/logger.dart';
import '../utilities/prefs.dart';
import '../utilities/tor_plain_net_option_enum.dart';
import '../wallets/crypto_currency/crypto_currency.dart';
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
import 'client_manager.dart';
Expand All @@ -42,19 +43,27 @@ typedef SparkMempoolData = ({

class WifiOnlyException implements Exception {}

class TorOnlyException implements Exception {}

class ClearnetOnlyException implements Exception {}

class ElectrumXNode {
ElectrumXNode({
required this.address,
required this.port,
required this.name,
required this.id,
required this.useSSL,
required this.torEnabled,
required this.clearnetEnabled,
});
final String address;
final int port;
final String name;
final String id;
final bool useSSL;
final bool torEnabled;
final bool clearnetEnabled;

factory ElectrumXNode.from(ElectrumXNode node) {
return ElectrumXNode(
Expand All @@ -63,6 +72,8 @@ class ElectrumXNode {
name: node.name,
id: node.id,
useSSL: node.useSSL,
torEnabled: node.torEnabled,
clearnetEnabled: node.clearnetEnabled,
);
}

Expand All @@ -74,6 +85,7 @@ class ElectrumXNode {

class ElectrumXClient {
final CryptoCurrency cryptoCurrency;
final TorPlainNetworkOption netType;

String get host => _host;
late String _host;
Expand All @@ -90,6 +102,7 @@ class ElectrumXClient {
ElectrumClient? getElectrumAdapter() =>
ClientManager.sharedInstance.getClient(
cryptoCurrency: cryptoCurrency,
netType: netType,
);

late Prefs _prefs;
Expand Down Expand Up @@ -119,6 +132,7 @@ class ElectrumXClient {
required int port,
required bool useSSL,
required Prefs prefs,
required this.netType,
required List<ElectrumXNode> failovers,
required this.cryptoCurrency,
this.connectionTimeoutForSpecialCaseJsonRPCClients =
Expand Down Expand Up @@ -168,6 +182,7 @@ class ElectrumXClient {
_electrumAdapterChannel = null;
await (await ClientManager.sharedInstance
.remove(cryptoCurrency: cryptoCurrency))
.$1
?.close();

// Also close any chain height services that are currently open.
Expand All @@ -193,6 +208,10 @@ class ElectrumXClient {
failovers: failovers,
globalEventBusForTesting: globalEventBusForTesting,
cryptoCurrency: cryptoCurrency,
netType: TorPlainNetworkOption.fromNodeData(
node.torEnabled,
node.clearnetEnabled,
),
);
}

Expand Down Expand Up @@ -236,6 +255,18 @@ class ElectrumXClient {
// Get the proxy info from the TorService.
proxyInfo = _torService.getProxyInfo();
}

if (netType == TorPlainNetworkOption.clear) {
_electrumAdapterChannel = null;
await ClientManager.sharedInstance
.remove(cryptoCurrency: cryptoCurrency);
}
} else {
if (netType == TorPlainNetworkOption.tor) {
_electrumAdapterChannel = null;
await ClientManager.sharedInstance
.remove(cryptoCurrency: cryptoCurrency);
}
}

// If the current ElectrumAdapterClient is closed, create a new one.
Expand Down Expand Up @@ -288,9 +319,10 @@ class ElectrumXClient {
);
}

ClientManager.sharedInstance.addClient(
await ClientManager.sharedInstance.addClient(
newClient,
cryptoCurrency: cryptoCurrency,
netType: netType,
);
}

Expand Down Expand Up @@ -352,6 +384,10 @@ class ElectrumXClient {
return response;
} on WifiOnlyException {
rethrow;
} on ClearnetOnlyException {
rethrow;
} on TorOnlyException {
rethrow;
} on SocketException {
// likely timed out so then retry
if (retries > 0) {
Expand Down Expand Up @@ -442,6 +478,10 @@ class ElectrumXClient {
return response;
} on WifiOnlyException {
rethrow;
} on ClearnetOnlyException {
rethrow;
} on TorOnlyException {
rethrow;
} on SocketException {
// likely timed out so then retry
if (retries > 0) {
Expand Down Expand Up @@ -488,10 +528,10 @@ class ElectrumXClient {
return await request(
requestID: requestID,
command: 'server.ping',
requestTimeout: const Duration(seconds: 2),
requestTimeout: const Duration(seconds: 3),
retries: retryCount,
).timeout(
const Duration(seconds: 2),
const Duration(seconds: 3),
onTimeout: () {
Logging.instance.log(
"ElectrumxClient.ping timed out with retryCount=$retryCount, host=$_host",
Expand Down
8 changes: 8 additions & 0 deletions lib/exceptions/wallet/node_tor_mismatch_config_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class NodeTorMismatchConfigException implements Exception {
final String message;

NodeTorMismatchConfigException({required this.message});

@override
String toString() => message;
}
13 changes: 13 additions & 0 deletions lib/models/node_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import 'package:hive/hive.dart';

import '../utilities/default_nodes.dart';
import '../utilities/flutter_secure_storage_interface.dart';

Expand Down Expand Up @@ -38,6 +39,10 @@ class NodeModel {
final bool isDown;
// @HiveField(10)
final bool? trusted;
// @HiveField(11)
final bool torEnabled;
// @HiveField(12)
final bool clearnetEnabled;

NodeModel({
required this.host,
Expand All @@ -49,6 +54,8 @@ class NodeModel {
required this.coinName,
required this.isFailover,
required this.isDown,
required this.torEnabled,
required this.clearnetEnabled,
this.loginName,
this.trusted,
});
Expand All @@ -64,6 +71,8 @@ class NodeModel {
bool? isFailover,
bool? isDown,
bool? trusted,
bool? torEnabled,
bool? clearnetEnabled,
}) {
return NodeModel(
host: host ?? this.host,
Expand All @@ -77,6 +86,8 @@ class NodeModel {
isFailover: isFailover ?? this.isFailover,
isDown: isDown ?? this.isDown,
trusted: trusted ?? this.trusted,
torEnabled: torEnabled ?? this.torEnabled,
clearnetEnabled: clearnetEnabled ?? this.clearnetEnabled,
);
}

Expand All @@ -98,6 +109,8 @@ class NodeModel {
map['isFailover'] = isFailover;
map['isDown'] = isDown;
map['trusted'] = trusted;
map['torEnabled'] = torEnabled;
map['clearEnabled'] = clearnetEnabled;
return map;
}

Expand Down
10 changes: 8 additions & 2 deletions lib/models/type_adaptors/node_model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading