mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-22 11:29:23 +00:00
commit
1ce4bee68c
57 changed files with 1178 additions and 120 deletions
|
@ -77,6 +77,8 @@ class DbVersionMigrator with WalletDB {
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearnetEnabled: e.clearnetEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
@ -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,
|
||||
|
|
|
@ -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.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(
|
||||
|
@ -63,6 +72,8 @@ class ElectrumXNode {
|
|||
name: node.name,
|
||||
id: node.id,
|
||||
useSSL: node.useSSL,
|
||||
torEnabled: node.torEnabled,
|
||||
clearnetEnabled: node.clearnetEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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.clearnetEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,8 @@
|
|||
class NodeTorMismatchConfigException implements Exception {
|
||||
final String message;
|
||||
|
||||
NodeTorMismatchConfigException({required this.message});
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import '../utilities/default_nodes.dart';
|
||||
import '../utilities/flutter_secure_storage_interface.dart';
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -64,6 +71,8 @@ class NodeModel {
|
|||
bool? isFailover,
|
||||
bool? isDown,
|
||||
bool? trusted,
|
||||
bool? torEnabled,
|
||||
bool? clearnetEnabled,
|
||||
}) {
|
||||
return NodeModel(
|
||||
host: host ?? this.host,
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -98,6 +109,8 @@ class NodeModel {
|
|||
map['isFailover'] = isFailover;
|
||||
map['isDown'] = isDown;
|
||||
map['trusted'] = trusted;
|
||||
map['torEnabled'] = torEnabled;
|
||||
map['clearEnabled'] = clearnetEnabled;
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,13 +28,15 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
|
|||
isFailover: fields[8] as bool,
|
||||
isDown: fields[9] as bool,
|
||||
trusted: fields[10] as bool?,
|
||||
torEnabled: fields[11] as bool? ?? true,
|
||||
clearnetEnabled: fields[12] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, NodeModel obj) {
|
||||
writer
|
||||
..writeByte(11)
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
|
@ -56,7 +58,11 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
|
|||
..writeByte(9)
|
||||
..write(obj.isDown)
|
||||
..writeByte(10)
|
||||
..write(obj.trusted);
|
||||
..write(obj.trusted)
|
||||
..writeByte(11)
|
||||
..write(obj.torEnabled)
|
||||
..writeByte(12)
|
||||
..write(obj.clearnetEnabled);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:mutex/mutex.dart';
|
|||
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart';
|
||||
import '../../providers/global/node_service_provider.dart';
|
||||
import '../../providers/global/prefs_provider.dart';
|
||||
import '../../providers/global/secure_store_provider.dart';
|
||||
import '../../providers/global/wallets_provider.dart';
|
||||
|
@ -25,7 +26,9 @@ import '../../utilities/assets.dart';
|
|||
import '../../utilities/biometrics.dart';
|
||||
import '../../utilities/flutter_secure_storage_interface.dart';
|
||||
import '../../utilities/show_loading.dart';
|
||||
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../../utilities/text_styles.dart';
|
||||
import '../../utilities/util.dart';
|
||||
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
||||
import '../../widgets/background.dart';
|
||||
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
|
@ -101,6 +104,20 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
final walletId = widget.routeOnSuccessArguments as String;
|
||||
|
||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||
|
||||
final canContinue = await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: wallet.cryptoCurrency,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService: ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: false,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
loadFuture =
|
||||
|
|
|
@ -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,
|
||||
clearnetEnabled: 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,
|
||||
clearnetEnabled: 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;
|
||||
|
@ -422,7 +469,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
condition: isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: 500,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -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.clearnetEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (node.clearnetEnabled && !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,145 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (widget.coin is! Ethereum)
|
||||
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.clearnetEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
clearnetEnabled:
|
||||
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();
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1272,6 +1272,8 @@ abstract class SWB {
|
|||
loginName: node['loginName'] as String?,
|
||||
isFailover: node['isFailover'] as bool,
|
||||
isDown: node['isDown'] as bool,
|
||||
torEnabled: node['torEnabled'] as bool? ?? true,
|
||||
clearnetEnabled: node['plainEnabled'] as bool? ?? true,
|
||||
),
|
||||
node["password"] as String?,
|
||||
true,
|
||||
|
|
|
@ -22,6 +22,7 @@ import '../../../utilities/amount/amount.dart';
|
|||
import '../../../utilities/amount/amount_formatter.dart';
|
||||
import '../../../utilities/constants.dart';
|
||||
import '../../../utilities/show_loading.dart';
|
||||
import '../../../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../utilities/util.dart';
|
||||
import '../../../wallets/crypto_currency/coins/firo.dart';
|
||||
|
@ -117,6 +118,19 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
onTap: () async {
|
||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||
|
||||
final canContinue = await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: coin,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService: ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: true,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
loadFuture =
|
||||
|
|
|
@ -21,6 +21,7 @@ import '../../../themes/stack_colors.dart';
|
|||
import '../../../utilities/amount/amount.dart';
|
||||
import '../../../utilities/constants.dart';
|
||||
import '../../../utilities/show_loading.dart';
|
||||
import '../../../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../utilities/util.dart';
|
||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
@ -83,6 +84,20 @@ class WalletListItem extends ConsumerWidget {
|
|||
.read(pWallets)
|
||||
.wallets
|
||||
.firstWhere((e) => e.info.coin == coin);
|
||||
|
||||
final canContinue = await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: coin,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService: ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: true,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
loadFuture =
|
||||
|
|
|
@ -12,10 +12,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../providers/global/active_wallet_provider.dart';
|
||||
import '../../providers/global/node_service_provider.dart';
|
||||
import '../../providers/global/prefs_provider.dart';
|
||||
import '../../providers/global/wallets_provider.dart';
|
||||
import '../../themes/stack_colors.dart';
|
||||
import '../../utilities/constants.dart';
|
||||
import '../../utilities/show_loading.dart';
|
||||
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../../utilities/util.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
||||
|
@ -81,6 +84,22 @@ class CoinWalletsTable extends ConsumerWidget {
|
|||
|
||||
final wallet =
|
||||
ref.read(pWallets).getWallet(walletIds[i]);
|
||||
|
||||
final canContinue =
|
||||
await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: coin,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService:
|
||||
ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: true,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
loadFuture = wallet
|
||||
|
|
|
@ -20,6 +20,7 @@ import '../../themes/coin_icon_provider.dart';
|
|||
import '../../themes/stack_colors.dart';
|
||||
import '../../utilities/amount/amount.dart';
|
||||
import '../../utilities/show_loading.dart';
|
||||
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../../utilities/text_styles.dart';
|
||||
import '../../utilities/util.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
@ -123,6 +124,19 @@ class _DesktopWalletSummaryRowState
|
|||
final wallet = ref.read(pWallets).wallets.firstWhere(
|
||||
(e) => e.cryptoCurrency.identifier == widget.coin.identifier);
|
||||
|
||||
final canContinue = await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: wallet.cryptoCurrency,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService: ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: true,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
loadFuture =
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../db/hive/db.dart';
|
||||
import '../models/node_model.dart';
|
||||
|
@ -58,6 +59,8 @@ class NodeService extends ChangeNotifier {
|
|||
enabled: savedNode.enabled,
|
||||
isFailover: savedNode.isFailover,
|
||||
trusted: savedNode.trusted,
|
||||
torEnabled: savedNode.torEnabled,
|
||||
clearnetEnabled: savedNode.clearnetEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -74,6 +77,8 @@ class NodeService extends ChangeNotifier {
|
|||
enabled: primaryNode.enabled,
|
||||
isFailover: primaryNode.isFailover,
|
||||
trusted: primaryNode.trusted,
|
||||
torEnabled: primaryNode.torEnabled,
|
||||
clearnetEnabled: primaryNode.clearnetEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -253,7 +258,9 @@ class NodeService extends ChangeNotifier {
|
|||
enabled: true,
|
||||
coinName: coin.identifier,
|
||||
isFailover: true,
|
||||
torEnabled: nodeMap["torEnabled"] == "true",
|
||||
isDown: nodeMap["isDown"] == "true",
|
||||
clearnetEnabled: nodeMap["plainEnabled"] == "true",
|
||||
);
|
||||
final currentNode = getNodeById(id: nodeMap["id"] as String);
|
||||
if (currentNode != null) {
|
||||
|
|
|
@ -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.clearnetEnabled && !node.torEnabled) {
|
||||
// just ignore I guess??
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (node.torEnabled && !node.clearnetEnabled) {
|
||||
// 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,
|
||||
clearnetEnabled: node.clearnetEnabled,
|
||||
);
|
||||
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,
|
||||
clearnetEnabled: node.clearnetEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
97
lib/utilities/show_node_tor_settings_mismatch.dart
Normal file
97
lib/utilities/show_node_tor_settings_mismatch.dart
Normal file
|
@ -0,0 +1,97 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../services/node_service.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../widgets/conditional_parent.dart';
|
||||
import '../widgets/desktop/desktop_dialog.dart';
|
||||
import '../widgets/desktop/primary_button.dart';
|
||||
import '../widgets/desktop/secondary_button.dart';
|
||||
import '../widgets/stack_dialog.dart';
|
||||
import 'prefs.dart';
|
||||
import 'text_styles.dart';
|
||||
import 'util.dart';
|
||||
|
||||
Future<bool> checkShowNodeTorSettingsMismatch({
|
||||
required BuildContext context,
|
||||
required CryptoCurrency currency,
|
||||
required Prefs prefs,
|
||||
required NodeService nodeService,
|
||||
required bool allowCancel,
|
||||
bool rootNavigator = false,
|
||||
}) async {
|
||||
final node =
|
||||
nodeService.getPrimaryNodeFor(currency: currency) ?? currency.defaultNode;
|
||||
if (prefs.useTor) {
|
||||
if (node.torEnabled) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (node.clearnetEnabled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => ConditionalParent(
|
||||
condition: Util.isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: ConditionalParent(
|
||||
condition: !Util.isDesktop,
|
||||
builder: (child) => StackDialogBase(
|
||||
child: child,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Attention! Node connection issue detected. "
|
||||
"The current node will not sync due to its connectivity settings. "
|
||||
"Please adjust the node settings or enable/disable TOR.",
|
||||
style: STextStyles.w600_16(context),
|
||||
),
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 32 : 24,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
allowCancel
|
||||
? Expanded(
|
||||
child: SecondaryButton(
|
||||
buttonHeight: Util.isDesktop ? ButtonHeight.l : null,
|
||||
label: "Cancel",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const Spacer(),
|
||||
SizedBox(
|
||||
width: Util.isDesktop ? 24 : 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
buttonHeight: Util.isDesktop ? ButtonHeight.l : null,
|
||||
label: "Continue",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return result ?? true;
|
||||
}
|
|
@ -15,6 +15,7 @@ import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit
|
|||
import '../services/tor_service.dart';
|
||||
import 'logger.dart';
|
||||
import 'prefs.dart';
|
||||
import 'tor_plain_net_option_enum.dart';
|
||||
|
||||
Future<bool> _testEpicBoxNodeConnection(Uri uri) async {
|
||||
final HTTP client = HTTP();
|
||||
|
@ -50,6 +51,17 @@ Future<NodeFormData?> testEpicNodeConnection(NodeFormData data) async {
|
|||
if (data.host == null || data.port == null || data.useSSL == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Prefs.instance.useTor) {
|
||||
if (data.netOption == TorPlainNetworkOption.clear) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (data.netOption == TorPlainNetworkOption.tor) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const String path_postfix = "/v1/version";
|
||||
|
||||
if (data.host!.startsWith("https://")) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -186,7 +201,7 @@ Future<bool> testNodeConnection({
|
|||
case Stellar():
|
||||
try {
|
||||
testPassed =
|
||||
await testStellarNodeConnection(formData.host!, formData.port!);
|
||||
await testStellarNodeConnection(formData.host!, formData.port!);
|
||||
} catch (_) {}
|
||||
break;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,6 +77,8 @@ class Banano extends NanoCurrency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -239,6 +239,8 @@ class Bitcoin extends Bip39HDCurrency
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -252,6 +254,8 @@ class Bitcoin extends Bip39HDCurrency
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test4:
|
||||
|
@ -265,6 +269,8 @@ class Bitcoin extends Bip39HDCurrency
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -73,6 +73,8 @@ class BitcoinFrost extends FrostCurrency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -86,6 +88,8 @@ class BitcoinFrost extends FrostCurrency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test4:
|
||||
|
@ -99,6 +103,8 @@ class BitcoinFrost extends FrostCurrency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -298,6 +298,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -311,6 +313,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -77,6 +77,8 @@ class Cardano extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
@ -91,7 +93,8 @@ class Cardano extends Bip39Currency {
|
|||
int get fractionDigits => 6;
|
||||
|
||||
@override
|
||||
String get genesisHash => "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f";
|
||||
String get genesisHash =>
|
||||
"f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f";
|
||||
|
||||
@override
|
||||
bool get hasBuySupport => false;
|
||||
|
|
|
@ -189,6 +189,8 @@ class Dash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -189,6 +189,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -202,6 +204,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -289,6 +289,8 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -80,6 +80,8 @@ class Epiccash extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -57,6 +57,8 @@ class Ethereum extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
@ -231,6 +231,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -257,6 +259,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -220,6 +220,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -233,6 +235,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -72,6 +72,8 @@ class Monero extends CryptonoteCurrency {
|
|||
isFailover: true,
|
||||
isDown: false,
|
||||
trusted: true,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -102,6 +102,8 @@ class Namecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
// case CryptoCurrencyNetwork.test:
|
||||
// TODO: [prio=low] Add testnet support.
|
||||
|
|
|
@ -77,6 +77,8 @@ class Nano extends NanoCurrency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -97,6 +97,8 @@ class Particl extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
// case CryptoCurrencyNetwork.test:
|
||||
// TODO: [prio=low] Add testnet.
|
||||
|
|
|
@ -103,6 +103,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -116,6 +118,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -55,6 +55,8 @@ class Solana extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
default:
|
||||
throw Exception("Unsupported network: $network");
|
||||
|
|
|
@ -68,6 +68,8 @@ class Stellar extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
|
@ -81,6 +83,8 @@ class Stellar extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -118,6 +118,8 @@ class Tezos extends Bip39Currency {
|
|||
coinName: identifier,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -72,6 +72,8 @@ class Wownero extends CryptonoteCurrency {
|
|||
isFailover: true,
|
||||
isDown: false,
|
||||
trusted: true,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
default:
|
||||
|
|
|
@ -1338,6 +1338,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
|||
name: node.name,
|
||||
useSSL: node.useSSL,
|
||||
id: node.id,
|
||||
torEnabled: node.torEnabled,
|
||||
clearnetEnabled: node.clearnetEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1352,6 +1354,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearnetEnabled: e.clearnetEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base.dart';
|
||||
|
@ -7,19 +6,21 @@ import 'package:blockchain_utils/bip/cardano/cip1852/cip1852.dart';
|
|||
import 'package:blockchain_utils/bip/cardano/cip1852/conf/cip1852_coins.dart';
|
||||
import 'package:blockchain_utils/bip/cardano/mnemonic/cardano_icarus_seed_generator.dart';
|
||||
import 'package:blockchain_utils/bip/cardano/shelley/cardano_shelley.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:on_chain/ada/ada.dart';
|
||||
import 'package:socks5_proxy/socks.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart' as isar;
|
||||
import '../../../models/paymint/fee_object_model.dart';
|
||||
import '../../../networking/http.dart';
|
||||
import '../../../services/tor_service.dart';
|
||||
import '../../../utilities/amount/amount.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/prefs.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../api/cardano/blockfrost_http_provider.dart';
|
||||
import '../../crypto_currency/crypto_currency.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
|
@ -30,7 +31,6 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
|
||||
// Source: https://cips.cardano.org/cip/CIP-1852
|
||||
static const String _addressDerivationPath = "m/1852'/1815'/0'/0/0";
|
||||
static final HTTP _httpClient = HTTP();
|
||||
|
||||
BlockforestProvider? blockfrostProvider;
|
||||
|
||||
|
@ -138,12 +138,12 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
final fee = params.calculateFee(284).toInt();
|
||||
|
||||
return FeeObject(
|
||||
numberOfBlocksFast: 2,
|
||||
numberOfBlocksAverage: 2,
|
||||
numberOfBlocksSlow: 2,
|
||||
fast: fee,
|
||||
medium: fee,
|
||||
slow: fee,
|
||||
numberOfBlocksFast: 2,
|
||||
numberOfBlocksAverage: 2,
|
||||
numberOfBlocksSlow: 2,
|
||||
fast: fee,
|
||||
medium: fee,
|
||||
slow: fee,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
|
@ -184,41 +184,59 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
totalBalance += BigInt.parse(utxo.amount.first.quantity);
|
||||
}
|
||||
|
||||
if (leftAmountForUtxos > BigInt.parse("0") || totalBalance < txData.amount!.raw) {
|
||||
if (leftAmountForUtxos > BigInt.parse("0") ||
|
||||
totalBalance < txData.amount!.raw) {
|
||||
throw Exception("Insufficient balance");
|
||||
}
|
||||
|
||||
final bip32 = CardanoIcarusBip32.fromSeed(CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||
final bip32 = CardanoIcarusBip32.fromSeed(
|
||||
CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||
final spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
||||
|
||||
// Calculate fees with example tx
|
||||
final exampleFee = ADAHelper.toLovelaces("0.10");
|
||||
final change = TransactionOutput(address: ADABaseAddress((await getCurrentReceivingAddress())!.value), amount: Value(coin: totalBalance - (txData.amount!.raw)));
|
||||
final change = TransactionOutput(
|
||||
address: ADABaseAddress((await getCurrentReceivingAddress())!.value),
|
||||
amount: Value(coin: totalBalance - (txData.amount!.raw)));
|
||||
final body = TransactionBody(
|
||||
inputs: listOfUtxosToBeUsed.map((e) => TransactionInput(transactionId: TransactionHash.fromHex(e.txHash), index: e.outputIndex)).toList(),
|
||||
outputs: [change, TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw - exampleFee))],
|
||||
inputs: listOfUtxosToBeUsed
|
||||
.map((e) => TransactionInput(
|
||||
transactionId: TransactionHash.fromHex(e.txHash),
|
||||
index: e.outputIndex))
|
||||
.toList(),
|
||||
outputs: [
|
||||
change,
|
||||
TransactionOutput(
|
||||
address: ADABaseAddress(txData.recipients!.first.address),
|
||||
amount: Value(coin: txData.amount!.raw - exampleFee))
|
||||
],
|
||||
fee: exampleFee,
|
||||
);
|
||||
final exampleTx = ADATransaction(
|
||||
body: body,
|
||||
witnessSet: TransactionWitnessSet(vKeys: [
|
||||
body: body,
|
||||
witnessSet: TransactionWitnessSet(
|
||||
vKeys: [
|
||||
privateKey.createSignatureWitness(body.toHash().data),
|
||||
],)
|
||||
,);
|
||||
final params = await blockfrostProvider!.request(BlockfrostRequestLatestEpochProtocolParameters());
|
||||
],
|
||||
),
|
||||
);
|
||||
final params = await blockfrostProvider!
|
||||
.request(BlockfrostRequestLatestEpochProtocolParameters());
|
||||
final fee = params.calculateFee(exampleTx.size);
|
||||
|
||||
// Check if we are sending all balance, which means no change and only one output for recipient.
|
||||
if (totalBalance == txData.amount!.raw) {
|
||||
final List<TxRecipient> newRecipients = [(
|
||||
address: txData.recipients!.first.address,
|
||||
amount: Amount(
|
||||
rawValue: txData.amount!.raw - fee,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
isChange: txData.recipients!.first.isChange,
|
||||
),];
|
||||
final List<TxRecipient> newRecipients = [
|
||||
(
|
||||
address: txData.recipients!.first.address,
|
||||
amount: Amount(
|
||||
rawValue: txData.amount!.raw - fee,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
isChange: txData.recipients!.first.isChange,
|
||||
),
|
||||
];
|
||||
return txData.copyWith(
|
||||
fee: Amount(
|
||||
rawValue: fee,
|
||||
|
@ -232,8 +250,10 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
}
|
||||
|
||||
// Minimum change in Cardano is 1 ADA and we need to have enough balance for that
|
||||
if (totalBalance - (txData.amount!.raw + fee) < ADAHelper.toLovelaces("1")) {
|
||||
throw Exception("Not enough balance for change. By network rules, please either send all balance or leave at least 1 ADA change.");
|
||||
if (totalBalance - (txData.amount!.raw + fee) <
|
||||
ADAHelper.toLovelaces("1")) {
|
||||
throw Exception(
|
||||
"Not enough balance for change. By network rules, please either send all balance or leave at least 1 ADA change.");
|
||||
}
|
||||
|
||||
return txData.copyWith(
|
||||
|
@ -266,7 +286,6 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
),
|
||||
);
|
||||
|
||||
|
||||
var leftAmountForUtxos = txData.amount!.raw + txData.fee!.raw;
|
||||
final listOfUtxosToBeUsed = <ADAAccountUTXOResponse>[];
|
||||
var totalBalance = BigInt.zero;
|
||||
|
@ -285,31 +304,53 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
totalUtxoAmount += BigInt.parse(utxo.amount.first.quantity);
|
||||
}
|
||||
|
||||
final bip32 = CardanoIcarusBip32.fromSeed(CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||
final bip32 = CardanoIcarusBip32.fromSeed(
|
||||
CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||
final spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
||||
|
||||
final change = TransactionOutput(address: ADABaseAddress((await getCurrentReceivingAddress())!.value), amount: Value(coin: totalUtxoAmount - (txData.amount!.raw + txData.fee!.raw)));
|
||||
final change = TransactionOutput(
|
||||
address: ADABaseAddress((await getCurrentReceivingAddress())!.value),
|
||||
amount: Value(
|
||||
coin: totalUtxoAmount - (txData.amount!.raw + txData.fee!.raw)));
|
||||
List<TransactionOutput> outputs = [];
|
||||
if (totalBalance == (txData.amount!.raw + txData.fee!.raw)) {
|
||||
outputs = [TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw))];
|
||||
outputs = [
|
||||
TransactionOutput(
|
||||
address: ADABaseAddress(txData.recipients!.first.address),
|
||||
amount: Value(coin: txData.amount!.raw))
|
||||
];
|
||||
} else {
|
||||
outputs = [change, TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw))];
|
||||
outputs = [
|
||||
change,
|
||||
TransactionOutput(
|
||||
address: ADABaseAddress(txData.recipients!.first.address),
|
||||
amount: Value(coin: txData.amount!.raw))
|
||||
];
|
||||
}
|
||||
final body = TransactionBody(
|
||||
inputs: listOfUtxosToBeUsed.map((e) => TransactionInput(transactionId: TransactionHash.fromHex(e.txHash), index: e.outputIndex)).toList(),
|
||||
inputs: listOfUtxosToBeUsed
|
||||
.map((e) => TransactionInput(
|
||||
transactionId: TransactionHash.fromHex(e.txHash),
|
||||
index: e.outputIndex))
|
||||
.toList(),
|
||||
outputs: outputs,
|
||||
fee: txData.fee!.raw,
|
||||
);
|
||||
final tx = ADATransaction(
|
||||
body: body,
|
||||
witnessSet: TransactionWitnessSet(vKeys: [
|
||||
body: body,
|
||||
witnessSet: TransactionWitnessSet(
|
||||
vKeys: [
|
||||
privateKey.createSignatureWitness(body.toHash().data),
|
||||
],)
|
||||
,);
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final sentTx = await blockfrostProvider!.request(BlockfrostRequestSubmitTransaction(
|
||||
transactionCborBytes: tx.serialize(),),);
|
||||
final sentTx = await blockfrostProvider!.request(
|
||||
BlockfrostRequestSubmitTransaction(
|
||||
transactionCborBytes: tx.serialize(),
|
||||
),
|
||||
);
|
||||
return txData.copyWith(
|
||||
txid: sentTx,
|
||||
);
|
||||
|
@ -365,11 +406,13 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
|
||||
final balance = Balance(
|
||||
total: Amount(
|
||||
rawValue: totalBalanceInLovelace,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,),
|
||||
rawValue: totalBalanceInLovelace,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
spendable: Amount(
|
||||
rawValue: totalBalanceInLovelace,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,),
|
||||
rawValue: totalBalanceInLovelace,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
),
|
||||
blockedTotal: Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
|
@ -399,8 +442,9 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
);
|
||||
|
||||
await info.updateCachedChainHeight(
|
||||
newHeight: latestBlock.height == null ? 0 : latestBlock.height!,
|
||||
isar: mainDB.isar,);
|
||||
newHeight: latestBlock.height == null ? 0 : latestBlock.height!,
|
||||
isar: mainDB.isar,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||
|
@ -473,13 +517,15 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
if (txType == isar.TransactionType.incoming) {
|
||||
receiverAddr = currentAddr;
|
||||
for (final output in utxoInfo.outputs) {
|
||||
if (output.address == currentAddr && output.amount.first.unit == "lovelace") {
|
||||
if (output.address == currentAddr &&
|
||||
output.amount.first.unit == "lovelace") {
|
||||
amount += int.parse(output.amount.first.quantity);
|
||||
}
|
||||
}
|
||||
} else if (txType == isar.TransactionType.outgoing) {
|
||||
for (final output in utxoInfo.outputs) {
|
||||
if (output.address != currentAddr && output.amount.first.unit == "lovelace") {
|
||||
if (output.address != currentAddr &&
|
||||
output.amount.first.unit == "lovelace") {
|
||||
receiverAddr = output.address;
|
||||
amount += int.parse(output.amount.first.quantity);
|
||||
}
|
||||
|
@ -532,6 +578,8 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
}
|
||||
|
||||
await mainDB.addNewTransactionData(parsedTxsList, walletId);
|
||||
} on NodeTorMismatchConfigException {
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||
|
@ -548,6 +596,7 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
|
||||
Future<void> updateProvider() async {
|
||||
final currentNode = getCurrentNode();
|
||||
|
||||
final client = HttpClient();
|
||||
if (prefs.useTor) {
|
||||
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
||||
|
@ -557,11 +606,45 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
|||
);
|
||||
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
|
||||
}
|
||||
blockfrostProvider = BlockforestProvider(
|
||||
blockfrostProvider = CustomBlockForestProvider(
|
||||
BlockfrostHttpProvider(
|
||||
url: "${currentNode.host}:${currentNode.port}/",
|
||||
client: client,
|
||||
),
|
||||
prefs,
|
||||
TorPlainNetworkOption.fromNodeData(
|
||||
currentNode.torEnabled,
|
||||
currentNode.clearnetEnabled,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomBlockForestProvider extends BlockforestProvider {
|
||||
CustomBlockForestProvider(super.rpc, this.prefs, this.netOption);
|
||||
|
||||
final Prefs prefs;
|
||||
final TorPlainNetworkOption netOption;
|
||||
|
||||
@override
|
||||
Future<T> request<T, E>(
|
||||
BlockforestRequestParam<T, E> request, [
|
||||
Duration? timeout,
|
||||
]) async {
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return super.request(request, timeout);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:mutex/mutex.dart';
|
|||
import 'package:stack_wallet_backup/generate_password.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/epicbox_config_model.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
|
@ -32,6 +33,7 @@ import '../../../utilities/flutter_secure_storage_interface.dart';
|
|||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/stack_file_system.dart';
|
||||
import '../../../utilities/test_epic_box_connection.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../crypto_currency/crypto_currency.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../intermediate/bip39_wallet.dart';
|
||||
|
@ -82,6 +84,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
/// returns an empty String on success, error message on failure
|
||||
Future<String> cancelPendingTransactionAndPost(String txSlateId) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final String wallet = (await secureStorageInterface.read(
|
||||
key: '${walletId}_wallet',
|
||||
))!;
|
||||
|
@ -173,6 +176,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
}) async {
|
||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final available = info.cachedBalance.spendable.raw.toInt();
|
||||
|
||||
final transactionFees = await epiccash.LibEpiccash.getTransactionFees(
|
||||
|
@ -198,6 +202,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
}
|
||||
|
||||
Future<void> _startSync() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
Logging.instance.log("request start sync", level: LogLevel.Info);
|
||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||
const int refreshFromNode = 1;
|
||||
|
@ -222,6 +227,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
double spendable,
|
||||
double total
|
||||
})> _allWalletBalances() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||
const refreshFromNode = 0;
|
||||
return await epiccash.LibEpiccash.getWalletBalances(
|
||||
|
@ -232,6 +238,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
}
|
||||
|
||||
Future<bool> _testEpicboxServer(EpicBoxConfigModel epicboxConfig) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final host = epicboxConfig.host;
|
||||
final port = epicboxConfig.port ?? 443;
|
||||
WebSocketChannel? channel;
|
||||
|
@ -576,6 +583,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
@override
|
||||
Future<TxData> confirmSend({required TxData txData}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final wallet =
|
||||
await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||
final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
|
||||
|
@ -638,6 +646,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
@override
|
||||
Future<TxData> prepareSend({required TxData txData}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
if (txData.recipients?.length != 1) {
|
||||
throw Exception("Epic cash prepare send requires a single recipient!");
|
||||
}
|
||||
|
@ -679,6 +688,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
@override
|
||||
Future<void> recover({required bool isRescan}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
await refreshMutex.protect(() async {
|
||||
if (isRescan) {
|
||||
// clear blockchain info
|
||||
|
@ -782,6 +792,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
cryptoCurrency,
|
||||
),
|
||||
);
|
||||
_hackedCheckTorNodePrefs();
|
||||
|
||||
// if (info.epicData?.creationHeight == null) {
|
||||
// await info.updateExtraEpiccashWalletInfo(epicData: inf, isar: isar)
|
||||
|
@ -880,6 +891,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
@override
|
||||
Future<void> updateBalance() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final balances = await _allWalletBalances();
|
||||
final balance = Balance(
|
||||
total: Amount.fromDecimal(
|
||||
|
@ -915,6 +927,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final wallet =
|
||||
await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||
const refreshFromNode = 1;
|
||||
|
@ -1083,7 +1096,11 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
NodeFormData()
|
||||
..host = node!.host
|
||||
..useSSL = node.useSSL
|
||||
..port = node.port,
|
||||
..port = node.port
|
||||
..netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
),
|
||||
) !=
|
||||
null;
|
||||
} catch (e, s) {
|
||||
|
@ -1094,6 +1111,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
|
||||
@override
|
||||
Future<void> updateChainHeight() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final config = await _getRealConfig();
|
||||
final latestHeight =
|
||||
await epiccash.LibEpiccash.getChainHeight(config: config);
|
||||
|
@ -1105,6 +1123,7 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
|
||||
@override
|
||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
// setting ifErrorEstimateFee doesn't do anything as its not used in the nativeFee function?????
|
||||
final int currentFee = await _nativeFee(
|
||||
amount.raw.toInt(),
|
||||
|
@ -1143,6 +1162,28 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
await super.exit();
|
||||
Logging.instance.log("EpicCash_wallet exit finished", level: LogLevel.Info);
|
||||
}
|
||||
|
||||
void _hackedCheckTorNodePrefs() {
|
||||
final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency)!;
|
||||
final netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
);
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> deleteEpicWallet({
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:solana/dto.dart';
|
|||
import 'package:solana/solana.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart' as isar;
|
||||
import '../../../models/isar/models/isar_models.dart';
|
||||
|
@ -19,6 +20,7 @@ import '../../../services/tor_service.dart';
|
|||
import '../../../utilities/amount/amount.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/prefs.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../crypto_currency/crypto_currency.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../intermediate/bip39_wallet.dart';
|
||||
|
@ -60,6 +62,7 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
}
|
||||
|
||||
Future<int?> _getEstimatedNetworkFee(Amount transferAmount) async {
|
||||
_checkClient();
|
||||
final latestBlockhash = await _rpcClient?.getLatestBlockhash();
|
||||
final pubKey = (await _getKeyPair()).publicKey;
|
||||
|
||||
|
@ -129,11 +132,14 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
|
||||
// Rent exemption of Solana
|
||||
final accInfo = await _rpcClient?.getAccountInfo(address!.value);
|
||||
if (accInfo!.value == null) {
|
||||
throw Exception("Account does not appear to exist");
|
||||
}
|
||||
|
||||
final int minimumRent =
|
||||
await _rpcClient?.getMinimumBalanceForRentExemption(
|
||||
accInfo!.value!.data.toString().length,
|
||||
) ??
|
||||
0; // TODO revisit null condition.
|
||||
await _rpcClient!.getMinimumBalanceForRentExemption(
|
||||
accInfo.value!.data.toString().length,
|
||||
);
|
||||
if (minimumRent >
|
||||
((await _getCurrentBalanceInLamports()) -
|
||||
txData.amount!.raw.toInt() -
|
||||
|
@ -254,7 +260,7 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
return health != null;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"$runtimeType Solana pingCheck failed \"health=$health\": $e\n$s",
|
||||
"$runtimeType Solana pingCheck failed \"health response=$health\": $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
return Future.value(false);
|
||||
|
@ -289,21 +295,22 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
|
||||
@override
|
||||
Future<void> updateBalance() async {
|
||||
_checkClient();
|
||||
try {
|
||||
final address = await getCurrentReceivingAddress();
|
||||
_checkClient();
|
||||
|
||||
final balance = await _rpcClient?.getBalance(address!.value);
|
||||
|
||||
// Rent exemption of Solana
|
||||
final accInfo = await _rpcClient?.getAccountInfo(address!.value);
|
||||
// TODO [prio=low]: handle null account info.
|
||||
if (accInfo!.value == null) {
|
||||
throw Exception("Account does not appear to exist");
|
||||
}
|
||||
|
||||
final int minimumRent =
|
||||
await _rpcClient?.getMinimumBalanceForRentExemption(
|
||||
accInfo!.value!.data.toString().length,
|
||||
) ??
|
||||
0;
|
||||
// TODO [prio=low]: revisit null condition.
|
||||
await _rpcClient!.getMinimumBalanceForRentExemption(
|
||||
accInfo.value!.data.toString().length,
|
||||
);
|
||||
final spendableBalance = balance!.value - minimumRent;
|
||||
|
||||
final newBalance = Balance(
|
||||
|
@ -357,16 +364,19 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
|
||||
@override
|
||||
Future<void> updateNode() async {
|
||||
_solNode = getCurrentNode();
|
||||
_solNode = NodeService(secureStorageInterface: secureStorageInterface)
|
||||
.getPrimaryNodeFor(currency: info.coin) ??
|
||||
info.coin.defaultNode;
|
||||
await refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
NodeModel getCurrentNode() {
|
||||
return _solNode ??
|
||||
NodeService(secureStorageInterface: secureStorageInterface)
|
||||
_solNode ??= NodeService(secureStorageInterface: secureStorageInterface)
|
||||
.getPrimaryNodeFor(currency: info.coin) ??
|
||||
info.coin.defaultNode;
|
||||
|
||||
return _solNode!;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -445,6 +455,8 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
txsList.add(Tuple2(transaction, txAddress));
|
||||
}
|
||||
await mainDB.addNewTransactionData(txsList, walletId);
|
||||
} on NodeTorMismatchConfigException {
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Error occurred in solana_wallet.dart while getting"
|
||||
|
@ -464,6 +476,28 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
|||
///
|
||||
void _checkClient() {
|
||||
final node = getCurrentNode();
|
||||
|
||||
final netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
);
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
_rpcClient = null;
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
_rpcClient = null;
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_rpcClient = createRpcClient(
|
||||
node.host,
|
||||
node.port,
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:mutex/mutex.dart';
|
|||
import 'package:socks5_proxy/socks.dart';
|
||||
import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar;
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart';
|
||||
|
@ -22,6 +23,7 @@ import '../../../utilities/amount/amount.dart';
|
|||
import '../../../utilities/enums/fee_rate_type_enum.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/test_stellar_node_connection.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../crypto_currency/crypto_currency.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../intermediate/bip39_wallet.dart';
|
||||
|
@ -61,12 +63,40 @@ class StellarWallet extends Bip39Wallet<Stellar> {
|
|||
);
|
||||
}
|
||||
|
||||
void _hackedCheck() {
|
||||
final node = getCurrentNode();
|
||||
final netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
);
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
_stellarSdk?.httpClient.close();
|
||||
_stellarSdk = null;
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
_stellarSdk?.httpClient.close();
|
||||
_stellarSdk = null;
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<stellar.StellarSDK> get stellarSdk async {
|
||||
if (_requireMutex) {
|
||||
await _torConnectingLock.protect(() async {
|
||||
_hackedCheck();
|
||||
_stellarSdk ??= _getFreshSdk();
|
||||
});
|
||||
} else {
|
||||
_hackedCheck();
|
||||
_stellarSdk ??= _getFreshSdk();
|
||||
}
|
||||
return _stellarSdk!;
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:isar/isar.dart';
|
|||
import 'package:tezart/tezart.dart' as tezart;
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart';
|
||||
|
@ -14,6 +15,7 @@ import '../../../services/tor_service.dart';
|
|||
import '../../../utilities/amount/amount.dart';
|
||||
import '../../../utilities/extensions/impl/string.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../api/tezos/tezos_account.dart';
|
||||
import '../../api/tezos/tezos_api.dart';
|
||||
import '../../api/tezos/tezos_rpc_api.dart';
|
||||
|
@ -42,6 +44,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
String passphrase = "",
|
||||
}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
for (final path in Tezos.possibleDerivationPaths) {
|
||||
final ks = await _getKeyStore(path: path.value);
|
||||
|
||||
|
@ -99,6 +102,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
// Amount? customRevealFee,
|
||||
}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final sourceKeyStore = await _getKeyStore();
|
||||
final server = (_xtzNode ?? getCurrentNode()).host;
|
||||
// if (kDebugMode) {
|
||||
|
@ -178,6 +182,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
@override
|
||||
Future<TxData> prepareSend({required TxData txData}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
if (txData.recipients == null || txData.recipients!.length != 1) {
|
||||
throw Exception("$runtimeType prepareSend requires 1 recipient");
|
||||
}
|
||||
|
@ -288,6 +293,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
|
||||
@override
|
||||
Future<TxData> confirmSend({required TxData txData}) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
await txData.tezosOperationsList!.inject();
|
||||
await txData.tezosOperationsList!.monitor();
|
||||
return txData.copyWith(
|
||||
|
@ -301,6 +307,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
TezosAccount account,
|
||||
String recipientAddress,
|
||||
) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
try {
|
||||
final opList = await _buildSendTransaction(
|
||||
amount: Amount(
|
||||
|
@ -356,6 +363,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
int feeRate, {
|
||||
String recipientAddress = "tz1MXvDCyXSqBqXPNDcsdmVZKfoxL9FTHmp2",
|
||||
}) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
if (info.cachedBalance.spendable.raw == BigInt.zero) {
|
||||
return Amount(
|
||||
rawValue: BigInt.zero,
|
||||
|
@ -402,6 +410,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
|
||||
@override
|
||||
Future<bool> pingCheck() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final currentNode = getCurrentNode();
|
||||
return await TezosRpcAPI.testNetworkConnection(
|
||||
nodeInfo: (
|
||||
|
@ -413,6 +422,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
|
||||
@override
|
||||
Future<void> recover({required bool isRescan}) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
await refreshMutex.protect(() async {
|
||||
if (isRescan) {
|
||||
await mainDB.deleteWalletBlockchainData(walletId);
|
||||
|
@ -463,6 +473,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
@override
|
||||
Future<void> updateBalance() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final currentNode = _xtzNode ?? getCurrentNode();
|
||||
final balance = await TezosRpcAPI.getBalance(
|
||||
nodeInfo: (host: currentNode.host, port: currentNode.port),
|
||||
|
@ -498,6 +509,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
@override
|
||||
Future<void> updateChainHeight() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final currentNode = _xtzNode ?? getCurrentNode();
|
||||
final height = await TezosRpcAPI.getChainHeight(
|
||||
nodeInfo: (
|
||||
|
@ -530,14 +542,15 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
|
||||
@override
|
||||
NodeModel getCurrentNode() {
|
||||
return _xtzNode ??
|
||||
return _xtzNode ??=
|
||||
NodeService(secureStorageInterface: secureStorageInterface)
|
||||
.getPrimaryNodeFor(currency: info.coin) ??
|
||||
info.coin.defaultNode;
|
||||
.getPrimaryNodeFor(currency: info.coin) ??
|
||||
info.coin.defaultNode;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
// TODO: optimize updateTransactions and use V2
|
||||
|
||||
final myAddress = (await getCurrentReceivingAddress())!;
|
||||
|
@ -615,4 +628,26 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
// do nothing. Not used in tezos
|
||||
return false;
|
||||
}
|
||||
|
||||
void _hackedCheckTorNodePrefs() {
|
||||
final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency)!;
|
||||
final netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
);
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.clearnetEnabled && !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.clearnetEnabled && 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.clearnetEnabled && !node.torEnabled) {
|
||||
libMoneroWallet?.stopAutoSaving();
|
||||
libMoneroWallet?.stopListeners();
|
||||
libMoneroWallet?.stopSyncing();
|
||||
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
|
||||
throw Exception("TOR - clearnet mismatch");
|
||||
}
|
||||
} else {
|
||||
if (!node.clearnetEnabled && 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,
|
||||
clearnetEnabled: node.clearnetEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -919,6 +921,8 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
torEnabled: e.torEnabled,
|
||||
clearnetEnabled: e.clearnetEnabled,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:isar/isar.dart';
|
|||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||
import '../../../external_api_keys.dart';
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
|
@ -18,6 +19,7 @@ import '../../../services/tor_service.dart';
|
|||
import '../../../utilities/amount/amount.dart';
|
||||
import '../../../utilities/extensions/impl/string.dart';
|
||||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||
import '../../crypto_currency/intermediate/nano_currency.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../intermediate/bip39_wallet.dart';
|
||||
|
@ -47,6 +49,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
final _httpClient = HTTP();
|
||||
|
||||
Future<String?> _requestWork(String hash) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
return _httpClient
|
||||
.post(
|
||||
url: Uri.parse(_kWorkServer), // this should be a
|
||||
|
@ -104,6 +107,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
String amountRaw,
|
||||
String publicAddress,
|
||||
) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
// TODO: the opening block of an account is a special case
|
||||
bool openBlock = false;
|
||||
|
||||
|
@ -223,6 +227,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
}
|
||||
|
||||
Future<void> _confirmAllReceivable(String accountAddress) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final node = getCurrentNode();
|
||||
final receivableResponse = await _httpClient.post(
|
||||
url: Uri.parse(node.host),
|
||||
|
@ -254,6 +259,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
//========= public ===========================================================
|
||||
|
||||
Future<String> getCurrentRepresentative() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final serverURI = Uri.parse(getCurrentNode().host);
|
||||
final address =
|
||||
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
||||
|
@ -272,6 +278,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
|
||||
Future<bool> changeRepresentative(String newRepresentative) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final node = getCurrentNode();
|
||||
final serverURI = Uri.parse(node.host);
|
||||
await updateBalance();
|
||||
|
@ -347,6 +354,11 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
|
||||
@override
|
||||
Future<bool> pingCheck() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
final node = getCurrentNode();
|
||||
final uri = Uri.parse(node.host);
|
||||
final response = await _httpClient.post(
|
||||
|
@ -365,6 +377,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
|
||||
@override
|
||||
Future<TxData> prepareSend({required TxData txData}) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
if (txData.recipients!.length != 1) {
|
||||
throw ArgumentError(
|
||||
"${cryptoCurrency.runtimeType} currently only "
|
||||
|
@ -383,6 +396,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
@override
|
||||
Future<TxData> confirmSend({required TxData txData}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
// our address:
|
||||
final String publicAddress =
|
||||
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
||||
|
@ -483,6 +497,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
@override
|
||||
Future<void> recover({required bool isRescan}) async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
await refreshMutex.protect(() async {
|
||||
if (isRescan) {
|
||||
await mainDB.deleteWalletBlockchainData(walletId);
|
||||
|
@ -505,6 +520,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
String? previous,
|
||||
Map<String, dynamic>? data,
|
||||
) async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final node = getCurrentNode();
|
||||
final body = {
|
||||
"action": "account_history",
|
||||
|
@ -543,6 +559,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
_hackedCheckTorNodePrefs();
|
||||
await updateChainHeight();
|
||||
final receivingAddress =
|
||||
(_cachedAddress ?? await getCurrentReceivingAddress())!;
|
||||
|
@ -613,6 +630,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
@override
|
||||
Future<void> updateBalance() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final addressString =
|
||||
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
||||
final body = jsonEncode({
|
||||
|
@ -661,6 +679,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
@override
|
||||
Future<void> updateChainHeight() async {
|
||||
try {
|
||||
_hackedCheckTorNodePrefs();
|
||||
final String publicAddress =
|
||||
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
||||
|
||||
|
@ -724,4 +743,26 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
medium: 0,
|
||||
slow: 0,
|
||||
);
|
||||
|
||||
void _hackedCheckTorNodePrefs() {
|
||||
final node = getCurrentNode();
|
||||
final netOption = TorPlainNetworkOption.fromNodeData(
|
||||
node.torEnabled,
|
||||
node.clearnetEnabled,
|
||||
);
|
||||
|
||||
if (prefs.useTor) {
|
||||
if (netOption == TorPlainNetworkOption.clear) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR enabled but node set to clearnet only",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (netOption == TorPlainNetworkOption.tor) {
|
||||
throw NodeTorMismatchConfigException(
|
||||
message: "TOR off but node set to TOR only",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.clearnetEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (_node.clearnetEnabled && !_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,16 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
ref.read(secureStoreProvider),
|
||||
);
|
||||
if (context.mounted) {
|
||||
final TorPlainNetworkOption netOption;
|
||||
if (node.torEnabled &&
|
||||
!node.clearnetEnabled) {
|
||||
netOption = TorPlainNetworkOption.tor;
|
||||
} else if (node.clearnetEnabled &&
|
||||
!node.torEnabled) {
|
||||
netOption = TorPlainNetworkOption.clear;
|
||||
} else {
|
||||
netOption = TorPlainNetworkOption.both;
|
||||
}
|
||||
final canConnect = await testNodeConnection(
|
||||
context: context,
|
||||
nodeFormData: NodeFormData()
|
||||
|
@ -266,6 +277,7 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
..port = node.port
|
||||
..useSSL = node.useSSL
|
||||
..isFailover = node.isFailover
|
||||
..netOption = netOption
|
||||
..trusted = node.trusted,
|
||||
cryptoCurrency: coin,
|
||||
ref: ref,
|
||||
|
|
|
@ -23,6 +23,7 @@ import '../providers/providers.dart';
|
|||
import '../utilities/constants.dart';
|
||||
import '../utilities/logger.dart';
|
||||
import '../utilities/show_loading.dart';
|
||||
import '../utilities/show_node_tor_settings_mismatch.dart';
|
||||
import '../utilities/util.dart';
|
||||
import '../wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||
import '../wallets/wallet/impl/ethereum_wallet.dart';
|
||||
|
@ -95,6 +96,19 @@ class SimpleWalletCard extends ConsumerWidget {
|
|||
|
||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||
|
||||
final canContinue = await checkShowNodeTorSettingsMismatch(
|
||||
context: context,
|
||||
currency: wallet.cryptoCurrency,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
nodeService: ref.read(nodeServiceChangeNotifierProvider),
|
||||
allowCancel: true,
|
||||
rootNavigator: Util.isDesktop,
|
||||
);
|
||||
|
||||
if (!canContinue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
final Future<void> loadFuture;
|
||||
if (wallet is LibMoneroWallet) {
|
||||
|
|
|
@ -168,6 +168,8 @@ void main() {
|
|||
name: "some name",
|
||||
id: "some ID",
|
||||
useSSL: true,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: 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,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
await service.setPrimaryNodeFor(
|
||||
coin: Bitcoin(CryptoCurrencyNetwork.main),
|
||||
|
@ -129,6 +131,8 @@ void main() {
|
|||
coinName: "bitcoin",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
final nodeB = NodeModel(
|
||||
host: "host2",
|
||||
|
@ -140,6 +144,8 @@ void main() {
|
|||
coinName: "monero",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
final nodeC = NodeModel(
|
||||
host: "host3",
|
||||
|
@ -151,6 +157,8 @@ void main() {
|
|||
coinName: "epicCash",
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
|
|
|
@ -37,6 +37,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -51,6 +53,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -112,6 +116,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -126,6 +132,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -188,6 +196,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -202,6 +212,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -25,17 +25,20 @@ void main() {
|
|||
final mockPrefs = MockPrefs();
|
||||
final mockNodeService = MockNodeService();
|
||||
|
||||
when(mockNodeService.getNodeById(id: "node id")).thenAnswer(
|
||||
(realInvocation) => NodeModel(
|
||||
host: "127.0.0.1",
|
||||
port: 2000,
|
||||
name: "Some other name",
|
||||
id: "node id",
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false));
|
||||
when(mockNodeService.getNodeById(id: "node id"))
|
||||
.thenAnswer((realInvocation) => NodeModel(
|
||||
host: "127.0.0.1",
|
||||
port: 2000,
|
||||
name: "Some other name",
|
||||
id: "node id",
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
));
|
||||
|
||||
when(mockNodeService.getPrimaryNodeFor(
|
||||
currency: Bitcoin(CryptoCurrencyNetwork.main)))
|
||||
|
@ -48,6 +51,8 @@ void main() {
|
|||
enabled: true,
|
||||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
isDown: false));
|
||||
|
||||
await tester.pumpWidget(
|
||||
|
@ -109,6 +114,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -125,6 +132,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -186,6 +195,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -202,6 +213,8 @@ void main() {
|
|||
coinName: "Bitcoin",
|
||||
isFailover: false,
|
||||
isDown: false,
|
||||
torEnabled: true,
|
||||
clearnetEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue