2024-05-15 21:20:45 +00:00
|
|
|
import 'dart:async';
|
2024-06-12 21:12:53 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
2024-05-15 21:20:45 +00:00
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
2024-08-26 17:38:58 +00:00
|
|
|
import 'package:on_chain/ada/ada.dart';
|
|
|
|
import 'package:on_chain/ada/src/provider/provider/provider.dart';
|
2024-09-01 13:19:46 +00:00
|
|
|
import 'package:socks5_proxy/socks.dart';
|
2024-06-12 21:12:53 +00:00
|
|
|
|
|
|
|
import '../networking/http.dart';
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
|
|
|
|
import '../providers/global/prefs_provider.dart';
|
|
|
|
import '../services/tor_service.dart';
|
2024-08-26 17:38:58 +00:00
|
|
|
import '../wallets/api/cardano/blockfrost_http_provider.dart';
|
2024-06-12 21:12:53 +00:00
|
|
|
import '../wallets/api/tezos/tezos_rpc_api.dart';
|
|
|
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
|
|
|
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
|
|
|
|
import '../wallets/crypto_currency/intermediate/cryptonote_currency.dart';
|
|
|
|
import '../wallets/crypto_currency/intermediate/nano_currency.dart';
|
2024-06-28 22:05:20 +00:00
|
|
|
import '../wallets/wallet/impl/solana_wallet.dart';
|
2024-05-23 00:37:06 +00:00
|
|
|
import 'connection_check/electrum_connection_check.dart';
|
|
|
|
import 'logger.dart';
|
|
|
|
import 'test_epic_box_connection.dart';
|
|
|
|
import 'test_eth_node_connection.dart';
|
|
|
|
import 'test_monero_node_connection.dart';
|
|
|
|
import 'test_stellar_node_connection.dart';
|
2024-05-15 21:20:45 +00:00
|
|
|
|
|
|
|
Future<bool> _xmrHelper(
|
|
|
|
NodeFormData nodeFormData,
|
|
|
|
BuildContext context,
|
|
|
|
void Function(NodeFormData)? onSuccess,
|
2024-06-12 21:12:53 +00:00
|
|
|
({
|
|
|
|
InternetAddress host,
|
|
|
|
int port,
|
|
|
|
})? proxyInfo,
|
2024-05-15 21:20:45 +00:00
|
|
|
) async {
|
|
|
|
final data = nodeFormData;
|
|
|
|
final url = data.host!;
|
|
|
|
final port = data.port;
|
|
|
|
|
|
|
|
final uri = Uri.parse(url);
|
|
|
|
|
|
|
|
final String path = uri.path.isEmpty ? "/json_rpc" : uri.path;
|
|
|
|
|
|
|
|
final uriString = "${uri.scheme}://${uri.host}:${port ?? 0}$path";
|
|
|
|
|
2024-07-30 22:48:51 +00:00
|
|
|
|
|
|
|
if (proxyInfo == null && uri.host.endsWith(".onion")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-05-15 21:20:45 +00:00
|
|
|
final response = await testMoneroNodeConnection(
|
|
|
|
Uri.parse(uriString),
|
|
|
|
false,
|
2024-06-12 21:12:53 +00:00
|
|
|
proxyInfo: proxyInfo,
|
2024-07-30 22:35:27 +00:00
|
|
|
).timeout(Duration(seconds: proxyInfo != null ? 30 : 10));
|
2024-05-15 21:20:45 +00:00
|
|
|
|
|
|
|
if (response.cert != null) {
|
|
|
|
if (context.mounted) {
|
|
|
|
final shouldAllowBadCert = await showBadX509CertificateDialog(
|
|
|
|
response.cert!,
|
|
|
|
response.url!,
|
|
|
|
response.port!,
|
|
|
|
context,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (shouldAllowBadCert) {
|
2024-06-12 21:12:53 +00:00
|
|
|
final response = await testMoneroNodeConnection(
|
|
|
|
Uri.parse(uriString),
|
|
|
|
true,
|
|
|
|
proxyInfo: proxyInfo,
|
|
|
|
);
|
2024-05-15 21:20:45 +00:00
|
|
|
onSuccess?.call(data..host = url);
|
|
|
|
return response.success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
onSuccess?.call(data..host = url);
|
|
|
|
return response.success;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: probably pull this into each coin's functionality otherwise updating this separately will get irritating
|
|
|
|
Future<bool> testNodeConnection({
|
|
|
|
required BuildContext context,
|
|
|
|
required NodeFormData nodeFormData,
|
|
|
|
required CryptoCurrency cryptoCurrency,
|
|
|
|
required WidgetRef ref,
|
|
|
|
void Function(NodeFormData)? onSuccess,
|
|
|
|
}) async {
|
|
|
|
final formData = nodeFormData;
|
|
|
|
|
|
|
|
bool testPassed = false;
|
|
|
|
|
|
|
|
switch (cryptoCurrency) {
|
|
|
|
case Epiccash():
|
|
|
|
try {
|
|
|
|
final data = await testEpicNodeConnection(formData);
|
|
|
|
|
|
|
|
if (data != null) {
|
|
|
|
testPassed = true;
|
|
|
|
onSuccess?.call(data);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptonoteCurrency():
|
|
|
|
try {
|
2024-08-26 17:38:58 +00:00
|
|
|
final proxyInfo = ref
|
|
|
|
.read(prefsChangeNotifierProvider)
|
|
|
|
.useTor
|
2024-06-12 21:12:53 +00:00
|
|
|
? ref.read(pTorService).getProxyInfo()
|
|
|
|
: null;
|
|
|
|
|
2024-05-15 21:20:45 +00:00
|
|
|
final url = formData.host!;
|
|
|
|
final uri = Uri.tryParse(url);
|
|
|
|
if (uri != null) {
|
2024-08-08 21:37:20 +00:00
|
|
|
if (!uri.hasScheme && !uri.host.endsWith(".onion")) {
|
2024-05-15 21:20:45 +00:00
|
|
|
// try https first
|
|
|
|
testPassed = await _xmrHelper(
|
|
|
|
formData
|
|
|
|
..host = "https://$url"
|
|
|
|
..useSSL = true,
|
|
|
|
context,
|
|
|
|
onSuccess,
|
2024-06-12 21:12:53 +00:00
|
|
|
proxyInfo,
|
2024-05-15 21:20:45 +00:00
|
|
|
);
|
|
|
|
|
2024-06-12 21:12:53 +00:00
|
|
|
if (testPassed == false && context.mounted) {
|
2024-05-15 21:20:45 +00:00
|
|
|
// try http
|
|
|
|
testPassed = await _xmrHelper(
|
|
|
|
formData
|
|
|
|
..host = "http://$url"
|
|
|
|
..useSSL = false,
|
|
|
|
context,
|
|
|
|
onSuccess,
|
2024-06-12 21:12:53 +00:00
|
|
|
proxyInfo,
|
2024-05-15 21:20:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
testPassed = await _xmrHelper(
|
|
|
|
formData
|
|
|
|
..host = url
|
|
|
|
..useSSL = true,
|
|
|
|
context,
|
|
|
|
onSuccess,
|
2024-06-12 21:12:53 +00:00
|
|
|
proxyInfo,
|
2024-05-15 21:20:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ElectrumXCurrencyInterface():
|
|
|
|
case BitcoinFrost():
|
|
|
|
try {
|
|
|
|
testPassed = await checkElectrumServer(
|
|
|
|
host: formData.host!,
|
|
|
|
port: formData.port!,
|
|
|
|
useSSL: formData.useSSL!,
|
|
|
|
overridePrefs: ref.read(prefsChangeNotifierProvider),
|
|
|
|
overrideTorService: ref.read(pTorService),
|
|
|
|
);
|
|
|
|
} catch (_) {
|
|
|
|
testPassed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Ethereum():
|
|
|
|
try {
|
|
|
|
testPassed = await testEthNodeConnection(formData.host!);
|
|
|
|
} catch (_) {
|
|
|
|
testPassed = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stellar():
|
|
|
|
try {
|
|
|
|
testPassed =
|
2024-08-26 17:38:58 +00:00
|
|
|
await testStellarNodeConnection(formData.host!, formData.port!);
|
2024-05-15 21:20:45 +00:00
|
|
|
} catch (_) {}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NanoCurrency():
|
2024-06-12 21:13:34 +00:00
|
|
|
try {
|
|
|
|
final uri = Uri.parse(formData.host!);
|
|
|
|
|
|
|
|
final response = await HTTP().post(
|
|
|
|
url: uri,
|
|
|
|
headers: {"Content-Type": "application/json"},
|
|
|
|
body: jsonEncode(
|
|
|
|
{
|
|
|
|
"action": "version",
|
|
|
|
},
|
|
|
|
),
|
2024-08-26 17:38:58 +00:00
|
|
|
proxyInfo: ref
|
|
|
|
.read(prefsChangeNotifierProvider)
|
|
|
|
.useTor
|
2024-06-12 21:13:34 +00:00
|
|
|
? ref.read(pTorService).getProxyInfo()
|
|
|
|
: null,
|
|
|
|
);
|
|
|
|
|
|
|
|
testPassed = response.code == 200;
|
|
|
|
} catch (_) {}
|
|
|
|
break;
|
2024-05-15 21:20:45 +00:00
|
|
|
|
|
|
|
case Tezos():
|
|
|
|
try {
|
|
|
|
testPassed = await TezosRpcAPI.testNetworkConnection(
|
|
|
|
nodeInfo: (host: formData.host!, port: formData.port!),
|
|
|
|
);
|
|
|
|
} catch (_) {}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Solana():
|
|
|
|
try {
|
2024-06-28 22:05:20 +00:00
|
|
|
final rpcClient = SolanaWallet.createRpcClient(
|
|
|
|
formData.host!,
|
|
|
|
formData.port!,
|
|
|
|
formData.useSSL ?? false,
|
|
|
|
ref.read(prefsChangeNotifierProvider),
|
|
|
|
ref.read(pTorService),
|
|
|
|
);
|
|
|
|
|
|
|
|
final health = await rpcClient.getHealth();
|
|
|
|
Logging.instance.log(
|
|
|
|
"Solana testNodeConnection \"health=$health\"",
|
|
|
|
level: LogLevel.Info,
|
|
|
|
);
|
|
|
|
return true;
|
2024-05-15 21:20:45 +00:00
|
|
|
} catch (_) {
|
|
|
|
testPassed = false;
|
|
|
|
}
|
|
|
|
break;
|
2024-08-26 17:38:58 +00:00
|
|
|
|
|
|
|
case Cardano():
|
|
|
|
try {
|
2024-09-01 13:19:46 +00:00
|
|
|
final client = HttpClient();
|
|
|
|
if (ref
|
|
|
|
.read(prefsChangeNotifierProvider)
|
|
|
|
.useTor) {
|
|
|
|
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
|
|
|
final proxySettings = ProxySettings(
|
|
|
|
proxyInfo.host,
|
|
|
|
proxyInfo.port,
|
|
|
|
);
|
|
|
|
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
|
|
|
|
}
|
2024-08-26 17:38:58 +00:00
|
|
|
final blockfrostProvider = BlockforestProvider(
|
|
|
|
BlockfrostHttpProvider(
|
|
|
|
url: "${formData.host!}:${formData.port!}/api/v0",
|
2024-09-01 13:19:46 +00:00
|
|
|
client: client,
|
2024-08-26 17:38:58 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
final health = await blockfrostProvider.request(
|
|
|
|
BlockfrostRequestBackendHealthStatus(),
|
|
|
|
);
|
|
|
|
|
|
|
|
Logging.instance.log(
|
|
|
|
"Cardano testNodeConnection \"health=$health\"",
|
|
|
|
level: LogLevel.Info,
|
|
|
|
);
|
|
|
|
|
|
|
|
return health;
|
|
|
|
} catch (_) {
|
|
|
|
testPassed = false;
|
|
|
|
}
|
|
|
|
break;
|
2024-05-15 21:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return testPassed;
|
|
|
|
}
|