mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-22 11:29:23 +00:00
quick (quite quick) and dirty (very dirty) tor/clearnet/both config option for coin network/node connections
This commit is contained in:
parent
6cfe9e9c0f
commit
d6d4df7822
19 changed files with 536 additions and 45 deletions
|
@ -77,6 +77,8 @@ class DbVersionMigrator with WalletDB {
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearEnabled: e.plainEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
@ -88,6 +90,8 @@ class DbVersionMigrator with WalletDB {
|
|||
name: node.name,
|
||||
id: node.id,
|
||||
useSSL: node.useSSL,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.plainEnabled,
|
||||
),
|
||||
prefs: prefs,
|
||||
failovers: failovers,
|
||||
|
|
|
@ -3,6 +3,8 @@ 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 {
|
||||
|
@ -10,6 +12,7 @@ class 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 = {};
|
||||
|
@ -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>();
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -99,6 +135,7 @@ class ClientManager {
|
|||
_heightCompleters.clear();
|
||||
_heights.clear();
|
||||
_subscriptions.clear();
|
||||
_mapNet.clear();
|
||||
_map.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
@ -42,6 +43,10 @@ typedef SparkMempoolData = ({
|
|||
|
||||
class WifiOnlyException implements Exception {}
|
||||
|
||||
class TorOnlyException implements Exception {}
|
||||
|
||||
class ClearnetOnlyException implements Exception {}
|
||||
|
||||
class ElectrumXNode {
|
||||
ElectrumXNode({
|
||||
required this.address,
|
||||
|
@ -49,12 +54,16 @@ class ElectrumXNode {
|
|||
required this.name,
|
||||
required this.id,
|
||||
required this.useSSL,
|
||||
required this.torEnabled,
|
||||
required this.clearEnabled,
|
||||
});
|
||||
final String address;
|
||||
final int port;
|
||||
final String name;
|
||||
final String id;
|
||||
final bool useSSL;
|
||||
final bool torEnabled;
|
||||
final bool clearEnabled;
|
||||
|
||||
factory ElectrumXNode.from(ElectrumXNode node) {
|
||||
return ElectrumXNode(
|
||||
|
@ -63,6 +72,8 @@ class ElectrumXNode {
|
|||
name: node.name,
|
||||
id: node.id,
|
||||
useSSL: node.useSSL,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.clearEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,6 +85,7 @@ class ElectrumXNode {
|
|||
|
||||
class ElectrumXClient {
|
||||
final CryptoCurrency cryptoCurrency;
|
||||
final TorPlainNetworkOption netType;
|
||||
|
||||
String get host => _host;
|
||||
late String _host;
|
||||
|
@ -90,6 +102,7 @@ class ElectrumXClient {
|
|||
ElectrumClient? getElectrumAdapter() =>
|
||||
ClientManager.sharedInstance.getClient(
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
netType: netType,
|
||||
);
|
||||
|
||||
late Prefs _prefs;
|
||||
|
@ -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 =
|
||||
|
@ -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.
|
||||
|
@ -193,6 +208,10 @@ class ElectrumXClient {
|
|||
failovers: failovers,
|
||||
globalEventBusForTesting: globalEventBusForTesting,
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
netType: TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -288,9 +319,10 @@ class ElectrumXClient {
|
|||
);
|
||||
}
|
||||
|
||||
ClientManager.sharedInstance.addClient(
|
||||
await ClientManager.sharedInstance.addClient(
|
||||
newClient,
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
netType: netType,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
class NodeTorMismatchConfigException implements Exception {}
|
|
@ -18,14 +18,17 @@ import 'package:uuid/uuid.dart';
|
|||
|
||||
import '../../../../models/node_model.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/active_wallet_provider.dart';
|
||||
import '../../../../providers/global/secure_store_provider.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
import '../../../../utilities/enums/sync_type_enum.dart';
|
||||
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
||||
import '../../../../utilities/test_node_connection.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart';
|
||||
|
@ -229,6 +232,11 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
}
|
||||
}
|
||||
|
||||
final torEnabled = formData.netOption == TorPlainNetworkOption.tor ||
|
||||
formData.netOption == TorPlainNetworkOption.both;
|
||||
final plainEnabled = formData.netOption == TorPlainNetworkOption.clear ||
|
||||
formData.netOption == TorPlainNetworkOption.both;
|
||||
|
||||
switch (viewType) {
|
||||
case AddEditNodeViewType.add:
|
||||
final NodeModel node = NodeModel(
|
||||
|
@ -243,6 +251,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
isFailover: formData.isFailover!,
|
||||
trusted: formData.trusted!,
|
||||
isDown: false,
|
||||
torEnabled: torEnabled,
|
||||
plainEnabled: plainEnabled,
|
||||
);
|
||||
|
||||
await ref.read(nodeServiceChangeNotifierProvider).add(
|
||||
|
@ -250,6 +260,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
formData.password,
|
||||
true,
|
||||
);
|
||||
await _notifyWalletsOfUpdatedNode();
|
||||
if (mounted) {
|
||||
Navigator.of(context)
|
||||
.popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
||||
|
@ -268,6 +279,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
isFailover: formData.isFailover!,
|
||||
trusted: formData.trusted!,
|
||||
isDown: false,
|
||||
torEnabled: torEnabled,
|
||||
plainEnabled: plainEnabled,
|
||||
);
|
||||
|
||||
await ref.read(nodeServiceChangeNotifierProvider).add(
|
||||
|
@ -275,6 +288,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
formData.password,
|
||||
true,
|
||||
);
|
||||
await _notifyWalletsOfUpdatedNode();
|
||||
if (mounted) {
|
||||
Navigator.of(context)
|
||||
.popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
||||
|
@ -283,6 +297,39 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _notifyWalletsOfUpdatedNode() async {
|
||||
final wallets =
|
||||
ref.read(pWallets).wallets.where((e) => e.info.coin == widget.coin);
|
||||
final prefs = ref.read(prefsChangeNotifierProvider);
|
||||
|
||||
switch (prefs.syncType) {
|
||||
case SyncingType.currentWalletOnly:
|
||||
for (final wallet in wallets) {
|
||||
if (ref.read(currentWalletIdProvider) == wallet.walletId) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
} else {
|
||||
unawaited(wallet.updateNode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyncingType.selectedWalletsAtStartup:
|
||||
final List<String> walletIdsToSync = prefs.walletIdsSyncOnStartup;
|
||||
for (final wallet in wallets) {
|
||||
if (walletIdsToSync.contains(wallet.walletId)) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
} else {
|
||||
unawaited(wallet.updateNode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyncingType.allWalletsOnStartup:
|
||||
for (final wallet in wallets) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
|
@ -568,10 +615,11 @@ class NodeFormData {
|
|||
String? name, host, login, password;
|
||||
int? port;
|
||||
bool? useSSL, isFailover, trusted;
|
||||
TorPlainNetworkOption? netOption;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "{ name: $name, host: $host, port: $port, useSSL: $useSSL, trusted: $trusted }";
|
||||
return "{ name: $name, host: $host, port: $port, useSSL: $useSSL, trusted: $trusted, netOption: $netOption }";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,6 +663,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
bool _trusted = false;
|
||||
int? port;
|
||||
late bool enableSSLCheckbox;
|
||||
late TorPlainNetworkOption netOption;
|
||||
|
||||
late final bool enableAuthFields;
|
||||
|
||||
|
@ -672,6 +721,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
ref.read(nodeFormDataProvider).useSSL = _useSSL;
|
||||
ref.read(nodeFormDataProvider).isFailover = _isFailover;
|
||||
ref.read(nodeFormDataProvider).trusted = _trusted;
|
||||
ref.read(nodeFormDataProvider).netOption = netOption;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -704,6 +754,15 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
_useSSL = node.useSSL;
|
||||
_isFailover = node.isFailover;
|
||||
_trusted = node.trusted ?? false;
|
||||
|
||||
if (node.torEnabled && !node.plainEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (node.plainEnabled && !node.torEnabled) {
|
||||
netOption = TorPlainNetworkOption.clear;
|
||||
} else {
|
||||
netOption = TorPlainNetworkOption.both;
|
||||
}
|
||||
|
||||
if (widget.coin is Epiccash) {
|
||||
enableSSLCheckbox = !node.host.startsWith("http");
|
||||
} else {
|
||||
|
@ -716,6 +775,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
});
|
||||
} else {
|
||||
enableSSLCheckbox = true;
|
||||
netOption = TorPlainNetworkOption.both;
|
||||
// default to port 3413
|
||||
// _portController.text = "3413";
|
||||
}
|
||||
|
@ -1168,7 +1228,139 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
RadioTextButton(
|
||||
label: "Only TOR traffic",
|
||||
enabled: !widget.readOnly,
|
||||
value: TorPlainNetworkOption.tor,
|
||||
groupValue: netOption,
|
||||
onChanged: (value) {
|
||||
if (!widget.readOnly) {
|
||||
setState(
|
||||
() => netOption = TorPlainNetworkOption.tor,
|
||||
);
|
||||
_updateState();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
RadioTextButton(
|
||||
label: "Only non-TOR traffic",
|
||||
enabled: !widget.readOnly,
|
||||
value: TorPlainNetworkOption.clear,
|
||||
groupValue: netOption,
|
||||
onChanged: (value) {
|
||||
if (!widget.readOnly) {
|
||||
setState(
|
||||
() => netOption = TorPlainNetworkOption.clear,
|
||||
);
|
||||
_updateState();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
RadioTextButton(
|
||||
label: "Allow both",
|
||||
enabled: !widget.readOnly,
|
||||
value: TorPlainNetworkOption.both,
|
||||
groupValue: netOption,
|
||||
onChanged: (value) {
|
||||
if (!widget.readOnly) {
|
||||
setState(
|
||||
() => netOption = TorPlainNetworkOption.both,
|
||||
);
|
||||
_updateState();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RadioTextButton<T> extends StatelessWidget {
|
||||
const RadioTextButton({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.label,
|
||||
required this.groupValue,
|
||||
required this.onChanged,
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
final T value;
|
||||
final String label;
|
||||
final T groupValue;
|
||||
final bool enabled;
|
||||
final void Function(T) onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConditionalParent(
|
||||
condition: Util.isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (value != groupValue) {
|
||||
onChanged.call(value);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: Radio<T>(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: value,
|
||||
groupValue: groupValue,
|
||||
onChanged: !enabled
|
||||
? null
|
||||
: (_) {
|
||||
if (value != groupValue) {
|
||||
onChanged.call(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: STextStyles.w500_14(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,15 +13,19 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import 'add_edit_node_view.dart';
|
||||
import '../../../../providers/global/active_wallet_provider.dart';
|
||||
import '../../../../providers/global/secure_store_provider.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/enums/sync_type_enum.dart';
|
||||
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
||||
import '../../../../utilities/test_node_connection.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../widgets/background.dart';
|
||||
|
@ -31,7 +35,7 @@ import '../../../../widgets/desktop/delete_button.dart';
|
|||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/desktop/secondary_button.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'add_edit_node_view.dart';
|
||||
|
||||
class NodeDetailsView extends ConsumerStatefulWidget {
|
||||
const NodeDetailsView({
|
||||
|
@ -59,6 +63,39 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
|
||||
bool _desktopReadOnly = true;
|
||||
|
||||
Future<void> _notifyWalletsOfUpdatedNode() async {
|
||||
final wallets =
|
||||
ref.read(pWallets).wallets.where((e) => e.info.coin == widget.coin);
|
||||
final prefs = ref.read(prefsChangeNotifierProvider);
|
||||
|
||||
switch (prefs.syncType) {
|
||||
case SyncingType.currentWalletOnly:
|
||||
for (final wallet in wallets) {
|
||||
if (ref.read(currentWalletIdProvider) == wallet.walletId) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
} else {
|
||||
unawaited(wallet.updateNode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyncingType.selectedWalletsAtStartup:
|
||||
final List<String> walletIdsToSync = prefs.walletIdsSyncOnStartup;
|
||||
for (final wallet in wallets) {
|
||||
if (walletIdsToSync.contains(wallet.walletId)) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
} else {
|
||||
unawaited(wallet.updateNode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyncingType.allWalletsOnStartup:
|
||||
for (final wallet in wallets) {
|
||||
unawaited(wallet.updateNode().then((value) => wallet.refresh()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
initState() {
|
||||
secureStore = ref.read(secureStoreProvider);
|
||||
|
@ -265,6 +302,16 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
.read(nodeServiceChangeNotifierProvider)
|
||||
.getNodeById(id: nodeId)!;
|
||||
|
||||
final TorPlainNetworkOption netOption;
|
||||
if (ref.read(nodeFormDataProvider).netOption != null) {
|
||||
netOption = ref.read(nodeFormDataProvider).netOption!;
|
||||
} else {
|
||||
netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.plainEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
final nodeFormData = NodeFormData()
|
||||
..useSSL = node.useSSL
|
||||
..trusted = node.trusted
|
||||
|
@ -272,7 +319,8 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
..host = node.host
|
||||
..login = node.loginName
|
||||
..port = node.port
|
||||
..isFailover = node.isFailover;
|
||||
..isFailover = node.isFailover
|
||||
..netOption = netOption;
|
||||
nodeFormData.password = await node.getPassword(
|
||||
ref.read(secureStoreProvider),
|
||||
);
|
||||
|
@ -338,6 +386,16 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
loginName: ref.read(nodeFormDataProvider).login,
|
||||
isFailover:
|
||||
ref.read(nodeFormDataProvider).isFailover,
|
||||
torEnabled:
|
||||
ref.read(nodeFormDataProvider).netOption ==
|
||||
TorPlainNetworkOption.tor ||
|
||||
ref.read(nodeFormDataProvider).netOption ==
|
||||
TorPlainNetworkOption.both,
|
||||
plainEnabled:
|
||||
ref.read(nodeFormDataProvider).netOption ==
|
||||
TorPlainNetworkOption.clear ||
|
||||
ref.read(nodeFormDataProvider).netOption ==
|
||||
TorPlainNetworkOption.both,
|
||||
);
|
||||
|
||||
await ref
|
||||
|
@ -347,6 +405,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
ref.read(nodeFormDataProvider).password,
|
||||
true,
|
||||
);
|
||||
await _notifyWalletsOfUpdatedNode();
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -59,6 +59,8 @@ class NodeService extends ChangeNotifier {
|
|||
enabled: savedNode.enabled,
|
||||
isFailover: savedNode.isFailover,
|
||||
trusted: savedNode.trusted,
|
||||
torEnabled: savedNode.torEnabled,
|
||||
plainEnabled: savedNode.plainEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -75,6 +77,8 @@ class NodeService extends ChangeNotifier {
|
|||
enabled: primaryNode.enabled,
|
||||
isFailover: primaryNode.isFailover,
|
||||
trusted: primaryNode.trusted,
|
||||
torEnabled: primaryNode.torEnabled,
|
||||
plainEnabled: primaryNode.plainEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,23 +11,23 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../db/hive/db.dart';
|
||||
import '../electrumx_rpc/electrumx_client.dart';
|
||||
import '../exceptions/electrumx/no_such_transaction.dart';
|
||||
import '../models/exchange/response_objects/trade.dart';
|
||||
import '../models/notification_model.dart';
|
||||
import '../utilities/logger.dart';
|
||||
import '../utilities/prefs.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
import 'exchange/exchange.dart';
|
||||
import 'exchange/exchange_response.dart';
|
||||
import 'node_service.dart';
|
||||
import 'notifications_api.dart';
|
||||
import 'trade_service.dart';
|
||||
import 'wallets.dart';
|
||||
import '../utilities/logger.dart';
|
||||
import '../utilities/prefs.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
|
||||
import 'exchange/exchange.dart';
|
||||
|
||||
class NotificationsService extends ChangeNotifier {
|
||||
late NodeService nodeService;
|
||||
|
@ -136,12 +136,26 @@ class NotificationsService extends ChangeNotifier {
|
|||
final node = nodeService.getPrimaryNodeFor(currency: coin);
|
||||
if (node != null) {
|
||||
if (wallet is ElectrumXInterface) {
|
||||
if (prefs.useTor) {
|
||||
if (node.plainEnabled && !node.torEnabled) {
|
||||
// just ignore I guess??
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (node.torEnabled && !node.plainEnabled) {
|
||||
// just ignore I guess??
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final eNode = ElectrumXNode(
|
||||
address: node.host,
|
||||
port: node.port,
|
||||
name: node.name,
|
||||
id: node.id,
|
||||
useSSL: node.useSSL,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.plainEnabled,
|
||||
);
|
||||
final failovers = nodeService
|
||||
.failoverNodesFor(currency: coin)
|
||||
|
@ -152,6 +166,8 @@ class NotificationsService extends ChangeNotifier {
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.plainEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:on_chain/ada/ada.dart';
|
||||
import 'package:on_chain/ada/src/provider/provider/provider.dart';
|
||||
import 'package:socks5_proxy/socks.dart';
|
||||
|
||||
import '../networking/http.dart';
|
||||
|
@ -25,6 +24,7 @@ import 'test_epic_box_connection.dart';
|
|||
import 'test_eth_node_connection.dart';
|
||||
import 'test_monero_node_connection.dart';
|
||||
import 'test_stellar_node_connection.dart';
|
||||
import 'tor_plain_net_option_enum.dart';
|
||||
|
||||
Future<bool> _xmrHelper(
|
||||
NodeFormData nodeFormData,
|
||||
|
@ -45,7 +45,6 @@ Future<bool> _xmrHelper(
|
|||
|
||||
final uriString = "${uri.scheme}://${uri.host}:${port ?? 0}$path";
|
||||
|
||||
|
||||
if (proxyInfo == null && uri.host.endsWith(".onion")) {
|
||||
return false;
|
||||
}
|
||||
|
@ -93,6 +92,24 @@ Future<bool> testNodeConnection({
|
|||
}) async {
|
||||
final formData = nodeFormData;
|
||||
|
||||
if (ref.read(prefsChangeNotifierProvider).useTor) {
|
||||
if (formData.netOption! == TorPlainNetworkOption.clear) {
|
||||
Logging.instance.log(
|
||||
"This node is configured for non-TOR only but TOR is enabled",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (formData.netOption! == TorPlainNetworkOption.tor) {
|
||||
Logging.instance.log(
|
||||
"This node is configured for TOR only but TOR is disabled",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool testPassed = false;
|
||||
|
||||
switch (cryptoCurrency) {
|
||||
|
@ -111,9 +128,7 @@ Future<bool> testNodeConnection({
|
|||
|
||||
case CryptonoteCurrency():
|
||||
try {
|
||||
final proxyInfo = ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.useTor
|
||||
final proxyInfo = ref.read(prefsChangeNotifierProvider).useTor
|
||||
? ref.read(pTorService).getProxyInfo()
|
||||
: null;
|
||||
|
||||
|
@ -202,9 +217,7 @@ Future<bool> testNodeConnection({
|
|||
"action": "version",
|
||||
},
|
||||
),
|
||||
proxyInfo: ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.useTor
|
||||
proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
|
||||
? ref.read(pTorService).getProxyInfo()
|
||||
: null,
|
||||
);
|
||||
|
@ -245,9 +258,7 @@ Future<bool> testNodeConnection({
|
|||
case Cardano():
|
||||
try {
|
||||
final client = HttpClient();
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.useTor) {
|
||||
if (ref.read(prefsChangeNotifierProvider).useTor) {
|
||||
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
||||
final proxySettings = ProxySettings(
|
||||
proxyInfo.host,
|
||||
|
|
23
lib/utilities/tor_plain_net_option_enum.dart
Normal file
23
lib/utilities/tor_plain_net_option_enum.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
enum TorPlainNetworkOption {
|
||||
tor,
|
||||
clear,
|
||||
both;
|
||||
|
||||
bool allowsTor() => this == tor || this == both;
|
||||
bool allowsClear() => this == clear || this == both;
|
||||
|
||||
static TorPlainNetworkOption fromNodeData(
|
||||
bool torEnabled,
|
||||
bool clearEnabled,
|
||||
) {
|
||||
if (clearEnabled && torEnabled) {
|
||||
return TorPlainNetworkOption.both;
|
||||
} else if (torEnabled) {
|
||||
return TorPlainNetworkOption.tor;
|
||||
} else if (clearEnabled) {
|
||||
return TorPlainNetworkOption.clear;
|
||||
} else {
|
||||
return TorPlainNetworkOption.both;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1338,6 +1338,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
|||
name: node.name,
|
||||
useSSL: node.useSSL,
|
||||
id: node.id,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.plainEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1352,6 +1354,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearEnabled: e.plainEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
|
@ -467,7 +467,22 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
|
|||
final host = Uri.parse(node.host).host;
|
||||
({InternetAddress host, int port})? proxy;
|
||||
if (prefs.useTor) {
|
||||
if (node.plainEnabled && !node.torEnabled) {
|
||||
libMoneroWallet?.stopAutoSaving();
|
||||
libMoneroWallet?.stopListeners();
|
||||
libMoneroWallet?.stopSyncing();
|
||||
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
|
||||
throw Exception("TOR - clearnet mismatch");
|
||||
}
|
||||
proxy = TorService.sharedInstance.getProxyInfo();
|
||||
} else {
|
||||
if (!node.plainEnabled && node.torEnabled) {
|
||||
libMoneroWallet?.stopAutoSaving();
|
||||
libMoneroWallet?.stopListeners();
|
||||
libMoneroWallet?.stopSyncing();
|
||||
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
|
||||
throw Exception("TOR - clearnet mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
_setSyncStatus(lib_monero_compat.ConnectingSyncStatus());
|
||||
|
@ -495,6 +510,9 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
|
|||
proxy == null ? null : "${proxy.host.address}:${proxy.port}",
|
||||
);
|
||||
}
|
||||
libMoneroWallet?.startSyncing();
|
||||
libMoneroWallet?.startListeners();
|
||||
libMoneroWallet?.startAutoSaving();
|
||||
|
||||
_setSyncStatus(lib_monero_compat.ConnectedSyncStatus());
|
||||
} catch (e, s) {
|
||||
|
@ -1020,6 +1038,26 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
|
|||
return;
|
||||
}
|
||||
|
||||
final node = getCurrentNode();
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (node.plainEnabled && !node.torEnabled) {
|
||||
libMoneroWallet?.stopAutoSaving();
|
||||
libMoneroWallet?.stopListeners();
|
||||
libMoneroWallet?.stopSyncing();
|
||||
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
|
||||
throw Exception("TOR - clearnet mismatch");
|
||||
}
|
||||
} else {
|
||||
if (!node.plainEnabled && node.torEnabled) {
|
||||
libMoneroWallet?.stopAutoSaving();
|
||||
libMoneroWallet?.stopListeners();
|
||||
libMoneroWallet?.stopSyncing();
|
||||
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
|
||||
throw Exception("TOR - clearnet mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
// this acquire should be almost instant due to above check.
|
||||
// Slight possibility of race but should be irrelevant
|
||||
await refreshMutex.acquire();
|
||||
|
|
|
@ -906,6 +906,8 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
name: node.name,
|
||||
useSSL: node.useSSL,
|
||||
id: node.id,
|
||||
torEnabled: node.torEnabled,
|
||||
clearEnabled: node.plainEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -919,6 +921,8 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearEnabled: e.plainEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
|
@ -13,6 +13,8 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../notifications/show_flush_bar.dart';
|
||||
import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
|
||||
import '../pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart';
|
||||
|
@ -26,6 +28,7 @@ import '../utilities/default_nodes.dart';
|
|||
import '../utilities/enums/sync_type_enum.dart';
|
||||
import '../utilities/test_node_connection.dart';
|
||||
import '../utilities/text_styles.dart';
|
||||
import '../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../utilities/util.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'conditional_parent.dart';
|
||||
|
@ -33,7 +36,6 @@ import 'custom_buttons/blue_text_button.dart';
|
|||
import 'expandable.dart';
|
||||
import 'node_options_sheet.dart';
|
||||
import 'rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class NodeCard extends ConsumerStatefulWidget {
|
||||
const NodeCard({
|
||||
|
@ -165,6 +167,15 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
|||
text: "Connect",
|
||||
enabled: _status == "Disconnected",
|
||||
onTap: () async {
|
||||
final TorPlainNetworkOption netOption;
|
||||
if (_node.torEnabled && !_node.plainEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (_node.plainEnabled && !_node.torEnabled) {
|
||||
netOption = TorPlainNetworkOption.clear;
|
||||
} else {
|
||||
netOption = TorPlainNetworkOption.both;
|
||||
}
|
||||
|
||||
final nodeFormData = NodeFormData()
|
||||
..useSSL = _node.useSSL
|
||||
..trusted = _node.trusted
|
||||
|
@ -172,6 +183,7 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
|||
..host = _node.host
|
||||
..login = _node.loginName
|
||||
..port = _node.port
|
||||
..netOption = netOption
|
||||
..isFailover = _node.isFailover;
|
||||
nodeFormData.password = await _node.getPassword(
|
||||
ref.read(secureStoreProvider),
|
||||
|
|
|
@ -27,6 +27,7 @@ import '../utilities/default_nodes.dart';
|
|||
import '../utilities/enums/sync_type_enum.dart';
|
||||
import '../utilities/test_node_connection.dart';
|
||||
import '../utilities/text_styles.dart';
|
||||
import '../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'rounded_white_container.dart';
|
||||
|
||||
|
@ -256,6 +257,15 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
ref.read(secureStoreProvider),
|
||||
);
|
||||
if (context.mounted) {
|
||||
final TorPlainNetworkOption netOption;
|
||||
if (node.torEnabled && !node.plainEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (node.plainEnabled &&
|
||||
!node.torEnabled) {
|
||||
netOption = TorPlainNetworkOption.clear;
|
||||
} else {
|
||||
netOption = TorPlainNetworkOption.both;
|
||||
}
|
||||
final canConnect = await testNodeConnection(
|
||||
context: context,
|
||||
nodeFormData: NodeFormData()
|
||||
|
@ -266,6 +276,7 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
..port = node.port
|
||||
..useSSL = node.useSSL
|
||||
..isFailover = node.isFailover
|
||||
..netOption = netOption
|
||||
..trusted = node.trusted,
|
||||
cryptoCurrency: coin,
|
||||
ref: ref,
|
||||
|
|
|
@ -168,6 +168,8 @@ void main() {
|
|||
name: "some name",
|
||||
id: "some ID",
|
||||
useSSL: true,
|
||||
torEnabled: true,
|
||||
clearEnabled: true,
|
||||
);
|
||||
|
||||
final client =
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_test/hive_test.dart';
|
||||
import 'package:stackwallet/app_config.dart';
|
||||
import 'package:stackwallet/db/hive/db.dart';
|
||||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/services/node_service.dart';
|
||||
import 'package:stackwallet/app_config.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
|
@ -48,6 +48,8 @@ void main() {
|
|||
coinName: "bitcoin",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
);
|
||||
await service.setPrimaryNodeFor(
|
||||
coin: Bitcoin(CryptoCurrencyNetwork.main),
|
||||
|
@ -129,6 +131,8 @@ void main() {
|
|||
coinName: "bitcoin",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
);
|
||||
final nodeB = NodeModel(
|
||||
host: "host2",
|
||||
|
@ -140,6 +144,8 @@ void main() {
|
|||
coinName: "monero",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
);
|
||||
final nodeC = NodeModel(
|
||||
host: "host3",
|
||||
|
@ -151,6 +157,8 @@ void main() {
|
|||
coinName: "epicCash",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
|
|
|
@ -37,6 +37,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -51,6 +53,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -112,6 +116,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -126,6 +132,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -188,6 +196,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -202,6 +212,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ void main() {
|
|||
final mockPrefs = MockPrefs();
|
||||
final mockNodeService = MockNodeService();
|
||||
|
||||
when(mockNodeService.getNodeById(id: "node id")).thenAnswer(
|
||||
(realInvocation) => NodeModel(
|
||||
when(mockNodeService.getNodeById(id: "node id"))
|
||||
.thenAnswer((realInvocation) => NodeModel(
|
||||
host: "127.0.0.1",
|
||||
port: 2000,
|
||||
name: "Some other name",
|
||||
|
@ -35,7 +35,10 @@ void main() {
|
|||
enabled: true,
|
||||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false));
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
));
|
||||
|
||||
when(mockNodeService.getPrimaryNodeFor(
|
||||
currency: Bitcoin(CryptoCurrencyNetwork.main)))
|
||||
|
@ -48,6 +51,8 @@ void main() {
|
|||
enabled: true,
|
||||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
isDown: false));
|
||||
|
||||
await tester.pumpWidget(
|
||||
|
@ -109,6 +114,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -125,6 +132,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -186,6 +195,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -202,6 +213,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
plainEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue