Skip to content

Commit 6221c1d

Browse files
fix: validate orbot availability before initiating recoverbull connection
1 parent 1c4fe13 commit 6221c1d

12 files changed

Lines changed: 88 additions & 30 deletions

File tree

lib/core/recoverbull/data/repository/recoverbull_repository.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class RecoverBullRepository {
5050
String salt,
5151
String vaultKey,
5252
) async {
53-
final externalProxy = await torConfigPort.getExternalTorConfig();
53+
final externalProxy = await torConfigPort.getAvailableExternalTorConfig();
5454
await remoteDatasource.store(
5555
HEX.decode(identifier),
5656
utf8.encode(password),
@@ -65,7 +65,7 @@ class RecoverBullRepository {
6565
String password,
6666
String salt,
6767
) async {
68-
final externalProxy = await torConfigPort.getExternalTorConfig();
68+
final externalProxy = await torConfigPort.getAvailableExternalTorConfig();
6969
final vaultKey = await remoteDatasource.fetch(
7070
HEX.decode(identifier),
7171
utf8.encode(password),
@@ -80,7 +80,7 @@ class RecoverBullRepository {
8080
String password,
8181
String salt,
8282
) async {
83-
final externalProxy = await torConfigPort.getExternalTorConfig();
83+
final externalProxy = await torConfigPort.getAvailableExternalTorConfig();
8484
await remoteDatasource.trash(
8585
HEX.decode(identifier),
8686
utf8.encode(password),
@@ -90,7 +90,7 @@ class RecoverBullRepository {
9090
}
9191

9292
Future<void> checkConnection() async {
93-
final externalProxy = await torConfigPort.getExternalTorConfig();
93+
final externalProxy = await torConfigPort.getAvailableExternalTorConfig();
9494
await remoteDatasource.checkConnection(externalProxy: externalProxy);
9595
}
9696

lib/core/recoverbull/recoverbull_locator.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import 'package:bb_mobile/core/settings/domain/repositories/settings_repository.
2929
import 'package:bb_mobile/core/storage/sqlite_database.dart';
3030
import 'package:bb_mobile/core/tor/data/datasources/tor_datasource.dart';
3131
import 'package:bb_mobile/core/tor/domain/ports/tor_config_port.dart';
32+
import 'package:bb_mobile/core/tor/infrastructure/services/tor_connectivity_service.dart';
3233
import 'package:bb_mobile/core/tor/interface_adapters/adapters/tor_config_adapter.dart';
3334
import 'package:bb_mobile/core/wallet/data/repositories/wallet_repository.dart';
3435
import 'package:bb_mobile/core/wallet/domain/usecases/create_default_wallets_usecase.dart';
@@ -69,7 +70,10 @@ class RecoverbullLocator {
6970
);
7071

7172
locator.registerLazySingleton<TorConfigPort>(
72-
() => TorConfigAdapter(settingsRepository: locator<SettingsRepository>()),
73+
() => TorConfigAdapter(
74+
settingsRepository: locator<SettingsRepository>(),
75+
torConnectivityService: locator<TorConnectivityService>(),
76+
),
7377
);
7478

7579
locator.registerSingletonWithDependencies<RecoverBullRepository>(
File renamed without changes.

lib/core/tor/domain/ports/tor_config_port.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ abstract class TorConfigPort {
44
/// Get the Tor proxy configuration
55
/// Returns null if internal Tor should be used
66
/// Returns TorProxyConfig if external Tor proxy is configured
7-
Future<TorProxyConfig?> getExternalTorConfig();
7+
Future<TorProxyConfig?> getAvailableExternalTorConfig();
88
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'package:bb_mobile/core/tor/infrastructure/services/tor_connectivity_service.dart';
2+
import 'package:bb_mobile/core/tor/tor_status.dart';
3+
4+
class CheckTorProxyConnectionUsecase {
5+
final TorConnectivityService _torConnectivityService;
6+
7+
CheckTorProxyConnectionUsecase({
8+
required TorConnectivityService torConnectivityService,
9+
}) : _torConnectivityService = torConnectivityService;
10+
11+
Future<TorStatus> execute(int port) async {
12+
return _torConnectivityService.checkConnection(port);
13+
}
14+
}

lib/features/tor_settings/infrastructure/adapters/socket_adapter.dart renamed to lib/core/tor/infrastructure/adapters/socket_adapter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'dart:async';
22
import 'dart:io';
33

4-
import 'package:bb_mobile/features/tor_settings/domain/ports/socket_port.dart';
4+
import 'package:bb_mobile/core/tor/domain/ports/socket_port.dart';
55

66
class SocketAdapter implements SocketPort {
77
@override

lib/features/tor_settings/domain/usecases/check_tor_proxy_connection_usecase.dart renamed to lib/core/tor/infrastructure/services/tor_connectivity_service.dart

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import 'dart:async';
22
import 'dart:io';
33

4+
import 'package:bb_mobile/core/tor/domain/ports/socket_port.dart';
45
import 'package:bb_mobile/core/tor/tor_status.dart';
56
import 'package:bb_mobile/core/utils/logger.dart';
6-
import 'package:bb_mobile/features/tor_settings/domain/ports/socket_port.dart';
77

8-
class CheckTorProxyConnectionUsecase {
8+
class TorConnectivityService {
99
final SocketPort _socketPort;
1010

11-
const CheckTorProxyConnectionUsecase({required SocketPort socketPort})
11+
// cache to avoid repeated connection checks
12+
TorStatus? _cachedStatus;
13+
int? _cachedPort;
14+
DateTime? _cacheTime;
15+
static const _cacheDuration = Duration(seconds: 30);
16+
17+
TorConnectivityService({required SocketPort socketPort})
1218
: _socketPort = socketPort;
1319

1420
/// Checks if a Tor SOCKS5 proxy is running and accessible on the given port
@@ -19,7 +25,25 @@ class CheckTorProxyConnectionUsecase {
1925
/// 3. The app has permission to connect
2026
///
2127
/// Returns the appropriate [TorStatus] based on the connection check
22-
Future<TorStatus> execute(int port) async {
28+
Future<TorStatus> checkConnection(int port) async {
29+
if (_cachedStatus != null &&
30+
_cachedPort == port &&
31+
_cacheTime != null &&
32+
DateTime.now().difference(_cacheTime!) < _cacheDuration) {
33+
log.config('Using cached Tor proxy status for port $port');
34+
return _cachedStatus!;
35+
}
36+
37+
final status = await _performCheck(port);
38+
39+
_cachedStatus = status;
40+
_cachedPort = port;
41+
_cacheTime = DateTime.now();
42+
43+
return status;
44+
}
45+
46+
Future<TorStatus> _performCheck(int port) async {
2347
try {
2448
// Test SOCKS5 proxy functionality by attempting SOCKS5 handshake
2549
// This verifies both that the port is open AND that our app can use it

lib/core/tor/interface_adapters/adapters/tor_config_adapter.dart

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import 'package:bb_mobile/core/settings/domain/repositories/settings_repository.dart';
22
import 'package:bb_mobile/core/tor/domain/ports/tor_config_port.dart';
33
import 'package:bb_mobile/core/tor/domain/value_objects/tor_proxy_config.dart';
4+
import 'package:bb_mobile/core/tor/infrastructure/services/tor_connectivity_service.dart';
5+
import 'package:bb_mobile/core/tor/tor_status.dart';
46

57
class TorConfigAdapter implements TorConfigPort {
68
final SettingsRepository _settingsRepository;
9+
final TorConnectivityService _torConnectivityService;
710

8-
TorConfigAdapter({required SettingsRepository settingsRepository})
9-
: _settingsRepository = settingsRepository;
11+
TorConfigAdapter({
12+
required SettingsRepository settingsRepository,
13+
required TorConnectivityService torConnectivityService,
14+
}) : _settingsRepository = settingsRepository,
15+
_torConnectivityService = torConnectivityService;
1016

1117
@override
12-
Future<TorProxyConfig?> getExternalTorConfig() async {
18+
Future<TorProxyConfig?> getAvailableExternalTorConfig() async {
1319
final settings = await _settingsRepository.fetch();
1420

15-
if (settings.useTorProxy) {
21+
final status = await _torConnectivityService.checkConnection(
22+
settings.torProxyPort,
23+
);
24+
25+
if (status == TorStatus.online) {
1626
return TorProxyConfig(port: settings.torProxyPort);
1727
}
1828

lib/core/tor/tor_locator.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import 'package:bb_mobile/core/tor/data/repository/tor_repository.dart';
44
import 'package:bb_mobile/core/tor/data/usecases/init_tor_usecase.dart';
55
import 'package:bb_mobile/core/tor/data/usecases/is_tor_required_usecase.dart';
66
import 'package:bb_mobile/core/tor/data/usecases/tor_status_usecase.dart';
7+
import 'package:bb_mobile/core/tor/domain/ports/socket_port.dart';
8+
import 'package:bb_mobile/core/tor/domain/usecases/check_tor_proxy_connection_usecase.dart';
9+
import 'package:bb_mobile/core/tor/infrastructure/adapters/socket_adapter.dart';
10+
import 'package:bb_mobile/core/tor/infrastructure/services/tor_connectivity_service.dart';
711
import 'package:bb_mobile/core/wallet/data/repositories/wallet_repository.dart';
812
import 'package:get_it/get_it.dart';
913

@@ -15,6 +19,11 @@ class TorLocator {
1519
});
1620
}
1721
await locator.isReady<TorDatasource>();
22+
23+
locator.registerLazySingleton<SocketPort>(() => SocketAdapter());
24+
locator.registerLazySingleton<TorConnectivityService>(
25+
() => TorConnectivityService(socketPort: locator<SocketPort>()),
26+
);
1827
}
1928

2029
static Future<void> registerRepositories(GetIt locator) async {
@@ -40,5 +49,11 @@ class TorLocator {
4049
locator.registerFactory<TorStatusUsecase>(
4150
() => TorStatusUsecase(locator<TorRepository>()),
4251
);
52+
53+
locator.registerFactory<CheckTorProxyConnectionUsecase>(
54+
() => CheckTorProxyConnectionUsecase(
55+
torConnectivityService: locator<TorConnectivityService>(),
56+
),
57+
);
4358
}
4459
}

lib/features/recoverbull/presentation/bloc.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ class RecoverBullBloc extends Bloc<RecoverBullEvent, RecoverBullState> {
109109
Emitter<RecoverBullState> emit,
110110
) async {
111111
try {
112-
final externalTorConfig = await _torConfigPort.getExternalTorConfig();
112+
final externalTorConfig = await _torConfigPort
113+
.getAvailableExternalTorConfig();
113114

114115
if (externalTorConfig == null) {
115116
await _initializeTorUsecase.execute();
@@ -253,8 +254,8 @@ class RecoverBullBloc extends Bloc<RecoverBullEvent, RecoverBullState> {
253254
switch (event.provider) {
254255
case VaultProvider.googleDrive:
255256
await _connectToGoogleDriveUsecase.execute();
256-
final encryptedVault =
257-
await _fetchLatestGoogleDriveVaultUsecase.execute();
257+
final encryptedVault = await _fetchLatestGoogleDriveVaultUsecase
258+
.execute();
258259
emit(state.copyWith(vault: encryptedVault));
259260
return;
260261
case VaultProvider.customLocation:

0 commit comments

Comments
 (0)