From e491921fa80cf46d0c8443fddd7805abb89a0042 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 9 Jan 2025 13:05:00 +0100 Subject: [PATCH 01/10] tor wip --- cw_bitcoin/pubspec.lock | 8 + cw_core/lib/node.dart | 13 +- cw_core/pubspec.lock | 8 + cw_core/pubspec.yaml | 5 +- cw_haven/pubspec.lock | 8 + cw_monero/lib/monero_wallet.dart | 11 +- .../.plugin_symlinks/path_provider_linux | 2 +- .../flutter/ephemeral/.plugin_symlinks/tor | 1 + .../linux/flutter/generated_plugins.cmake | 1 + cw_monero/pubspec.lock | 8 + cw_monero/pubspec.yaml | 1 + cw_nano/pubspec.lock | 8 + cw_wownero/pubspec.lock | 8 + lib/core/backup_service.dart | 4 + lib/di.dart | 2 - lib/entities/background_tasks.dart | 11 +- lib/entities/preferences_key.dart | 1 + lib/reactions/check_connection.dart | 5 + lib/reactions/on_current_wallet_change.dart | 5 + lib/router.dart | 4 - lib/routes.dart | 1 - lib/src/screens/nodes/widgets/node_form.dart | 22 +- .../settings/connection_sync_page.dart | 12 +- lib/src/screens/settings/tor_page.dart | 268 ------------------ lib/src/widgets/standard_checkbox.dart | 4 +- lib/store/settings_store.dart | 14 + lib/utils/feature_flag.dart | 2 +- lib/utils/tor.dart | 53 ++++ .../dashboard/dashboard_view_model.dart | 24 ++ .../node_create_or_edit_view_model.dart | 4 + linux/flutter/generated_plugins.cmake | 1 + pubspec_base.yaml | 8 + res/values/strings_ar.arb | 2 + res/values/strings_bg.arb | 2 + res/values/strings_cs.arb | 2 + res/values/strings_de.arb | 4 +- res/values/strings_en.arb | 2 + res/values/strings_es.arb | 2 + res/values/strings_fr.arb | 2 + res/values/strings_ha.arb | 2 + res/values/strings_hi.arb | 2 + res/values/strings_hr.arb | 2 + res/values/strings_hy.arb | 2 + res/values/strings_id.arb | 2 + res/values/strings_it.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 4 +- res/values/strings_my.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 2 + res/values/strings_ru.arb | 2 + res/values/strings_th.arb | 2 + res/values/strings_tl.arb | 2 + res/values/strings_tr.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_ur.arb | 2 + res/values/strings_vi.arb | 2 + res/values/strings_yo.arb | 2 + res/values/strings_zh.arb | 2 + windows/flutter/generated_plugins.cmake | 1 + 61 files changed, 290 insertions(+), 298 deletions(-) create mode 120000 cw_monero/linux/flutter/ephemeral/.plugin_symlinks/tor delete mode 100644 lib/src/screens/settings/tor_page.dart create mode 100644 lib/utils/tor.dart diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index c65f056bbf..21e4d829be 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -979,6 +979,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + name: tor + sha256: eeed80e5c912a1806c2f81825c12e84f4dc5a0b50aebedea59e3a8ba53df3142 + url: "https://pub.dev" + source: hosted + version: "0.0.8" tuple: dependency: transitive description: diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 7d0c2411f1..b890e58a7e 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -1,13 +1,13 @@ import 'dart:io'; import 'package:cw_core/keyable.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:hive/hive.dart'; import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:http/io_client.dart' as ioc; - -// import 'package:tor/tor.dart'; +import 'package:tor/tor.dart' as tor; part 'node.g.dart'; @@ -231,15 +231,16 @@ class Node extends HiveObject with Keyable { } Future requestNodeWithProxy() async { - if (!isValidProxyAddress /* && !Tor.instance.enabled*/) { + if (!isValidProxyAddress && !tor.Tor.instance.enabled) { return false; } String? proxy = socksProxyAddress; - // if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) { - // proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}"; - // } + if ((proxy?.isEmpty ?? true) && tor.Tor.instance.enabled) { + proxy = "${InternetAddress.loopbackIPv4.address}:${tor.Tor.instance.port}"; + } + printV("proxy: $proxy"); if (proxy == null) { return false; } diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index c12839a19d..7a620e9036 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -686,6 +686,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: "direct main" + description: + name: tor + sha256: eeed80e5c912a1806c2f81825c12e84f4dc5a0b50aebedea59e3a8ba53df3142 + url: "https://pub.dev" + source: hosted + version: "0.0.8" tuple: dependency: transitive description: diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 19eda51e0c..9e10fa7c3e 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -26,10 +26,7 @@ dependencies: version: 1.0.0 socks5_proxy: ^1.0.4 unorm_dart: ^0.3.0 -# tor: -# git: -# url: https://github.com/cake-tech/tor.git -# ref: main + tor: any dev_dependencies: flutter_test: diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index b6cae9f397..1723ae31b6 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -680,6 +680,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tor: + dependency: transitive + description: + name: tor + sha256: eeed80e5c912a1806c2f81825c12e84f4dc5a0b50aebedea59e3a8ba53df3142 + url: "https://pub.dev" + source: hosted + version: "0.0.8" tuple: dependency: transitive description: diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index b46e8dd104..2e24a56791 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -39,6 +39,7 @@ import 'package:hive/hive.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; import 'package:mobx/mobx.dart'; import 'package:monero/monero.dart' as monero; +import 'package:tor/tor.dart'; part 'monero_wallet.g.dart'; @@ -183,6 +184,14 @@ abstract class MoneroWalletBase extends WalletBase connectToNode({required Node node}) async { + String socksProxy = node.socksProxyAddress ?? ''; + printV("bootstrapped: ${Tor.instance.bootstrapped}"); + printV(" enabled: ${Tor.instance.enabled}"); + printV(" port: ${Tor.instance.port}"); + printV(" started: ${Tor.instance.started}"); + if (Tor.instance.enabled) { + socksProxy = "127.0.0.1:${Tor.instance.port}"; + } try { syncStatus = ConnectingSyncStatus(); await monero_wallet.setupNode( @@ -192,7 +201,7 @@ abstract class MoneroWalletBase extends WalletBase setup({ () => WalletConnectConnectionsView(web3walletService: getIt.get())); getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); - getIt.registerFactory(() => TorPage(getIt.get())); getIt.registerFactory(() => SignViewModel(getIt.get().wallet!)); diff --git a/lib/entities/background_tasks.dart b/lib/entities/background_tasks.dart index 60e4c86cd6..8c6c897e52 100644 --- a/lib/entities/background_tasks.dart +++ b/lib/entities/background_tasks.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; +import 'package:cake_wallet/utils/tor.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; @@ -48,6 +49,10 @@ void callbackDispatcher() { wallet = await walletLoadingService.load(moneroWallets[i].type, moneroWallets[i].name); final node = getIt.get().getCurrentNode(moneroWallets[i].type); + final settingsStore = getIt.get(); + if (settingsStore.builtinTor) { + await ensureTorStarted(context: null); + } await wallet.connectToNode(node: node); await wallet.startSync(); } @@ -60,7 +65,11 @@ void callbackDispatcher() { wallet = await walletLoadingService.load(WalletType.values[typeRaw!], name!); final node = getIt.get().getCurrentNode(WalletType.values[typeRaw]); - + final settingsStore = getIt.get(); + if (settingsStore.builtinTor) { + await ensureTorStarted(context: null); + } + await wallet.connectToNode(node: node); await wallet.startSync(); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 58a5402786..686ac21494 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -60,6 +60,7 @@ class PreferencesKey { static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1'; static const syncModeKey = 'sync_mode'; static const syncAllKey = 'sync_all'; + static const builtinTorKey = 'builtin_tor'; static const lastPopupDate = 'last_popup_date'; static const lastAppReviewDate = 'last_app_review_date'; static const sortBalanceBy = 'sort_balance_by'; diff --git a/lib/reactions/check_connection.dart b/lib/reactions/check_connection.dart index d60037543a..50cfecb248 100644 --- a/lib/reactions/check_connection.dart +++ b/lib/reactions/check_connection.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:cake_wallet/utils/tor.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_base.dart'; @@ -33,6 +34,10 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore final alive = await settingsStore.getCurrentNode(wallet.type).requestNode(); if (alive) { + if (settingsStore.builtinTor) { + await ensureTorStarted(context: null); + } + await wallet.connectToNode(node: settingsStore.getCurrentNode(wallet.type)); } } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 3840b042e2..ba1c3ab3b3 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/utils/tor.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/balance.dart'; @@ -74,6 +75,10 @@ void startCurrentWalletChangeReaction( _setAutoGenerateSubaddressStatus(wallet, settingsStore); } + if (settingsStore.builtinTor) { + await ensureTorStarted(context: null); + } + await wallet.connectToNode(node: node); if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) { final powNode = settingsStore.getCurrentPowNode(wallet.type); diff --git a/lib/router.dart b/lib/router.dart index 315c171783..3bf29772c8 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -83,7 +83,6 @@ import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart'; -import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; @@ -776,9 +775,6 @@ Route createRoute(RouteSettings settings) { ), ); - case Routes.torPage: - return MaterialPageRoute(builder: (_) => getIt.get()); - case Routes.signPage: return MaterialPageRoute( builder: (_) => SignPage( diff --git a/lib/routes.dart b/lib/routes.dart index 517efca296..dc5af4ffb6 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -109,7 +109,6 @@ class Routes { static const walletConnectConnectionsListing = '/wallet-connect-connections-listing'; static const nftDetailsPage = '/nft_details_page'; static const importNFTPage = '/import_nft_page'; - static const torPage = '/tor_page'; static const signPage = '/sign_page'; static const connectDevices = '/device/connect'; diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index 74daa41cce..37bac1b5da 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -9,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:mobx/mobx.dart'; +import 'package:tor/tor.dart'; class NodeForm extends StatelessWidget { NodeForm({ @@ -184,8 +185,27 @@ class NodeForm extends StatelessWidget { Observer( builder: (_) => Column( children: [ + if (nodeViewModel.usesEmbeddedProxy) ...[ + Padding( + padding: EdgeInsets.only(top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + StandardCheckbox( + value: nodeViewModel.usesEmbeddedProxy, + gradientBackground: false, + borderColor: Theme.of(context).dividerColor, + iconColor: Theme.of(context).colorScheme.primary, + onChanged: null, + caption: 'Embedded Tor SOCKS Proxy', + ), + ], + ), + ), + ], Padding( - padding: EdgeInsets.only(top: 20), + padding: EdgeInsets.only(top: 10), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart index c4d85a3a5b..31f95b2482 100644 --- a/lib/src/screens/settings/connection_sync_page.dart +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -115,10 +115,14 @@ class ConnectionSyncPage extends BasePage { ), ], if (FeatureFlag.isInAppTorEnabled) - SettingsCellWithArrow( - title: S.current.tor_connection, - handler: (context) => Navigator.of(context).pushNamed(Routes.torPage), - ), + Observer(builder: (context) { + return SettingsSwitcherCell( + title: S.current.enable_builtin_tor, + value: dashboardViewModel.builtinTor, + onValueChange: (_, bool value) => dashboardViewModel.setBuiltinTor(value, context), + // onTap: (c) => Navigator.of(c).pushNamed(Routes.torPage), + ); + }), ], ), ); diff --git a/lib/src/screens/settings/tor_page.dart b/lib/src/screens/settings/tor_page.dart deleted file mode 100644 index 2eb8d6c11a..0000000000 --- a/lib/src/screens/settings/tor_page.dart +++ /dev/null @@ -1,268 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:cw_core/utils/print_verbose.dart'; -import 'package:flutter/material.dart'; -// import 'package:tor/tor.dart'; - -class TorPage extends BasePage { - final AppStore appStore; - - TorPage(this.appStore); - - @override - Widget body(BuildContext context) { - return TorPageBody(appStore); - } -} - -class TorPageBody extends StatefulWidget { - final AppStore appStore; - - const TorPageBody(this.appStore, {Key? key}) : super(key: key); - - @override - State createState() => _TorPageBodyState(); -} - -class _TorPageBodyState extends State { - bool torEnabled = false; - bool connecting = false; - - // Set the default text for the host input field. - final hostController = TextEditingController(text: 'https://icanhazip.com/'); - - // https://check.torproject.org is another good option. - - Future startTor() async { - setState(() { - connecting = true; // Update flag - }); - - // await Tor.init(); - // - // // Start the proxy - // await Tor.instance.start(); - // - // // Toggle started flag. - // setState(() { - // torEnabled = Tor.instance.enabled; // Update flag - // connecting = false; - // }); - // - // final node = widget.appStore.settingsStore.getCurrentNode(widget.appStore.wallet!.type); - // if (node.socksProxyAddress?.isEmpty ?? true) { - // node.socksProxyAddress = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}"; - // } - // widget.appStore.wallet!.connectToNode(node: node); - - printV('Done awaiting; tor should be running'); - } - - Future endTor() async { - // // Start the proxy - // Tor.instance.disable(); - // - // // Toggle started flag. - // setState(() { - // torEnabled = Tor.instance.enabled; // Update flag - // }); - // - // printV('Done awaiting; tor should be stopped'); - } - // - // @override - // void initState() { - // super.initState(); - // - // torEnabled = Tor.instance.enabled; - // } - - @override - void dispose() { - // Clean up the controller when the widget is disposed. - hostController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Container( - padding: const EdgeInsets.all(10), - child: connecting - ? ConnectingScreen() - : torEnabled - ? DisconnectScreen(disconnect: endTor) - : ConnectScreen(connect: startTor), - ), - ); - } -} - -class ConnectScreen extends StatelessWidget { - final Function() connect; - - const ConnectScreen({super.key, required this.connect}); - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 200, - height: 200, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue, - ), - child: Icon( - Icons.lock, - color: Colors.white, - size: 100, - ), - ), - SizedBox(height: 20), - Text( - 'Connect to Tor', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 10), - Text( - 'Your connection to the Tor network ensures privacy and security.', - style: TextStyle( - fontSize: 16, - color: Colors.grey, - ), - textAlign: TextAlign.center, - ), - SizedBox(height: 30), - ElevatedButton( - onPressed: connect, - style: ElevatedButton.styleFrom( - // primary: Colors.blue, - padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - ), - child: Text( - 'Connect', - style: TextStyle( - fontSize: 18, - color: Colors.white, - ), - ), - ), - ], - ), - ); - } -} - -class DisconnectScreen extends StatelessWidget { - final Function() disconnect; - - const DisconnectScreen({super.key, required this.disconnect}); - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 200, - height: 200, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.green, - ), - child: Icon( - Icons.check, - color: Colors.white, - size: 100, - ), - ), - SizedBox(height: 20), - Text( - 'Connected to Tor', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 10), - Text( - 'You are currently connected to the Tor network.', - style: TextStyle( - fontSize: 16, - color: Colors.grey, - ), - textAlign: TextAlign.center, - ), - SizedBox(height: 30), - ElevatedButton( - onPressed: disconnect, - style: ElevatedButton.styleFrom( - // primary: Colors.red, - padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - ), - child: Text( - 'Disconnect', - style: TextStyle( - fontSize: 18, - color: Colors.white, - ), - ), - ), - ], - ), - ); - } -} - -class ConnectingScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 200, - height: 200, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.yellow, - ), - child: Icon( - Icons.hourglass_bottom, - color: Colors.white, - size: 100, - ), - ), - SizedBox(height: 20), - Text( - 'Connecting...', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 10), - ], - ), - ); - } -} diff --git a/lib/src/widgets/standard_checkbox.dart b/lib/src/widgets/standard_checkbox.dart index d61b84d1dd..80119f34a4 100644 --- a/lib/src/widgets/standard_checkbox.dart +++ b/lib/src/widgets/standard_checkbox.dart @@ -16,7 +16,7 @@ class StandardCheckbox extends StatelessWidget { final bool gradientBackground; final Color? borderColor; final Color? iconColor; - final Function(bool) onChanged; + final Function(bool)? onChanged; @override Widget build(BuildContext context) { @@ -39,7 +39,7 @@ class StandardCheckbox extends StatelessWidget { BoxDecoration(border: boxBorder, borderRadius: BorderRadius.all(Radius.circular(8.0))); return GestureDetector( - onTap: () => onChanged(!value), + onTap: onChanged == null ? null : () => onChanged!(!value), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index aa7df4ba96..b5922d456f 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -80,6 +80,7 @@ abstract class SettingsStoreBase with Store { required String initialLanguageCode, required SyncMode initialSyncMode, required bool initialSyncAll, + required bool initialBuiltinTor, // required String initialCurrentLocale, required this.appVersion, required this.deviceName, @@ -179,6 +180,7 @@ abstract class SettingsStoreBase with Store { initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings, currentSyncMode = initialSyncMode, currentSyncAll = initialSyncAll, + builtinTor = initialBuiltinTor, priority = ObservableMap() { //this.nodes = ObservableMap.of(nodes); @@ -391,6 +393,13 @@ abstract class SettingsStoreBase with Store { _backgroundTasks.registerSyncTask(changeExisting: true); }); + reaction((_) => builtinTor, (bool syncAll) { + sharedPreferences.setBool(PreferencesKey.builtinTorKey, builtinTor); + + _backgroundTasks.registerSyncTask(changeExisting: true); + }); + + reaction( (_) => exchangeStatus, (ExchangeApiMode mode) => @@ -778,6 +787,9 @@ abstract class SettingsStoreBase with Store { @observable bool currentSyncAll; + @observable + bool builtinTor; + String appVersion; String deviceName; @@ -1099,6 +1111,7 @@ abstract class SettingsStoreBase with Store { return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 0); }); final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true; + final builtinTor = sharedPreferences.getBool(PreferencesKey.builtinTorKey) ?? false; // migrated to secure: final timeOutDuration = await SecureKey.getInt( @@ -1279,6 +1292,7 @@ abstract class SettingsStoreBase with Store { initialSyncAll: savedSyncAll, shouldShowYatPopup: shouldShowYatPopup, shouldShowRepWarning: shouldShowRepWarning, + initialBuiltinTor: builtinTor, ); } diff --git a/lib/utils/feature_flag.dart b/lib/utils/feature_flag.dart index 593e0f216d..a5da055f6e 100644 --- a/lib/utils/feature_flag.dart +++ b/lib/utils/feature_flag.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; class FeatureFlag { static const bool isCakePayEnabled = false; static const bool isExolixEnabled = true; - static const bool isInAppTorEnabled = false; + static const bool isInAppTorEnabled = true; static const bool isBackgroundSyncEnabled = false; static const int verificationWordsCount = kDebugMode ? 0 : 2; } \ No newline at end of file diff --git a/lib/utils/tor.dart b/lib/utils/tor.dart new file mode 100644 index 0000000000..67a076610a --- /dev/null +++ b/lib/utils/tor.dart @@ -0,0 +1,53 @@ +import 'package:cw_core/utils/print_verbose.dart'; +import 'package:flutter/material.dart'; +import 'package:tor/tor.dart'; + +bool didTorStart = false; +Future ensureTorStopped({required BuildContext? context}) async { + if (!didTorStart) { + printV("Tor hasn't been initialized yet, so it can't be stopped."); + return; + } + if (context != null) showFullscreenDialog(context); + didTorStart = false; + printV("Stopping tor"); + await Tor.instance.stop(); + printV("Tor stopped"); + if (context != null) dismissFullscreenDialog(context); +} + +Future ensureTorStarted({required BuildContext? context}) async { + if (didTorStart) { + printV("Tor has already started"); + return; + } + if (context != null) showFullscreenDialog(context); + didTorStart = true; + printV("Initializing tor"); + await Tor.init(); + printV("Starting tor"); + await Tor.instance.start(); + printV("Tor started"); + if (context != null) dismissFullscreenDialog(context); +} + +Future showFullscreenDialog(BuildContext context) async { + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return Container( + color: Colors.transparent, + child: Center( + child: CircularProgressIndicator( + color: Colors.white, + ), + ), + ); + }, + ); +} + +Future dismissFullscreenDialog(BuildContext context) async { + Navigator.of(context).pop(); +} \ No newline at end of file diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 4ab171a15d..e994579fe9 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -13,6 +13,7 @@ import 'package:cake_wallet/entities/service_status.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/utils/tor.dart'; import 'package:cake_wallet/wownero/wownero.dart' as wow; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; @@ -42,15 +43,19 @@ import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/utils/file.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_wownero/api/wallet.dart'; import 'package:eth_sig_util/util/utils.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tor/tor.dart'; part 'dashboard_view_model.g.dart'; @@ -750,6 +755,25 @@ abstract class DashboardViewModelBase with Store { @computed bool get syncAll => settingsStore.currentSyncAll; + @computed + bool get builtinTor => settingsStore.builtinTor; + + @action + void setBuiltinTor(bool value, BuildContext context) { + settingsStore.builtinTor = value; + if (value) { + unawaited(ensureTorStarted(context: context).then((_) async { + if (settingsStore.builtinTor == false) return; // return when tor got disabled in the meantime; + await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type)); + })); + } else { + unawaited(ensureTorStopped(context: context).then((_) async { + if (settingsStore.builtinTor == true) return; // return when tor got enabled in the meantime; + await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type)); + })); + } + } + @action void setSyncAll(bool value) => settingsStore.currentSyncAll = value; diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart index 8b3c70c5e6..f368c251ee 100644 --- a/lib/view_model/node_list/node_create_or_edit_view_model.dart +++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart @@ -9,6 +9,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:collection/collection.dart'; import 'package:cake_wallet/utils/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:tor/tor.dart'; part 'node_create_or_edit_view_model.g.dart'; @@ -58,6 +59,9 @@ abstract class NodeCreateOrEditViewModelBase with Store { @observable bool useSocksProxy; + @computed + bool get usesEmbeddedProxy => Tor.instance.started; + @observable String socksProxyAddress; diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f52be7481a..35948e2f3d 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST sp_scanner + tor ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index e87b5a44ed..4481e77a1e 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -109,6 +109,10 @@ dependencies: solana: ^0.31.0+1 ledger_flutter_plus: ^1.4.1 hashlib: ^1.19.2 + tor: + git: + url: https://github.com/Foundation-Devices/tor/ + ref: d01450765029a4af436be1f6bf1a2d4400dea222 dev_dependencies: flutter_test: @@ -146,6 +150,10 @@ dependency_overrides: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v9 ffi: 2.1.0 + tor: + git: + url: https://github.com/Foundation-Devices/tor/ + ref: d01450765029a4af436be1f6bf1a2d4400dea222 flutter_icons: image_path: "assets/images/app_logo.png" diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index b0955b6d7c..6925c7a5d7 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "نقوم بإنشاء عناوين جديدة في كل مرة تستخدم فيها عنوانًا ، لكن العناوين السابقة تستمر في العمل", "email_address": "عنوان البريد الالكترونى", "enable": "يُمكَِن", + "enable_builtin_tor": "تمكين بنيت في تور", "enable_mempool_api": "MEMPOOL API للحصول على رسوم وتواريخ دقيقة", "enable_replace_by_fee": "تمكين الاستبدال", "enable_silent_payments_scanning": "ابدأ في مسح المدفوعات الصامتة ، حتى يتم الوصول إلى الطرف", @@ -765,6 +766,7 @@ "sort_by": "ترتيب حسب", "spend_key_private": "مفتاح الإنفاق (خاص)", "spend_key_public": "مفتاح الإنفاق (عام)", + "starting_tor_proxy": "بدء الوكيل Tor", "status": "الحالة:", "string_default": "تقصير", "subaddress_title": "قائمة العناوين الفرعية", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index e1fc3bd877..e91426db30 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят", "email_address": "Имейл адрес", "enable": "Активиране", + "enable_builtin_tor": "Активирайте вграден Tor", "enable_mempool_api": "Mempool API за точни такси и дати", "enable_replace_by_fee": "Активиране на замяна по забрана", "enable_silent_payments_scanning": "Започнете да сканирате безшумните плащания, докато се достигне съветът", @@ -765,6 +766,7 @@ "sort_by": "Сортирай по", "spend_key_private": "Spend key (таен)", "spend_key_public": "Spend key (публичен)", + "starting_tor_proxy": "Стартиране на прокси", "status": "Статус: ", "string_default": "По подразбиране", "subaddress_title": "Лист от подадреси", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 29c04496d5..85492cac62 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují", "email_address": "E-mailová adresa", "enable": "Umožnit", + "enable_builtin_tor": "Povolte vestavěné tor", "enable_mempool_api": "Mempool API pro přesné poplatky a data", "enable_replace_by_fee": "Povolit výměnu podle poplatku", "enable_silent_payments_scanning": "Začněte skenovat tiché platby, dokud není dosaženo špičky", @@ -765,6 +766,7 @@ "sort_by": "Seřazeno podle", "spend_key_private": "Klíč pro platby (soukromý)", "spend_key_public": "Klíč pro platby (veřejný)", + "starting_tor_proxy": "Zahájení proxy Tor", "status": "Status: ", "string_default": "Výchozí", "subaddress_title": "Seznam subadres", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 54c3a70e76..b3ff88304f 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin", "email_address": "E-Mail-Adresse", "enable": "Aktivieren", + "enable_builtin_tor": "Aktivieren Sie den bau-in tor", "enable_mempool_api": "Mempool-API für genaue Gebühren und Daten", "enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee", "enable_silent_payments_scanning": "Scannen Sie stille Zahlungen, bis die Spitze erreicht ist", @@ -511,8 +512,8 @@ "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist", "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.", - "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.", + "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_select": "Bitte auswählen:", "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden", @@ -766,6 +767,7 @@ "sort_by": "Sortiere nach", "spend_key_private": "Spend Key (geheim)", "spend_key_public": "Spend Key (öffentlich)", + "starting_tor_proxy": "TOR -Proxy beginnen", "status": "Status: ", "string_default": "Standard", "subaddress_title": "Unteradressenliste", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 995fb55951..c24fe56f7b 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work", "email_address": "Email Address", "enable": "Enable", + "enable_builtin_tor": "Enable built-in Tor", "enable_mempool_api": "Mempool API for accurate fees and dates", "enable_replace_by_fee": "Enable Replace-By-Fee", "enable_silent_payments_scanning": "Start scanning for transactions sent to your Silent Payment address.", @@ -765,6 +766,7 @@ "sort_by": "Sort by", "spend_key_private": "Spend key (private)", "spend_key_public": "Spend key (public)", + "starting_tor_proxy": "Starting Tor proxy", "status": "Status: ", "string_default": "Default", "subaddress_title": "Subaddress list", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 011206fbf7..8ab54ed278 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Generamos nuevas direcciones cada vez que usa una, pero las direcciones anteriores siguen funcionando", "email_address": "Dirección de correo electrónico", "enable": "Permitir", + "enable_builtin_tor": "Habilitar buildi-in tor", "enable_mempool_api": "API de Mempool para tarifas y fechas precisas", "enable_replace_by_fee": "Habilitar reemplazar por tarea", "enable_silent_payments_scanning": "Comienza a escanear pagos silenciosos, hasta que se alcance la altura actual", @@ -766,6 +767,7 @@ "sort_by": "Ordenar por", "spend_key_private": "Llave de gasto (privada)", "spend_key_public": "Llave de gasto (pública)", + "starting_tor_proxy": "Inicio Tor Proxy", "status": "Estado: ", "string_default": "Por defecto", "subaddress_title": "Lista de subdirecciones", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 7e2907c8f8..7d101b75ae 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner", "email_address": "Adresse e-mail", "enable": "Activer", + "enable_builtin_tor": "Activer le Tor construit", "enable_mempool_api": "API Mempool pour les frais et dates précis", "enable_replace_by_fee": "Activer Remplace-by-Fee", "enable_silent_payments_scanning": "Commencez à analyser les transactions envoyées à votre adresse de paiement silencieux.", @@ -765,6 +766,7 @@ "sort_by": "Trier par", "spend_key_private": "Clef de dépense (spend key) (privée)", "spend_key_public": "Clef de dépense (spend key) (publique)", + "starting_tor_proxy": "Démarrer le proxy Tor", "status": "Statut: ", "string_default": "Défaut", "subaddress_title": "Liste des sous-adresses", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index d06210e3d7..bd8e49d253 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Muna samar da sababbin adireshi duk lokacin da kuka yi amfani da ɗaya, amma adiresoshin da suka gabata suna ci gaba da aiki", "email_address": "Adireshin i-mel", "enable": "Ba dama", + "enable_builtin_tor": "Sanya riguna-inf", "enable_mempool_api": "Mampool API don ingantattun kudade da kwanakin", "enable_replace_by_fee": "Ba da damar maye gurbin-by-kudin", "enable_silent_payments_scanning": "Fara bincika biya na shiru, har sai tip ɗin ya kai", @@ -767,6 +768,7 @@ "sort_by": "Kasa", "spend_key_private": "makullin biya (maɓallin kalmar sirri)", "spend_key_public": "makullin biya (maɓallin jama'a)", + "starting_tor_proxy": "Fara tor proxy", "status": "Matsayi:", "string_default": "Ƙin cika alƙawari", "subaddress_title": "Jagorar subaddress", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 74de1126e1..c49d2e0655 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "हर बार जब आप एक का उपयोग करते हैं तो हम नए पते उत्पन्न करते हैं, लेकिन पिछले पते काम करना जारी रखते हैं", "email_address": "ईमेल पता", "enable": "सक्षम", + "enable_builtin_tor": "बिल्ट-इन टोर को सक्षम करें", "enable_mempool_api": "सटीक शुल्क और तिथियों के लिए मेमपूल एपीआई", "enable_replace_by_fee": "प्रतिस्थापित-दर-शुल्क सक्षम करें", "enable_silent_payments_scanning": "मूक भुगतान स्कैनिंग सक्षम करें", @@ -767,6 +768,7 @@ "sort_by": "इसके अनुसार क्रमबद्ध करें", "spend_key_private": "खर्च करना (निजी)", "spend_key_public": "खर्च करना (जनता)", + "starting_tor_proxy": "टोर प्रॉक्सी शुरू करना", "status": "स्थिति: ", "string_default": "गलती करना", "subaddress_title": "उपखंड सूची", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 41768bd4f3..4d40c4b2ca 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Minden egyes alkalommal új címeket generálunk, de a korábbi címek továbbra is működnek", "email_address": "Adresa e-pošte", "enable": "Omogućiti", + "enable_builtin_tor": "Omogućite ugrađeni u tor", "enable_mempool_api": "Mempool API za točne naknade i datume", "enable_replace_by_fee": "Omogući zamjenu", "enable_silent_payments_scanning": "Započnite skeniranje tihih plaćanja, dok se ne postigne savjet", @@ -765,6 +766,7 @@ "sort_by": "Poredaj po", "spend_key_private": "Spend key (privatni)", "spend_key_public": "Spend key (javni)", + "starting_tor_proxy": "Početak Tor proxy", "status": "Status: ", "string_default": "Zadano", "subaddress_title": "Lista podadresa", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 2a3aeed6d6..a4a0ab833e 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Մենք ստեղծում ենք նոր հասցե ամեն անգամ, երբ դուք օգտագործում եք այն, բայց նախորդ հասցեները շարունակում են աշխատել", "email_address": "Էլ. փոստի հասցե", "enable": "Միացնել", + "enable_builtin_tor": "Միացրեք ներկառուցված ջահը", "enable_mempool_api": "Mempool API ճշգրիտ վճարների եւ ամսաթվերի համար", "enable_replace_by_fee": "Միացնել փոխարինումը միջնորդավճարով", "enable_silent_payments_scanning": "Միացնել Լուռ Վճարումների սկանավորումը", @@ -765,6 +766,7 @@ "sort_by": "Դասավորել ըստ", "spend_key_private": "Վճարման բանալի (գախտնի)", "spend_key_public": "Վճարման բանալի (հանրային)", + "starting_tor_proxy": "Սկսելով Tor վստահված անձը", "status": "Կարգավիճակ՝ ", "string_default": "Լռելայն", "subaddress_title": "Ենթահասցեների ցանկ", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 90bf4d8069..469beb15af 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Kami menghasilkan alamat baru setiap kali Anda menggunakan satu, tetapi alamat sebelumnya tetap berfungsi", "email_address": "Alamat Email", "enable": "Memungkinkan", + "enable_builtin_tor": "Aktifkan built-in tor", "enable_mempool_api": "API Mempool untuk biaya dan tanggal yang akurat", "enable_replace_by_fee": "Aktifkan ganti-by-fee", "enable_silent_payments_scanning": "Mulailah memindai pembayaran diam, sampai ujung tercapai", @@ -768,6 +769,7 @@ "sort_by": "Sortir dengan", "spend_key_private": "Kunci pengeluaran (privat)", "spend_key_public": "Kunci pengeluaran (publik)", + "starting_tor_proxy": "Mulai untuk proxy", "status": "Status: ", "string_default": "Bawaan", "subaddress_title": "Daftar sub-alamat", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index c3796be5af..774dde6906 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -249,6 +249,7 @@ "electrum_address_disclaimer": "Generiamo nuovi indirizzi ogni volta che ne utilizzi uno, ma gli indirizzi precedenti continuano a funzionare", "email_address": "Indirizzo e-mail", "enable": "Abilitare", + "enable_builtin_tor": "Abilita buildi-in tor", "enable_mempool_api": "API di Mempool per commissioni e date accurate", "enable_replace_by_fee": "Abilita sostituzione per fee", "enable_silent_payments_scanning": "Inizia a scansionare i pagamenti silenziosi, fino a raggiungere la punta", @@ -767,6 +768,7 @@ "sort_by": "Ordina per", "spend_key_private": "Chiave di spesa (privata)", "spend_key_public": "Chiave di spesa (pubblica)", + "starting_tor_proxy": "Avvio del proxy di Tor", "status": "Stato: ", "string_default": "Predefinito", "subaddress_title": "Lista sottoindirizzi", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 4ccd2d8300..feb10ff279 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "使用するたびに新しいアドレスが生成されますが、以前のアドレスは引き続き機能します", "email_address": "メールアドレス", "enable": "有効にする", + "enable_builtin_tor": "組み込みのTORを有効にします", "enable_mempool_api": "正確な料金と日付のMempool API", "enable_replace_by_fee": "交換ごとに有効にします", "enable_silent_payments_scanning": "先端に達するまで、サイレント決済のスキャンを開始します", @@ -766,6 +767,7 @@ "sort_by": "並び替え", "spend_key_private": "キーを使う (プライベート)", "spend_key_public": "キーを使う (パブリック)", + "starting_tor_proxy": "プロキシを開始します", "status": "状態: ", "string_default": "デフォルト", "subaddress_title": "サブアドレス一覧", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 67a69e26bb..80acd38eba 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "사용할 때마다 새 주소가 생성되지만 이전 주소는 계속 작동합니다.", "email_address": "이메일 주소", "enable": "할 수 있게 하다", + "enable_builtin_tor": "내장 Tor를 활성화하십시오", "enable_mempool_api": "정확한 수수료 및 날짜에 대한 Mempool API", "enable_replace_by_fee": "대체별로 활성화하십시오", "enable_silent_payments_scanning": "팁에 도달 할 때까지 사일런트 지불을 스캔하기 시작합니다.", @@ -511,8 +512,8 @@ "placeholder_transactions": "거래가 여기에 표시됩니다", "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.", "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.", - "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", + "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "please_select": "선택 해주세요:", "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.", "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오", @@ -766,6 +767,7 @@ "sort_by": "정렬 기준", "spend_key_private": "지출 키 (은밀한)", "spend_key_public": "지출 키 (공공의)", + "starting_tor_proxy": "Tor 프록시 시작", "status": "지위: ", "string_default": "기본", "subaddress_title": "하위 주소 목록", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index dd2909d3ff..a5f6aedf10 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "သင်အသုံးပြုသည့်အချိန်တိုင်းတွင် ကျွန်ုပ်တို့သည် လိပ်စာအသစ်များကို ထုတ်ပေးသော်လည်း ယခင်လိပ်စာများသည် ဆက်လက်အလုပ်လုပ်နေပါသည်။", "email_address": "အီးမေးလ်လိပ်စာ", "enable": "စွမ်းဆောင်နိုင်စေ", + "enable_builtin_tor": "built-in Tor ကို Enable လုပ်ပါ", "enable_mempool_api": "Mempool API တိကျသောအခကြေးငွေနှင့်ရက်စွဲများအတွက်", "enable_replace_by_fee": "အစားထိုး - by- အခကြေးငွေ enable", "enable_silent_payments_scanning": "အစွန်အဖျားသို့ရောက်ရှိသည်အထိအသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးပါ", @@ -765,6 +766,7 @@ "sort_by": "အလိုက်စဥ်သည်", "spend_key_private": "သော့သုံးရန် (သီးသန့်)", "spend_key_public": "သုံးစွဲရန်သော့ (အများပြည်သူ)", + "starting_tor_proxy": "Tor proxy ကိုစတင်ခြင်း", "status": "အခြေအနေ:", "string_default": "ပျက်ကွက်ခြင်း", "subaddress_title": "လိပ်စာစာရင်း", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 8b32d669f6..9a978e6941 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "We genereren nieuwe adressen elke keer dat u er een gebruikt, maar eerdere adressen blijven werken", "email_address": "E-mailadres", "enable": "Inschakelen", + "enable_builtin_tor": "Schakel ingebouwde tor in", "enable_mempool_api": "Mempool API voor nauwkeurige kosten en datums", "enable_replace_by_fee": "Schakel vervangen door een fee", "enable_silent_payments_scanning": "Begin met het scannen van stille betalingen, totdat de tip is bereikt", @@ -765,6 +766,7 @@ "sort_by": "Sorteer op", "spend_key_private": "Sleutel uitgeven (privaat)", "spend_key_public": "Sleutel uitgeven (openbaar)", + "starting_tor_proxy": "Tor Proxy starten", "status": "Staat: ", "string_default": "Standaard", "subaddress_title": "Subadreslijst", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 2f2c19546d..9e4bef8be7 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Za każdym razem, gdy wykorzystasz adres, dla wiekszej prywatności generujemy nowy, ale poprzednie adresy nadal działają, i moga odbierać środki", "email_address": "Adres e-mail", "enable": "Włączać", + "enable_builtin_tor": "Włącz wbudowany tor", "enable_mempool_api": "Mempool API dla dokładnych opłat i dat", "enable_replace_by_fee": "Włącz wymianę po lewej", "enable_silent_payments_scanning": "Zacznij skanować ciche płatności, aż do osiągnięcia wskazówki", @@ -765,6 +766,7 @@ "sort_by": "Sortuj według", "spend_key_private": "Klucz prywatny", "spend_key_public": "Klucz publiczny", + "starting_tor_proxy": "Rozpoczęcie proxy TOR", "status": "Status: ", "string_default": "Domyślny", "subaddress_title": "Lista podadresów", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index cf3adcf821..d428b0f190 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Geramos novos endereços cada vez que você usa um, mas os endereços anteriores continuam funcionando", "email_address": "Endereço de e-mail", "enable": "Habilitar", + "enable_builtin_tor": "Ative o Buildi-in Tor", "enable_mempool_api": "Mempool API para taxas e datas precisas", "enable_replace_by_fee": "Habilite substituir por taxa", "enable_silent_payments_scanning": "Comece a escanear pagamentos silenciosos, até que o topo seja alcançada", @@ -767,6 +768,7 @@ "sort_by": "Ordenar por", "spend_key_private": "Chave de gastos (privada)", "spend_key_public": "Chave de gastos (pública)", + "starting_tor_proxy": "Começando por proxy", "status": "Status: ", "string_default": "Padrão", "subaddress_title": "Sub-endereços", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 0ba732a1c9..5f753ea15c 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Мы генерируем новые адреса каждый раз, когда вы их используете, но предыдущие адреса продолжают работать.", "email_address": "Адрес электронной почты", "enable": "Давать возможность", + "enable_builtin_tor": "Включить встроенный в Tor", "enable_mempool_api": "Mempool API за точные сборы и даты", "enable_replace_by_fee": "Включить замену за пикой", "enable_silent_payments_scanning": "Начните сканировать безмолвные платежи, пока не будет достигнут наконечник", @@ -766,6 +767,7 @@ "sort_by": "Сортировать по", "spend_key_private": "Приватный ключ траты", "spend_key_public": "Публичный ключ траты", + "starting_tor_proxy": "Запуск прокси", "status": "Статус: ", "string_default": "По умолчанию", "subaddress_title": "Список субадресов", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 29764b302b..094c275c2b 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "เราสร้างที่อยู่ใหม่ทุกครั้งที่คุณใช้หนึ่งอย่าง แต่ที่อยู่เก่ายังสามารถใช้ได้ต่อไป", "email_address": "ที่อยู่อีเมล", "enable": "เปิดใช้งาน", + "enable_builtin_tor": "เปิดใช้งาน Tor ในตัว", "enable_mempool_api": "Mempool API สำหรับค่าธรรมเนียมและวันที่ที่ถูกต้อง", "enable_replace_by_fee": "เปิดใช้งานการเปลี่ยนโดยค่าธรรมเนียม", "enable_silent_payments_scanning": "เริ่มสแกนการชำระเงินแบบเงียบจนกว่าจะถึงปลาย", @@ -765,6 +766,7 @@ "sort_by": "เรียงตาม", "spend_key_private": "คีย์จ่าย (ส่วนตัว)", "spend_key_public": "คีย์จ่าย (สาธารณะ)", + "starting_tor_proxy": "เริ่มพร็อกซี TOR", "status": "สถานะ: ", "string_default": "ค่าเริ่มต้น", "subaddress_title": "รายการที่อยู่ย่อย", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 772ed2cc0b..40f7880e38 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Bumubuo kami ng mga bagong address sa tuwing gagamit ka ng isa, ngunit ang mga nakaraang address ay patuloy na gumagana", "email_address": "Email Address", "enable": "Paganahin", + "enable_builtin_tor": "Paganahin ang built-in tor", "enable_mempool_api": "Mempool API para sa tumpak na bayad at mga petsa", "enable_replace_by_fee": "Paganahin ang Replace-By-Fee", "enable_silent_payments_scanning": "Simulan ang pag -scan ng tahimik na pagbabayad, hanggang sa maabot ang tip", @@ -765,6 +766,7 @@ "sort_by": "Pag-uri-uriin sa pamamagitan ng", "spend_key_private": "Spend key (private)", "spend_key_public": "Spend key (public)", + "starting_tor_proxy": "Simula sa Tor Proxy", "status": "Katayuan: ", "string_default": "Default", "subaddress_title": "Listahan ng Subaddress", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 34fc7da8ca..1fc8c4d7dd 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Adresini her kullandığında yeni adres oluşturuyoruz, ancak önceki adresler de çalışmaya devam eder", "email_address": "E-posta Adresi", "enable": "Olanak vermek", + "enable_builtin_tor": "Yerleşik Tor'u etkinleştirin", "enable_mempool_api": "Doğru ücretler ve tarihler için Mempool API'si", "enable_replace_by_fee": "Farklı Değiştir'i Etkinleştir", "enable_silent_payments_scanning": "Bahşiş ulaşılıncaya kadar sessiz ödemeleri taramaya başlayın", @@ -765,6 +766,7 @@ "sort_by": "Göre sırala", "spend_key_private": "Harcama anahtarı (özel)", "spend_key_public": "Harcama anahtarı (genel)", + "starting_tor_proxy": "Tor Proxy'yi Başlatma", "status": "Durum: ", "string_default": "Varsayılan", "subaddress_title": "Alt adres listesi", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 35d12cbbb2..a033c02cdd 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "Ми створюємо нові адреси щоразу, коли ви використовуєте їх, але попередні адреси продовжують працювати", "email_address": "Адреса електронної пошти", "enable": "Ввімкнути", + "enable_builtin_tor": "Увімкнути вбудований tor", "enable_mempool_api": "API Mempool для точних зборів та дат", "enable_replace_by_fee": "Увімкнути заміну з комісією", "enable_silent_payments_scanning": "Почніть сканувати мовчазні платежі, поки не буде досягнуто наконечника", @@ -766,6 +767,7 @@ "sort_by": "Сортувати за", "spend_key_private": "Приватний ключ витрати", "spend_key_public": "Публічний ключ витрати", + "starting_tor_proxy": "Початок Tor Proxy", "status": "Статус: ", "string_default": "За замовчуванням", "subaddress_title": "Список Субадрес", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 874b9913ea..80c3545a67 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "جب بھی آپ ایک کا استعمال کرتے ہیں تو ہم نئے پتے تیار کرتے ہیں، لیکن پچھلے پتے کام کرتے رہتے ہیں۔", "email_address": "ای میل اڈریس", "enable": "قابل بنائیں", + "enable_builtin_tor": "بلٹٹی ان ٹور کو فعال کریں", "enable_mempool_api": "درست فیسوں اور تاریخوں کے لئے میمپول API", "enable_replace_by_fee": "فی فیس کو تبدیل کریں", "enable_silent_payments_scanning": "خاموش ادائیگیوں کو اسکین کرنا شروع کریں ، جب تک کہ نوک نہ پہنچ جائے", @@ -767,6 +768,7 @@ "sort_by": "ترتیب دیں", "spend_key_private": "خرچ کی کلید (نجی)", "spend_key_public": "خرچ کی کلید (عوامی)", + "starting_tor_proxy": "ٹور پراکسی شروع کرنا", "status": "حالت:", "string_default": "پہلے سے طے شدہ", "subaddress_title": "ذیلی ایڈریس کی فہرست", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index 5b3b08a6bb..8b5f071e48 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -247,6 +247,7 @@ "electrum_address_disclaimer": "Chúng tôi tạo địa chỉ mới mỗi khi bạn sử dụng, nhưng các địa chỉ cũ vẫn tiếp tục hoạt động", "email_address": "Địa chỉ Email", "enable": "Cho phép", + "enable_builtin_tor": "Bật Buildi-In Tor", "enable_mempool_api": "API Mempool cho các khoản phí và ngày chính xác", "enable_replace_by_fee": "Bật Thay thế Bằng Phí", "enable_silent_payments_scanning": "Bật quét thanh toán im lặng", @@ -764,6 +765,7 @@ "sort_by": "Sắp xếp theo", "spend_key_private": "Khóa chi tiêu (riêng tư)", "spend_key_public": "Khóa chi tiêu (công khai)", + "starting_tor_proxy": "Bắt đầu proxy tor", "status": "Trạng thái: ", "string_default": "Mặc định", "subaddress_title": "Danh sách địa chỉ phụ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 360676a7e9..5bdbf81b45 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -249,6 +249,7 @@ "electrum_address_disclaimer": "A dá àwọn àdírẹ́sì títun ní gbogbo àwọn ìgbà t'ẹ́ lo ó kan ṣùgbọ́n ẹ lè tẹ̀síwájú lo àwọn àdírẹ́sì tẹ́lẹ̀tẹ́lẹ̀.", "email_address": "Àdírẹ́sì ímeèlì", "enable": "Mu ṣiṣẹ", + "enable_builtin_tor": "Mu ṣiṣẹ", "enable_mempool_api": "Mempool API fun awọn owo deede ati awọn ọjọ", "enable_replace_by_fee": "Mu ki o rọpo", "enable_silent_payments_scanning": "Bẹrẹ awọn sisanwo ipalọlọ, titi ti o fi de opin", @@ -766,6 +767,7 @@ "sort_by": "Sa pelu", "spend_key_private": "Kọ́kọ́rọ́ sísan (àdáni)", "spend_key_public": "Kọ́kọ́rọ́ sísan (kò àdáni)", + "starting_tor_proxy": "Bibẹrẹ Oluṣaaju Aṣoju", "status": "Tó ń ṣẹlẹ̀: ", "string_default": "Aiyipada", "subaddress_title": "Àkọsílẹ̀ ni nínú àwọn àdírẹ́sì tíwọn rẹ̀lẹ̀", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index e032c40c4f..bfd0c56af1 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -248,6 +248,7 @@ "electrum_address_disclaimer": "每次您使用一个地址时,我们都会生成新地址,但之前的地址仍然有效", "email_address": "电子邮件地址", "enable": "使能够", + "enable_builtin_tor": "启用内置的tor", "enable_mempool_api": "Mempool API获得准确的费用和日期", "enable_replace_by_fee": "启用by-Fee替换", "enable_silent_payments_scanning": "开始扫描无声付款,直到达到提示", @@ -765,6 +766,7 @@ "sort_by": "排序方式", "spend_key_private": "Spend 密钥 (私钥)", "spend_key_public": "Spend 密钥 (公钥)", + "starting_tor_proxy": "启动Tor代理", "status": "状态: ", "string_default": "默认", "subaddress_title": "子地址列表", diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index e0f2c11c04..0f6145698d 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -15,6 +15,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST sp_scanner + tor ) set(PLUGIN_BUNDLED_LIBRARIES) From b70007da8b86fca4f9248bd4e9a949caa37335df Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 13 Jan 2025 12:34:59 +0100 Subject: [PATCH 02/10] Enable tor on iOS --- lib/utils/feature_flag.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/feature_flag.dart b/lib/utils/feature_flag.dart index a5da055f6e..65b5cf99fe 100644 --- a/lib/utils/feature_flag.dart +++ b/lib/utils/feature_flag.dart @@ -1,9 +1,10 @@ import 'package:flutter/foundation.dart'; +import 'dart:io'; class FeatureFlag { static const bool isCakePayEnabled = false; static const bool isExolixEnabled = true; - static const bool isInAppTorEnabled = true; + static final bool isInAppTorEnabled = (Platform.isAndroid || Platform.isIOS); static const bool isBackgroundSyncEnabled = false; static const int verificationWordsCount = kDebugMode ? 0 : 2; } \ No newline at end of file From bd46f65f0836a6c5d51aeb41edf1aad330ed6acb Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 14 Jan 2025 17:23:35 +0100 Subject: [PATCH 03/10] Prevent app lag when node is exceptionally slow (usually over tor) --- cw_monero/lib/api/wallet.dart | 14 +++++++++++++- cw_wownero/lib/api/wallet.dart | 15 +++++++++++++-- .../node_list/node_list_view_model.dart | 3 ++- .../node_list/pow_node_list_view_model.dart | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 537c9802e7..29db50249d 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -91,7 +91,19 @@ int getUnlockedBalance({int accountIndex = 0}) => int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!); -int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!); +bool isHeightRefreshing = false; +int cachedNodeHeight = 0; +int getNodeHeightSync() { + if (isHeightRefreshing == false) { + (() async { + cachedNodeHeight = await Isolate.run(() async { + return monero.Wallet_daemonBlockChainHeight(wptr!); + }); + isHeightRefreshing = true; + })(); + } + return cachedNodeHeight; +} bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0; diff --git a/cw_wownero/lib/api/wallet.dart b/cw_wownero/lib/api/wallet.dart index 21dcd04c7f..4d631132b1 100644 --- a/cw_wownero/lib/api/wallet.dart +++ b/cw_wownero/lib/api/wallet.dart @@ -90,8 +90,19 @@ int getUnlockedBalance({int accountIndex = 0}) => int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!); -int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!); - +bool isHeightRefreshing = false; +int cachedNodeHeight = 0; +int getNodeHeightSync() { + if (isHeightRefreshing == false) { + (() async { + cachedNodeHeight = await Isolate.run(() async { + return wownero.Wallet_daemonBlockChainHeight(wptr!); + }); + isHeightRefreshing = true; + })(); + } + return cachedNodeHeight; +} bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0; Future setupNodeSync( diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 2721fd7b36..9128a9af6f 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -9,6 +9,7 @@ import 'package:cw_core/node.dart'; import 'package:cake_wallet/entities/node_list.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:tor/tor.dart'; part 'node_list_view_model.g.dart'; @@ -38,7 +39,7 @@ abstract class NodeListViewModelBase with Store { String getAlertContent(String uri) => S.current.change_current_node(uri) + - '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}'; + '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + (Tor.instance.enabled ? '' : S.current.orbot_running_alert) : ''}'; final ObservableList nodes; final SettingsStore settingsStore; diff --git a/lib/view_model/node_list/pow_node_list_view_model.dart b/lib/view_model/node_list/pow_node_list_view_model.dart index 5467be31a8..e43e16bc5b 100644 --- a/lib/view_model/node_list/pow_node_list_view_model.dart +++ b/lib/view_model/node_list/pow_node_list_view_model.dart @@ -9,6 +9,7 @@ import 'package:cw_core/node.dart'; import 'package:cake_wallet/entities/node_list.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:tor/tor.dart'; part 'pow_node_list_view_model.g.dart'; @@ -38,7 +39,7 @@ abstract class PowNodeListViewModelBase with Store { String getAlertContent(String uri) => S.current.change_current_node(uri) + - '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}'; + '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + (Tor.instance.enabled ? '' : S.current.orbot_running_alert) : ''}'; final ObservableList nodes; final SettingsStore settingsStore; From 561c4341fdaef99ba84df339d305b7b375186d0c Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 15 Jan 2025 12:57:16 +0100 Subject: [PATCH 04/10] fix: logic in daemonBlockchainHeight refresh fix: storing tor state --- cw_monero/lib/api/wallet.dart | 18 ++++++++---------- cw_wownero/lib/api/wallet.dart | 18 ++++++++---------- lib/core/backup_service.dart | 1 + lib/entities/background_tasks.dart | 4 ++-- lib/reactions/check_connection.dart | 2 +- lib/reactions/on_current_wallet_change.dart | 2 +- lib/store/settings_store.dart | 8 +++----- .../dashboard/dashboard_view_model.dart | 8 ++++---- 8 files changed, 28 insertions(+), 33 deletions(-) diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 29db50249d..f9fcb4271f 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -91,17 +91,15 @@ int getUnlockedBalance({int accountIndex = 0}) => int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!); -bool isHeightRefreshing = false; int cachedNodeHeight = 0; int getNodeHeightSync() { - if (isHeightRefreshing == false) { - (() async { - cachedNodeHeight = await Isolate.run(() async { - return monero.Wallet_daemonBlockChainHeight(wptr!); - }); - isHeightRefreshing = true; - })(); - } + printV("getNodeHeightSync: $cachedNodeHeight"); + (() async { + final wptrAddress = wptr!.address; + cachedNodeHeight = await Isolate.run(() async { + return monero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress)); + }); + })(); return cachedNodeHeight; } @@ -157,7 +155,7 @@ Future setupNodeSync( } void startRefreshSync() { - monero.Wallet_refreshAsync(wptr!); + // monero.Wallet_refreshAsync(wptr!); monero.Wallet_startRefresh(wptr!); } diff --git a/cw_wownero/lib/api/wallet.dart b/cw_wownero/lib/api/wallet.dart index 4d631132b1..7cdb943b6b 100644 --- a/cw_wownero/lib/api/wallet.dart +++ b/cw_wownero/lib/api/wallet.dart @@ -90,19 +90,17 @@ int getUnlockedBalance({int accountIndex = 0}) => int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!); -bool isHeightRefreshing = false; int cachedNodeHeight = 0; int getNodeHeightSync() { - if (isHeightRefreshing == false) { - (() async { - cachedNodeHeight = await Isolate.run(() async { - return wownero.Wallet_daemonBlockChainHeight(wptr!); - }); - isHeightRefreshing = true; - })(); - } + (() async { + final wptrAddress = wptr!.address; + cachedNodeHeight = await Isolate.run(() async { + return wownero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress)); + }); + })(); return cachedNodeHeight; } + bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0; Future setupNodeSync( @@ -145,7 +143,7 @@ Future setupNodeSync( } void startRefreshSync() { - wownero.Wallet_refreshAsync(wptr!); + // wownero.Wallet_refreshAsync(wptr!); wownero.Wallet_startRefresh(wptr!); } diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 7f598a9efc..b079a9944a 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -386,6 +386,7 @@ class BackupService { await _sharedPreferences.setString(PreferencesKey.defaultBananoRep, defaultBananoRep); if (syncAll != null) await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll); + if (builtinTor != null) await _sharedPreferences.setBool(PreferencesKey.builtinTorKey, builtinTor); if (lookupsTwitter != null) await _sharedPreferences.setBool(PreferencesKey.lookupsTwitter, lookupsTwitter); diff --git a/lib/entities/background_tasks.dart b/lib/entities/background_tasks.dart index 8c6c897e52..9075f7d94b 100644 --- a/lib/entities/background_tasks.dart +++ b/lib/entities/background_tasks.dart @@ -50,7 +50,7 @@ void callbackDispatcher() { await walletLoadingService.load(moneroWallets[i].type, moneroWallets[i].name); final node = getIt.get().getCurrentNode(moneroWallets[i].type); final settingsStore = getIt.get(); - if (settingsStore.builtinTor) { + if (settingsStore.currentBuiltinTor) { await ensureTorStarted(context: null); } await wallet.connectToNode(node: node); @@ -66,7 +66,7 @@ void callbackDispatcher() { wallet = await walletLoadingService.load(WalletType.values[typeRaw!], name!); final node = getIt.get().getCurrentNode(WalletType.values[typeRaw]); final settingsStore = getIt.get(); - if (settingsStore.builtinTor) { + if (settingsStore.currentBuiltinTor) { await ensureTorStarted(context: null); } diff --git a/lib/reactions/check_connection.dart b/lib/reactions/check_connection.dart index 50cfecb248..0ddf28dfaf 100644 --- a/lib/reactions/check_connection.dart +++ b/lib/reactions/check_connection.dart @@ -34,7 +34,7 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore final alive = await settingsStore.getCurrentNode(wallet.type).requestNode(); if (alive) { - if (settingsStore.builtinTor) { + if (settingsStore.currentBuiltinTor) { await ensureTorStarted(context: null); } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index ba1c3ab3b3..34894c3784 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -75,7 +75,7 @@ void startCurrentWalletChangeReaction( _setAutoGenerateSubaddressStatus(wallet, settingsStore); } - if (settingsStore.builtinTor) { + if (settingsStore.currentBuiltinTor) { await ensureTorStarted(context: null); } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index b5922d456f..f515adc5bf 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -180,7 +180,7 @@ abstract class SettingsStoreBase with Store { initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings, currentSyncMode = initialSyncMode, currentSyncAll = initialSyncAll, - builtinTor = initialBuiltinTor, + currentBuiltinTor = initialBuiltinTor, priority = ObservableMap() { //this.nodes = ObservableMap.of(nodes); @@ -393,10 +393,8 @@ abstract class SettingsStoreBase with Store { _backgroundTasks.registerSyncTask(changeExisting: true); }); - reaction((_) => builtinTor, (bool syncAll) { + reaction((_) => currentBuiltinTor, (bool builtinTor) { sharedPreferences.setBool(PreferencesKey.builtinTorKey, builtinTor); - - _backgroundTasks.registerSyncTask(changeExisting: true); }); @@ -788,7 +786,7 @@ abstract class SettingsStoreBase with Store { bool currentSyncAll; @observable - bool builtinTor; + bool currentBuiltinTor; String appVersion; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index e994579fe9..c90918d7ff 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -756,19 +756,19 @@ abstract class DashboardViewModelBase with Store { bool get syncAll => settingsStore.currentSyncAll; @computed - bool get builtinTor => settingsStore.builtinTor; + bool get builtinTor => settingsStore.currentBuiltinTor; @action void setBuiltinTor(bool value, BuildContext context) { - settingsStore.builtinTor = value; + settingsStore.currentBuiltinTor = value; if (value) { unawaited(ensureTorStarted(context: context).then((_) async { - if (settingsStore.builtinTor == false) return; // return when tor got disabled in the meantime; + if (settingsStore.currentBuiltinTor == false) return; // return when tor got disabled in the meantime; await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type)); })); } else { unawaited(ensureTorStopped(context: context).then((_) async { - if (settingsStore.builtinTor == true) return; // return when tor got enabled in the meantime; + if (settingsStore.currentBuiltinTor == true) return; // return when tor got enabled in the meantime; await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type)); })); } From 098907f4e31f0eb2e0fff057b10ebd43b799a8dd Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 15 Jan 2025 15:09:27 +0100 Subject: [PATCH 05/10] Pin ledger_flutter_plus dependency to fix builds --- pubspec_base.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 4481e77a1e..0974c733e2 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -107,7 +107,10 @@ dependencies: polyseed: ^0.0.6 nostr_tools: ^1.0.9 solana: ^0.31.0+1 - ledger_flutter_plus: ^1.4.1 + ledger_flutter_plus: + git: + url: https://github.com/vespr-wallet/ledger-flutter-plus + ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76 hashlib: ^1.19.2 tor: git: @@ -154,6 +157,10 @@ dependency_overrides: git: url: https://github.com/Foundation-Devices/tor/ ref: d01450765029a4af436be1f6bf1a2d4400dea222 + ledger_flutter_plus: + git: + url: https://github.com/vespr-wallet/ledger-flutter-plus + ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76 flutter_icons: image_path: "assets/images/app_logo.png" From 250920f00e223ab210ecee775334907a44e27ad9 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 15 Jan 2025 18:05:13 +0100 Subject: [PATCH 06/10] bump arti version --- pubspec_base.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 0974c733e2..26828bd264 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -155,8 +155,8 @@ dependency_overrides: ffi: 2.1.0 tor: git: - url: https://github.com/Foundation-Devices/tor/ - ref: d01450765029a4af436be1f6bf1a2d4400dea222 + url: https://github.com/MrCyjaneK/tor/ + ref: 7cbedbade71a66b2226aca087665c600369f3e31 ledger_flutter_plus: git: url: https://github.com/vespr-wallet/ledger-flutter-plus From 2e4ebdd7b45ddb4c3779d95ae63d45e01df8389f Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 16 Jan 2025 08:22:52 +0100 Subject: [PATCH 07/10] wip --- cw_core/lib/node.dart | 3 +- lib/utils/proxy_wrapper.dart | 231 +++++++++++++++++++++++++++++++++++ pubspec_base.yaml | 5 +- 3 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 lib/utils/proxy_wrapper.dart diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index b890e58a7e..d1e1657ab7 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:cw_core/keyable.dart'; +import 'package:cw_core/utils/http_client.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; @@ -179,7 +180,7 @@ class Node extends HiveObject with Keyable { final body = {'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'}; try { - final authenticatingClient = HttpClient(); + final authenticatingClient = getHttpClient(); authenticatingClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true); diff --git a/lib/utils/proxy_wrapper.dart b/lib/utils/proxy_wrapper.dart new file mode 100644 index 0000000000..0160eceb5a --- /dev/null +++ b/lib/utils/proxy_wrapper.dart @@ -0,0 +1,231 @@ +import 'dart:io'; + +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:socks5_proxy/socks.dart'; + +class ProxyWrapper { + ProxyWrapper({ + this.settingsStore, + this.torViewModel, + }); + + SettingsStore? settingsStore; + TorViewModel? torViewModel; + + HttpClient? _torClient; + + int getPort() { + TorConnectionMode mode = settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; + if (mode == TorConnectionMode.disabled) { + return -1; + } + return torViewModel?.torInstance.port ?? -1; + } + + bool started = false; + + Future getProxyHttpClient({int? portOverride}) async { + if (portOverride == -1 || portOverride == null) { + portOverride = torViewModel?.torInstance.port ?? -1; + } + + if (!started) { + started = true; + _torClient = HttpClient(); + + // Assign connection factory. + SocksTCPClient.assignToHttpClient(_torClient!, [ + ProxySettings( + InternetAddress.loopbackIPv4, + portOverride, + password: null, + ), + ]); + } + + return _torClient!; + } + + Future makeGet({ + required HttpClient client, + required Uri uri, + required Map? headers, + }) async { + final request = await client.getUrl(uri); + if (headers != null) { + headers.forEach((key, value) { + request.headers.add(key, value); + }); + } + return await request.close(); + } + + Future makePost({ + required HttpClient client, + required Uri uri, + required Map? headers, + }) async { + final request = await client.postUrl(uri); + if (headers != null) { + headers.forEach((key, value) { + request.headers.add(key, value); + }); + } + return await request.close(); + } + + Future get({ + Map? headers, + int? portOverride, + TorConnectionMode? torConnectionMode, + TorConnectionStatus? torConnectionStatus, + Uri? clearnetUri, + Uri? onionUri, + }) async { + HttpClient? torClient; + late bool torEnabled; + torConnectionMode ??= settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; + torConnectionStatus ??= torViewModel?.torConnectionStatus ?? TorConnectionStatus.disconnected; + + if (torConnectionMode == TorConnectionMode.torOnly || + torConnectionMode == TorConnectionMode.enabled) { + torEnabled = true; + } else { + torEnabled = false; + } + + if (torEnabled) { + torConnectionMode = TorConnectionMode.torOnly; + } + + if (torEnabled && torConnectionStatus == TorConnectionStatus.connecting) { + throw Exception("Tor is still connecting"); + } + + // if tor is enabled, try to connect to the onion url first: + if (torEnabled) { + try { + torClient = await getProxyHttpClient(portOverride: portOverride); + } catch (_) {} + + if (onionUri != null) { + try { + return await makeGet( + client: torClient!, + uri: onionUri, + headers: headers, + ); + } catch (_) {} + } + + if (clearnetUri != null) { + try { + return await makeGet( + client: torClient!, + uri: clearnetUri, + headers: headers, + ); + } catch (_) {} + } + } + + if (clearnetUri != null && torConnectionMode != TorConnectionMode.torOnly) { + try { + return HttpOverrides.runZoned( + () async { + return await 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 post({ + Map? headers, + int? portOverride, + TorConnectionMode? torConnectionMode, + TorConnectionStatus? torConnectionStatus, + Uri? clearnetUri, + Uri? onionUri, + }) async { + HttpClient? torClient; + late bool torEnabled; + torConnectionMode ??= settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; + torConnectionStatus ??= torViewModel?.torConnectionStatus ?? TorConnectionStatus.disconnected; + + if (torConnectionMode == TorConnectionMode.torOnly || + torConnectionMode == TorConnectionMode.enabled) { + torEnabled = true; + } else { + torEnabled = false; + } + + if (torEnabled) { + torConnectionMode = TorConnectionMode.torOnly; + } + + if (torEnabled && torConnectionStatus == TorConnectionStatus.connecting) { + throw Exception("Tor is still connecting"); + } + + // if tor is enabled, try to connect to the onion url first: + + if (torEnabled) { + try { + torClient = await getProxyHttpClient(portOverride: portOverride); + } catch (_) {} + + if (onionUri != null) { + try { + return await makePost( + client: torClient!, + uri: onionUri, + headers: headers, + ); + } catch (_) {} + } + + if (clearnetUri != null) { + try { + return await makePost( + client: torClient!, + uri: clearnetUri, + headers: headers, + ); + } catch (_) {} + } + } + + if (clearnetUri != null && torConnectionMode != TorConnectionMode.torOnly) { + try { + return HttpOverrides.runZoned( + () async { + return await 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"); + } +} diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 26828bd264..84d28f3de6 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -102,7 +102,10 @@ dependencies: # git: # url: https://github.com/cake-tech/tor.git # ref: main - socks5_proxy: ^1.0.4 + socks5_proxy: + git: + url: https://github.com/cake-tech/socks_dart.git + ref: d304fcfcc97cb7212bcd347aeb5d96792c128ff3 flutter_svg: ^2.0.9 polyseed: ^0.0.6 nostr_tools: ^1.0.9 From 70c7a9d810690fef822c9339bb64c2bd069ae97c Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 16 Jan 2025 08:23:31 +0100 Subject: [PATCH 08/10] add single httpclient --- cw_core/lib/utils/http_client.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 cw_core/lib/utils/http_client.dart diff --git a/cw_core/lib/utils/http_client.dart b/cw_core/lib/utils/http_client.dart new file mode 100644 index 0000000000..d295bc2277 --- /dev/null +++ b/cw_core/lib/utils/http_client.dart @@ -0,0 +1,19 @@ +import 'dart:io'; +import 'package:http/http.dart'; +import 'package:socks5_proxy/socks_client.dart'; +import 'package:tor/tor.dart'; + +HttpClient getHttpClient() { + final client = HttpClient(); + + if (Tor.instance.enabled) { + SocksTCPClient.assignToHttpClient(client, [ + ProxySettings(InternetAddress.loopbackIPv4, + Tor.instance.port, + password: null, + ), + ]); + } + + return client; +} From f4431fd63abe46d00b8eca84ad050a7eb8844adc Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 16 Jan 2025 14:56:52 +0100 Subject: [PATCH 09/10] Route everything I was able to catch trough the built-in tor node --- cw_bitcoin/lib/electrum_wallet.dart | 45 ++++++----- cw_core/lib/get_height_by_date.dart | 21 +++-- cw_core/lib/node.dart | 11 +-- lib/anonpay/anonpay_api.dart | 21 ++--- lib/buy/dfx/dfx_buy_provider.dart | 15 ++-- lib/buy/meld/meld_buy_provider.dart | 8 +- lib/buy/moonpay/moonpay_provider.dart | 17 ++-- lib/buy/onramper/onramper_buy_provider.dart | 18 +++-- lib/buy/robinhood/robinhood_buy_provider.dart | 6 +- lib/buy/wyre/wyre_buy_provider.dart | 13 ++-- lib/cake_pay/cake_pay_api.dart | 19 +++-- lib/core/fiat_conversion_service.dart | 6 +- lib/core/yat_service.dart | 6 +- lib/decred/decred.dart | 64 +++++++++++++++ .../provider/changenow_exchange_provider.dart | 20 +++-- .../provider/exolix_exchange_provider.dart | 23 +++--- .../letsexchange_exchange_provider.dart | 8 +- .../provider/quantex_exchange_provider.dart | 6 +- .../provider/sideshift_exchange_provider.dart | 31 ++++---- .../simpleswap_exchange_provider.dart | 26 ++++--- .../stealth_ex_exchange_provider.dart | 10 ++- .../provider/thorchain_exchange.provider.dart | 22 +++--- .../provider/trocador_exchange_provider.dart | 27 ++++--- lib/mastodon/mastodon_api.dart | 11 ++- lib/twitter/twitter_api.dart | 6 +- lib/utils/proxy_wrapper.dart | 77 ++++--------------- .../dashboard/dashboard_view_model.dart | 11 +-- .../dashboard/home_settings_view_model.dart | 24 +++--- lib/view_model/dashboard/nft_view_model.dart | 16 ++-- .../exchange/exchange_view_model.dart | 8 +- pubspec_base.yaml | 9 ++- 31 files changed, 344 insertions(+), 261 deletions(-) create mode 100644 lib/decred/decred.dart diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 9a7e45d05a..5156c659a3 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:cw_core/utils/http_client.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:cw_bitcoin/litecoin_wallet.dart'; @@ -491,11 +492,12 @@ abstract class ElectrumWalletBase Future updateFeeRates() async { if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) { try { - final response = await http - .get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended")) - .timeout(Duration(seconds: 5)); - - final result = json.decode(response.body) as Map; + final req = await getHttpClient() + .getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended")) + .timeout(Duration(seconds: 15)); + final response = await req.close(); + final stringData = await response.transform(utf8.decoder).join(); + final result = json.decode(stringData) as Map; final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0; int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0; int fastFee = (result['fastestFee'] as num?)?.toInt() ?? 0; @@ -1873,24 +1875,25 @@ abstract class ElectrumWalletBase if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) { try { - final blockHash = await http.get( - Uri.parse( - "https://mempool.cakewallet.com/api/v1/block-height/$height", - ), - ); + final req = await getHttpClient() + .getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height")) + .timeout(Duration(seconds: 15)); + final blockHash = await req.close(); + final stringData = await blockHash.transform(utf8.decoder).join(); if (blockHash.statusCode == 200 && - blockHash.body.isNotEmpty && - jsonDecode(blockHash.body) != null) { - final blockResponse = await http.get( - Uri.parse( - "https://mempool.cakewallet.com/api/v1/block/${blockHash.body}", - ), - ); - if (blockResponse.statusCode == 200 && - blockResponse.body.isNotEmpty && - jsonDecode(blockResponse.body)['timestamp'] != null) { - time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString()); + stringData.isNotEmpty && + jsonDecode(stringData) != null) { + final blockResponseReq = await getHttpClient() + .getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/block/${stringData}")) + .timeout(Duration(seconds: 15)); + final blockResponseRes = await blockResponseReq.close(); + final blockResponse = await blockResponseRes.transform(utf8.decoder).join(); + + if (blockResponseRes == 200 && + blockResponse.isNotEmpty && + jsonDecode(blockResponse)['timestamp'] != null) { + time = int.parse(jsonDecode(blockResponse)['timestamp'].toString()); } } } catch (_) {} diff --git a/cw_core/lib/get_height_by_date.dart b/cw_core/lib/get_height_by_date.dart index aee12b4236..cb9881398d 100644 --- a/cw_core/lib/get_height_by_date.dart +++ b/cw_core/lib/get_height_by_date.dart @@ -1,3 +1,4 @@ +import 'package:cw_core/utils/http_client.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:intl/intl.dart'; import 'dart:convert'; @@ -234,10 +235,14 @@ int getHavenHeightByDate({required DateTime date}) { } Future getHavenCurrentHeight() async { - final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo')); + final req = await getHttpClient() + .getUrl(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo')) + .timeout(Duration(seconds: 15)); + final response = await req.close(); + final stringResponse = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final info = jsonDecode(response.body); + final info = jsonDecode(stringResponse); return info['data']['height'] as int; } else { throw Exception('Failed to load current blockchain height'); @@ -269,13 +274,13 @@ const bitcoinDates = { }; Future getBitcoinHeightByDateAPI({required DateTime date}) async { - final response = await http.get( - Uri.parse( - "https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}", - ), - ); + final req = await getHttpClient() + .getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}")) + .timeout(Duration(seconds: 15)); + final response = await req.close(); + final stringResponse = await response.transform(utf8.decoder).join(); - return jsonDecode(response.body)['height'] as int; + return jsonDecode(stringResponse)['height'] as int; } int getBitcoinHeightByDate({required DateTime date}) { diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index d1e1657ab7..1a95e4cb79 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -307,13 +307,14 @@ class Node extends HiveObject with Keyable { Future requestEthereumServer() async { try { - final response = await http.get( - uri, - headers: {'Content-Type': 'application/json'}, - ); + final req = await getHttpClient() + .getUrl(uri,) + .timeout(Duration(seconds: 15)); + final response = await req.close(); return response.statusCode >= 200 && response.statusCode < 300; - } catch (_) { + } catch (err) { + printV("Failed to request ethereum server: $err"); return false; } } diff --git a/lib/anonpay/anonpay_api.dart b/lib/anonpay/anonpay_api.dart index acab662d14..64085c6b00 100644 --- a/lib/anonpay/anonpay_api.dart +++ b/lib/anonpay/anonpay_api.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/anonpay/anonpay_status_response.dart'; import 'package:cake_wallet/core/fiat_conversion_service.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:http/http.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -30,8 +31,9 @@ class AnonPayApi { Future paymentStatus(String id) async { final authority = await _getAuthority(); - final response = await get(Uri.https(authority, "$anonPayStatus/$id")); - final responseJSON = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: Uri.https(authority, "$anonPayStatus/$id")); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as Map; final status = responseJSON['Status'] as String; final fiatAmount = responseJSON['Fiat_Amount'] as double?; final fiatEquiv = responseJSON['Fiat_Equiv'] as String?; @@ -71,9 +73,9 @@ class AnonPayApi { } final authority = await _getAuthority(); - final response = await get(Uri.https(authority, anonPayPath, body)); - - final responseJSON = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: Uri.https(authority, anonPayPath, body)); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as Map; final id = responseJSON['ID'] as String; final url = responseJSON['url'] as String; final urlOnion = responseJSON['url_onion'] as String; @@ -149,14 +151,13 @@ class AnonPayApi { final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, coinPath, params); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as List; if (response.statusCode != 200) { throw Exception('Unexpected http status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body) as List; - if (responseJSON.isEmpty) { throw Exception('No data'); } @@ -204,7 +205,7 @@ class AnonPayApi { return onionApiAuthority; } final uri = Uri.https(onionApiAuthority, '/anonpay'); - await get(uri); + await ProxyWrapper().get(clearnetUri: uri); return onionApiAuthority; } catch (e) { return clearNetAuthority; diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart index c11a0d7719..f79f9071bf 100644 --- a/lib/buy/dfx/dfx_buy_provider.dart +++ b/lib/buy/dfx/dfx_buy_provider.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -123,10 +124,12 @@ class DFXBuyProvider extends BuyProvider { final url = Uri.https(_baseUrl, '/v1/fiat'); try { - final response = await http.get(url, headers: {'accept': 'application/json'}); - + final response = await ProxyWrapper().get( + clearnetUri: url, + headers: {'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final data = jsonDecode(response.body) as List; + final data = jsonDecode(responseString) as List; for (final item in data) { if (item['name'] == fiatCurrency) return item as Map; } @@ -146,10 +149,10 @@ class DFXBuyProvider extends BuyProvider { final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain}); try { - final response = await http.get(url, headers: {'accept': 'application/json'}); - + final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final responseData = jsonDecode(response.body); + final responseData = jsonDecode(responseString); if (responseData is List && responseData.isNotEmpty) { return responseData.first as Map; diff --git a/lib/buy/meld/meld_buy_provider.dart b/lib/buy/meld/meld_buy_provider.dart index a9759aab8a..7a4b2a3eb5 100644 --- a/lib/buy/meld/meld_buy_provider.dart +++ b/lib/buy/meld/meld_buy_provider.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/buy/payment_method.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -60,8 +61,8 @@ class MeldBuyProvider extends BuyProvider { final url = Uri.https(_baseUrl, path, params); try { - final response = await http.get( - url, + final response = await ProxyWrapper().get( + clearnetUri: url, headers: { 'Authorization': _isProduction ? '' : _testApiKey, 'Meld-Version': '2023-12-19', @@ -69,9 +70,10 @@ class MeldBuyProvider extends BuyProvider { 'content-type': 'application/json', }, ); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final data = jsonDecode(response.body) as List; + final data = jsonDecode(responseString) as List; final paymentMethods = data.map((e) => PaymentMethod.fromMeldJson(e as Map)).toList(); return paymentMethods; diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart index 5794e0794d..cf3be6d0ac 100644 --- a/lib/buy/moonpay/moonpay_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -15,6 +15,7 @@ import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; @@ -110,9 +111,10 @@ class MoonPayProvider extends BuyProvider { final url = Uri.https(_baseUrl, path, params); try { - final response = await get(url, headers: {'accept': 'application/json'}); + final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - return jsonDecode(response.body) as Map; + return jsonDecode(responseString) as Map; } else { printV('MoonPay does not support fiat: $fiatCurrency'); return {}; @@ -180,10 +182,10 @@ class MoonPayProvider extends BuyProvider { final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath'; final url = Uri.https(_baseUrl, path, params); try { - final response = await get(url); - + final response = await ProxyWrapper().get(clearnetUri: url); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final data = jsonDecode(response.body) as Map; + final data = jsonDecode(responseString) as Map; // Check if the response is for the correct fiat currency if (isBuyAction) { @@ -300,13 +302,14 @@ class MoonPayProvider extends BuyProvider { Future findOrderById(String id) async { final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey; final uri = Uri.parse(url); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { throw BuyException(title: providerDescription, content: 'Transaction $id is not found!'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final status = responseJSON['status'] as String; final state = TradeState.deserialize(raw: status); final createdAtRaw = responseJSON['createdAt'] as String; diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart index f229cb833a..910d1ff2e1 100644 --- a/lib/buy/onramper/onramper_buy_provider.dart +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -59,10 +60,10 @@ class OnRamperBuyProvider extends BuyProvider { try { final response = - await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'}); - + await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final Map data = jsonDecode(response.body) as Map; + final Map data = jsonDecode(responseString) as Map; final List message = data['message'] as List; return message .map((item) => PaymentMethod.fromOnramperJson(item as Map)) @@ -82,10 +83,11 @@ class OnRamperBuyProvider extends BuyProvider { try { final response = - await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'}); + await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final Map data = jsonDecode(response.body) as Map; + final Map data = jsonDecode(responseString) as Map; final List onramps = data['message'] as List; @@ -147,10 +149,10 @@ class OnRamperBuyProvider extends BuyProvider { final headers = {'Authorization': _apiKey, 'accept': 'application/json'}; try { - final response = await http.get(url, headers: headers); - + final response = await ProxyWrapper().get(clearnetUri: url, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 200) { - final data = jsonDecode(response.body) as List; + final data = jsonDecode(responseString) as List; if (data.isEmpty) return null; List validQuotes = []; diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart index 937af03154..18e7ec273b 100644 --- a/lib/buy/robinhood/robinhood_buy_provider.dart +++ b/lib/buy/robinhood/robinhood_buy_provider.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -165,8 +166,9 @@ class RobinhoodBuyProvider extends BuyProvider { Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams); try { - final response = await http.get(uri, headers: {'accept': 'application/json'}); - final responseData = jsonDecode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'}); + final responseString = await response.transform(utf8.decoder).join(); + final responseData = jsonDecode(responseString) as Map; if (response.statusCode == 200) { final paymentType = _getPaymentTypeByString(responseData['paymentMethod'] as String?); diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart index 78b109ac0e..911dd0db0c 100644 --- a/lib/buy/wyre/wyre_buy_provider.dart +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:cake_wallet/buy/buy_exception.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/buy/buy_amount.dart'; @@ -113,13 +114,13 @@ class WyreBuyProvider extends BuyProvider { Future findOrderById(String id) async { final orderUrl = baseApiUrl + _ordersSuffix + '/$id'; final orderUri = Uri.parse(orderUrl); - final orderResponse = await get(orderUri); - + final orderResponse = await ProxyWrapper().get(clearnetUri: orderUri); + final responseString = await orderResponse.transform(utf8.decoder).join(); if (orderResponse.statusCode != 200) { throw BuyException(title: providerDescription, content: 'Order $id is not found!'); } - final orderResponseJSON = json.decode(orderResponse.body) as Map; + final orderResponseJSON = json.decode(responseString) as Map; final transferId = orderResponseJSON['transferId'] as String; final from = orderResponseJSON['sourceCurrency'] as String; final to = orderResponseJSON['destCurrency'] as String; @@ -130,13 +131,13 @@ class WyreBuyProvider extends BuyProvider { final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix; final transferUri = Uri.parse(transferUrl); - final transferResponse = await get(transferUri); - + final transferResponse = await ProxyWrapper().get(clearnetUri: transferUri); + final transferResponseString = await transferResponse.transform(utf8.decoder).join(); if (transferResponse.statusCode != 200) { throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!'); } - final transferResponseJSON = json.decode(transferResponse.body) as Map; + final transferResponseJSON = json.decode(transferResponseString) as Map; final amount = transferResponseJSON['destAmount'] as double; return Order( diff --git a/lib/cake_pay/cake_pay_api.dart b/lib/cake_pay/cake_pay_api.dart index 5f1a350c00..b88bcf8eb3 100644 --- a/lib/cake_pay/cake_pay_api.dart +++ b/lib/cake_pay/cake_pay_api.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:cake_wallet/cake_pay/cake_pay_order.dart'; import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart'; import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cake_wallet/entities/country.dart'; import 'package:http/http.dart' as http; @@ -145,7 +146,8 @@ class CakePayApi { 'X-CSRFToken': CSRFToken, }; - final response = await http.get(uri, headers: headers); + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); printV('Response: ${response.statusCode}'); @@ -153,7 +155,7 @@ class CakePayApi { throw Exception('Unexpected http status: ${response.statusCode}'); } - final bodyJson = json.decode(response.body) as Map; + final bodyJson = json.decode(responseString) as Map; throw Exception('You just bot a gift card with id: ${bodyJson['order_id']}'); } @@ -187,13 +189,13 @@ class CakePayApi { 'Authorization': 'Api-Key $apiKey', }; - final response = await http.get(uri, headers: headers); - + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { throw Exception('Unexpected http status: ${response.statusCode}'); } - final bodyJson = json.decode(response.body) as List; + final bodyJson = json.decode(responseString) as List; return bodyJson .map((country) => country['name'] as String) .map((name) => Country.fromCakePayName(name)) @@ -234,14 +236,15 @@ class CakePayApi { 'Authorization': 'Api-Key $apiKey', }; - var response = await http.get(uri, headers: headers); + var response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { throw Exception( - 'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}'); + 'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${responseString}'); } - final bodyJson = json.decode(utf8.decode(response.bodyBytes)); + final bodyJson = json.decode(responseString); if (bodyJson is List && bodyJson.isEmpty) { return []; diff --git a/lib/core/fiat_conversion_service.dart b/lib/core/fiat_conversion_service.dart index 8a37175b45..713ae587d8 100644 --- a/lib/core/fiat_conversion_service.dart +++ b/lib/core/fiat_conversion_service.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'dart:convert'; @@ -31,13 +32,14 @@ Future _fetchPrice(Map args) async { uri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams); } - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { return 0.0; } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final results = responseJSON['results'] as Map; if (results.isNotEmpty) { diff --git a/lib/core/yat_service.dart b/lib/core/yat_service.dart index 92e81e5eff..e067058b74 100644 --- a/lib/core/yat_service.dart +++ b/lib/core/yat_service.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:cake_wallet/entities/yat_record.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:http/http.dart'; class YatService { @@ -33,8 +34,9 @@ class YatService { final yatRecords = []; try { - final response = await get(uri); - final resBody = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + final resBody = json.decode(responseString) as Map; final results = resBody["result"] as Map; // Favour a subaddress over a standard address. final yatRecord = ( diff --git a/lib/decred/decred.dart b/lib/decred/decred.dart new file mode 100644 index 0000000000..87125d7f73 --- /dev/null +++ b/lib/decred/decred.dart @@ -0,0 +1,64 @@ +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/output_info.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cw_core/unspent_transaction_output.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cake_wallet/view_model/send/output.dart'; +import 'package:hive/hive.dart'; + +import 'package:cw_decred/transaction_priority.dart'; +import 'package:cw_decred/wallet.dart'; +import 'package:cw_decred/wallet_service.dart'; +import 'package:cw_decred/wallet_creation_credentials.dart'; +import 'package:cw_decred/amount_format.dart'; +import 'package:cw_decred/transaction_credentials.dart'; +import 'package:cw_decred/mnemonic.dart'; + +part 'cw_decred.dart'; + +Decred? decred = CWDecred(); + + +abstract class Decred { + WalletCredentials createDecredNewWalletCredentials( + {required String name, WalletInfo? walletInfo}); + WalletCredentials createDecredRestoreWalletFromSeedCredentials( + {required String name, + required String mnemonic, + required String password}); + WalletCredentials createDecredRestoreWalletFromPubkeyCredentials( + {required String name, + required String pubkey, + required String password}); + WalletService createDecredWalletService(Box walletInfoSource, + Box unspentCoinSource); + + List getTransactionPriorities(); + TransactionPriority getMediumTransactionPriority(); + TransactionPriority getDecredTransactionPriorityMedium(); + TransactionPriority getDecredTransactionPrioritySlow(); + TransactionPriority deserializeDecredTransactionPriority(int raw); + + int getFeeRate(Object wallet, TransactionPriority priority); + Object createDecredTransactionCredentials( + List outputs, TransactionPriority priority); + + List getAddresses(Object wallet); + String getAddress(Object wallet); + Future generateNewAddress(Object wallet); + + String formatterDecredAmountToString({required int amount}); + double formatterDecredAmountToDouble({required int amount}); + int formatterStringDoubleToDecredAmount(String amount); + + List getUnspents(Object wallet); + void updateUnspents(Object wallet); + + int heightByDate(DateTime date); + + List getDecredWordList(); + + String pubkey(Object wallet); +} diff --git a/lib/exchange/provider/changenow_exchange_provider.dart b/lib/exchange/provider/changenow_exchange_provider.dart index 79f8d70d4c..ae13da03b0 100644 --- a/lib/exchange/provider/changenow_exchange_provider.dart +++ b/lib/exchange/provider/changenow_exchange_provider.dart @@ -13,6 +13,7 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/distribution_info.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -73,10 +74,11 @@ class ChangeNowExchangeProvider extends ExchangeProvider { 'flow': _getFlow(isFixedRateMode) }; final uri = Uri.https(apiAuthority, rangePath, params); - final response = await get(uri, headers: headers); + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error'] as String; final message = responseJSON['message'] as String; throw Exception('${error}\n$message'); @@ -85,7 +87,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; return Limits( min: responseJSON['minAmount'] as double?, max: responseJSON['maxAmount'] as double?); } @@ -118,8 +120,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider { params['fromAmount'] = amount.toString(); final uri = Uri.https(apiAuthority, estimatedAmountPath, params); - final response = await get(uri, headers: headers); - final responseJSON = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as Map; final fromAmount = double.parse(responseJSON['fromAmount'].toString()); final toAmount = double.parse(responseJSON['toAmount'].toString()); final rateId = responseJSON['rateId'] as String? ?? ''; @@ -220,12 +223,13 @@ class ChangeNowExchangeProvider extends ExchangeProvider { final headers = {apiHeaderKey: apiKey}; final params = {'id': id}; final uri = Uri.https(apiAuthority, findTradeByIdPath, params); - final response = await get(uri, headers: headers); + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description); if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['message'] as String; throw TradeNotFoundException(id, provider: description, description: error); @@ -234,7 +238,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final fromCurrency = responseJSON['fromCurrency'] as String; final from = CryptoCurrency.fromString(fromCurrency); final toCurrency = responseJSON['toCurrency'] as String; diff --git a/lib/exchange/provider/exolix_exchange_provider.dart b/lib/exchange/provider/exolix_exchange_provider.dart index ee5a46bbc3..e3c73cd7d1 100644 --- a/lib/exchange/provider/exolix_exchange_provider.dart +++ b/lib/exchange/provider/exolix_exchange_provider.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:http/http.dart'; @@ -86,15 +87,16 @@ class ExolixExchangeProvider extends ExchangeProvider { // Maximum of 2 attempts to fetch limits for (int i = 0; i < 2; i++) { final uri = Uri.https(apiBaseUrl, ratePath, params); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + if (response.statusCode == 200) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final minAmount = responseJSON['minAmount']; final maxAmount = responseJSON['maxAmount']; return Limits(min: _toDouble(minAmount), max: _toDouble(maxAmount)); } else if (response.statusCode == 422) { - final errorResponse = json.decode(response.body) as Map; + final errorResponse = json.decode(responseString) as Map; if (errorResponse.containsKey('minAmount')) { params['amount'] = errorResponse['minAmount'].toString(); continue; @@ -133,8 +135,9 @@ class ExolixExchangeProvider extends ExchangeProvider { params['amount'] = amount.toString(); final uri = Uri.https(apiBaseUrl, ratePath, params); - final response = await get(uri); - final responseJSON = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as Map; if (response.statusCode != 200) { final message = responseJSON['message'] as String?; @@ -214,12 +217,12 @@ class ExolixExchangeProvider extends ExchangeProvider { Future findTradeById({required String id}) async { final findTradeByIdPath = '$transactionsPath/$id'; final uri = Uri.https(apiBaseUrl, findTradeByIdPath); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description); if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final errors = responseJSON['errors'] as Map; final errorMessage = errors.values.join(', '); @@ -229,7 +232,7 @@ class ExolixExchangeProvider extends ExchangeProvider { if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final coinFrom = responseJSON['coinFrom']['coinCode'] as String; final coinTo = responseJSON['coinTo']['coinCode'] as String; final inputAddress = responseJSON['depositAddress'] as String; diff --git a/lib/exchange/provider/letsexchange_exchange_provider.dart b/lib/exchange/provider/letsexchange_exchange_provider.dart index 95520d5f0a..a6cb7785c4 100644 --- a/lib/exchange/provider/letsexchange_exchange_provider.dart +++ b/lib/exchange/provider/letsexchange_exchange_provider.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:http/http.dart' as http; @@ -217,12 +218,13 @@ class LetsExchangeExchangeProvider extends ExchangeProvider { }; final url = Uri.https(_baseUrl, '$_getTransactionPath/$id'); - final response = await http.get(url, headers: headers); + final response = await ProxyWrapper().get(clearnetUri: url, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { - throw Exception('LetsExchange fetch trade failed: ${response.body}'); + throw Exception('LetsExchange fetch trade failed: ${responseString}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final from = responseJSON['coin_from'] as String; final to = responseJSON['coin_to'] as String; final payoutAddress = responseJSON['withdrawal'] as String; diff --git a/lib/exchange/provider/quantex_exchange_provider.dart b/lib/exchange/provider/quantex_exchange_provider.dart index ee34733605..931e777318 100644 --- a/lib/exchange/provider/quantex_exchange_provider.dart +++ b/lib/exchange/provider/quantex_exchange_provider.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:http/http.dart'; @@ -66,9 +67,10 @@ class QuantexExchangeProvider extends ExchangeProvider { }) async { try { final uri = Uri.https(apiAuthority, getCoins); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); diff --git a/lib/exchange/provider/sideshift_exchange_provider.dart b/lib/exchange/provider/sideshift_exchange_provider.dart index 7373d5f2d1..58c707602d 100644 --- a/lib/exchange/provider/sideshift_exchange_provider.dart +++ b/lib/exchange/provider/sideshift_exchange_provider.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:http/http.dart'; @@ -59,10 +60,10 @@ class SideShiftExchangeProvider extends ExchangeProvider { Future checkIsAvailable() async { const url = apiBaseUrl + permissionPath; final uri = Uri.parse(url); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 500) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error']['message'] as String; throw Exception('$error'); @@ -70,7 +71,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { if (response.statusCode != 200) return false; - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; return responseJSON['createShift'] as bool; } @@ -89,10 +90,11 @@ class SideShiftExchangeProvider extends ExchangeProvider { "$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork"; final uri = Uri.parse(url); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 500) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error']['message'] as String; throw Exception('$error'); @@ -102,7 +104,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { throw Exception('Unexpected http status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final min = double.tryParse(responseJSON['min'] as String? ?? ''); final max = double.tryParse(responseJSON['max'] as String? ?? ''); @@ -136,11 +138,12 @@ class SideShiftExchangeProvider extends ExchangeProvider { "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount"; final uri = Uri.parse(url); - final response = await get(uri); - final responseJSON = json.decode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + final responseJSON = json.decode(responseString) as Map; if (response.statusCode == 500) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error']['message'] as String; throw Exception('SideShift Internal Server Error: $error'); @@ -226,14 +229,14 @@ class SideShiftExchangeProvider extends ExchangeProvider { Future findTradeById({required String id}) async { final url = apiBaseUrl + orderPath + '/' + id; final uri = Uri.parse(url); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 404) { throw TradeNotFoundException(id, provider: description); } if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error']['message'] as String; throw TradeNotFoundException(id, provider: description, description: error); @@ -243,7 +246,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { throw Exception('Unexpected http status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final fromCurrency = responseJSON['depositCoin'] as String; final toCurrency = responseJSON['settleCoin'] as String; final inputAddress = responseJSON['depositAddress'] as String; diff --git a/lib/exchange/provider/simpleswap_exchange_provider.dart b/lib/exchange/provider/simpleswap_exchange_provider.dart index 2a72ac793f..d4803b8259 100644 --- a/lib/exchange/provider/simpleswap_exchange_provider.dart +++ b/lib/exchange/provider/simpleswap_exchange_provider.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:http/http.dart'; @@ -48,7 +49,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { @override Future checkIsAvailable() async { final uri = Uri.https(apiAuthority, getEstimatePath, {'api_key': apiKey}); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); return !(response.statusCode == 403); } @@ -66,10 +67,11 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { }; final uri = Uri.https(apiAuthority, rangePath, params); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 500) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['message'] as String; throw Exception('$error'); @@ -79,7 +81,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { throw Exception('Unexpected http status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final min = double.tryParse(responseJSON['min'] as String? ?? ''); final max = double.tryParse(responseJSON['max'] as String? ?? ''); @@ -104,11 +106,12 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { 'fixed': isFixedRateMode.toString() }; final uri = Uri.https(apiAuthority, getEstimatePath, params); - final response = await get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); - if (response.body == "null") return 0.00; + if (responseString == "null") return 0.00; - final data = json.decode(response.body) as String; + final data = json.decode(responseString) as String; return double.parse(data) / amount; } catch (_) { @@ -176,14 +179,15 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { Future findTradeById({required String id}) async { final params = {'api_key': apiKey, 'id': id}; final uri = Uri.https(apiAuthority, getExchangePath, params); - final response = await get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + if (response.statusCode == 404) { throw TradeNotFoundException(id, provider: description); } if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['message'] as String; throw TradeNotFoundException(id, provider: description, description: error); @@ -193,7 +197,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { throw Exception('Unexpected http status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final fromCurrency = responseJSON['currency_from'] as String; final toCurrency = responseJSON['currency_to'] as String; final inputAddress = responseJSON['address_from'] as String; diff --git a/lib/exchange/provider/stealth_ex_exchange_provider.dart b/lib/exchange/provider/stealth_ex_exchange_provider.dart index 53c40ee62c..b24c6f37f9 100644 --- a/lib/exchange/provider/stealth_ex_exchange_provider.dart +++ b/lib/exchange/provider/stealth_ex_exchange_provider.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:http/http.dart' as http; @@ -202,12 +203,13 @@ class StealthExExchangeProvider extends ExchangeProvider { final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'}; final uri = Uri.parse('$_baseUrl$_exchangesPath/$id'); - final response = await http.get(uri, headers: headers); - + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers); + final responseString = await response.transform(utf8.decoder).join(); + if (response.statusCode != 200) { - throw Exception('StealthEx fetch trade failed: ${response.body}'); + throw Exception('StealthEx fetch trade failed: ${responseString}'); } - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final deposit = responseJSON['deposit'] as Map; final withdrawal = responseJSON['withdrawal'] as Map; diff --git a/lib/exchange/provider/thorchain_exchange.provider.dart b/lib/exchange/provider/thorchain_exchange.provider.dart index aa7ab2d274..ebe12c1413 100644 --- a/lib/exchange/provider/thorchain_exchange.provider.dart +++ b/lib/exchange/provider/thorchain_exchange.provider.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:hive/hive.dart'; @@ -164,7 +165,8 @@ class ThorChainExchangeProvider extends ExchangeProvider { if (id.isEmpty) throw Exception('Trade id is empty'); final formattedId = id.startsWith('0x') ? id.substring(2) : id; final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId'); - final response = await http.get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode == 404) { throw Exception('Trade not found for id: $formattedId'); @@ -172,7 +174,7 @@ class ThorChainExchangeProvider extends ExchangeProvider { throw Exception('Unexpected HTTP status: ${response.statusCode}'); } - final responseJSON = json.decode(response.body); + final responseJSON = json.decode(responseString); final Map stagesJson = responseJSON['stages'] as Map; final inboundObservedStarted = stagesJson['inbound_observed']?['started'] as bool? ?? true; @@ -217,13 +219,13 @@ class ThorChainExchangeProvider extends ExchangeProvider { static Future?>? lookupAddressByName(String name) async { final uri = Uri.https(_baseURL, '$_nameLookUpPath$name'); - final response = await http.get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { return null; } - final body = json.decode(response.body) as Map; + final body = json.decode(responseString) as Map; final entries = body['entries'] as List?; if (entries == null || entries.isEmpty) { @@ -244,17 +246,17 @@ class ThorChainExchangeProvider extends ExchangeProvider { Future> _getSwapQuote(Map params) async { Uri uri = Uri.https(_baseNodeURL, _quotePath, params); - final response = await http.get(uri); - + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { throw Exception('Unexpected HTTP status: ${response.statusCode}'); } - if (response.body.contains('error')) { - throw Exception('Unexpected response: ${response.body}'); + if (responseString.contains('error')) { + throw Exception('Unexpected response: ${responseString}'); } - return json.decode(response.body) as Map; + return json.decode(responseString) as Map; } String _normalizeCurrency(CryptoCurrency currency) { diff --git a/lib/exchange/provider/trocador_exchange_provider.dart b/lib/exchange/provider/trocador_exchange_provider.dart index 151ded371c..08ca4ff2e1 100644 --- a/lib/exchange/provider/trocador_exchange_provider.dart +++ b/lib/exchange/provider/trocador_exchange_provider.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:http/http.dart'; @@ -95,12 +96,13 @@ class TrocadorExchangeProvider extends ExchangeProvider { }; final uri = await _getUri(coinPath, params); - final response = await get(uri, headers: {'API-Key': apiKey}); + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); - final responseJSON = json.decode(response.body) as List; + final responseJSON = json.decode(responseString) as List; if (responseJSON.isEmpty) throw Exception('No data'); @@ -135,9 +137,10 @@ class TrocadorExchangeProvider extends ExchangeProvider { }; final uri = await _getUri(newRatePath, params); - final response = await get(uri, headers: {'API-Key': apiKey}); + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}); + final responseString = await response.transform(utf8.decoder).join(); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final fromAmount = double.parse(responseJSON['amount_from'].toString()); final toAmount = double.parse(responseJSON['amount_to'].toString()); final rateId = responseJSON['trade_id'] as String? ?? ''; @@ -202,10 +205,11 @@ class TrocadorExchangeProvider extends ExchangeProvider { params['provider'] = firstAvailableProvider; final uri = await _getUri(createTradePath, params); - final response = await get(uri, headers: {'API-Key': apiKey}); - + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}); + final responseString = await response.transform(utf8.decoder).join(); + if (response.statusCode == 400) { - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final error = responseJSON['error'] as String; final message = responseJSON['message'] as String; throw Exception('${error}\n$message'); @@ -214,7 +218,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); - final responseJSON = json.decode(response.body) as Map; + final responseJSON = json.decode(responseString) as Map; final id = responseJSON['trade_id'] as String; final inputAddress = responseJSON['address_provider'] as String; final refundAddress = responseJSON['refund_address'] as String; @@ -249,11 +253,12 @@ class TrocadorExchangeProvider extends ExchangeProvider { @override Future findTradeById({required String id}) async { final uri = await _getUri(tradePath, {'id': id}); - return get(uri, headers: {'API-Key': apiKey}).then((response) { + return ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}).then((response) async { if (response.statusCode != 200) throw Exception('Unexpected http status: ${response.statusCode}'); + final responseString = await response.transform(utf8.decoder).join(); - final responseListJson = json.decode(response.body) as List; + final responseListJson = json.decode(responseString) as List; final responseJSON = responseListJson.first; final id = responseJSON['trade_id'] as String; final payoutAddress = responseJSON['address_user'] as String; @@ -331,7 +336,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { if (useTorOnly) return uri; try { - await get(uri); + await ProxyWrapper().get(clearnetUri: uri); return uri; } catch (e) { diff --git a/lib/mastodon/mastodon_api.dart b/lib/mastodon/mastodon_api.dart index a2fdc97bdc..4e8787ad6f 100644 --- a/lib/mastodon/mastodon_api.dart +++ b/lib/mastodon/mastodon_api.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:http/http.dart' as http; import 'package:cake_wallet/mastodon/mastodon_user.dart'; @@ -20,11 +21,12 @@ class MastodonAPI { queryParameters: queryParams, ); - final response = await http.get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) return null; - final Map responseJSON = json.decode(response.body) as Map; + final Map responseJSON = json.decode(responseString) as Map; return MastodonUser.fromJson(responseJSON); } catch (e) { @@ -47,13 +49,14 @@ class MastodonAPI { queryParameters: queryParams, ); - final response = await http.get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); if (response.statusCode != 200) { throw Exception('Unexpected HTTP status: ${response.statusCode}'); } - final List responseJSON = json.decode(response.body) as List; + final List responseJSON = json.decode(responseString) as List; return responseJSON.map((json) => PinnedPost.fromJson(json as Map)).toList(); } catch (e) { diff --git a/lib/twitter/twitter_api.dart b/lib/twitter/twitter_api.dart index 5acb00e2a0..2e19882b22 100644 --- a/lib/twitter/twitter_api.dart +++ b/lib/twitter/twitter_api.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/twitter/twitter_user.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:http/http.dart' as http; class TwitterApi { @@ -23,15 +24,16 @@ class TwitterApi { path: userPath + userName, queryParameters: queryParams); - final response = await http.get(uri, headers: headers).catchError((error) { + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers).catchError((error) { throw Exception('HTTP request failed: $error'); }); if (response.statusCode != 200) { throw Exception('Unexpected http status: ${response.statusCode}'); } + final responseString = await response.transform(utf8.decoder).join(); - final Map responseJSON = jsonDecode(response.body) as Map; + final Map responseJSON = jsonDecode(responseString) as Map; if (responseJSON['errors'] != null && !responseJSON['errors'][0]['detail'] .toString() diff --git a/lib/utils/proxy_wrapper.dart b/lib/utils/proxy_wrapper.dart index 0160eceb5a..96a933d9ad 100644 --- a/lib/utils/proxy_wrapper.dart +++ b/lib/utils/proxy_wrapper.dart @@ -1,51 +1,34 @@ import 'dart:io'; 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:socks5_proxy/socks.dart'; +import 'package:tor/tor.dart'; class ProxyWrapper { ProxyWrapper({ this.settingsStore, - this.torViewModel, }); SettingsStore? settingsStore; - TorViewModel? torViewModel; - HttpClient? _torClient; - int getPort() { - TorConnectionMode mode = settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; - if (mode == TorConnectionMode.disabled) { - return -1; - } - return torViewModel?.torInstance.port ?? -1; - } - - bool started = false; + int getPort() => Tor.instance.port; - Future getProxyHttpClient({int? portOverride}) async { - if (portOverride == -1 || portOverride == null) { - portOverride = torViewModel?.torInstance.port ?? -1; - } - - if (!started) { - started = true; - _torClient = HttpClient(); + HttpClient getProxyHttpClient({int? portOverride}) { + final torClient = HttpClient(); + if (Tor.instance.started) { // Assign connection factory. - SocksTCPClient.assignToHttpClient(_torClient!, [ + SocksTCPClient.assignToHttpClient(torClient, [ ProxySettings( InternetAddress.loopbackIPv4, - portOverride, + portOverride ?? getPort(), password: null, ), ]); } - return _torClient!; + return torClient; } Future makeGet({ @@ -79,31 +62,18 @@ class ProxyWrapper { Future get({ Map? headers, int? portOverride, - TorConnectionMode? torConnectionMode, - TorConnectionStatus? torConnectionStatus, Uri? clearnetUri, Uri? onionUri, }) async { HttpClient? torClient; - late bool torEnabled; - torConnectionMode ??= settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; - torConnectionStatus ??= torViewModel?.torConnectionStatus ?? TorConnectionStatus.disconnected; + bool torEnabled = Tor.instance.started; - if (torConnectionMode == TorConnectionMode.torOnly || - torConnectionMode == TorConnectionMode.enabled) { + if (Tor.instance.started) { torEnabled = true; } else { torEnabled = false; } - if (torEnabled) { - torConnectionMode = TorConnectionMode.torOnly; - } - - if (torEnabled && torConnectionStatus == TorConnectionStatus.connecting) { - throw Exception("Tor is still connecting"); - } - // if tor is enabled, try to connect to the onion url first: if (torEnabled) { try { @@ -131,7 +101,7 @@ class ProxyWrapper { } } - if (clearnetUri != null && torConnectionMode != TorConnectionMode.torOnly) { + if (clearnetUri != null) { try { return HttpOverrides.runZoned( () async { @@ -155,32 +125,11 @@ class ProxyWrapper { Future post({ Map? headers, int? portOverride, - TorConnectionMode? torConnectionMode, - TorConnectionStatus? torConnectionStatus, Uri? clearnetUri, Uri? onionUri, }) async { HttpClient? torClient; - late bool torEnabled; - torConnectionMode ??= settingsStore?.torConnectionMode ?? TorConnectionMode.disabled; - torConnectionStatus ??= torViewModel?.torConnectionStatus ?? TorConnectionStatus.disconnected; - - if (torConnectionMode == TorConnectionMode.torOnly || - torConnectionMode == TorConnectionMode.enabled) { - torEnabled = true; - } else { - torEnabled = false; - } - - if (torEnabled) { - torConnectionMode = TorConnectionMode.torOnly; - } - - if (torEnabled && torConnectionStatus == TorConnectionStatus.connecting) { - throw Exception("Tor is still connecting"); - } - - // if tor is enabled, try to connect to the onion url first: + bool torEnabled = Tor.instance.started; if (torEnabled) { try { @@ -208,7 +157,7 @@ class ProxyWrapper { } } - if (clearnetUri != null && torConnectionMode != TorConnectionMode.torOnly) { + if (clearnetUri != null) { try { return HttpOverrides.runZoned( () async { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index c90918d7ff..6b9d5d239c 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -13,6 +13,7 @@ import 'package:cake_wallet/entities/service_status.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/utils/tor.dart'; import 'package:cake_wallet/wownero/wownero.dart' as wow; import 'package:cake_wallet/nano/nano.dart'; @@ -829,21 +830,21 @@ abstract class DashboardViewModelBase with Store { {'key': secrets.fiatApiKey}, ); - final res = await http.get(uri); - + final res = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await res.transform(utf8.decoder).join(); if (res.statusCode < 200 || res.statusCode >= 300) { - throw res.body; + throw responseString; } final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey); - final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body)); + final hash = await Cryptography.instance.sha256().hash(utf8.encode(responseString)); final currentSha = bytesToHex(hash.bytes); final hasUpdates = oldSha != currentSha; return ServicesResponse.fromJson( - json.decode(res.body) as Map, + json.decode(responseString) as Map, hasUpdates, currentSha, ); diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart index 0c3a611eb2..4984394272 100644 --- a/lib/view_model/dashboard/home_settings_view_model.dart +++ b/lib/view_model/dashboard/home_settings_view_model.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/erc20_token.dart'; @@ -190,15 +191,15 @@ abstract class HomeSettingsViewModelBase with Store { ); try { - final response = await http.get( - uri, + final response = await ProxyWrapper().get( + clearnetUri: uri, headers: { "Accept": "application/json", "X-API-Key": secrets.moralisApiKey, }, ); - - final decodedResponse = jsonDecode(response.body); + final responseString = await response.transform(utf8.decoder).join(); + final decodedResponse = jsonDecode(responseString); final tokenInfo = Erc20TokenInfoMoralis.fromJson(decodedResponse[0] as Map); @@ -258,12 +259,12 @@ abstract class HomeSettingsViewModelBase with Store { ); try { - final response = await http.get(uri); - - final decodedResponse = jsonDecode(response.body) as Map; + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); + final decodedResponse = jsonDecode(responseString) as Map; if (decodedResponse['status'] != '1') { - log('${response.body}\n'); + log('${responseString}\n'); log('${decodedResponse['result']}\n'); return true; } @@ -299,12 +300,13 @@ abstract class HomeSettingsViewModelBase with Store { ); try { - final response = await http.get(uri); + final response = await ProxyWrapper().get(clearnetUri: uri); + final responseString = await response.transform(utf8.decoder).join(); - final decodedResponse = jsonDecode(response.body) as Map; + final decodedResponse = jsonDecode(responseString) as Map; if (decodedResponse['status'] == '0') { - printV('${response.body}\n'); + printV('${responseString}\n'); printV('${decodedResponse['result']}\n'); return true; } diff --git a/lib/view_model/dashboard/nft_view_model.dart b/lib/view_model/dashboard/nft_view_model.dart index f00f929a37..1bd392d7d2 100644 --- a/lib/view_model/dashboard/nft_view_model.dart +++ b/lib/view_model/dashboard/nft_view_model.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:http/http.dart' as http; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; @@ -65,15 +66,16 @@ abstract class NFTViewModelBase with Store { try { isLoading = true; - final response = await http.get( - uri, + final response = await ProxyWrapper().get( + clearnetUri: uri, headers: { "Accept": "application/json", "X-API-Key": secrets.moralisApiKey, }, ); + final responseString = await response.transform(utf8.decoder).join(); - final decodedResponse = jsonDecode(response.body) as Map; + final decodedResponse = jsonDecode(responseString) as Map; final result = WalletNFTsResponseModel.fromJson(decodedResponse).result ?? []; @@ -116,15 +118,15 @@ abstract class NFTViewModelBase with Store { try { isImportNFTLoading = true; - final response = await http.get( - uri, + final response = await ProxyWrapper().get( + clearnetUri: uri, headers: { "Accept": "application/json", "X-API-Key": secrets.moralisApiKey, }, ); - - final decodedResponse = jsonDecode(response.body) as Map; + final responseString = await response.transform(utf8.decoder).join(); + final decodedResponse = jsonDecode(responseString) as Map; final nftAsset = NFTAssetModel.fromJson(decodedResponse); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 63e1db6bca..981510e98d 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -6,6 +6,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cake_wallet/core/create_trade_result.dart'; import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart'; +import 'package:cake_wallet/utils/proxy_wrapper.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -937,15 +938,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with ); try { - final response = await httpClient.get( - uri, + final response = await ProxyWrapper().get( + clearnetUri: uri, headers: { "Accept": "application/json", "X-API-Key": secrets.moralisApiKey, }, ); + final responseString = await response.transform(utf8.decoder).join(); - final decodedResponse = jsonDecode(response.body)[0] as Map; + final decodedResponse = jsonDecode(responseString)[0] as Map; final name = decodedResponse['name'] as String?; diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 84d28f3de6..3870e50c04 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -102,10 +102,7 @@ dependencies: # git: # url: https://github.com/cake-tech/tor.git # ref: main - socks5_proxy: - git: - url: https://github.com/cake-tech/socks_dart.git - ref: d304fcfcc97cb7212bcd347aeb5d96792c128ff3 + socks5_proxy: any flutter_svg: ^2.0.9 polyseed: ^0.0.6 nostr_tools: ^1.0.9 @@ -164,6 +161,10 @@ dependency_overrides: git: url: https://github.com/vespr-wallet/ledger-flutter-plus ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76 + socks5_proxy: + git: + url: https://github.com/cake-tech/socks_dart.git + ref: d304fcfcc97cb7212bcd347aeb5d96792c128ff3 flutter_icons: image_path: "assets/images/app_logo.png" From 7c8f6481ccb7c3c18fb36c603aa8fe9a0c6600c5 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 17 Jan 2025 11:00:59 +0100 Subject: [PATCH 10/10] Enable proxy for http.Client [run tests] --- cw_solana/lib/solana_client.dart | 7 +++++-- cw_tron/lib/tron_http_provider.dart | 10 ++++++---- .../chain_service/eth/evm_chain_service.dart | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cw_solana/lib/solana_client.dart b/cw_solana/lib/solana_client.dart index 431f5f7fbe..33d2fc83ee 100644 --- a/cw_solana/lib/solana_client.dart +++ b/cw_solana/lib/solana_client.dart @@ -4,19 +4,20 @@ import 'dart:math' as math; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/node.dart'; +import 'package:cw_core/utils/http_client.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_solana/pending_solana_transaction.dart'; import 'package:cw_solana/solana_balance.dart'; import 'package:cw_solana/solana_exceptions.dart'; import 'package:cw_solana/solana_transaction_model.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as ioc; import 'package:solana/dto.dart'; import 'package:solana/encoder.dart'; import 'package:solana/solana.dart'; import '.secrets.g.dart' as secrets; class SolanaWalletClient { - final httpClient = http.Client(); SolanaClient? _client; bool connect(Node node) { @@ -636,7 +637,9 @@ class SolanaWalletClient { Future getIconImageFromTokenUri(String uri) async { try { - final response = await httpClient.get(Uri.parse(uri)); + final httpClient = getHttpClient(); + final http.Client client = ioc.IOClient(httpClient); + final response = await client.get(Uri.parse(uri)); final jsonResponse = json.decode(response.body) as Map; diff --git a/cw_tron/lib/tron_http_provider.dart b/cw_tron/lib/tron_http_provider.dart index 8a3301f878..26abdf4855 100644 --- a/cw_tron/lib/tron_http_provider.dart +++ b/cw_tron/lib/tron_http_provider.dart @@ -1,18 +1,20 @@ import 'dart:convert'; +import 'dart:io'; +import 'package:cw_core/utils/http_client.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as ioc; import 'package:on_chain/tron/tron.dart'; import '.secrets.g.dart' as secrets; class TronHTTPProvider implements TronServiceProvider { TronHTTPProvider( {required this.url, - http.Client? client, - this.defaultRequestTimeout = const Duration(seconds: 30)}) - : client = client ?? http.Client(); + this.defaultRequestTimeout = const Duration(seconds: 30)}); @override final String url; - final http.Client client; + final httpClient = getHttpClient(); + late final http.Client client = ioc.IOClient(httpClient); final Duration defaultRequestTimeout; @override diff --git a/lib/core/wallet_connect/chain_service/eth/evm_chain_service.dart b/lib/core/wallet_connect/chain_service/eth/evm_chain_service.dart index 6f3c8fa98a..6ce30a09e5 100644 --- a/lib/core/wallet_connect/chain_service/eth/evm_chain_service.dart +++ b/lib/core/wallet_connect/chain_service/eth/evm_chain_service.dart @@ -15,9 +15,11 @@ import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_widget import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart'; import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart'; import 'package:convert/convert.dart'; +import 'package:cw_core/utils/http_client.dart'; import 'package:eth_sig_util/eth_sig_util.dart'; import 'package:eth_sig_util/util/utils.dart'; import 'package:http/http.dart' as http; +import 'package:http/io_client.dart' as ioc; import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:web3dart/web3dart.dart'; import '../chain_service.dart'; @@ -50,7 +52,7 @@ class EvmChainServiceImpl implements ChainService { }) : ethClient = web3Client ?? Web3Client( appStore.settingsStore.getCurrentNode(appStore.wallet!.type).uri.toString(), - http.Client(), + ioc.IOClient(getHttpClient()), ) { for (final String event in getEvents()) { wallet.registerEventEmitter(chainId: getChainId(), event: event);