mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-07 03:09:31 +00:00
commit
1ce4bee68c
57 changed files with 1178 additions and 120 deletions
lib
db
electrumx_rpc
exceptions/wallet
models
pages
pinpad_views
settings_views/global_settings_view
manage_nodes_views
stack_backup_views/helpers
wallets_view/sub_widgets
pages_desktop_specific/my_stack_view
services
utilities
show_node_tor_settings_mismatch.darttest_epic_box_connection.darttest_node_connection.darttor_plain_net_option_enum.dart
wallets
crypto_currency/coins
banano.dartbitcoin.dartbitcoin_frost.dartbitcoincash.dartcardano.dartdash.dartdogecoin.dartecash.dartepiccash.dartethereum.dartfiro.dartlitecoin.dartmonero.dartnamecoin.dartnano.dartparticl.dartpeercoin.dartsolana.dartstellar.darttezos.dartwownero.dart
wallet
widgets
test
|
@ -77,6 +77,8 @@ class DbVersionMigrator with WalletDB {
|
||||||
name: e.name,
|
name: e.name,
|
||||||
id: e.id,
|
id: e.id,
|
||||||
useSSL: e.useSSL,
|
useSSL: e.useSSL,
|
||||||
|
torEnabled: e.torEnabled,
|
||||||
|
clearnetEnabled: e.clearnetEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -88,6 +90,8 @@ class DbVersionMigrator with WalletDB {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
id: node.id,
|
id: node.id,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
),
|
),
|
||||||
prefs: prefs,
|
prefs: prefs,
|
||||||
failovers: failovers,
|
failovers: failovers,
|
||||||
|
|
|
@ -3,6 +3,8 @@ import 'dart:async';
|
||||||
import 'package:electrum_adapter/electrum_adapter.dart';
|
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||||
|
|
||||||
import '../utilities/logger.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/crypto_currency.dart';
|
||||||
|
|
||||||
class ClientManager {
|
class ClientManager {
|
||||||
|
@ -10,6 +12,7 @@ class ClientManager {
|
||||||
static final ClientManager sharedInstance = ClientManager._();
|
static final ClientManager sharedInstance = ClientManager._();
|
||||||
|
|
||||||
final Map<String, ElectrumClient> _map = {};
|
final Map<String, ElectrumClient> _map = {};
|
||||||
|
final Map<String, TorPlainNetworkOption> _mapNet = {};
|
||||||
final Map<String, int> _heights = {};
|
final Map<String, int> _heights = {};
|
||||||
final Map<String, StreamSubscription<BlockHeader>> _subscriptions = {};
|
final Map<String, StreamSubscription<BlockHeader>> _subscriptions = {};
|
||||||
final Map<String, Completer<int>> _heightCompleters = {};
|
final Map<String, Completer<int>> _heightCompleters = {};
|
||||||
|
@ -24,18 +27,37 @@ class ClientManager {
|
||||||
|
|
||||||
ElectrumClient? getClient({
|
ElectrumClient? getClient({
|
||||||
required CryptoCurrency cryptoCurrency,
|
required CryptoCurrency cryptoCurrency,
|
||||||
}) =>
|
required TorPlainNetworkOption netType,
|
||||||
_map[_keyHelper(cryptoCurrency)];
|
}) {
|
||||||
|
final _key = _keyHelper(cryptoCurrency);
|
||||||
|
|
||||||
void addClient(
|
if (netType == _mapNet[_key]) {
|
||||||
|
return _map[_key];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addClient(
|
||||||
ElectrumClient client, {
|
ElectrumClient client, {
|
||||||
required CryptoCurrency cryptoCurrency,
|
required CryptoCurrency cryptoCurrency,
|
||||||
}) {
|
required TorPlainNetworkOption netType,
|
||||||
|
}) async {
|
||||||
final key = _keyHelper(cryptoCurrency);
|
final key = _keyHelper(cryptoCurrency);
|
||||||
if (_map[key] != null) {
|
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 {
|
} else {
|
||||||
_map[key] = client;
|
_map[key] = client;
|
||||||
|
_mapNet[key] = netType;
|
||||||
}
|
}
|
||||||
|
|
||||||
_heightCompleters[key] = Completer<int>();
|
_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;
|
return _heights[key] ?? await _heightCompleters[key]!.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ElectrumClient?> remove({
|
Future<(ElectrumClient?, TorPlainNetworkOption?)> remove({
|
||||||
required CryptoCurrency cryptoCurrency,
|
required CryptoCurrency cryptoCurrency,
|
||||||
}) async {
|
}) async {
|
||||||
final key = _keyHelper(cryptoCurrency);
|
final key = _keyHelper(cryptoCurrency);
|
||||||
|
@ -80,7 +116,7 @@ class ClientManager {
|
||||||
_heights.remove(key);
|
_heights.remove(key);
|
||||||
_heightCompleters.remove(key);
|
_heightCompleters.remove(key);
|
||||||
|
|
||||||
return _map.remove(key);
|
return (_map.remove(key), _mapNet.remove(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> closeAll() async {
|
Future<void> closeAll() async {
|
||||||
|
@ -99,6 +135,7 @@ class ClientManager {
|
||||||
_heightCompleters.clear();
|
_heightCompleters.clear();
|
||||||
_heights.clear();
|
_heights.clear();
|
||||||
_subscriptions.clear();
|
_subscriptions.clear();
|
||||||
|
_mapNet.clear();
|
||||||
_map.clear();
|
_map.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import '../utilities/amount/amount.dart';
|
||||||
import '../utilities/extensions/impl/string.dart';
|
import '../utilities/extensions/impl/string.dart';
|
||||||
import '../utilities/logger.dart';
|
import '../utilities/logger.dart';
|
||||||
import '../utilities/prefs.dart';
|
import '../utilities/prefs.dart';
|
||||||
|
import '../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
|
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||||
import 'client_manager.dart';
|
import 'client_manager.dart';
|
||||||
|
@ -42,6 +43,10 @@ typedef SparkMempoolData = ({
|
||||||
|
|
||||||
class WifiOnlyException implements Exception {}
|
class WifiOnlyException implements Exception {}
|
||||||
|
|
||||||
|
class TorOnlyException implements Exception {}
|
||||||
|
|
||||||
|
class ClearnetOnlyException implements Exception {}
|
||||||
|
|
||||||
class ElectrumXNode {
|
class ElectrumXNode {
|
||||||
ElectrumXNode({
|
ElectrumXNode({
|
||||||
required this.address,
|
required this.address,
|
||||||
|
@ -49,12 +54,16 @@ class ElectrumXNode {
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.useSSL,
|
required this.useSSL,
|
||||||
|
required this.torEnabled,
|
||||||
|
required this.clearnetEnabled,
|
||||||
});
|
});
|
||||||
final String address;
|
final String address;
|
||||||
final int port;
|
final int port;
|
||||||
final String name;
|
final String name;
|
||||||
final String id;
|
final String id;
|
||||||
final bool useSSL;
|
final bool useSSL;
|
||||||
|
final bool torEnabled;
|
||||||
|
final bool clearnetEnabled;
|
||||||
|
|
||||||
factory ElectrumXNode.from(ElectrumXNode node) {
|
factory ElectrumXNode.from(ElectrumXNode node) {
|
||||||
return ElectrumXNode(
|
return ElectrumXNode(
|
||||||
|
@ -63,6 +72,8 @@ class ElectrumXNode {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
id: node.id,
|
id: node.id,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +85,7 @@ class ElectrumXNode {
|
||||||
|
|
||||||
class ElectrumXClient {
|
class ElectrumXClient {
|
||||||
final CryptoCurrency cryptoCurrency;
|
final CryptoCurrency cryptoCurrency;
|
||||||
|
final TorPlainNetworkOption netType;
|
||||||
|
|
||||||
String get host => _host;
|
String get host => _host;
|
||||||
late String _host;
|
late String _host;
|
||||||
|
@ -90,6 +102,7 @@ class ElectrumXClient {
|
||||||
ElectrumClient? getElectrumAdapter() =>
|
ElectrumClient? getElectrumAdapter() =>
|
||||||
ClientManager.sharedInstance.getClient(
|
ClientManager.sharedInstance.getClient(
|
||||||
cryptoCurrency: cryptoCurrency,
|
cryptoCurrency: cryptoCurrency,
|
||||||
|
netType: netType,
|
||||||
);
|
);
|
||||||
|
|
||||||
late Prefs _prefs;
|
late Prefs _prefs;
|
||||||
|
@ -119,6 +132,7 @@ class ElectrumXClient {
|
||||||
required int port,
|
required int port,
|
||||||
required bool useSSL,
|
required bool useSSL,
|
||||||
required Prefs prefs,
|
required Prefs prefs,
|
||||||
|
required this.netType,
|
||||||
required List<ElectrumXNode> failovers,
|
required List<ElectrumXNode> failovers,
|
||||||
required this.cryptoCurrency,
|
required this.cryptoCurrency,
|
||||||
this.connectionTimeoutForSpecialCaseJsonRPCClients =
|
this.connectionTimeoutForSpecialCaseJsonRPCClients =
|
||||||
|
@ -168,6 +182,7 @@ class ElectrumXClient {
|
||||||
_electrumAdapterChannel = null;
|
_electrumAdapterChannel = null;
|
||||||
await (await ClientManager.sharedInstance
|
await (await ClientManager.sharedInstance
|
||||||
.remove(cryptoCurrency: cryptoCurrency))
|
.remove(cryptoCurrency: cryptoCurrency))
|
||||||
|
.$1
|
||||||
?.close();
|
?.close();
|
||||||
|
|
||||||
// Also close any chain height services that are currently open.
|
// Also close any chain height services that are currently open.
|
||||||
|
@ -193,6 +208,10 @@ class ElectrumXClient {
|
||||||
failovers: failovers,
|
failovers: failovers,
|
||||||
globalEventBusForTesting: globalEventBusForTesting,
|
globalEventBusForTesting: globalEventBusForTesting,
|
||||||
cryptoCurrency: cryptoCurrency,
|
cryptoCurrency: cryptoCurrency,
|
||||||
|
netType: TorPlainNetworkOption.fromNodeData(
|
||||||
|
node.torEnabled,
|
||||||
|
node.clearnetEnabled,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +255,18 @@ class ElectrumXClient {
|
||||||
// Get the proxy info from the TorService.
|
// Get the proxy info from the TorService.
|
||||||
proxyInfo = _torService.getProxyInfo();
|
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.
|
// If the current ElectrumAdapterClient is closed, create a new one.
|
||||||
|
@ -288,9 +319,10 @@ class ElectrumXClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientManager.sharedInstance.addClient(
|
await ClientManager.sharedInstance.addClient(
|
||||||
newClient,
|
newClient,
|
||||||
cryptoCurrency: cryptoCurrency,
|
cryptoCurrency: cryptoCurrency,
|
||||||
|
netType: netType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +384,10 @@ class ElectrumXClient {
|
||||||
return response;
|
return response;
|
||||||
} on WifiOnlyException {
|
} on WifiOnlyException {
|
||||||
rethrow;
|
rethrow;
|
||||||
|
} on ClearnetOnlyException {
|
||||||
|
rethrow;
|
||||||
|
} on TorOnlyException {
|
||||||
|
rethrow;
|
||||||
} on SocketException {
|
} on SocketException {
|
||||||
// likely timed out so then retry
|
// likely timed out so then retry
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
|
@ -442,6 +478,10 @@ class ElectrumXClient {
|
||||||
return response;
|
return response;
|
||||||
} on WifiOnlyException {
|
} on WifiOnlyException {
|
||||||
rethrow;
|
rethrow;
|
||||||
|
} on ClearnetOnlyException {
|
||||||
|
rethrow;
|
||||||
|
} on TorOnlyException {
|
||||||
|
rethrow;
|
||||||
} on SocketException {
|
} on SocketException {
|
||||||
// likely timed out so then retry
|
// likely timed out so then retry
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
|
@ -488,10 +528,10 @@ class ElectrumXClient {
|
||||||
return await request(
|
return await request(
|
||||||
requestID: requestID,
|
requestID: requestID,
|
||||||
command: 'server.ping',
|
command: 'server.ping',
|
||||||
requestTimeout: const Duration(seconds: 2),
|
requestTimeout: const Duration(seconds: 3),
|
||||||
retries: retryCount,
|
retries: retryCount,
|
||||||
).timeout(
|
).timeout(
|
||||||
const Duration(seconds: 2),
|
const Duration(seconds: 3),
|
||||||
onTimeout: () {
|
onTimeout: () {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"ElectrumxClient.ping timed out with retryCount=$retryCount, host=$_host",
|
"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 'package:hive/hive.dart';
|
||||||
|
|
||||||
import '../utilities/default_nodes.dart';
|
import '../utilities/default_nodes.dart';
|
||||||
import '../utilities/flutter_secure_storage_interface.dart';
|
import '../utilities/flutter_secure_storage_interface.dart';
|
||||||
|
|
||||||
|
@ -38,6 +39,10 @@ class NodeModel {
|
||||||
final bool isDown;
|
final bool isDown;
|
||||||
// @HiveField(10)
|
// @HiveField(10)
|
||||||
final bool? trusted;
|
final bool? trusted;
|
||||||
|
// @HiveField(11)
|
||||||
|
final bool torEnabled;
|
||||||
|
// @HiveField(12)
|
||||||
|
final bool clearnetEnabled;
|
||||||
|
|
||||||
NodeModel({
|
NodeModel({
|
||||||
required this.host,
|
required this.host,
|
||||||
|
@ -49,6 +54,8 @@ class NodeModel {
|
||||||
required this.coinName,
|
required this.coinName,
|
||||||
required this.isFailover,
|
required this.isFailover,
|
||||||
required this.isDown,
|
required this.isDown,
|
||||||
|
required this.torEnabled,
|
||||||
|
required this.clearnetEnabled,
|
||||||
this.loginName,
|
this.loginName,
|
||||||
this.trusted,
|
this.trusted,
|
||||||
});
|
});
|
||||||
|
@ -64,6 +71,8 @@ class NodeModel {
|
||||||
bool? isFailover,
|
bool? isFailover,
|
||||||
bool? isDown,
|
bool? isDown,
|
||||||
bool? trusted,
|
bool? trusted,
|
||||||
|
bool? torEnabled,
|
||||||
|
bool? clearnetEnabled,
|
||||||
}) {
|
}) {
|
||||||
return NodeModel(
|
return NodeModel(
|
||||||
host: host ?? this.host,
|
host: host ?? this.host,
|
||||||
|
@ -77,6 +86,8 @@ class NodeModel {
|
||||||
isFailover: isFailover ?? this.isFailover,
|
isFailover: isFailover ?? this.isFailover,
|
||||||
isDown: isDown ?? this.isDown,
|
isDown: isDown ?? this.isDown,
|
||||||
trusted: trusted ?? this.trusted,
|
trusted: trusted ?? this.trusted,
|
||||||
|
torEnabled: torEnabled ?? this.torEnabled,
|
||||||
|
clearnetEnabled: clearnetEnabled ?? this.clearnetEnabled,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +109,8 @@ class NodeModel {
|
||||||
map['isFailover'] = isFailover;
|
map['isFailover'] = isFailover;
|
||||||
map['isDown'] = isDown;
|
map['isDown'] = isDown;
|
||||||
map['trusted'] = trusted;
|
map['trusted'] = trusted;
|
||||||
|
map['torEnabled'] = torEnabled;
|
||||||
|
map['clearEnabled'] = clearnetEnabled;
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,15 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
|
||||||
isFailover: fields[8] as bool,
|
isFailover: fields[8] as bool,
|
||||||
isDown: fields[9] as bool,
|
isDown: fields[9] as bool,
|
||||||
trusted: fields[10] as bool?,
|
trusted: fields[10] as bool?,
|
||||||
|
torEnabled: fields[11] as bool? ?? true,
|
||||||
|
clearnetEnabled: fields[12] as bool? ?? true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, NodeModel obj) {
|
void write(BinaryWriter writer, NodeModel obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(11)
|
..writeByte(13)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.id)
|
..write(obj.id)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
|
@ -56,7 +58,11 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
|
||||||
..writeByte(9)
|
..writeByte(9)
|
||||||
..write(obj.isDown)
|
..write(obj.isDown)
|
||||||
..writeByte(10)
|
..writeByte(10)
|
||||||
..write(obj.trusted);
|
..write(obj.trusted)
|
||||||
|
..writeByte(11)
|
||||||
|
..write(obj.torEnabled)
|
||||||
|
..writeByte(12)
|
||||||
|
..write(obj.clearnetEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:mutex/mutex.dart';
|
||||||
|
|
||||||
import '../../notifications/show_flush_bar.dart';
|
import '../../notifications/show_flush_bar.dart';
|
||||||
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.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/prefs_provider.dart';
|
||||||
import '../../providers/global/secure_store_provider.dart';
|
import '../../providers/global/secure_store_provider.dart';
|
||||||
import '../../providers/global/wallets_provider.dart';
|
import '../../providers/global/wallets_provider.dart';
|
||||||
|
@ -25,7 +26,9 @@ import '../../utilities/assets.dart';
|
||||||
import '../../utilities/biometrics.dart';
|
import '../../utilities/biometrics.dart';
|
||||||
import '../../utilities/flutter_secure_storage_interface.dart';
|
import '../../utilities/flutter_secure_storage_interface.dart';
|
||||||
import '../../utilities/show_loading.dart';
|
import '../../utilities/show_loading.dart';
|
||||||
|
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../../utilities/text_styles.dart';
|
import '../../utilities/text_styles.dart';
|
||||||
|
import '../../utilities/util.dart';
|
||||||
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
||||||
import '../../widgets/background.dart';
|
import '../../widgets/background.dart';
|
||||||
import '../../widgets/custom_buttons/app_bar_icon_button.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 walletId = widget.routeOnSuccessArguments as String;
|
||||||
|
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
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;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
loadFuture =
|
loadFuture =
|
||||||
|
|
|
@ -18,14 +18,17 @@ import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../../models/node_model.dart';
|
import '../../../../models/node_model.dart';
|
||||||
import '../../../../notifications/show_flush_bar.dart';
|
import '../../../../notifications/show_flush_bar.dart';
|
||||||
|
import '../../../../providers/global/active_wallet_provider.dart';
|
||||||
import '../../../../providers/global/secure_store_provider.dart';
|
import '../../../../providers/global/secure_store_provider.dart';
|
||||||
import '../../../../providers/providers.dart';
|
import '../../../../providers/providers.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
import '../../../../utilities/assets.dart';
|
import '../../../../utilities/assets.dart';
|
||||||
import '../../../../utilities/constants.dart';
|
import '../../../../utilities/constants.dart';
|
||||||
|
import '../../../../utilities/enums/sync_type_enum.dart';
|
||||||
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
||||||
import '../../../../utilities/test_node_connection.dart';
|
import '../../../../utilities/test_node_connection.dart';
|
||||||
import '../../../../utilities/text_styles.dart';
|
import '../../../../utilities/text_styles.dart';
|
||||||
|
import '../../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../../../utilities/util.dart';
|
import '../../../../utilities/util.dart';
|
||||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../../../wallets/crypto_currency/intermediate/cryptonote_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) {
|
switch (viewType) {
|
||||||
case AddEditNodeViewType.add:
|
case AddEditNodeViewType.add:
|
||||||
final NodeModel node = NodeModel(
|
final NodeModel node = NodeModel(
|
||||||
|
@ -243,6 +251,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
||||||
isFailover: formData.isFailover!,
|
isFailover: formData.isFailover!,
|
||||||
trusted: formData.trusted!,
|
trusted: formData.trusted!,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: torEnabled,
|
||||||
|
clearnetEnabled: plainEnabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
await ref.read(nodeServiceChangeNotifierProvider).add(
|
await ref.read(nodeServiceChangeNotifierProvider).add(
|
||||||
|
@ -250,6 +260,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
||||||
formData.password,
|
formData.password,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await _notifyWalletsOfUpdatedNode();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
.popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
||||||
|
@ -268,6 +279,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
||||||
isFailover: formData.isFailover!,
|
isFailover: formData.isFailover!,
|
||||||
trusted: formData.trusted!,
|
trusted: formData.trusted!,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: torEnabled,
|
||||||
|
clearnetEnabled: plainEnabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
await ref.read(nodeServiceChangeNotifierProvider).add(
|
await ref.read(nodeServiceChangeNotifierProvider).add(
|
||||||
|
@ -275,6 +288,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
||||||
formData.password,
|
formData.password,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await _notifyWalletsOfUpdatedNode();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
.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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
isDesktop = Util.isDesktop;
|
isDesktop = Util.isDesktop;
|
||||||
|
@ -422,7 +469,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
||||||
condition: isDesktop,
|
condition: isDesktop,
|
||||||
builder: (child) => DesktopDialog(
|
builder: (child) => DesktopDialog(
|
||||||
maxWidth: 580,
|
maxWidth: 580,
|
||||||
maxHeight: 500,
|
maxHeight: double.infinity,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
@ -568,10 +615,11 @@ class NodeFormData {
|
||||||
String? name, host, login, password;
|
String? name, host, login, password;
|
||||||
int? port;
|
int? port;
|
||||||
bool? useSSL, isFailover, trusted;
|
bool? useSSL, isFailover, trusted;
|
||||||
|
TorPlainNetworkOption? netOption;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
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;
|
bool _trusted = false;
|
||||||
int? port;
|
int? port;
|
||||||
late bool enableSSLCheckbox;
|
late bool enableSSLCheckbox;
|
||||||
|
late TorPlainNetworkOption netOption;
|
||||||
|
|
||||||
late final bool enableAuthFields;
|
late final bool enableAuthFields;
|
||||||
|
|
||||||
|
@ -672,6 +721,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
||||||
ref.read(nodeFormDataProvider).useSSL = _useSSL;
|
ref.read(nodeFormDataProvider).useSSL = _useSSL;
|
||||||
ref.read(nodeFormDataProvider).isFailover = _isFailover;
|
ref.read(nodeFormDataProvider).isFailover = _isFailover;
|
||||||
ref.read(nodeFormDataProvider).trusted = _trusted;
|
ref.read(nodeFormDataProvider).trusted = _trusted;
|
||||||
|
ref.read(nodeFormDataProvider).netOption = netOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -704,6 +754,15 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
||||||
_useSSL = node.useSSL;
|
_useSSL = node.useSSL;
|
||||||
_isFailover = node.isFailover;
|
_isFailover = node.isFailover;
|
||||||
_trusted = node.trusted ?? false;
|
_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) {
|
if (widget.coin is Epiccash) {
|
||||||
enableSSLCheckbox = !node.host.startsWith("http");
|
enableSSLCheckbox = !node.host.startsWith("http");
|
||||||
} else {
|
} else {
|
||||||
|
@ -716,6 +775,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
enableSSLCheckbox = true;
|
enableSSLCheckbox = true;
|
||||||
|
netOption = TorPlainNetworkOption.both;
|
||||||
// default to port 3413
|
// default to port 3413
|
||||||
// _portController.text = "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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../../../../notifications/show_flush_bar.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/global/secure_store_provider.dart';
|
||||||
import '../../../../providers/providers.dart';
|
import '../../../../providers/providers.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
import '../../../../utilities/assets.dart';
|
import '../../../../utilities/assets.dart';
|
||||||
|
import '../../../../utilities/enums/sync_type_enum.dart';
|
||||||
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
import '../../../../utilities/flutter_secure_storage_interface.dart';
|
||||||
import '../../../../utilities/test_node_connection.dart';
|
import '../../../../utilities/test_node_connection.dart';
|
||||||
import '../../../../utilities/text_styles.dart';
|
import '../../../../utilities/text_styles.dart';
|
||||||
|
import '../../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../../../utilities/util.dart';
|
import '../../../../utilities/util.dart';
|
||||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../../../widgets/background.dart';
|
import '../../../../widgets/background.dart';
|
||||||
|
@ -31,7 +35,7 @@ import '../../../../widgets/desktop/delete_button.dart';
|
||||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../../widgets/desktop/primary_button.dart';
|
import '../../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../../widgets/desktop/secondary_button.dart';
|
import '../../../../widgets/desktop/secondary_button.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'add_edit_node_view.dart';
|
||||||
|
|
||||||
class NodeDetailsView extends ConsumerStatefulWidget {
|
class NodeDetailsView extends ConsumerStatefulWidget {
|
||||||
const NodeDetailsView({
|
const NodeDetailsView({
|
||||||
|
@ -59,6 +63,39 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||||
|
|
||||||
bool _desktopReadOnly = true;
|
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
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
secureStore = ref.read(secureStoreProvider);
|
secureStore = ref.read(secureStoreProvider);
|
||||||
|
@ -265,6 +302,16 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||||
.read(nodeServiceChangeNotifierProvider)
|
.read(nodeServiceChangeNotifierProvider)
|
||||||
.getNodeById(id: nodeId)!;
|
.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()
|
final nodeFormData = NodeFormData()
|
||||||
..useSSL = node.useSSL
|
..useSSL = node.useSSL
|
||||||
..trusted = node.trusted
|
..trusted = node.trusted
|
||||||
|
@ -272,7 +319,8 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||||
..host = node.host
|
..host = node.host
|
||||||
..login = node.loginName
|
..login = node.loginName
|
||||||
..port = node.port
|
..port = node.port
|
||||||
..isFailover = node.isFailover;
|
..isFailover = node.isFailover
|
||||||
|
..netOption = netOption;
|
||||||
nodeFormData.password = await node.getPassword(
|
nodeFormData.password = await node.getPassword(
|
||||||
ref.read(secureStoreProvider),
|
ref.read(secureStoreProvider),
|
||||||
);
|
);
|
||||||
|
@ -338,6 +386,16 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||||
loginName: ref.read(nodeFormDataProvider).login,
|
loginName: ref.read(nodeFormDataProvider).login,
|
||||||
isFailover:
|
isFailover:
|
||||||
ref.read(nodeFormDataProvider).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
|
await ref
|
||||||
|
@ -347,6 +405,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||||
ref.read(nodeFormDataProvider).password,
|
ref.read(nodeFormDataProvider).password,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await _notifyWalletsOfUpdatedNode();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1272,6 +1272,8 @@ abstract class SWB {
|
||||||
loginName: node['loginName'] as String?,
|
loginName: node['loginName'] as String?,
|
||||||
isFailover: node['isFailover'] as bool,
|
isFailover: node['isFailover'] as bool,
|
||||||
isDown: node['isDown'] as bool,
|
isDown: node['isDown'] as bool,
|
||||||
|
torEnabled: node['torEnabled'] as bool? ?? true,
|
||||||
|
clearnetEnabled: node['plainEnabled'] as bool? ?? true,
|
||||||
),
|
),
|
||||||
node["password"] as String?,
|
node["password"] as String?,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import '../../../utilities/amount/amount.dart';
|
||||||
import '../../../utilities/amount/amount_formatter.dart';
|
import '../../../utilities/amount/amount_formatter.dart';
|
||||||
import '../../../utilities/constants.dart';
|
import '../../../utilities/constants.dart';
|
||||||
import '../../../utilities/show_loading.dart';
|
import '../../../utilities/show_loading.dart';
|
||||||
|
import '../../../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../../../utilities/text_styles.dart';
|
import '../../../utilities/text_styles.dart';
|
||||||
import '../../../utilities/util.dart';
|
import '../../../utilities/util.dart';
|
||||||
import '../../../wallets/crypto_currency/coins/firo.dart';
|
import '../../../wallets/crypto_currency/coins/firo.dart';
|
||||||
|
@ -117,6 +118,19 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
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;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
loadFuture =
|
loadFuture =
|
||||||
|
|
|
@ -21,6 +21,7 @@ import '../../../themes/stack_colors.dart';
|
||||||
import '../../../utilities/amount/amount.dart';
|
import '../../../utilities/amount/amount.dart';
|
||||||
import '../../../utilities/constants.dart';
|
import '../../../utilities/constants.dart';
|
||||||
import '../../../utilities/show_loading.dart';
|
import '../../../utilities/show_loading.dart';
|
||||||
|
import '../../../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../../../utilities/text_styles.dart';
|
import '../../../utilities/text_styles.dart';
|
||||||
import '../../../utilities/util.dart';
|
import '../../../utilities/util.dart';
|
||||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
@ -83,6 +84,20 @@ class WalletListItem extends ConsumerWidget {
|
||||||
.read(pWallets)
|
.read(pWallets)
|
||||||
.wallets
|
.wallets
|
||||||
.firstWhere((e) => e.info.coin == coin);
|
.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;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
loadFuture =
|
loadFuture =
|
||||||
|
|
|
@ -12,10 +12,13 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import '../../providers/global/active_wallet_provider.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 '../../providers/global/wallets_provider.dart';
|
||||||
import '../../themes/stack_colors.dart';
|
import '../../themes/stack_colors.dart';
|
||||||
import '../../utilities/constants.dart';
|
import '../../utilities/constants.dart';
|
||||||
import '../../utilities/show_loading.dart';
|
import '../../utilities/show_loading.dart';
|
||||||
|
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../../utilities/util.dart';
|
import '../../utilities/util.dart';
|
||||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
import '../../wallets/wallet/intermediate/lib_monero_wallet.dart';
|
||||||
|
@ -81,6 +84,22 @@ class CoinWalletsTable extends ConsumerWidget {
|
||||||
|
|
||||||
final wallet =
|
final wallet =
|
||||||
ref.read(pWallets).getWallet(walletIds[i]);
|
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;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
loadFuture = wallet
|
loadFuture = wallet
|
||||||
|
|
|
@ -20,6 +20,7 @@ import '../../themes/coin_icon_provider.dart';
|
||||||
import '../../themes/stack_colors.dart';
|
import '../../themes/stack_colors.dart';
|
||||||
import '../../utilities/amount/amount.dart';
|
import '../../utilities/amount/amount.dart';
|
||||||
import '../../utilities/show_loading.dart';
|
import '../../utilities/show_loading.dart';
|
||||||
|
import '../../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../../utilities/text_styles.dart';
|
import '../../utilities/text_styles.dart';
|
||||||
import '../../utilities/util.dart';
|
import '../../utilities/util.dart';
|
||||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
@ -123,6 +124,19 @@ class _DesktopWalletSummaryRowState
|
||||||
final wallet = ref.read(pWallets).wallets.firstWhere(
|
final wallet = ref.read(pWallets).wallets.firstWhere(
|
||||||
(e) => e.cryptoCurrency.identifier == widget.coin.identifier);
|
(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;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
loadFuture =
|
loadFuture =
|
||||||
|
|
|
@ -12,6 +12,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
import '../app_config.dart';
|
import '../app_config.dart';
|
||||||
import '../db/hive/db.dart';
|
import '../db/hive/db.dart';
|
||||||
import '../models/node_model.dart';
|
import '../models/node_model.dart';
|
||||||
|
@ -58,6 +59,8 @@ class NodeService extends ChangeNotifier {
|
||||||
enabled: savedNode.enabled,
|
enabled: savedNode.enabled,
|
||||||
isFailover: savedNode.isFailover,
|
isFailover: savedNode.isFailover,
|
||||||
trusted: savedNode.trusted,
|
trusted: savedNode.trusted,
|
||||||
|
torEnabled: savedNode.torEnabled,
|
||||||
|
clearnetEnabled: savedNode.clearnetEnabled,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +77,8 @@ class NodeService extends ChangeNotifier {
|
||||||
enabled: primaryNode.enabled,
|
enabled: primaryNode.enabled,
|
||||||
isFailover: primaryNode.isFailover,
|
isFailover: primaryNode.isFailover,
|
||||||
trusted: primaryNode.trusted,
|
trusted: primaryNode.trusted,
|
||||||
|
torEnabled: primaryNode.torEnabled,
|
||||||
|
clearnetEnabled: primaryNode.clearnetEnabled,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -253,7 +258,9 @@ class NodeService extends ChangeNotifier {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
coinName: coin.identifier,
|
coinName: coin.identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
|
torEnabled: nodeMap["torEnabled"] == "true",
|
||||||
isDown: nodeMap["isDown"] == "true",
|
isDown: nodeMap["isDown"] == "true",
|
||||||
|
clearnetEnabled: nodeMap["plainEnabled"] == "true",
|
||||||
);
|
);
|
||||||
final currentNode = getNodeById(id: nodeMap["id"] as String);
|
final currentNode = getNodeById(id: nodeMap["id"] as String);
|
||||||
if (currentNode != null) {
|
if (currentNode != null) {
|
||||||
|
|
|
@ -11,23 +11,23 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import '../app_config.dart';
|
import '../app_config.dart';
|
||||||
import '../db/hive/db.dart';
|
import '../db/hive/db.dart';
|
||||||
import '../electrumx_rpc/electrumx_client.dart';
|
import '../electrumx_rpc/electrumx_client.dart';
|
||||||
import '../exceptions/electrumx/no_such_transaction.dart';
|
import '../exceptions/electrumx/no_such_transaction.dart';
|
||||||
import '../models/exchange/response_objects/trade.dart';
|
import '../models/exchange/response_objects/trade.dart';
|
||||||
import '../models/notification_model.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 'exchange/exchange_response.dart';
|
||||||
import 'node_service.dart';
|
import 'node_service.dart';
|
||||||
import 'notifications_api.dart';
|
import 'notifications_api.dart';
|
||||||
import 'trade_service.dart';
|
import 'trade_service.dart';
|
||||||
import 'wallets.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 {
|
class NotificationsService extends ChangeNotifier {
|
||||||
late NodeService nodeService;
|
late NodeService nodeService;
|
||||||
|
@ -136,12 +136,26 @@ class NotificationsService extends ChangeNotifier {
|
||||||
final node = nodeService.getPrimaryNodeFor(currency: coin);
|
final node = nodeService.getPrimaryNodeFor(currency: coin);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
if (wallet is ElectrumXInterface) {
|
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(
|
final eNode = ElectrumXNode(
|
||||||
address: node.host,
|
address: node.host,
|
||||||
port: node.port,
|
port: node.port,
|
||||||
name: node.name,
|
name: node.name,
|
||||||
id: node.id,
|
id: node.id,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
);
|
);
|
||||||
final failovers = nodeService
|
final failovers = nodeService
|
||||||
.failoverNodesFor(currency: coin)
|
.failoverNodesFor(currency: coin)
|
||||||
|
@ -152,6 +166,8 @@ class NotificationsService extends ChangeNotifier {
|
||||||
name: e.name,
|
name: e.name,
|
||||||
id: e.id,
|
id: e.id,
|
||||||
useSSL: e.useSSL,
|
useSSL: e.useSSL,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.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 '../services/tor_service.dart';
|
||||||
import 'logger.dart';
|
import 'logger.dart';
|
||||||
import 'prefs.dart';
|
import 'prefs.dart';
|
||||||
|
import 'tor_plain_net_option_enum.dart';
|
||||||
|
|
||||||
Future<bool> _testEpicBoxNodeConnection(Uri uri) async {
|
Future<bool> _testEpicBoxNodeConnection(Uri uri) async {
|
||||||
final HTTP client = HTTP();
|
final HTTP client = HTTP();
|
||||||
|
@ -50,6 +51,17 @@ Future<NodeFormData?> testEpicNodeConnection(NodeFormData data) async {
|
||||||
if (data.host == null || data.port == null || data.useSSL == null) {
|
if (data.host == null || data.port == null || data.useSSL == null) {
|
||||||
return 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";
|
const String path_postfix = "/v1/version";
|
||||||
|
|
||||||
if (data.host!.startsWith("https://")) {
|
if (data.host!.startsWith("https://")) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:on_chain/ada/ada.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 'package:socks5_proxy/socks.dart';
|
||||||
|
|
||||||
import '../networking/http.dart';
|
import '../networking/http.dart';
|
||||||
|
@ -25,6 +24,7 @@ import 'test_epic_box_connection.dart';
|
||||||
import 'test_eth_node_connection.dart';
|
import 'test_eth_node_connection.dart';
|
||||||
import 'test_monero_node_connection.dart';
|
import 'test_monero_node_connection.dart';
|
||||||
import 'test_stellar_node_connection.dart';
|
import 'test_stellar_node_connection.dart';
|
||||||
|
import 'tor_plain_net_option_enum.dart';
|
||||||
|
|
||||||
Future<bool> _xmrHelper(
|
Future<bool> _xmrHelper(
|
||||||
NodeFormData nodeFormData,
|
NodeFormData nodeFormData,
|
||||||
|
@ -45,7 +45,6 @@ Future<bool> _xmrHelper(
|
||||||
|
|
||||||
final uriString = "${uri.scheme}://${uri.host}:${port ?? 0}$path";
|
final uriString = "${uri.scheme}://${uri.host}:${port ?? 0}$path";
|
||||||
|
|
||||||
|
|
||||||
if (proxyInfo == null && uri.host.endsWith(".onion")) {
|
if (proxyInfo == null && uri.host.endsWith(".onion")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +92,24 @@ Future<bool> testNodeConnection({
|
||||||
}) async {
|
}) async {
|
||||||
final formData = nodeFormData;
|
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;
|
bool testPassed = false;
|
||||||
|
|
||||||
switch (cryptoCurrency) {
|
switch (cryptoCurrency) {
|
||||||
|
@ -111,9 +128,7 @@ Future<bool> testNodeConnection({
|
||||||
|
|
||||||
case CryptonoteCurrency():
|
case CryptonoteCurrency():
|
||||||
try {
|
try {
|
||||||
final proxyInfo = ref
|
final proxyInfo = ref.read(prefsChangeNotifierProvider).useTor
|
||||||
.read(prefsChangeNotifierProvider)
|
|
||||||
.useTor
|
|
||||||
? ref.read(pTorService).getProxyInfo()
|
? ref.read(pTorService).getProxyInfo()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
@ -186,7 +201,7 @@ Future<bool> testNodeConnection({
|
||||||
case Stellar():
|
case Stellar():
|
||||||
try {
|
try {
|
||||||
testPassed =
|
testPassed =
|
||||||
await testStellarNodeConnection(formData.host!, formData.port!);
|
await testStellarNodeConnection(formData.host!, formData.port!);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -202,9 +217,7 @@ Future<bool> testNodeConnection({
|
||||||
"action": "version",
|
"action": "version",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
proxyInfo: ref
|
proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
|
||||||
.read(prefsChangeNotifierProvider)
|
|
||||||
.useTor
|
|
||||||
? ref.read(pTorService).getProxyInfo()
|
? ref.read(pTorService).getProxyInfo()
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
|
@ -245,9 +258,7 @@ Future<bool> testNodeConnection({
|
||||||
case Cardano():
|
case Cardano():
|
||||||
try {
|
try {
|
||||||
final client = HttpClient();
|
final client = HttpClient();
|
||||||
if (ref
|
if (ref.read(prefsChangeNotifierProvider).useTor) {
|
||||||
.read(prefsChangeNotifierProvider)
|
|
||||||
.useTor) {
|
|
||||||
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
||||||
final proxySettings = ProxySettings(
|
final proxySettings = ProxySettings(
|
||||||
proxyInfo.host,
|
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,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -239,6 +239,8 @@ class Bitcoin extends Bip39HDCurrency
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -252,6 +254,8 @@ class Bitcoin extends Bip39HDCurrency
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test4:
|
case CryptoCurrencyNetwork.test4:
|
||||||
|
@ -265,6 +269,8 @@ class Bitcoin extends Bip39HDCurrency
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -73,6 +73,8 @@ class BitcoinFrost extends FrostCurrency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -86,6 +88,8 @@ class BitcoinFrost extends FrostCurrency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test4:
|
case CryptoCurrencyNetwork.test4:
|
||||||
|
@ -99,6 +103,8 @@ class BitcoinFrost extends FrostCurrency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -298,6 +298,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -311,6 +313,8 @@ class Bitcoincash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -77,6 +77,8 @@ class Cardano extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -91,7 +93,8 @@ class Cardano extends Bip39Currency {
|
||||||
int get fractionDigits => 6;
|
int get fractionDigits => 6;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get genesisHash => "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f";
|
String get genesisHash =>
|
||||||
|
"f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get hasBuySupport => false;
|
bool get hasBuySupport => false;
|
||||||
|
|
|
@ -189,6 +189,8 @@ class Dash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -189,6 +189,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -202,6 +204,8 @@ class Dogecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -289,6 +289,8 @@ class Ecash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -80,6 +80,8 @@ class Epiccash extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -57,6 +57,8 @@ class Ethereum extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -231,6 +231,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -257,6 +259,8 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -220,6 +220,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -233,6 +235,8 @@ class Litecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -72,6 +72,8 @@ class Monero extends CryptonoteCurrency {
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
trusted: true,
|
trusted: true,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -102,6 +102,8 @@ class Namecoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
// case CryptoCurrencyNetwork.test:
|
// case CryptoCurrencyNetwork.test:
|
||||||
// TODO: [prio=low] Add testnet support.
|
// TODO: [prio=low] Add testnet support.
|
||||||
|
|
|
@ -77,6 +77,8 @@ class Nano extends NanoCurrency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -97,6 +97,8 @@ class Particl extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
// case CryptoCurrencyNetwork.test:
|
// case CryptoCurrencyNetwork.test:
|
||||||
// TODO: [prio=low] Add testnet.
|
// TODO: [prio=low] Add testnet.
|
||||||
|
|
|
@ -103,6 +103,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -116,6 +118,8 @@ class Peercoin extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -55,6 +55,8 @@ class Solana extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
throw Exception("Unsupported network: $network");
|
throw Exception("Unsupported network: $network");
|
||||||
|
|
|
@ -68,6 +68,8 @@ class Stellar extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
case CryptoCurrencyNetwork.test:
|
case CryptoCurrencyNetwork.test:
|
||||||
|
@ -81,6 +83,8 @@ class Stellar extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -118,6 +118,8 @@ class Tezos extends Bip39Currency {
|
||||||
coinName: identifier,
|
coinName: identifier,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -72,6 +72,8 @@ class Wownero extends CryptonoteCurrency {
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
trusted: true,
|
trusted: true,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1338,6 +1338,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
||||||
name: node.name,
|
name: node.name,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
id: node.id,
|
id: node.id,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1352,6 +1354,8 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
||||||
name: e.name,
|
name: e.name,
|
||||||
id: e.id,
|
id: e.id,
|
||||||
useSSL: e.useSSL,
|
useSSL: e.useSSL,
|
||||||
|
torEnabled: e.torEnabled,
|
||||||
|
clearnetEnabled: e.clearnetEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base.dart';
|
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/cip1852/conf/cip1852_coins.dart';
|
||||||
import 'package:blockchain_utils/bip/cardano/mnemonic/cardano_icarus_seed_generator.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:blockchain_utils/bip/cardano/shelley/cardano_shelley.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
import 'package:on_chain/ada/ada.dart';
|
import 'package:on_chain/ada/ada.dart';
|
||||||
import 'package:socks5_proxy/socks.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/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.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/isar/models/blockchain_data/transaction.dart' as isar;
|
||||||
import '../../../models/paymint/fee_object_model.dart';
|
import '../../../models/paymint/fee_object_model.dart';
|
||||||
import '../../../networking/http.dart';
|
|
||||||
import '../../../services/tor_service.dart';
|
import '../../../services/tor_service.dart';
|
||||||
import '../../../utilities/amount/amount.dart';
|
import '../../../utilities/amount/amount.dart';
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
import '../../../utilities/logger.dart';
|
import '../../../utilities/logger.dart';
|
||||||
import '../../../utilities/prefs.dart';
|
import '../../../utilities/prefs.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../api/cardano/blockfrost_http_provider.dart';
|
import '../../api/cardano/blockfrost_http_provider.dart';
|
||||||
import '../../crypto_currency/crypto_currency.dart';
|
import '../../crypto_currency/crypto_currency.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
|
@ -30,7 +31,6 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
|
|
||||||
// Source: https://cips.cardano.org/cip/CIP-1852
|
// Source: https://cips.cardano.org/cip/CIP-1852
|
||||||
static const String _addressDerivationPath = "m/1852'/1815'/0'/0/0";
|
static const String _addressDerivationPath = "m/1852'/1815'/0'/0/0";
|
||||||
static final HTTP _httpClient = HTTP();
|
|
||||||
|
|
||||||
BlockforestProvider? blockfrostProvider;
|
BlockforestProvider? blockfrostProvider;
|
||||||
|
|
||||||
|
@ -138,12 +138,12 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
final fee = params.calculateFee(284).toInt();
|
final fee = params.calculateFee(284).toInt();
|
||||||
|
|
||||||
return FeeObject(
|
return FeeObject(
|
||||||
numberOfBlocksFast: 2,
|
numberOfBlocksFast: 2,
|
||||||
numberOfBlocksAverage: 2,
|
numberOfBlocksAverage: 2,
|
||||||
numberOfBlocksSlow: 2,
|
numberOfBlocksSlow: 2,
|
||||||
fast: fee,
|
fast: fee,
|
||||||
medium: fee,
|
medium: fee,
|
||||||
slow: fee,
|
slow: fee,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
|
@ -184,41 +184,59 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
totalBalance += BigInt.parse(utxo.amount.first.quantity);
|
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");
|
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 spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||||
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
||||||
|
|
||||||
// Calculate fees with example tx
|
// Calculate fees with example tx
|
||||||
final exampleFee = ADAHelper.toLovelaces("0.10");
|
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(
|
final body = TransactionBody(
|
||||||
inputs: listOfUtxosToBeUsed.map((e) => TransactionInput(transactionId: TransactionHash.fromHex(e.txHash), index: e.outputIndex)).toList(),
|
inputs: listOfUtxosToBeUsed
|
||||||
outputs: [change, TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw - exampleFee))],
|
.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,
|
fee: exampleFee,
|
||||||
);
|
);
|
||||||
final exampleTx = ADATransaction(
|
final exampleTx = ADATransaction(
|
||||||
body: body,
|
body: body,
|
||||||
witnessSet: TransactionWitnessSet(vKeys: [
|
witnessSet: TransactionWitnessSet(
|
||||||
|
vKeys: [
|
||||||
privateKey.createSignatureWitness(body.toHash().data),
|
privateKey.createSignatureWitness(body.toHash().data),
|
||||||
],)
|
],
|
||||||
,);
|
),
|
||||||
final params = await blockfrostProvider!.request(BlockfrostRequestLatestEpochProtocolParameters());
|
);
|
||||||
|
final params = await blockfrostProvider!
|
||||||
|
.request(BlockfrostRequestLatestEpochProtocolParameters());
|
||||||
final fee = params.calculateFee(exampleTx.size);
|
final fee = params.calculateFee(exampleTx.size);
|
||||||
|
|
||||||
// Check if we are sending all balance, which means no change and only one output for recipient.
|
// Check if we are sending all balance, which means no change and only one output for recipient.
|
||||||
if (totalBalance == txData.amount!.raw) {
|
if (totalBalance == txData.amount!.raw) {
|
||||||
final List<TxRecipient> newRecipients = [(
|
final List<TxRecipient> newRecipients = [
|
||||||
address: txData.recipients!.first.address,
|
(
|
||||||
amount: Amount(
|
address: txData.recipients!.first.address,
|
||||||
rawValue: txData.amount!.raw - fee,
|
amount: Amount(
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
rawValue: txData.amount!.raw - fee,
|
||||||
),
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
isChange: txData.recipients!.first.isChange,
|
),
|
||||||
),];
|
isChange: txData.recipients!.first.isChange,
|
||||||
|
),
|
||||||
|
];
|
||||||
return txData.copyWith(
|
return txData.copyWith(
|
||||||
fee: Amount(
|
fee: Amount(
|
||||||
rawValue: fee,
|
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
|
// 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")) {
|
if (totalBalance - (txData.amount!.raw + fee) <
|
||||||
throw Exception("Not enough balance for change. By network rules, please either send all balance or leave at least 1 ADA change.");
|
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(
|
return txData.copyWith(
|
||||||
|
@ -266,7 +286,6 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
var leftAmountForUtxos = txData.amount!.raw + txData.fee!.raw;
|
var leftAmountForUtxos = txData.amount!.raw + txData.fee!.raw;
|
||||||
final listOfUtxosToBeUsed = <ADAAccountUTXOResponse>[];
|
final listOfUtxosToBeUsed = <ADAAccountUTXOResponse>[];
|
||||||
var totalBalance = BigInt.zero;
|
var totalBalance = BigInt.zero;
|
||||||
|
@ -285,31 +304,53 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
totalUtxoAmount += BigInt.parse(utxo.amount.first.quantity);
|
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 spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||||
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
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 = [];
|
List<TransactionOutput> outputs = [];
|
||||||
if (totalBalance == (txData.amount!.raw + txData.fee!.raw)) {
|
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 {
|
} 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(
|
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,
|
outputs: outputs,
|
||||||
fee: txData.fee!.raw,
|
fee: txData.fee!.raw,
|
||||||
);
|
);
|
||||||
final tx = ADATransaction(
|
final tx = ADATransaction(
|
||||||
body: body,
|
body: body,
|
||||||
witnessSet: TransactionWitnessSet(vKeys: [
|
witnessSet: TransactionWitnessSet(
|
||||||
|
vKeys: [
|
||||||
privateKey.createSignatureWitness(body.toHash().data),
|
privateKey.createSignatureWitness(body.toHash().data),
|
||||||
],)
|
],
|
||||||
,);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final sentTx = await blockfrostProvider!.request(BlockfrostRequestSubmitTransaction(
|
final sentTx = await blockfrostProvider!.request(
|
||||||
transactionCborBytes: tx.serialize(),),);
|
BlockfrostRequestSubmitTransaction(
|
||||||
|
transactionCborBytes: tx.serialize(),
|
||||||
|
),
|
||||||
|
);
|
||||||
return txData.copyWith(
|
return txData.copyWith(
|
||||||
txid: sentTx,
|
txid: sentTx,
|
||||||
);
|
);
|
||||||
|
@ -365,11 +406,13 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
|
|
||||||
final balance = Balance(
|
final balance = Balance(
|
||||||
total: Amount(
|
total: Amount(
|
||||||
rawValue: totalBalanceInLovelace,
|
rawValue: totalBalanceInLovelace,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,),
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
spendable: Amount(
|
spendable: Amount(
|
||||||
rawValue: totalBalanceInLovelace,
|
rawValue: totalBalanceInLovelace,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,),
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
blockedTotal: Amount(
|
blockedTotal: Amount(
|
||||||
rawValue: BigInt.zero,
|
rawValue: BigInt.zero,
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
@ -399,8 +442,9 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
);
|
);
|
||||||
|
|
||||||
await info.updateCachedChainHeight(
|
await info.updateCachedChainHeight(
|
||||||
newHeight: latestBlock.height == null ? 0 : latestBlock.height!,
|
newHeight: latestBlock.height == null ? 0 : latestBlock.height!,
|
||||||
isar: mainDB.isar,);
|
isar: mainDB.isar,
|
||||||
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||||
|
@ -473,13 +517,15 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
if (txType == isar.TransactionType.incoming) {
|
if (txType == isar.TransactionType.incoming) {
|
||||||
receiverAddr = currentAddr;
|
receiverAddr = currentAddr;
|
||||||
for (final output in utxoInfo.outputs) {
|
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);
|
amount += int.parse(output.amount.first.quantity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (txType == isar.TransactionType.outgoing) {
|
} else if (txType == isar.TransactionType.outgoing) {
|
||||||
for (final output in utxoInfo.outputs) {
|
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;
|
receiverAddr = output.address;
|
||||||
amount += int.parse(output.amount.first.quantity);
|
amount += int.parse(output.amount.first.quantity);
|
||||||
}
|
}
|
||||||
|
@ -532,6 +578,8 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
}
|
}
|
||||||
|
|
||||||
await mainDB.addNewTransactionData(parsedTxsList, walletId);
|
await mainDB.addNewTransactionData(parsedTxsList, walletId);
|
||||||
|
} on NodeTorMismatchConfigException {
|
||||||
|
rethrow;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||||
|
@ -548,6 +596,7 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
|
|
||||||
Future<void> updateProvider() async {
|
Future<void> updateProvider() async {
|
||||||
final currentNode = getCurrentNode();
|
final currentNode = getCurrentNode();
|
||||||
|
|
||||||
final client = HttpClient();
|
final client = HttpClient();
|
||||||
if (prefs.useTor) {
|
if (prefs.useTor) {
|
||||||
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
||||||
|
@ -557,11 +606,45 @@ class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
);
|
);
|
||||||
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
|
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
|
||||||
}
|
}
|
||||||
blockfrostProvider = BlockforestProvider(
|
blockfrostProvider = CustomBlockForestProvider(
|
||||||
BlockfrostHttpProvider(
|
BlockfrostHttpProvider(
|
||||||
url: "${currentNode.host}:${currentNode.port}/",
|
url: "${currentNode.host}:${currentNode.port}/",
|
||||||
client: client,
|
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:stack_wallet_backup/generate_password.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.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/balance.dart';
|
||||||
import '../../../models/epicbox_config_model.dart';
|
import '../../../models/epicbox_config_model.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.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/logger.dart';
|
||||||
import '../../../utilities/stack_file_system.dart';
|
import '../../../utilities/stack_file_system.dart';
|
||||||
import '../../../utilities/test_epic_box_connection.dart';
|
import '../../../utilities/test_epic_box_connection.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../crypto_currency/crypto_currency.dart';
|
import '../../crypto_currency/crypto_currency.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../intermediate/bip39_wallet.dart';
|
import '../intermediate/bip39_wallet.dart';
|
||||||
|
@ -82,6 +84,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
/// returns an empty String on success, error message on failure
|
/// returns an empty String on success, error message on failure
|
||||||
Future<String> cancelPendingTransactionAndPost(String txSlateId) async {
|
Future<String> cancelPendingTransactionAndPost(String txSlateId) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final String wallet = (await secureStorageInterface.read(
|
final String wallet = (await secureStorageInterface.read(
|
||||||
key: '${walletId}_wallet',
|
key: '${walletId}_wallet',
|
||||||
))!;
|
))!;
|
||||||
|
@ -173,6 +176,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
}) async {
|
}) async {
|
||||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final available = info.cachedBalance.spendable.raw.toInt();
|
final available = info.cachedBalance.spendable.raw.toInt();
|
||||||
|
|
||||||
final transactionFees = await epiccash.LibEpiccash.getTransactionFees(
|
final transactionFees = await epiccash.LibEpiccash.getTransactionFees(
|
||||||
|
@ -198,6 +202,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startSync() async {
|
Future<void> _startSync() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
Logging.instance.log("request start sync", level: LogLevel.Info);
|
Logging.instance.log("request start sync", level: LogLevel.Info);
|
||||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||||
const int refreshFromNode = 1;
|
const int refreshFromNode = 1;
|
||||||
|
@ -222,6 +227,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
double spendable,
|
double spendable,
|
||||||
double total
|
double total
|
||||||
})> _allWalletBalances() async {
|
})> _allWalletBalances() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
final wallet = await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||||
const refreshFromNode = 0;
|
const refreshFromNode = 0;
|
||||||
return await epiccash.LibEpiccash.getWalletBalances(
|
return await epiccash.LibEpiccash.getWalletBalances(
|
||||||
|
@ -232,6 +238,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _testEpicboxServer(EpicBoxConfigModel epicboxConfig) async {
|
Future<bool> _testEpicboxServer(EpicBoxConfigModel epicboxConfig) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final host = epicboxConfig.host;
|
final host = epicboxConfig.host;
|
||||||
final port = epicboxConfig.port ?? 443;
|
final port = epicboxConfig.port ?? 443;
|
||||||
WebSocketChannel? channel;
|
WebSocketChannel? channel;
|
||||||
|
@ -576,6 +583,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
@override
|
@override
|
||||||
Future<TxData> confirmSend({required TxData txData}) async {
|
Future<TxData> confirmSend({required TxData txData}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final wallet =
|
final wallet =
|
||||||
await secureStorageInterface.read(key: '${walletId}_wallet');
|
await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||||
final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
|
final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig();
|
||||||
|
@ -638,6 +646,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
@override
|
@override
|
||||||
Future<TxData> prepareSend({required TxData txData}) async {
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
if (txData.recipients?.length != 1) {
|
if (txData.recipients?.length != 1) {
|
||||||
throw Exception("Epic cash prepare send requires a single recipient!");
|
throw Exception("Epic cash prepare send requires a single recipient!");
|
||||||
}
|
}
|
||||||
|
@ -679,6 +688,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
@override
|
@override
|
||||||
Future<void> recover({required bool isRescan}) async {
|
Future<void> recover({required bool isRescan}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
await refreshMutex.protect(() async {
|
await refreshMutex.protect(() async {
|
||||||
if (isRescan) {
|
if (isRescan) {
|
||||||
// clear blockchain info
|
// clear blockchain info
|
||||||
|
@ -782,6 +792,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
cryptoCurrency,
|
cryptoCurrency,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
|
|
||||||
// if (info.epicData?.creationHeight == null) {
|
// if (info.epicData?.creationHeight == null) {
|
||||||
// await info.updateExtraEpiccashWalletInfo(epicData: inf, isar: isar)
|
// await info.updateExtraEpiccashWalletInfo(epicData: inf, isar: isar)
|
||||||
|
@ -880,6 +891,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
@override
|
@override
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final balances = await _allWalletBalances();
|
final balances = await _allWalletBalances();
|
||||||
final balance = Balance(
|
final balance = Balance(
|
||||||
total: Amount.fromDecimal(
|
total: Amount.fromDecimal(
|
||||||
|
@ -915,6 +927,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final wallet =
|
final wallet =
|
||||||
await secureStorageInterface.read(key: '${walletId}_wallet');
|
await secureStorageInterface.read(key: '${walletId}_wallet');
|
||||||
const refreshFromNode = 1;
|
const refreshFromNode = 1;
|
||||||
|
@ -1083,7 +1096,11 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
NodeFormData()
|
NodeFormData()
|
||||||
..host = node!.host
|
..host = node!.host
|
||||||
..useSSL = node.useSSL
|
..useSSL = node.useSSL
|
||||||
..port = node.port,
|
..port = node.port
|
||||||
|
..netOption = TorPlainNetworkOption.fromNodeData(
|
||||||
|
node.torEnabled,
|
||||||
|
node.clearnetEnabled,
|
||||||
|
),
|
||||||
) !=
|
) !=
|
||||||
null;
|
null;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
@ -1094,6 +1111,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateChainHeight() async {
|
Future<void> updateChainHeight() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final config = await _getRealConfig();
|
final config = await _getRealConfig();
|
||||||
final latestHeight =
|
final latestHeight =
|
||||||
await epiccash.LibEpiccash.getChainHeight(config: config);
|
await epiccash.LibEpiccash.getChainHeight(config: config);
|
||||||
|
@ -1105,6 +1123,7 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
// setting ifErrorEstimateFee doesn't do anything as its not used in the nativeFee function?????
|
// setting ifErrorEstimateFee doesn't do anything as its not used in the nativeFee function?????
|
||||||
final int currentFee = await _nativeFee(
|
final int currentFee = await _nativeFee(
|
||||||
amount.raw.toInt(),
|
amount.raw.toInt(),
|
||||||
|
@ -1143,6 +1162,28 @@ class EpiccashWallet extends Bip39Wallet {
|
||||||
await super.exit();
|
await super.exit();
|
||||||
Logging.instance.log("EpicCash_wallet exit finished", level: LogLevel.Info);
|
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({
|
Future<String> deleteEpicWallet({
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:solana/dto.dart';
|
||||||
import 'package:solana/solana.dart';
|
import 'package:solana/solana.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||||
import '../../../models/balance.dart';
|
import '../../../models/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/transaction.dart' as isar;
|
import '../../../models/isar/models/blockchain_data/transaction.dart' as isar;
|
||||||
import '../../../models/isar/models/isar_models.dart';
|
import '../../../models/isar/models/isar_models.dart';
|
||||||
|
@ -19,6 +20,7 @@ import '../../../services/tor_service.dart';
|
||||||
import '../../../utilities/amount/amount.dart';
|
import '../../../utilities/amount/amount.dart';
|
||||||
import '../../../utilities/logger.dart';
|
import '../../../utilities/logger.dart';
|
||||||
import '../../../utilities/prefs.dart';
|
import '../../../utilities/prefs.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../crypto_currency/crypto_currency.dart';
|
import '../../crypto_currency/crypto_currency.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../intermediate/bip39_wallet.dart';
|
import '../intermediate/bip39_wallet.dart';
|
||||||
|
@ -60,6 +62,7 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int?> _getEstimatedNetworkFee(Amount transferAmount) async {
|
Future<int?> _getEstimatedNetworkFee(Amount transferAmount) async {
|
||||||
|
_checkClient();
|
||||||
final latestBlockhash = await _rpcClient?.getLatestBlockhash();
|
final latestBlockhash = await _rpcClient?.getLatestBlockhash();
|
||||||
final pubKey = (await _getKeyPair()).publicKey;
|
final pubKey = (await _getKeyPair()).publicKey;
|
||||||
|
|
||||||
|
@ -129,11 +132,14 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
|
|
||||||
// Rent exemption of Solana
|
// Rent exemption of Solana
|
||||||
final accInfo = await _rpcClient?.getAccountInfo(address!.value);
|
final accInfo = await _rpcClient?.getAccountInfo(address!.value);
|
||||||
|
if (accInfo!.value == null) {
|
||||||
|
throw Exception("Account does not appear to exist");
|
||||||
|
}
|
||||||
|
|
||||||
final int minimumRent =
|
final int minimumRent =
|
||||||
await _rpcClient?.getMinimumBalanceForRentExemption(
|
await _rpcClient!.getMinimumBalanceForRentExemption(
|
||||||
accInfo!.value!.data.toString().length,
|
accInfo.value!.data.toString().length,
|
||||||
) ??
|
);
|
||||||
0; // TODO revisit null condition.
|
|
||||||
if (minimumRent >
|
if (minimumRent >
|
||||||
((await _getCurrentBalanceInLamports()) -
|
((await _getCurrentBalanceInLamports()) -
|
||||||
txData.amount!.raw.toInt() -
|
txData.amount!.raw.toInt() -
|
||||||
|
@ -254,7 +260,7 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
return health != null;
|
return health != null;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
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,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
return Future.value(false);
|
return Future.value(false);
|
||||||
|
@ -289,21 +295,22 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
|
_checkClient();
|
||||||
try {
|
try {
|
||||||
final address = await getCurrentReceivingAddress();
|
final address = await getCurrentReceivingAddress();
|
||||||
_checkClient();
|
|
||||||
|
|
||||||
final balance = await _rpcClient?.getBalance(address!.value);
|
final balance = await _rpcClient?.getBalance(address!.value);
|
||||||
|
|
||||||
// Rent exemption of Solana
|
// Rent exemption of Solana
|
||||||
final accInfo = await _rpcClient?.getAccountInfo(address!.value);
|
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 =
|
final int minimumRent =
|
||||||
await _rpcClient?.getMinimumBalanceForRentExemption(
|
await _rpcClient!.getMinimumBalanceForRentExemption(
|
||||||
accInfo!.value!.data.toString().length,
|
accInfo.value!.data.toString().length,
|
||||||
) ??
|
);
|
||||||
0;
|
|
||||||
// TODO [prio=low]: revisit null condition.
|
|
||||||
final spendableBalance = balance!.value - minimumRent;
|
final spendableBalance = balance!.value - minimumRent;
|
||||||
|
|
||||||
final newBalance = Balance(
|
final newBalance = Balance(
|
||||||
|
@ -357,16 +364,19 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateNode() async {
|
Future<void> updateNode() async {
|
||||||
_solNode = getCurrentNode();
|
_solNode = NodeService(secureStorageInterface: secureStorageInterface)
|
||||||
|
.getPrimaryNodeFor(currency: info.coin) ??
|
||||||
|
info.coin.defaultNode;
|
||||||
await refresh();
|
await refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NodeModel getCurrentNode() {
|
NodeModel getCurrentNode() {
|
||||||
return _solNode ??
|
_solNode ??= NodeService(secureStorageInterface: secureStorageInterface)
|
||||||
NodeService(secureStorageInterface: secureStorageInterface)
|
|
||||||
.getPrimaryNodeFor(currency: info.coin) ??
|
.getPrimaryNodeFor(currency: info.coin) ??
|
||||||
info.coin.defaultNode;
|
info.coin.defaultNode;
|
||||||
|
|
||||||
|
return _solNode!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -445,6 +455,8 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
txsList.add(Tuple2(transaction, txAddress));
|
txsList.add(Tuple2(transaction, txAddress));
|
||||||
}
|
}
|
||||||
await mainDB.addNewTransactionData(txsList, walletId);
|
await mainDB.addNewTransactionData(txsList, walletId);
|
||||||
|
} on NodeTorMismatchConfigException {
|
||||||
|
rethrow;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Error occurred in solana_wallet.dart while getting"
|
"Error occurred in solana_wallet.dart while getting"
|
||||||
|
@ -464,6 +476,28 @@ class SolanaWallet extends Bip39Wallet<Solana> {
|
||||||
///
|
///
|
||||||
void _checkClient() {
|
void _checkClient() {
|
||||||
final node = getCurrentNode();
|
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(
|
_rpcClient = createRpcClient(
|
||||||
node.host,
|
node.host,
|
||||||
node.port,
|
node.port,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:mutex/mutex.dart';
|
||||||
import 'package:socks5_proxy/socks.dart';
|
import 'package:socks5_proxy/socks.dart';
|
||||||
import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar;
|
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/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/transaction.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/enums/fee_rate_type_enum.dart';
|
||||||
import '../../../utilities/logger.dart';
|
import '../../../utilities/logger.dart';
|
||||||
import '../../../utilities/test_stellar_node_connection.dart';
|
import '../../../utilities/test_stellar_node_connection.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../crypto_currency/crypto_currency.dart';
|
import '../../crypto_currency/crypto_currency.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../intermediate/bip39_wallet.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 {
|
Future<stellar.StellarSDK> get stellarSdk async {
|
||||||
if (_requireMutex) {
|
if (_requireMutex) {
|
||||||
await _torConnectingLock.protect(() async {
|
await _torConnectingLock.protect(() async {
|
||||||
|
_hackedCheck();
|
||||||
_stellarSdk ??= _getFreshSdk();
|
_stellarSdk ??= _getFreshSdk();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
_hackedCheck();
|
||||||
_stellarSdk ??= _getFreshSdk();
|
_stellarSdk ??= _getFreshSdk();
|
||||||
}
|
}
|
||||||
return _stellarSdk!;
|
return _stellarSdk!;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:isar/isar.dart';
|
||||||
import 'package:tezart/tezart.dart' as tezart;
|
import 'package:tezart/tezart.dart' as tezart;
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||||
import '../../../models/balance.dart';
|
import '../../../models/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/transaction.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/amount/amount.dart';
|
||||||
import '../../../utilities/extensions/impl/string.dart';
|
import '../../../utilities/extensions/impl/string.dart';
|
||||||
import '../../../utilities/logger.dart';
|
import '../../../utilities/logger.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../api/tezos/tezos_account.dart';
|
import '../../api/tezos/tezos_account.dart';
|
||||||
import '../../api/tezos/tezos_api.dart';
|
import '../../api/tezos/tezos_api.dart';
|
||||||
import '../../api/tezos/tezos_rpc_api.dart';
|
import '../../api/tezos/tezos_rpc_api.dart';
|
||||||
|
@ -42,6 +44,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
String passphrase = "",
|
String passphrase = "",
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
for (final path in Tezos.possibleDerivationPaths) {
|
for (final path in Tezos.possibleDerivationPaths) {
|
||||||
final ks = await _getKeyStore(path: path.value);
|
final ks = await _getKeyStore(path: path.value);
|
||||||
|
|
||||||
|
@ -99,6 +102,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
// Amount? customRevealFee,
|
// Amount? customRevealFee,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final sourceKeyStore = await _getKeyStore();
|
final sourceKeyStore = await _getKeyStore();
|
||||||
final server = (_xtzNode ?? getCurrentNode()).host;
|
final server = (_xtzNode ?? getCurrentNode()).host;
|
||||||
// if (kDebugMode) {
|
// if (kDebugMode) {
|
||||||
|
@ -178,6 +182,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
@override
|
@override
|
||||||
Future<TxData> prepareSend({required TxData txData}) async {
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
if (txData.recipients == null || txData.recipients!.length != 1) {
|
if (txData.recipients == null || txData.recipients!.length != 1) {
|
||||||
throw Exception("$runtimeType prepareSend requires 1 recipient");
|
throw Exception("$runtimeType prepareSend requires 1 recipient");
|
||||||
}
|
}
|
||||||
|
@ -288,6 +293,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TxData> confirmSend({required TxData txData}) async {
|
Future<TxData> confirmSend({required TxData txData}) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
await txData.tezosOperationsList!.inject();
|
await txData.tezosOperationsList!.inject();
|
||||||
await txData.tezosOperationsList!.monitor();
|
await txData.tezosOperationsList!.monitor();
|
||||||
return txData.copyWith(
|
return txData.copyWith(
|
||||||
|
@ -301,6 +307,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
TezosAccount account,
|
TezosAccount account,
|
||||||
String recipientAddress,
|
String recipientAddress,
|
||||||
) async {
|
) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
try {
|
try {
|
||||||
final opList = await _buildSendTransaction(
|
final opList = await _buildSendTransaction(
|
||||||
amount: Amount(
|
amount: Amount(
|
||||||
|
@ -356,6 +363,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
int feeRate, {
|
int feeRate, {
|
||||||
String recipientAddress = "tz1MXvDCyXSqBqXPNDcsdmVZKfoxL9FTHmp2",
|
String recipientAddress = "tz1MXvDCyXSqBqXPNDcsdmVZKfoxL9FTHmp2",
|
||||||
}) async {
|
}) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
if (info.cachedBalance.spendable.raw == BigInt.zero) {
|
if (info.cachedBalance.spendable.raw == BigInt.zero) {
|
||||||
return Amount(
|
return Amount(
|
||||||
rawValue: BigInt.zero,
|
rawValue: BigInt.zero,
|
||||||
|
@ -402,6 +410,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> pingCheck() async {
|
Future<bool> pingCheck() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final currentNode = getCurrentNode();
|
final currentNode = getCurrentNode();
|
||||||
return await TezosRpcAPI.testNetworkConnection(
|
return await TezosRpcAPI.testNetworkConnection(
|
||||||
nodeInfo: (
|
nodeInfo: (
|
||||||
|
@ -413,6 +422,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> recover({required bool isRescan}) async {
|
Future<void> recover({required bool isRescan}) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
await refreshMutex.protect(() async {
|
await refreshMutex.protect(() async {
|
||||||
if (isRescan) {
|
if (isRescan) {
|
||||||
await mainDB.deleteWalletBlockchainData(walletId);
|
await mainDB.deleteWalletBlockchainData(walletId);
|
||||||
|
@ -463,6 +473,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
@override
|
@override
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final currentNode = _xtzNode ?? getCurrentNode();
|
final currentNode = _xtzNode ?? getCurrentNode();
|
||||||
final balance = await TezosRpcAPI.getBalance(
|
final balance = await TezosRpcAPI.getBalance(
|
||||||
nodeInfo: (host: currentNode.host, port: currentNode.port),
|
nodeInfo: (host: currentNode.host, port: currentNode.port),
|
||||||
|
@ -498,6 +509,7 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
@override
|
@override
|
||||||
Future<void> updateChainHeight() async {
|
Future<void> updateChainHeight() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final currentNode = _xtzNode ?? getCurrentNode();
|
final currentNode = _xtzNode ?? getCurrentNode();
|
||||||
final height = await TezosRpcAPI.getChainHeight(
|
final height = await TezosRpcAPI.getChainHeight(
|
||||||
nodeInfo: (
|
nodeInfo: (
|
||||||
|
@ -530,14 +542,15 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NodeModel getCurrentNode() {
|
NodeModel getCurrentNode() {
|
||||||
return _xtzNode ??
|
return _xtzNode ??=
|
||||||
NodeService(secureStorageInterface: secureStorageInterface)
|
NodeService(secureStorageInterface: secureStorageInterface)
|
||||||
.getPrimaryNodeFor(currency: info.coin) ??
|
.getPrimaryNodeFor(currency: info.coin) ??
|
||||||
info.coin.defaultNode;
|
info.coin.defaultNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
// TODO: optimize updateTransactions and use V2
|
// TODO: optimize updateTransactions and use V2
|
||||||
|
|
||||||
final myAddress = (await getCurrentReceivingAddress())!;
|
final myAddress = (await getCurrentReceivingAddress())!;
|
||||||
|
@ -615,4 +628,26 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
||||||
// do nothing. Not used in tezos
|
// do nothing. Not used in tezos
|
||||||
return false;
|
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;
|
final host = Uri.parse(node.host).host;
|
||||||
({InternetAddress host, int port})? proxy;
|
({InternetAddress host, int port})? proxy;
|
||||||
if (prefs.useTor) {
|
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();
|
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());
|
_setSyncStatus(lib_monero_compat.ConnectingSyncStatus());
|
||||||
|
@ -495,6 +510,9 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
|
||||||
proxy == null ? null : "${proxy.host.address}:${proxy.port}",
|
proxy == null ? null : "${proxy.host.address}:${proxy.port}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
libMoneroWallet?.startSyncing();
|
||||||
|
libMoneroWallet?.startListeners();
|
||||||
|
libMoneroWallet?.startAutoSaving();
|
||||||
|
|
||||||
_setSyncStatus(lib_monero_compat.ConnectedSyncStatus());
|
_setSyncStatus(lib_monero_compat.ConnectedSyncStatus());
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
@ -1020,6 +1038,26 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
|
||||||
return;
|
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.
|
// this acquire should be almost instant due to above check.
|
||||||
// Slight possibility of race but should be irrelevant
|
// Slight possibility of race but should be irrelevant
|
||||||
await refreshMutex.acquire();
|
await refreshMutex.acquire();
|
||||||
|
|
|
@ -906,6 +906,8 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
name: node.name,
|
name: node.name,
|
||||||
useSSL: node.useSSL,
|
useSSL: node.useSSL,
|
||||||
id: node.id,
|
id: node.id,
|
||||||
|
torEnabled: node.torEnabled,
|
||||||
|
clearnetEnabled: node.clearnetEnabled,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -919,6 +921,8 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
name: e.name,
|
name: e.name,
|
||||||
id: e.id,
|
id: e.id,
|
||||||
useSSL: e.useSSL,
|
useSSL: e.useSSL,
|
||||||
|
torEnabled: e.torEnabled,
|
||||||
|
clearnetEnabled: e.clearnetEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:isar/isar.dart';
|
||||||
import 'package:nanodart/nanodart.dart';
|
import 'package:nanodart/nanodart.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart';
|
||||||
import '../../../external_api_keys.dart';
|
import '../../../external_api_keys.dart';
|
||||||
import '../../../models/balance.dart';
|
import '../../../models/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.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/amount/amount.dart';
|
||||||
import '../../../utilities/extensions/impl/string.dart';
|
import '../../../utilities/extensions/impl/string.dart';
|
||||||
import '../../../utilities/logger.dart';
|
import '../../../utilities/logger.dart';
|
||||||
|
import '../../../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../../crypto_currency/intermediate/nano_currency.dart';
|
import '../../crypto_currency/intermediate/nano_currency.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../intermediate/bip39_wallet.dart';
|
import '../intermediate/bip39_wallet.dart';
|
||||||
|
@ -47,6 +49,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
final _httpClient = HTTP();
|
final _httpClient = HTTP();
|
||||||
|
|
||||||
Future<String?> _requestWork(String hash) async {
|
Future<String?> _requestWork(String hash) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
return _httpClient
|
return _httpClient
|
||||||
.post(
|
.post(
|
||||||
url: Uri.parse(_kWorkServer), // this should be a
|
url: Uri.parse(_kWorkServer), // this should be a
|
||||||
|
@ -104,6 +107,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
String amountRaw,
|
String amountRaw,
|
||||||
String publicAddress,
|
String publicAddress,
|
||||||
) async {
|
) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
// TODO: the opening block of an account is a special case
|
// TODO: the opening block of an account is a special case
|
||||||
bool openBlock = false;
|
bool openBlock = false;
|
||||||
|
|
||||||
|
@ -223,6 +227,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _confirmAllReceivable(String accountAddress) async {
|
Future<void> _confirmAllReceivable(String accountAddress) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final node = getCurrentNode();
|
final node = getCurrentNode();
|
||||||
final receivableResponse = await _httpClient.post(
|
final receivableResponse = await _httpClient.post(
|
||||||
url: Uri.parse(node.host),
|
url: Uri.parse(node.host),
|
||||||
|
@ -254,6 +259,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
//========= public ===========================================================
|
//========= public ===========================================================
|
||||||
|
|
||||||
Future<String> getCurrentRepresentative() async {
|
Future<String> getCurrentRepresentative() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final serverURI = Uri.parse(getCurrentNode().host);
|
final serverURI = Uri.parse(getCurrentNode().host);
|
||||||
final address =
|
final address =
|
||||||
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
||||||
|
@ -272,6 +278,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
|
|
||||||
Future<bool> changeRepresentative(String newRepresentative) async {
|
Future<bool> changeRepresentative(String newRepresentative) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final node = getCurrentNode();
|
final node = getCurrentNode();
|
||||||
final serverURI = Uri.parse(node.host);
|
final serverURI = Uri.parse(node.host);
|
||||||
await updateBalance();
|
await updateBalance();
|
||||||
|
@ -347,6 +354,11 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> pingCheck() async {
|
Future<bool> pingCheck() async {
|
||||||
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final node = getCurrentNode();
|
final node = getCurrentNode();
|
||||||
final uri = Uri.parse(node.host);
|
final uri = Uri.parse(node.host);
|
||||||
final response = await _httpClient.post(
|
final response = await _httpClient.post(
|
||||||
|
@ -365,6 +377,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TxData> prepareSend({required TxData txData}) async {
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
if (txData.recipients!.length != 1) {
|
if (txData.recipients!.length != 1) {
|
||||||
throw ArgumentError(
|
throw ArgumentError(
|
||||||
"${cryptoCurrency.runtimeType} currently only "
|
"${cryptoCurrency.runtimeType} currently only "
|
||||||
|
@ -383,6 +396,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
@override
|
@override
|
||||||
Future<TxData> confirmSend({required TxData txData}) async {
|
Future<TxData> confirmSend({required TxData txData}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
// our address:
|
// our address:
|
||||||
final String publicAddress =
|
final String publicAddress =
|
||||||
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
(_cachedAddress ?? await getCurrentReceivingAddress())!.value;
|
||||||
|
@ -483,6 +497,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
@override
|
@override
|
||||||
Future<void> recover({required bool isRescan}) async {
|
Future<void> recover({required bool isRescan}) async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
await refreshMutex.protect(() async {
|
await refreshMutex.protect(() async {
|
||||||
if (isRescan) {
|
if (isRescan) {
|
||||||
await mainDB.deleteWalletBlockchainData(walletId);
|
await mainDB.deleteWalletBlockchainData(walletId);
|
||||||
|
@ -505,6 +520,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
String? previous,
|
String? previous,
|
||||||
Map<String, dynamic>? data,
|
Map<String, dynamic>? data,
|
||||||
) async {
|
) async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final node = getCurrentNode();
|
final node = getCurrentNode();
|
||||||
final body = {
|
final body = {
|
||||||
"action": "account_history",
|
"action": "account_history",
|
||||||
|
@ -543,6 +559,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
await updateChainHeight();
|
await updateChainHeight();
|
||||||
final receivingAddress =
|
final receivingAddress =
|
||||||
(_cachedAddress ?? await getCurrentReceivingAddress())!;
|
(_cachedAddress ?? await getCurrentReceivingAddress())!;
|
||||||
|
@ -613,6 +630,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
@override
|
@override
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final addressString =
|
final addressString =
|
||||||
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
||||||
final body = jsonEncode({
|
final body = jsonEncode({
|
||||||
|
@ -661,6 +679,7 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
@override
|
@override
|
||||||
Future<void> updateChainHeight() async {
|
Future<void> updateChainHeight() async {
|
||||||
try {
|
try {
|
||||||
|
_hackedCheckTorNodePrefs();
|
||||||
final String publicAddress =
|
final String publicAddress =
|
||||||
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
(_cachedAddress ??= (await getCurrentReceivingAddress())!).value;
|
||||||
|
|
||||||
|
@ -724,4 +743,26 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
||||||
medium: 0,
|
medium: 0,
|
||||||
slow: 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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../notifications/show_flush_bar.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/add_edit_node_view.dart';
|
||||||
import '../pages/settings_views/global_settings_view/manage_nodes_views/node_details_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/enums/sync_type_enum.dart';
|
||||||
import '../utilities/test_node_connection.dart';
|
import '../utilities/test_node_connection.dart';
|
||||||
import '../utilities/text_styles.dart';
|
import '../utilities/text_styles.dart';
|
||||||
|
import '../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../utilities/util.dart';
|
import '../utilities/util.dart';
|
||||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import 'conditional_parent.dart';
|
import 'conditional_parent.dart';
|
||||||
|
@ -33,7 +36,6 @@ import 'custom_buttons/blue_text_button.dart';
|
||||||
import 'expandable.dart';
|
import 'expandable.dart';
|
||||||
import 'node_options_sheet.dart';
|
import 'node_options_sheet.dart';
|
||||||
import 'rounded_white_container.dart';
|
import 'rounded_white_container.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class NodeCard extends ConsumerStatefulWidget {
|
class NodeCard extends ConsumerStatefulWidget {
|
||||||
const NodeCard({
|
const NodeCard({
|
||||||
|
@ -165,6 +167,15 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
||||||
text: "Connect",
|
text: "Connect",
|
||||||
enabled: _status == "Disconnected",
|
enabled: _status == "Disconnected",
|
||||||
onTap: () async {
|
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()
|
final nodeFormData = NodeFormData()
|
||||||
..useSSL = _node.useSSL
|
..useSSL = _node.useSSL
|
||||||
..trusted = _node.trusted
|
..trusted = _node.trusted
|
||||||
|
@ -172,6 +183,7 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
||||||
..host = _node.host
|
..host = _node.host
|
||||||
..login = _node.loginName
|
..login = _node.loginName
|
||||||
..port = _node.port
|
..port = _node.port
|
||||||
|
..netOption = netOption
|
||||||
..isFailover = _node.isFailover;
|
..isFailover = _node.isFailover;
|
||||||
nodeFormData.password = await _node.getPassword(
|
nodeFormData.password = await _node.getPassword(
|
||||||
ref.read(secureStoreProvider),
|
ref.read(secureStoreProvider),
|
||||||
|
|
|
@ -27,6 +27,7 @@ import '../utilities/default_nodes.dart';
|
||||||
import '../utilities/enums/sync_type_enum.dart';
|
import '../utilities/enums/sync_type_enum.dart';
|
||||||
import '../utilities/test_node_connection.dart';
|
import '../utilities/test_node_connection.dart';
|
||||||
import '../utilities/text_styles.dart';
|
import '../utilities/text_styles.dart';
|
||||||
|
import '../utilities/tor_plain_net_option_enum.dart';
|
||||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import 'rounded_white_container.dart';
|
import 'rounded_white_container.dart';
|
||||||
|
|
||||||
|
@ -256,6 +257,16 @@ class NodeOptionsSheet extends ConsumerWidget {
|
||||||
ref.read(secureStoreProvider),
|
ref.read(secureStoreProvider),
|
||||||
);
|
);
|
||||||
if (context.mounted) {
|
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(
|
final canConnect = await testNodeConnection(
|
||||||
context: context,
|
context: context,
|
||||||
nodeFormData: NodeFormData()
|
nodeFormData: NodeFormData()
|
||||||
|
@ -266,6 +277,7 @@ class NodeOptionsSheet extends ConsumerWidget {
|
||||||
..port = node.port
|
..port = node.port
|
||||||
..useSSL = node.useSSL
|
..useSSL = node.useSSL
|
||||||
..isFailover = node.isFailover
|
..isFailover = node.isFailover
|
||||||
|
..netOption = netOption
|
||||||
..trusted = node.trusted,
|
..trusted = node.trusted,
|
||||||
cryptoCurrency: coin,
|
cryptoCurrency: coin,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
|
|
|
@ -23,6 +23,7 @@ import '../providers/providers.dart';
|
||||||
import '../utilities/constants.dart';
|
import '../utilities/constants.dart';
|
||||||
import '../utilities/logger.dart';
|
import '../utilities/logger.dart';
|
||||||
import '../utilities/show_loading.dart';
|
import '../utilities/show_loading.dart';
|
||||||
|
import '../utilities/show_node_tor_settings_mismatch.dart';
|
||||||
import '../utilities/util.dart';
|
import '../utilities/util.dart';
|
||||||
import '../wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
import '../wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||||
import '../wallets/wallet/impl/ethereum_wallet.dart';
|
import '../wallets/wallet/impl/ethereum_wallet.dart';
|
||||||
|
@ -95,6 +96,19 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
|
|
||||||
final wallet = ref.read(pWallets).getWallet(walletId);
|
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) {
|
if (context.mounted) {
|
||||||
final Future<void> loadFuture;
|
final Future<void> loadFuture;
|
||||||
if (wallet is LibMoneroWallet) {
|
if (wallet is LibMoneroWallet) {
|
||||||
|
|
|
@ -168,6 +168,8 @@ void main() {
|
||||||
name: "some name",
|
name: "some name",
|
||||||
id: "some ID",
|
id: "some ID",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
final client =
|
final client =
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:hive_test/hive_test.dart';
|
import 'package:hive_test/hive_test.dart';
|
||||||
|
import 'package:stackwallet/app_config.dart';
|
||||||
import 'package:stackwallet/db/hive/db.dart';
|
import 'package:stackwallet/db/hive/db.dart';
|
||||||
import 'package:stackwallet/models/node_model.dart';
|
import 'package:stackwallet/models/node_model.dart';
|
||||||
import 'package:stackwallet/services/node_service.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/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ void main() {
|
||||||
coinName: "bitcoin",
|
coinName: "bitcoin",
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
await service.setPrimaryNodeFor(
|
await service.setPrimaryNodeFor(
|
||||||
coin: Bitcoin(CryptoCurrencyNetwork.main),
|
coin: Bitcoin(CryptoCurrencyNetwork.main),
|
||||||
|
@ -129,6 +131,8 @@ void main() {
|
||||||
coinName: "bitcoin",
|
coinName: "bitcoin",
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
final nodeB = NodeModel(
|
final nodeB = NodeModel(
|
||||||
host: "host2",
|
host: "host2",
|
||||||
|
@ -140,6 +144,8 @@ void main() {
|
||||||
coinName: "monero",
|
coinName: "monero",
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
final nodeC = NodeModel(
|
final nodeC = NodeModel(
|
||||||
host: "host3",
|
host: "host3",
|
||||||
|
@ -151,6 +157,8 @@ void main() {
|
||||||
coinName: "epicCash",
|
coinName: "epicCash",
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
|
|
|
@ -37,6 +37,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,6 +53,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -112,6 +116,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -126,6 +132,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -188,6 +196,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -202,6 +212,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,20 @@ void main() {
|
||||||
final mockPrefs = MockPrefs();
|
final mockPrefs = MockPrefs();
|
||||||
final mockNodeService = MockNodeService();
|
final mockNodeService = MockNodeService();
|
||||||
|
|
||||||
when(mockNodeService.getNodeById(id: "node id")).thenAnswer(
|
when(mockNodeService.getNodeById(id: "node id"))
|
||||||
(realInvocation) => NodeModel(
|
.thenAnswer((realInvocation) => NodeModel(
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2000,
|
port: 2000,
|
||||||
name: "Some other name",
|
name: "Some other name",
|
||||||
id: "node id",
|
id: "node id",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false));
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
|
));
|
||||||
|
|
||||||
when(mockNodeService.getPrimaryNodeFor(
|
when(mockNodeService.getPrimaryNodeFor(
|
||||||
currency: Bitcoin(CryptoCurrencyNetwork.main)))
|
currency: Bitcoin(CryptoCurrencyNetwork.main)))
|
||||||
|
@ -48,6 +51,8 @@ void main() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
isDown: false));
|
isDown: false));
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -109,6 +114,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -125,6 +132,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -186,6 +195,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -202,6 +213,8 @@ void main() {
|
||||||
coinName: "Bitcoin",
|
coinName: "Bitcoin",
|
||||||
isFailover: false,
|
isFailover: false,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
|
torEnabled: true,
|
||||||
|
clearnetEnabled: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue