tor updates

This commit is contained in:
fosse 2024-01-12 14:53:19 -05:00
parent 8644ba2069
commit df8057986e
7 changed files with 184 additions and 97 deletions

View file

@ -40,10 +40,10 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
// the proxywrapper class wraps all of the complexity of retrying on clearnet / settings handling: // the proxywrapper class wraps all of the complexity of retrying on clearnet / settings handling:
try { try {
httpResponse = await proxy.get( httpResponse = await proxy.get(
onionUri, onionUri: onionUri,
clearnetUri: clearnetUri,
portOverride: mainThreadProxyPort, portOverride: mainThreadProxyPort,
torOnly: torOnly, torOnly: torOnly,
clearnetUri: clearnetUri,
); );
responseBody = await utf8.decodeStream(httpResponse); responseBody = await utf8.decodeStream(httpResponse);
statusCode = httpResponse.statusCode; statusCode = httpResponse.statusCode;
@ -71,7 +71,15 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
Future<double> _fetchPriceAsync( Future<double> _fetchPriceAsync(
CryptoCurrency crypto, FiatCurrency fiat, bool torOnly, bool onionOnly) async => CryptoCurrency crypto, FiatCurrency fiat, bool torOnly, bool onionOnly) async =>
compute(_fetchPrice, { // compute(_fetchPrice, {
// 'fiat': fiat.toString(),
// 'crypto': crypto.toString(),
// 'torOnly': torOnly,
// 'onionOnly': onionOnly,
// 'port': ProxyWrapper.port,
// 'torEnabled': ProxyWrapper.enabled,
// });
_fetchPrice({
'fiat': fiat.toString(), 'fiat': fiat.toString(),
'crypto': crypto.toString(), 'crypto': crypto.toString(),
'torOnly': torOnly, 'torOnly': torOnly,

View file

@ -1,17 +1,13 @@
import 'dart:io';
import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/settings/tor_connection.dart';
import 'package:cake_wallet/view_model/settings/tor_view_model.dart'; import 'package:cake_wallet/view_model/settings/tor_view_model.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tor/tor.dart';
class WalletLoadingService { class WalletLoadingService {
WalletLoadingService( WalletLoadingService(

View file

@ -61,6 +61,7 @@ import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
@ -726,6 +727,7 @@ Future<void> setup({
getIt.registerFactory(() => TrocadorProvidersViewModel(getIt.get<SettingsStore>())); getIt.registerFactory(() => TrocadorProvidersViewModel(getIt.get<SettingsStore>()));
getIt.registerSingleton(TorViewModel(getIt.get<SettingsStore>())); getIt.registerSingleton(TorViewModel(getIt.get<SettingsStore>()));
getIt.registerSingleton(ProxyWrapper(settingsStore: getIt.get<SettingsStore>()));
if (DeviceInfo.instance.isMobile && settingsStore.shouldStartTorOnLaunch) { if (DeviceInfo.instance.isMobile && settingsStore.shouldStartTorOnLaunch) {
getIt.get<TorViewModel>().startTor(); getIt.get<TorViewModel>().startTor();

View file

@ -309,6 +309,10 @@ class TrocadorExchangeProvider extends ExchangeProvider {
ProxyWrapper proxy = await getIt.get<ProxyWrapper>(); ProxyWrapper proxy = await getIt.get<ProxyWrapper>();
Uri onionUri = Uri.http(onionApiAuthority, path, queryParams); Uri onionUri = Uri.http(onionApiAuthority, path, queryParams);
Uri clearnetUri = Uri.http(onionApiAuthority, path, queryParams); Uri clearnetUri = Uri.http(onionApiAuthority, path, queryParams);
return await proxy.get(onionUri, torOnly: useTorOnly, clearnetUri: clearnetUri); return await proxy.get(
onionUri: onionUri,
clearnetUri: clearnetUri,
torOnly: useTorOnly,
);
} }
} }

View file

@ -5,6 +5,16 @@ import 'package:cake_wallet/view_model/settings/tor_connection.dart';
import 'package:socks5_proxy/socks.dart'; import 'package:socks5_proxy/socks.dart';
import 'package:tor/tor.dart'; import 'package:tor/tor.dart';
// this is the only way to ensure we're making a non-tor connection:
class NullOverrides extends HttpOverrides {
NullOverrides();
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context);
}
}
class ProxyWrapper { class ProxyWrapper {
ProxyWrapper({ ProxyWrapper({
required this.settingsStore, required this.settingsStore,
@ -14,16 +24,11 @@ class ProxyWrapper {
HttpClient? _torClient; HttpClient? _torClient;
// Factory method to get the singleton instance of TorSingleton
// static ProxyWrapper get instance => _instance;
static int get port => Tor.instance.port; static int get port => Tor.instance.port;
static bool get enabled => Tor.instance.enabled; static bool get enabled => Tor.instance.enabled;
bool started = false; bool started = false;
// bool torEnabled = false;
// bool torOnly = false;
// Method to get or create the Tor proxy instance // Method to get or create the Tor proxy instance
Future<HttpClient> getProxyHttpClient({int? portOverride}) async { Future<HttpClient> getProxyHttpClient({int? portOverride}) async {
@ -44,113 +49,166 @@ class ProxyWrapper {
return _torClient!; return _torClient!;
} }
Future<HttpClientResponse> get( Future<HttpClientResponse> makeGet({
Uri uri, { required HttpClient client,
Map<String, String>? headers, required Uri uri,
int? portOverride, required Map<String, String>? headers,
bool torOnly = false,
Uri? clearnetUri,
}) async { }) async {
HttpClient? client;
late bool torEnabled;
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
client = await getProxyHttpClient(portOverride: portOverride);
torEnabled = true;
} else {
client = HttpClient();
torEnabled = false;
}
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
if (!uri.path.contains(".onion")) {
throw Exception("Cannot connect to clearnet");
}
}
HttpClientResponse? response;
try {
final request = await client.getUrl(uri); final request = await client.getUrl(uri);
if (headers != null) { if (headers != null) {
headers.forEach((key, value) { headers.forEach((key, value) {
request.headers.add(key, value); request.headers.add(key, value);
}); });
} }
response = await request.close(); return await request.close();
} catch (e) {
if (!torOnly &&
torEnabled &&
settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
// try again without tor:
client = HttpClient();
final request = await client.getUrl(clearnetUri ?? uri);
if (headers != null) {
headers.forEach((key, value) {
request.headers.add(key, value);
});
}
response = await request.close();
} else {
throw e;
}
} }
return response; Future<HttpClientResponse> makePost({
} required HttpClient client,
required Uri uri,
Future<HttpClientResponse> post( required Map<String, String>? headers,
Uri uri, {
Map<String, String>? headers,
int? portOverride,
bool torOnly = false,
Uri? clearnetUri,
}) async { }) async {
HttpClient? client;
late bool torEnabled;
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
client = await getProxyHttpClient(portOverride: portOverride);
torEnabled = true;
} else {
client = HttpClient();
torEnabled = false;
}
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
if (!uri.path.contains(".onion")) {
throw Exception("Cannot connect to clearnet");
}
}
HttpClientResponse? response;
try {
final request = await client.postUrl(uri); final request = await client.postUrl(uri);
if (headers != null) { if (headers != null) {
headers.forEach((key, value) { headers.forEach((key, value) {
request.headers.add(key, value); request.headers.add(key, value);
}); });
} }
response = await request.close(); return await request.close();
} catch (e) {
if (!torOnly &&
torEnabled &&
settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
// try again without tor:
client = HttpClient();
final request = await client.postUrl(clearnetUri ?? uri);
if (headers != null) {
headers.forEach((key, value) {
request.headers.add(key, value);
});
} }
response = await request.close();
Future<HttpClientResponse> get({
Map<String, String>? headers,
int? portOverride,
bool torOnly = false,
Uri? clearnetUri,
Uri? onionUri,
}) async {
HttpClient? torClient;
late bool torEnabled;
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
torClient = await getProxyHttpClient(portOverride: portOverride);
torEnabled = true;
} else { } else {
throw e; torEnabled = false;
}
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
if (onionUri == null) {
throw Exception("Cannot connect to clearnet");
} }
} }
return response; // if tor is enabled, try to connect to the onion url first:
if (torEnabled) {
if (onionUri != null) {
try {
return makeGet(
client: torClient!,
uri: onionUri,
headers: headers,
);
} catch (_) {}
}
if (clearnetUri != null && settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
try {
return makeGet(
client: torClient!,
uri: clearnetUri,
headers: headers,
);
} catch (_) {}
}
}
if (!torOnly && clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() {
return makeGet(
client: HttpClient(),
uri: clearnetUri,
headers: headers,
);
},
createHttpClient: NullOverrides().createHttpClient,
);
} catch (_) {
// we weren't able to get a response:
rethrow;
}
}
throw Exception("Unable to connect to server");
}
Future<HttpClientResponse> post({
Map<String, String>? headers,
int? portOverride,
bool torOnly = false,
Uri? clearnetUri,
Uri? onionUri,
}) async {
HttpClient? torClient;
late bool torEnabled;
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly ||
settingsStore.torConnectionMode == TorConnectionMode.enabled) {
torClient = await getProxyHttpClient(portOverride: portOverride);
torEnabled = true;
} else {
torEnabled = false;
}
if (settingsStore.torConnectionMode == TorConnectionMode.onionOnly) {
if (onionUri == null) {
throw Exception("Cannot connect to clearnet");
}
}
// if tor is enabled, try to connect to the onion url first:
if (torEnabled) {
if (onionUri != null) {
try {
return makePost(
client: torClient!,
uri: onionUri,
headers: headers,
);
} catch (_) {}
}
if (clearnetUri != null && settingsStore.torConnectionMode != TorConnectionMode.onionOnly) {
try {
return makePost(
client: torClient!,
uri: clearnetUri,
headers: headers,
);
} catch (_) {}
}
}
if (!torOnly && clearnetUri != null) {
try {
return HttpOverrides.runZoned(
() {
return makePost(
client: HttpClient(),
uri: clearnetUri,
headers: headers,
);
},
createHttpClient: NullOverrides().createHttpClient,
);
} catch (_) {
// we weren't able to get a response:
rethrow;
}
}
throw Exception("Unable to connect to server");
} }
} }

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/settings/tor_connection.dart'; import 'package:cake_wallet/view_model/settings/tor_connection.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:socks5_proxy/socks_client.dart';
import 'package:tor/tor.dart'; import 'package:tor/tor.dart';
part 'tor_view_model.g.dart'; part 'tor_view_model.g.dart';
@ -44,13 +45,26 @@ abstract class TorViewModelBase with Store {
Future<void> startTor() async { Future<void> startTor() async {
try { try {
torConnectionStatus = TorConnectionStatus.connecting; torConnectionStatus = TorConnectionStatus.connecting;
await Tor.init(); await Tor.init();
// start only if not already running:
if (Tor.instance.port == -1) {
await Tor.instance.enable(); await Tor.instance.enable();
}
_settingsStore.shouldStartTorOnLaunch = true; _settingsStore.shouldStartTorOnLaunch = true;
torConnectionStatus = TorConnectionStatus.connected; torConnectionStatus = TorConnectionStatus.connected;
SocksTCPClient.setProxy(proxies: [
ProxySettings(
InternetAddress.loopbackIPv4,
Tor.instance.port,
password: null,
),
]);
// connect to node through the proxy: // connect to node through the proxy:
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
if (appStore.wallet != null) { if (appStore.wallet != null) {
@ -70,5 +84,6 @@ abstract class TorViewModelBase with Store {
Tor.instance.disable(); Tor.instance.disable();
_settingsStore.shouldStartTorOnLaunch = false; _settingsStore.shouldStartTorOnLaunch = false;
torConnectionStatus = TorConnectionStatus.disconnected; torConnectionStatus = TorConnectionStatus.disconnected;
SocksTCPClient.setProxy(proxies: null);
} }
} }

View file

@ -102,7 +102,11 @@ dependencies:
git: git:
url: https://github.com/cake-tech/tor.git url: https://github.com/cake-tech/tor.git
ref: main ref: main
socks5_proxy: ^1.0.4 # socks5_proxy: ^1.0.4
socks5_proxy:
git:
url: https://github.com/perishllc/socks_dart.git
ref: main
flutter_svg: ^2.0.9 flutter_svg: ^2.0.9
polyseed: ^0.0.2 polyseed: ^0.0.2