This commit is contained in:
ryleedavis 2023-09-08 16:53:14 -06:00
commit 126229a38b
6 changed files with 286 additions and 146 deletions

View file

@ -174,6 +174,7 @@ void main() async {
// Some refactoring will need to be done here to make sure we don't make any
// network calls before starting up tor
if (Prefs.instance.useTor) {
TorService.sharedInstance.init();
await TorService.sharedInstance.start();
}

View file

@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -35,7 +36,7 @@ class TorSettingsView extends ConsumerStatefulWidget {
}
class _TorSettingsViewState extends ConsumerState<TorSettingsView> {
TorConnectionStatus _networkStatus = TorConnectionStatus.disconnected;
late TorConnectionStatus _networkStatus;
Widget _buildTorIcon(TorConnectionStatus status) {
switch (status) {
@ -123,6 +124,9 @@ class _TorSettingsViewState extends ConsumerState<TorSettingsView> {
@override
void initState() {
_networkStatus = ref.read(pTorService).enabled
? TorConnectionStatus.connected
: TorConnectionStatus.disconnected;
super.initState();
}
@ -195,7 +199,7 @@ class _TorSettingsViewState extends ConsumerState<TorSettingsView> {
),
],
),
SizedBox(
const SizedBox(
height: 30,
),
RoundedWhiteContainer(
@ -213,7 +217,7 @@ class _TorSettingsViewState extends ConsumerState<TorSettingsView> {
),
),
),
SizedBox(
const SizedBox(
height: 8,
),
RoundedWhiteContainer(

View file

@ -35,6 +35,7 @@ import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
@ -550,14 +551,6 @@ class _WalletNetworkSettingsViewState
"Synchronized",
style: STextStyles.w600_12(context),
),
Text(
"100%",
style: STextStyles.syncPercent(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen,
),
),
],
),
),
@ -788,20 +781,47 @@ class _WalletNetworkSettingsViewState
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.smallMed12(context),
),
if (TorService.sharedInstance.enabled)
if (ref.watch(
prefsChangeNotifierProvider.select((value) => value.useTor)))
GestureDetector(
onTap: () {
TorService.sharedInstance.stop();
onTap: () async {
// Stop the Tor service.
try {
await ref.read(pTorService).stop();
// Toggle the useTor preference on success.
ref.read(prefsChangeNotifierProvider).useTor = false;
} catch (e, s) {
Logging.instance.log(
"Error stopping tor: $e\n$s",
level: LogLevel.Error,
);
}
},
child: Text(
"Disconnect",
style: STextStyles.link2(context),
),
),
if (!TorService.sharedInstance.enabled)
if (!ref.watch(
prefsChangeNotifierProvider.select((value) => value.useTor)))
GestureDetector(
onTap: () {
TorService.sharedInstance.start();
onTap: () async {
// Init the Tor service if it hasn't already been.
ref.read(pTorService).init();
// Start the Tor service.
try {
await ref.read(pTorService).start();
// Toggle the useTor preference on success.
ref.read(prefsChangeNotifierProvider).useTor = true;
} catch (e, s) {
Logging.instance.log(
"Error starting tor: $e\n$s",
level: LogLevel.Error,
);
}
},
child: Text(
"Connect",
@ -813,6 +833,99 @@ class _WalletNetworkSettingsViewState
SizedBox(
height: isDesktop ? 12 : 9,
),
RoundedWhiteContainer(
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.background
: null,
padding:
isDesktop ? const EdgeInsets.all(16) : const EdgeInsets.all(12),
child: Row(
children: [
if (ref.watch(prefsChangeNotifierProvider
.select((value) => value.useTor)))
Container(
width: _iconSize,
height: _iconSize,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen
.withOpacity(0.2),
borderRadius: BorderRadius.circular(_iconSize),
),
child: Center(
child: SvgPicture.asset(
Assets.svg.tor,
height: isDesktop ? 19 : 14,
width: isDesktop ? 19 : 14,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen,
),
),
),
if (!ref.watch(prefsChangeNotifierProvider
.select((value) => value.useTor)))
Container(
width: _iconSize,
height: _iconSize,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textDark
.withOpacity(0.08),
borderRadius: BorderRadius.circular(_iconSize),
),
child: Center(
child: SvgPicture.asset(
Assets.svg.tor,
height: isDesktop ? 19 : 14,
width: isDesktop ? 19 : 14,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
SizedBox(
width: _boxPadding,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Tor status",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
if (_torConnectionStatus == TorConnectionStatus.connected)
Text(
"Connected",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
if (_torConnectionStatus == TorConnectionStatus.connecting)
Text(
"Connecting...",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
if (_torConnectionStatus ==
TorConnectionStatus.disconnected)
Text(
"Disconnected",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
],
),
),
SizedBox(
height: isDesktop ? 32 : 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View file

@ -21,6 +21,7 @@ import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
@ -29,8 +30,6 @@ import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import '../../../../utilities/prefs.dart';
class TorSettings extends ConsumerStatefulWidget {
const TorSettings({Key? key}) : super(key: key);
@ -41,9 +40,6 @@ class TorSettings extends ConsumerStatefulWidget {
}
class _TorSettingsState extends ConsumerState<TorSettings> {
// The Prefs instance.
final Prefs _prefs = Prefs.instance;
/// The global event bus.
EventBus eventBus = GlobalEventBus.instance;
@ -52,8 +48,7 @@ class _TorSettingsState extends ConsumerState<TorSettings> {
_torConnectionStatusSubscription;
/// The current status of the Tor connection.
late TorConnectionStatus _torConnectionStatus =
TorConnectionStatus.disconnected;
late TorConnectionStatus _torConnectionStatus;
/// Build the connect/disconnect button.
Widget _buildConnectButton(TorConnectionStatus status) {
@ -63,12 +58,22 @@ class _TorSettingsState extends ConsumerState<TorSettings> {
label: "Connect to Tor",
width: 200,
buttonHeight: ButtonHeight.m,
onPressed: () {
// Toggle the useTor preference.
_prefs.useTor = true;
onPressed: () async {
// Init the Tor service if it hasn't already been.
ref.read(pTorService).init();
// Start the Tor service.
ref.read(pTorService).start();
try {
await ref.read(pTorService).start();
// Toggle the useTor preference on success.
ref.read(prefsChangeNotifierProvider).useTor = true;
} catch (e, s) {
Logging.instance.log(
"Error starting tor: $e\n$s",
level: LogLevel.Error,
);
}
},
);
case TorConnectionStatus.connecting:
@ -85,12 +90,19 @@ class _TorSettingsState extends ConsumerState<TorSettings> {
label: "Disconnect from Tor",
width: 200,
buttonHeight: ButtonHeight.m,
onPressed: () {
// Toggle the useTor preference.
_prefs.useTor = false;
onPressed: () async {
// Stop the Tor service.
ref.read(pTorService).stop();
try {
await ref.read(pTorService).stop();
// Toggle the useTor preference on success.
ref.read(prefsChangeNotifierProvider).useTor = false;
} catch (e, s) {
Logging.instance.log(
"Error stopping tor: $e\n$s",
level: LogLevel.Error,
);
}
},
);
}
@ -196,63 +208,62 @@ class _TorSettingsState extends ConsumerState<TorSettings> {
recognizer: TapGestureRecognizer()
..onTap = () {
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
DesktopDialogCloseButton(
onPressedOverride: () =>
Navigator.of(context)
.pop(true),
),
],
),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
DesktopDialogCloseButton(
onPressedOverride: () =>
Navigator.of(context)
.pop(true),
Text(
"What is Tor?",
style:
STextStyles.desktopH2(
context),
),
const SizedBox(
height: 20,
),
Text(
"Short for \"The Onion Router\", is an open-source software that enables internet communication"
" to remain anonymous by routing internet traffic through a series of layered nodes,"
" to obscure the origin and destination of data.",
style: STextStyles
.desktopTextMedium(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textDark3,
),
),
],
),
Padding(
padding:
const EdgeInsets.all(20),
child: Column(
mainAxisSize:
MainAxisSize.max,
children: [
Text(
"What is Tor?",
style:
STextStyles.desktopH2(
context),
),
const SizedBox(
height: 20,
),
Text(
"Short for \"The Onion Router\", is an open-source software that enables internet communication"
" to remain anonymous by routing internet traffic through a series of layered nodes,"
" to obscure the origin and destination of data.",
style: STextStyles
.desktopTextMedium(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textDark3,
),
),
],
),
),
],
),
);
});
),
],
),
);
},
);
},
),
],
@ -299,66 +310,64 @@ class _TorSettingsState extends ConsumerState<TorSettings> {
recognizer: TapGestureRecognizer()
..onTap = () {
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
DesktopDialogCloseButton(
onPressedOverride: () =>
Navigator.of(context)
.pop(true),
),
],
),
Padding(
padding:
const EdgeInsets.all(20),
child: Column(
mainAxisSize:
MainAxisSize.max,
children: [
DesktopDialogCloseButton(
onPressedOverride: () =>
Navigator.of(
Text(
"What is Tor killswitch?",
style: STextStyles
.desktopH2(context),
),
const SizedBox(
height: 20,
),
Text(
"A security feature that protects your information from accidental exposure by"
" disconnecting your device from the Tor network if your virtual private network (VPN)"
" connection is disrupted or compromised.",
style: STextStyles
.desktopTextMedium(
context)
.pop(true),
.copyWith(
color: Theme.of(
context)
.extension<
StackColors>()!
.textDark3,
),
),
],
),
Padding(
padding:
const EdgeInsets.all(
20),
child: Column(
mainAxisSize:
MainAxisSize.max,
children: [
Text(
"What is Tor killswitch?",
style: STextStyles
.desktopH2(
context),
),
const SizedBox(
height: 20,
),
Text(
"A security feature that protects your information from accidental exposure by"
" disconnecting your device from the Tor network if your virtual private network (VPN)"
" connection is disrupted or compromised.",
style: STextStyles
.desktopTextMedium(
context)
.copyWith(
color: Theme.of(
context)
.extension<
StackColors>()!
.textDark3,
),
),
],
),
),
],
),
);
});
),
],
),
);
},
);
},
),
],

View file

@ -9,7 +9,7 @@ import 'package:tor/tor.dart';
final pTorService = Provider((_) => TorService.sharedInstance);
class TorService {
final _tor = Tor();
Tor? _tor;
/// Flag to indicate that a Tor circuit is thought to have been established.
bool _enabled = false;
@ -30,9 +30,15 @@ class TorService {
int port,
}) get proxyInfo => (
host: InternetAddress.loopbackIPv4,
port: _tor.port,
port: _tor!.port,
);
/// Initialize the tor ffi lib instance if it hasn't already been set. Nothing
/// changes if _tor is already been set.
void init({Tor? mockableOverride}) {
_tor ??= mockableOverride ?? Tor();
}
/// Start the Tor service.
///
/// This will start the Tor service and establish a Tor circuit.
@ -41,6 +47,10 @@ class TorService {
///
/// Returns a Future that completes when the Tor service has started.
Future<void> start() async {
if (_tor == null) {
throw Exception("TorService.init has not been called!");
}
if (_enabled) {
// already started so just return
// could throw an exception here or something so the caller
@ -58,7 +68,7 @@ class TorService {
"Tor connection status changed: connecting",
),
);
await _tor.start();
await _tor!.start();
// no exception or error so we can (probably?) assume tor
// has started successfully
_enabled = true;
@ -89,6 +99,10 @@ class TorService {
}
Future<void> stop() async {
if (_tor == null) {
throw Exception("TorService.init has not been called!");
}
if (!_enabled) {
// already stopped so just return
// could throw an exception here or something so the caller
@ -99,7 +113,7 @@ class TorService {
// Stop the Tor service.
try {
await _tor.disable();
await _tor!.disable();
// no exception or error so we can (probably?) assume tor
// has started successfully
_enabled = false;

View file

@ -129,7 +129,6 @@ class _DesktopTorStatusButtonState extends ConsumerState<DesktopTorStatusButton>
// Clean up the subscription to the TorConnectionStatusChangedEvent.
_torConnectionStatusSubscription.cancel();
controller?.dispose();
animationController.dispose();
super.dispose();